[
  {
    "path": ".github/workflows/style-clippy.yml",
    "content": "name: Clippy Style Checker\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  style-checker:\n\n    runs-on: \"ubuntu-20.04\"\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Install Clippy\n      run: |\n        rustup update\n        rustup component add clippy\n    - name: Build\n      run: rake build_rs[--verbose] # Only build Rust, not stdlib\n    - name: Run Clippy\n      run: rake clippy[--verbose]\n"
  },
  {
    "path": ".github/workflows/style-docs.yml",
    "content": "name: Rustdoc Generation\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  style-checker:\n\n    runs-on: \"ubuntu-20.04\"\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Generate Documentation\n      run: rake doc\n"
  },
  {
    "path": ".github/workflows/test-Godotv3.3.3-Ubuntu.yml",
    "content": "name: \"Ubuntu 20.04 (Godot v3.3.3)\"\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  test-suite:\n\n    runs-on: \"ubuntu-20.04\"\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Install Godot\n      run: |\n        mkdir godot-build\n        cd godot-build\n        wget https://downloads.tuxfamily.org/godotengine/3.3.3/Godot_v3.3.3-stable_linux_headless.64.zip\n        unzip Godot_v3.3.3-stable_linux_headless.64.zip\n        ln -s Godot_v3.3.3-stable_linux_headless.64 godot\n        ls -l\n        cd ..\n    - name: Build\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake build[--verbose]\n    - name: Run Standard Test Suite\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake 'test[--verbose]'\n"
  },
  {
    "path": ".github/workflows/test-Godotv3.4-Ubuntu.yml",
    "content": "name: \"Ubuntu 20.04 (Godot v3.4)\"\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  test-suite:\n\n    runs-on: \"ubuntu-20.04\"\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Install Godot\n      run: |\n        mkdir godot-build\n        cd godot-build\n        wget https://downloads.tuxfamily.org/godotengine/3.4/Godot_v3.4-stable_linux_headless.64.zip\n        unzip Godot_v3.4-stable_linux_headless.64.zip\n        ln -s Godot_v3.4-stable_linux_headless.64 godot\n        ls -l\n        cd ..\n    - name: Build\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake build[--verbose]\n    - name: Run Standard Test Suite\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake 'test[--verbose]'\n"
  },
  {
    "path": ".github/workflows/test-Godotv3.5-Minimal-Ubuntu.yml",
    "content": "name: \"Ubuntu 20.04 (Godot v3.5 Minimal)\"\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  test-suite:\n\n    runs-on: \"ubuntu-20.04\"\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Install Godot\n      run: |\n        mkdir godot-build\n        cd godot-build\n        wget https://www.dropbox.com/s/9zi0ij7cqetaw34/godot_server.x11.opt.tools.64.zip?dl=1 -O godot_server.x11.opt.tools.64.zip\n        unzip godot_server.x11.opt.tools.64.zip\n        ln -s godot_server.x11.opt.tools.64 godot\n        ls -l\n        cd ..\n    - name: Build\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake build[--verbose]\n    - name: Run Standard Test Suite\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake 'test[--verbose]'\n"
  },
  {
    "path": ".github/workflows/test-Godotv3.5-Mono-Ubuntu.yml",
    "content": "name: \"Ubuntu 20.04 (Godot v3.5 Mono)\"\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  test-suite:\n\n    runs-on: \"ubuntu-20.04\"\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Install Godot\n      run: |\n        mkdir godot-build\n        cd godot-build\n        wget https://downloads.tuxfamily.org/godotengine/3.5/mono/Godot_v3.5-stable_mono_linux_headless_64.zip\n        unzip Godot_v3.5-stable_mono_linux_headless_64.zip\n        ln -s Godot_v3.5-stable_mono_linux_headless_64/Godot_v3.5-stable_mono_linux_headless.64 godot\n        ls -l .\n        ls -l Godot_v3.5-stable_mono_linux_headless_64/\n        cd ..\n    - name: Build\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake build[--verbose]\n    - name: Run Standard Test Suite\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake 'test[--verbose]'\n"
  },
  {
    "path": ".github/workflows/test-Godotv3.5-Ubuntu.yml",
    "content": "name: \"Ubuntu 20.04 (Godot v3.5)\"\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  test-suite:\n\n    runs-on: \"ubuntu-20.04\"\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Install Godot\n      run: |\n        mkdir godot-build\n        cd godot-build\n        wget https://downloads.tuxfamily.org/godotengine/3.5/Godot_v3.5-stable_linux_headless.64.zip\n        unzip Godot_v3.5-stable_linux_headless.64.zip\n        ln -s Godot_v3.5-stable_linux_headless.64 godot\n        ls -l\n        cd ..\n    - name: Build\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake build[--verbose]\n    - name: Run Standard Test Suite\n      run: |\n        PATH=\"$PWD/godot-build/:$PATH\"\n        echo \"$PATH\"\n        rake 'test[--verbose]'\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\n/bin\n*~\nGDLisp.gd\nGDLisp.msgpack\n# Generated file that is always identical to /GDLisp.gd\n/MacroServer/GDLisp.gd\n/tmp/\n/etc/\n/doc/readthedocs/_build/"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "\nversion: 2\n\nbuild:\n  os: ubuntu-20.04\n  tools:\n    python: \"3.9\"\n\nsphinx:\n  configuration: doc/readthedocs/conf.py\n\npython:\n  install:\n  - requirements: doc/readthedocs/requirements.txt\n"
  },
  {
    "path": "COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program 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 program 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 <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"gdlisp\"\nversion = \"1.0.0\"\nauthors = [\"Silvio Mayolo <mercerenies@gmail.com>\"]\nedition = \"2018\"\nbuild = \"build.rs\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nlalrpop-util = \"0.19.0\"\nregex = \"1\"\nphf = { version = \"0.8.0\", features = [\"macros\"] }\nordered-float = \"2.0.0\"\ntempfile = \"3.0.7\"\ndyn-clone = \"1.0.4\"\ngetopts = \"0.2\"\nwalkdir = \"2\"\njson = \"0.11.13\"\nlazy_static = \"1.4.0\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nrmp-serde = \"0.14.4\"\nserde_json = \"1.0.82\"\n\n[build-dependencies]\nlalrpop = { version = \"0.19.0\", features = [\"lexer\"] }"
  },
  {
    "path": "GDLisp.lisp",
    "content": "\n;;;; Copyright 2023 Silvio Mayolo\n;;;;\n;;;; This file is part of GDLisp.\n;;;;\n;;;; GDLisp is free software: you can redistribute it and/or modify it\n;;;; 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;;;; GDLisp is distributed in the hope that it will be useful, but\n;;;; WITHOUT ANY WARRANTY; without even the implied warranty of\n;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n;;;; General Public License for more details.\n;;;;\n;;;; You should have received a copy of the GNU General Public License\n;;;; along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n;;;;\n;;;; As a special exception to the terms of the GNU General Public\n;;;; License, you may use this GDLisp.lisp source file in your own\n;;;; projects without restriction.\n\n;;;; Library file for GDLisp. This file must be included as a singleton\n;;;; in any project which uses GDLisp compiled files.\n\n(sys/nostdlib)\n\n;;; Global GDLisp constants and enums\n\n(defconst nil ())\n(defconst GODOT-VERSION (sys/special-ref godot-version))\n(sys/declare superglobal GDLisp public) ; This file :)\n(sys/bootstrap constants)\n(sys/bootstrap constant-enums)\n\n(sys/declare superglobal (PI PI) public)\n(sys/declare superglobal (TAU TAU) public)\n(sys/declare superglobal (INF INF) public)\n\n(defenum ConnectFlags\n  (DEFERRED 1)\n  (PERSIST 2)\n  (ONESHOT 4)\n  (REFERENCE_COUNTED 8))\n\n(defenum Notification\n  (POSTINITIALIZE 0)\n  (PREDELETE 1))\n\n;; Note: Godot doesn't consider the below names constant for some\n;; reason, since they're apparently defined *on* `Object` in some\n;; weird way. So we have to hardcode the numerical values in the\n;; enums. We have test cases to make sure these numbers are consistent\n;; with the Godot values for the same.\n\n(sys/declare value CONNECT_DEFERRED public)\n(sys/declare value CONNECT_PERSIST public)\n(sys/declare value CONNECT_ONESHOT public)\n(sys/declare value CONNECT_REFERENCE_COUNTED public)\n(sys/declare value NOTIFICATION_POSTINITIALIZE public)\n(sys/declare value NOTIFICATION_PREDELETE public)\n\n;;; GDScript global types\n\n(sys/bootstrap non-singleton-types)\n(sys/bootstrap singleton-types)\n\n;;; GDLisp main class initialization\n\n(defclass _GDLisp (Node) main\n  (defvar __gdlisp_Global_name_generator (sys/FreshNameGenerator:new [] 0))\n\n  (defvar __gdlisp_Global_symbol_table {})\n\n  ;; Primitive types\n  (defvar Null (PrimitiveType:new TYPE_NIL))\n  (defvar Bool (PrimitiveType:new TYPE_BOOL))\n  (defvar Int (PrimitiveType:new TYPE_INT))\n  (defvar Float (PrimitiveType:new TYPE_REAL))\n  (defvar String (PrimitiveType:new TYPE_STRING))\n  (defvar Vector2 (Vector2PrimitiveType:new))\n  (defvar Rect2 (PrimitiveType:new TYPE_RECT2))\n  (defvar Vector3 (Vector3PrimitiveType:new))\n  (defvar Transform2D (Transform2DPrimitiveType:new))\n  (defvar Plane (PlanePrimitiveType:new))\n  (defvar Quat (QuatPrimitiveType:new))\n  (defvar AABB (PrimitiveType:new TYPE_AABB))\n  (defvar Basis (BasisPrimitiveType:new))\n  (defvar Transform (TransformPrimitiveType:new))\n  (defvar Color (ColorPrimitiveType:new))\n  (defvar NodePath (PrimitiveType:new TYPE_NODE_PATH))\n  (defvar RID (PrimitiveType:new TYPE_RID))\n  (defvar Object (ObjectPrimitiveType:new))\n  (defvar Dictionary (PrimitiveType:new TYPE_DICTIONARY))\n  (defvar Array (PrimitiveType:new TYPE_ARRAY))\n  (defvar PoolByteArray (PrimitiveType:new TYPE_RAW_ARRAY))\n  (defvar PoolIntArray (PrimitiveType:new TYPE_INT_ARRAY))\n  (defvar PoolRealArray (PrimitiveType:new TYPE_REAL_ARRAY))\n  (defvar PoolStringArray (PrimitiveType:new TYPE_STRING_ARRAY))\n  (defvar PoolVector2Array (PrimitiveType:new TYPE_VECTOR2_ARRAY))\n  (defvar PoolVector3Array (PrimitiveType:new TYPE_VECTOR3_ARRAY))\n  (defvar PoolColorArray (PrimitiveType:new TYPE_COLOR_ARRAY))\n\n  ;; Synthetic types\n  (defvar Any (AnyType:new))\n  (defvar AnyRef (AnyRefType:new))\n  (defvar AnyVal (AnyValType:new))\n  (defvar Number (NumberType:new))\n  (defvar BaseArray (BaseArrayType:new))\n  (defvar Nothing (NothingType:new))\n\n  ;; GDScript native types lookup table\n  (defvar __gdlisp_Global_native_types_lookup)\n  (defvar __gdlisp_Global_primitive_types_lookup)\n\n  (sys/bootstrap singleton-backing-types)\n\n  (defn _init ()\n    (set self:__gdlisp_Global_primitive_types_lookup\n         {TYPE_NIL @Null TYPE_BOOL @Bool TYPE_INT @Int TYPE_REAL @Float TYPE_STRING @String TYPE_VECTOR2 @Vector2\n          TYPE_RECT2 @Rect2 TYPE_VECTOR3 @Vector3 TYPE_TRANSFORM2D @Transform2D TYPE_PLANE @Plane TYPE_QUAT @Quat\n          TYPE_AABB @AABB TYPE_BASIS @Basis TYPE_TRANSFORM @Transform TYPE_COLOR @Color TYPE_NODE_PATH @NodePath\n          TYPE_RID @RID TYPE_OBJECT @Object TYPE_DICTIONARY @Dictionary TYPE_ARRAY @Array\n          TYPE_RAW_ARRAY @PoolByteArray TYPE_INT_ARRAY @PoolIntArray TYPE_REAL_ARRAY @PoolRealArray\n          TYPE_STRING_ARRAY @PoolStringArray TYPE_VECTOR2_ARRAY @PoolVector2Array\n          TYPE_VECTOR3_ARRAY @PoolVector3Array TYPE_COLOR_ARRAY @PoolColorArray})\n\n    (sys/bootstrap native-types-table)\n    (set (elt self:__gdlisp_Global_native_types_lookup \"Object\") @Object))\n\n  (defn typeof (value)\n    (let ((t ((literally typeof) value)))\n      (cond\n        ((/= t TYPE_OBJECT) (elt self:__gdlisp_Global_primitive_types_lookup t))\n        ((value:get_script))\n        (#t (self:__gdlisp_Global_native_types_lookup:get (value:get_class) self:Any))))))\n\n;;; Public support classes\n\n(defclass Cons (Reference)\n  (defvar car)\n  (defvar cdr)\n\n  (defn _init (@car @cdr)\n    (self:set-meta \"__gdlisp_Primitive_Cons\" 1))\n\n  (defn (get caar) ()\n    self:car:car)\n\n  (defn (get cadr) ()\n    self:car:cdr)\n\n  (defn (get cdar) ()\n    self:cdr:car)\n\n  (defn (get cddr) ()\n    self:cdr:cdr)\n\n  (defn (get caaar) ()\n    self:car:car:car)\n\n  (defn (get caadr) ()\n    self:car:car:cdr)\n\n  (defn (get cadar) ()\n    self:car:cdr:car)\n\n  (defn (get caddr) ()\n    self:car:cdr:cdr)\n\n  (defn (get cdaar) ()\n    self:cdr:car:car)\n\n  (defn (get cdadr) ()\n    self:cdr:car:cdr)\n\n  (defn (get cddar) ()\n    self:cdr:cdr:car)\n\n  (defn (get cdddr) ()\n    self:cdr:cdr:cdr))\n\n(defclass Function (Reference)\n  (defvar __gdlisp_is_function #t)\n  (defvar __gdlisp_required 0)\n  (defvar __gdlisp_optional 0)\n  (defvar __gdlisp_rest 1)\n  ;; Constants for the different types of \"rest\" arguments a function\n  ;; can take. (i.e. the valid values for __gdlisp_rest)\n  (defconst __gdlisp_vararg_no   0)\n  (defconst __gdlisp_vararg_rest 1)\n  (defconst __gdlisp_vararg_arr  2))\n\n(defclass Cell (Reference)\n  (defvar contents)\n  (defn _init (@contents)))\n\n(defclass Symbol (Reference)\n  ;; Note: This will be obsolete once we have StringName in GDScript,\n  ;; which seems to be coming in Godot 4. For now, this manual wrapper\n  ;; stores symbols in the least efficient way possible.\n  (defvar __gdlisp_contents)\n  (defn _init (@__gdlisp_contents)\n    (self:set-meta \"__gdlisp_Primitive_Symbol\" 1)))\n\n(defclass sys/FreshNameGenerator (Reference)\n  ;; This is meant to be identical to FreshNameGenerator in the Rust\n  ;; source. We want to be able to consistently generate names in the\n  ;; same way on both Rust and Godot and to be able to communicate\n  ;; FreshNameGenerator state between the two (via to_json /\n  ;; from_json)\n\n  (defconst DEFAULT_PREFIX \"_G\")\n\n  (defvar reserved)\n  (defvar index)\n\n  (defn _init (@reserved @index))\n\n  (defn generate ()\n    (self:generate_with sys/FreshNameGenerator:DEFAULT_PREFIX))\n\n  (defn generate_with (prefix)\n    (let ((name (\"{}_{}\":format [prefix self:index] \"{}\")))\n      (set self:index (+ self:index 1))\n      (cond\n        ((member? name self:reserved) (self:generate_with prefix))\n        (#t name))))\n\n  (defn to_json ()\n    {\"reserved\" self:reserved \"index\" self:index})\n\n  (defn from_json (json) static\n    (sys/FreshNameGenerator:new\n     (elt json \"reserved\")\n     (elt json \"index\"))))\n\n;;; GDLisp type constants and support types\n\n(defclass GDLispSpecialType (Reference) private)\n\n(defclass PrimitiveType (GDLispSpecialType) private\n  (defvar primitive-value)\n\n  (defn _init (@primitive-value))\n\n  (defn satisfies? (value)\n    (= ((literally typeof) value) self:primitive-value)))\n\n(defclass ObjectPrimitiveType (PrimitiveType) private\n\n  (defn _init ()\n    (super TYPE_OBJECT)))\n\n;; NOTE: Due to current Godot bugs, we can't actually define this,\n;; since GDScript fails to parse constructor calls on Object directly.\n;; See https://github.com/godotengine/godot/issues/41462. Has been\n;; fixed in 4.0 and it doesn't look like they're planning to backport.\n;;\n;;  (defn new ()\n;;    ((literally Object):new)))\n\n(defclass Vector2PrimitiveType (PrimitiveType) private\n  (defconst AXIS_X 0)\n  (defconst AXIS_Y 1)\n  (defconst ZERO V{0 0})\n  (defconst ONE V{1 1})\n  (defconst INF V{INF INF})\n  (defconst LEFT V{-1 0})\n  (defconst RIGHT V{1 0})\n  (defconst UP V{0 -1})\n  (defconst DOWN V{0 1})\n\n  (defn _init ()\n    (super TYPE_VECTOR2)))\n\n(defclass Vector3PrimitiveType (PrimitiveType) private\n  (defconst AXIS_X 0)\n  (defconst AXIS_Y 1)\n  (defconst AXIS_Z 2)\n  (defconst ZERO V{0 0 0})\n  (defconst ONE V{1 1 1})\n  (defconst INF V{INF INF INF})\n  (defconst LEFT V{-1 0 0})\n  (defconst RIGHT V{1 0 0})\n  (defconst UP V{0 1 0})\n  (defconst DOWN V{0 -1 0})\n  (defconst FORWARD V{0 0 -1})\n  (defconst BACK V{0 0 1})\n\n  (defn _init ()\n    (super TYPE_VECTOR3)))\n\n(defclass Transform2DPrimitiveType (PrimitiveType) private\n  (defconst IDENTITY (Transform2D V{1 0} V{0 1} V{0 0}))\n  (defconst FLIP_X (Transform2D V{-1 0} V{0 1} V{0 0}))\n  (defconst FLIP_Y (Transform2D V{1 0} V{0 -1} V{0 0}))\n\n  (defn _init ()\n    (super TYPE_TRANSFORM2D)))\n\n(defclass PlanePrimitiveType (PrimitiveType) private\n  (defconst PLANE_YZ (Plane 1 0 0 0))\n  (defconst PLANE_XZ (Plane 0 1 0 0))\n  (defconst PLANE_XY (Plane 0 0 1 0))\n\n  (defn _init ()\n    (super TYPE_PLANE)))\n\n(defclass QuatPrimitiveType (PrimitiveType) private\n  (defconst IDENTITY (Quat 0 0 0 1))\n\n  (defn _init ()\n    (super TYPE_QUAT)))\n\n(defclass BasisPrimitiveType (PrimitiveType) private\n  (defconst IDENTITY (Basis V{1 0 0} V{0 1 0} V{0 0 1}))\n  (defconst FLIP_X (Basis V{-1 0 0} V{0 1 0} V{0 0 1}))\n  (defconst FLIP_Y (Basis V{1 0 0} V{0 -1 0} V{0 0 -1}))\n  (defconst FLIP_Z (Basis V{1 0 0} V{0 -1 0} V{0 0 -1}))\n\n  (defn _init ()\n    (super TYPE_BASIS)))\n\n(defclass TransformPrimitiveType (PrimitiveType) private\n  (defconst IDENTITY (Transform V{1 0 0} V{0 1 0} V{0 0 1} V{0 0 0}))\n  (defconst FLIP_X (Transform V{-1 0 0} V{0 1 0} V{0 0 1} V{0 0 0}))\n  (defconst FLIP_Y (Transform V{1 0 0} V{0 -1 0} V{0 0 -1} V{0 0 0}))\n  (defconst FLIP_Z (Transform V{1 0 0} V{0 -1 0} V{0 0 -1} V{0 0 0}))\n\n  (defn _init ()\n    (super TYPE_TRANSFORM)))\n\n(defclass ColorPrimitiveType (PrimitiveType) private\n    (defconst aliceblue (Color 0.941176 0.972549 1 1))\n    (defconst antiquewhite (Color 0.980392 0.921569 0.843137 1))\n    (defconst aqua (Color 0 1 1 1))\n    (defconst aquamarine (Color 0.498039 1 0.831373 1))\n    (defconst azure (Color 0.941176 1 1 1))\n    (defconst beige (Color 0.960784 0.960784 0.862745 1))\n    (defconst bisque (Color 1 0.894118 0.768627 1))\n    (defconst black (Color 0 0 0 1))\n    (defconst blanchedalmond (Color 1 0.921569 0.803922 1))\n    (defconst blue (Color 0 0 1 1))\n    (defconst blueviolet (Color 0.541176 0.168627 0.886275 1))\n    (defconst brown (Color 0.647059 0.164706 0.164706 1))\n    (defconst burlywood (Color 0.870588 0.721569 0.529412 1))\n    (defconst cadetblue (Color 0.372549 0.619608 0.627451 1))\n    (defconst chartreuse (Color 0.498039 1 0 1))\n    (defconst chocolate (Color 0.823529 0.411765 0.117647 1))\n    (defconst coral (Color 1 0.498039 0.313726 1))\n    (defconst cornflower (Color 0.392157 0.584314 0.929412 1))\n    (defconst cornsilk (Color 1 0.972549 0.862745 1))\n    (defconst crimson (Color 0.862745 0.0784314 0.235294 1))\n    (defconst cyan (Color 0 1 1 1))\n    (defconst darkblue (Color 0 0 0.545098 1))\n    (defconst darkcyan (Color 0 0.545098 0.545098 1))\n    (defconst darkgoldenrod (Color 0.721569 0.52549 0.0431373 1))\n    (defconst darkgray (Color 0.662745 0.662745 0.662745 1))\n    (defconst darkgreen (Color 0 0.392157 0 1))\n    (defconst darkkhaki (Color 0.741176 0.717647 0.419608 1))\n    (defconst darkmagenta (Color 0.545098 0 0.545098 1))\n    (defconst darkolivegreen (Color 0.333333 0.419608 0.184314 1))\n    (defconst darkorange (Color 1 0.54902 0 1))\n    (defconst darkorchid (Color 0.6 0.196078 0.8 1))\n    (defconst darkred (Color 0.545098 0 0 1))\n    (defconst darksalmon (Color 0.913725 0.588235 0.478431 1))\n    (defconst darkseagreen (Color 0.560784 0.737255 0.560784 1))\n    (defconst darkslateblue (Color 0.282353 0.239216 0.545098 1))\n    (defconst darkslategray (Color 0.184314 0.309804 0.309804 1))\n    (defconst darkturquoise (Color 0 0.807843 0.819608 1))\n    (defconst darkviolet (Color 0.580392 0 0.827451 1))\n    (defconst deeppink (Color 1 0.0784314 0.576471 1))\n    (defconst deepskyblue (Color 0 0.74902 1 1))\n    (defconst dimgray (Color 0.411765 0.411765 0.411765 1))\n    (defconst dodgerblue (Color 0.117647 0.564706 1 1))\n    (defconst firebrick (Color 0.698039 0.133333 0.133333 1))\n    (defconst floralwhite (Color 1 0.980392 0.941176 1))\n    (defconst forestgreen (Color 0.133333 0.545098 0.133333 1))\n    (defconst fuchsia (Color 1 0 1 1))\n    (defconst gainsboro (Color 0.862745 0.862745 0.862745 1))\n    (defconst ghostwhite (Color 0.972549 0.972549 1 1))\n    (defconst gold (Color 1 0.843137 0 1))\n    (defconst goldenrod (Color 0.854902 0.647059 0.12549 1))\n    (defconst gray (Color 0.745098 0.745098 0.745098 1))\n    (defconst green (Color 0 1 0 1))\n    (defconst greenyellow (Color 0.678431 1 0.184314 1))\n    (defconst honeydew (Color 0.941176 1 0.941176 1))\n    (defconst hotpink (Color 1 0.411765 0.705882 1))\n    (defconst indianred (Color 0.803922 0.360784 0.360784 1))\n    (defconst indigo (Color 0.294118 0 0.509804 1))\n    (defconst ivory (Color 1 1 0.941176 1))\n    (defconst khaki (Color 0.941176 0.901961 0.54902 1))\n    (defconst lavender (Color 0.901961 0.901961 0.980392 1))\n    (defconst lavenderblush (Color 1 0.941176 0.960784 1))\n    (defconst lawngreen (Color 0.486275 0.988235 0 1))\n    (defconst lemonchiffon (Color 1 0.980392 0.803922 1))\n    (defconst lightblue (Color 0.678431 0.847059 0.901961 1))\n    (defconst lightcoral (Color 0.941176 0.501961 0.501961 1))\n    (defconst lightcyan (Color 0.878431 1 1 1))\n    (defconst lightgoldenrod (Color 0.980392 0.980392 0.823529 1))\n    (defconst lightgray (Color 0.827451 0.827451 0.827451 1))\n    (defconst lightgreen (Color 0.564706 0.933333 0.564706 1))\n    (defconst lightpink (Color 1 0.713726 0.756863 1))\n    (defconst lightsalmon (Color 1 0.627451 0.478431 1))\n    (defconst lightseagreen (Color 0.12549 0.698039 0.666667 1))\n    (defconst lightskyblue (Color 0.529412 0.807843 0.980392 1))\n    (defconst lightslategray (Color 0.466667 0.533333 0.6 1))\n    (defconst lightsteelblue (Color 0.690196 0.768627 0.870588 1))\n    (defconst lightyellow (Color 1 1 0.878431 1))\n    (defconst lime (Color 0 1 0 1))\n    (defconst limegreen (Color 0.196078 0.803922 0.196078 1))\n    (defconst linen (Color 0.980392 0.941176 0.901961 1))\n    (defconst magenta (Color 1 0 1 1))\n    (defconst maroon (Color 0.690196 0.188235 0.376471 1))\n    (defconst mediumaquamarine (Color 0.4 0.803922 0.666667 1))\n    (defconst mediumblue (Color 0 0 0.803922 1))\n    (defconst mediumorchid (Color 0.729412 0.333333 0.827451 1))\n    (defconst mediumpurple (Color 0.576471 0.439216 0.858824 1))\n    (defconst mediumseagreen (Color 0.235294 0.701961 0.443137 1))\n    (defconst mediumslateblue (Color 0.482353 0.407843 0.933333 1))\n    (defconst mediumspringgreen (Color 0 0.980392 0.603922 1))\n    (defconst mediumturquoise (Color 0.282353 0.819608 0.8 1))\n    (defconst mediumvioletred (Color 0.780392 0.0823529 0.521569 1))\n    (defconst midnightblue (Color 0.0980392 0.0980392 0.439216 1))\n    (defconst mintcream (Color 0.960784 1 0.980392 1))\n    (defconst mistyrose (Color 1 0.894118 0.882353 1))\n    (defconst moccasin (Color 1 0.894118 0.709804 1))\n    (defconst navajowhite (Color 1 0.870588 0.678431 1))\n    (defconst navyblue (Color 0 0 0.501961 1))\n    (defconst oldlace (Color 0.992157 0.960784 0.901961 1))\n    (defconst olive (Color 0.501961 0.501961 0 1))\n    (defconst olivedrab (Color 0.419608 0.556863 0.137255 1))\n    (defconst orange (Color 1 0.647059 0 1))\n    (defconst orangered (Color 1 0.270588 0 1))\n    (defconst orchid (Color 0.854902 0.439216 0.839216 1))\n    (defconst palegoldenrod (Color 0.933333 0.909804 0.666667 1))\n    (defconst palegreen (Color 0.596078 0.984314 0.596078 1))\n    (defconst paleturquoise (Color 0.686275 0.933333 0.933333 1))\n    (defconst palevioletred (Color 0.858824 0.439216 0.576471 1))\n    (defconst papayawhip (Color 1 0.937255 0.835294 1))\n    (defconst peachpuff (Color 1 0.854902 0.72549 1))\n    (defconst peru (Color 0.803922 0.521569 0.247059 1))\n    (defconst pink (Color 1 0.752941 0.796078 1))\n    (defconst plum (Color 0.866667 0.627451 0.866667 1))\n    (defconst powderblue (Color 0.690196 0.878431 0.901961 1))\n    (defconst purple (Color 0.627451 0.12549 0.941176 1))\n    (defconst rebeccapurple (Color 0.4 0.2 0.6 1))\n    (defconst red (Color 1 0 0 1))\n    (defconst rosybrown (Color 0.737255 0.560784 0.560784 1))\n    (defconst royalblue (Color 0.254902 0.411765 0.882353 1))\n    (defconst saddlebrown (Color 0.545098 0.270588 0.0745098 1))\n    (defconst salmon (Color 0.980392 0.501961 0.447059 1))\n    (defconst sandybrown (Color 0.956863 0.643137 0.376471 1))\n    (defconst seagreen (Color 0.180392 0.545098 0.341176 1))\n    (defconst seashell (Color 1 0.960784 0.933333 1))\n    (defconst sienna (Color 0.627451 0.321569 0.176471 1))\n    (defconst silver (Color 0.752941 0.752941 0.752941 1))\n    (defconst skyblue (Color 0.529412 0.807843 0.921569 1))\n    (defconst slateblue (Color 0.415686 0.352941 0.803922 1))\n    (defconst slategray (Color 0.439216 0.501961 0.564706 1))\n    (defconst snow (Color 1 0.980392 0.980392 1))\n    (defconst springgreen (Color 0 1 0.498039 1))\n    (defconst steelblue (Color 0.27451 0.509804 0.705882 1))\n    (defconst tan (Color 0.823529 0.705882 0.54902 1))\n    (defconst teal (Color 0 0.501961 0.501961 1))\n    (defconst thistle (Color 0.847059 0.74902 0.847059 1))\n    (defconst tomato (Color 1 0.388235 0.278431 1))\n    (defconst transparent (Color 1 1 1 0))\n    (defconst turquoise (Color 0.25098 0.878431 0.815686 1))\n    (defconst violet (Color 0.933333 0.509804 0.933333 1))\n    (defconst webgray (Color 0.501961 0.501961 0.501961 1))\n    (defconst webgreen (Color 0 0.501961 0 1))\n    (defconst webmaroon (Color 0.501961 0 0 1))\n    (defconst webpurple (Color 0.501961 0 0.501961 1))\n    (defconst wheat (Color 0.960784 0.870588 0.701961 1))\n    (defconst white (Color 1 1 1 1))\n    (defconst whitesmoke (Color 0.960784 0.960784 0.960784 1))\n    (defconst yellow (Color 1 1 0 1))\n    (defconst yellowgreen (Color 0.603922 0.803922 0.196078 1))\n\n  (defn _init ()\n    (super TYPE_COLOR)))\n\n;; Named types like _Engine whose name can be returned from get_class\n;; but which do not exist in the runtime namespace exposed to\n;; GDScript.\n(defclass NamedSyntheticType (GDLispSpecialType) private\n  (defvar name)\n\n  (defn _init (@name))\n\n  (defn satisfies? (value)\n    (= (value:get-class) self:name)))\n\n;; Note: All of these synthetic types would theoretically be defobject\n;; if we weren't writing them in the standard library. But for\n;; complicated reasons, we can't use macro expansion at all in stdlib,\n;; and defobject is behind three layers of macro (defobject and\n;; deflazy, and the expansion includes define-symbol-macro), so it's\n;; off-limits.\n\n(defclass AnyType (GDLispSpecialType) private\n  (defn satisfies? (_value)\n    #t))\n\n(defclass AnyRefType (GDLispSpecialType) private\n  (defn satisfies? (value)\n    (= ((literally typeof) value) TYPE_OBJECT)))\n\n(defclass AnyValType (GDLispSpecialType) private\n  (defn satisfies? (value)\n    (/= ((literally typeof) value) TYPE_OBJECT)))\n\n(defclass NumberType (GDLispSpecialType) private\n  (defn satisfies? (value)\n    (let ((t ((literally typeof) value)))\n      (cond\n        ((= t TYPE_INT) #t)\n        ((= t TYPE_REAL) #t)\n        (#t #f)))))\n\n(defclass BaseArrayType (GDLispSpecialType) private\n  (defn satisfies? (value)\n    (<= TYPE_ARRAY ((literally typeof) value) TYPE_COLOR_ARRAY)))\n\n(defclass NothingType (GDLispSpecialType) private\n  (defn satisfies? (_value)\n    #f))\n\n(sys/declare value Any public)\n(sys/declare value AnyRef public)\n(sys/declare value AnyVal public)\n(sys/declare value Number public)\n(sys/declare value BaseArray public)\n(sys/declare value Nothing public)\n\n(sys/declare value Null public)\n(sys/declare value Bool public)\n(sys/declare value Int public)\n(sys/declare value Float public)\n(sys/declare value String public)\n(sys/declare value Vector2 public)\n(sys/declare value Rect2 public)\n(sys/declare value Vector3 public)\n(sys/declare value Transform2D public)\n(sys/declare value Plane public)\n(sys/declare value Quat public)\n(sys/declare value AABB public)\n(sys/declare value Basis public)\n(sys/declare value Transform public)\n(sys/declare value Color public)\n(sys/declare value NodePath public)\n(sys/declare value RID public)\n(sys/declare value Object public)\n(sys/declare value Dictionary public)\n(sys/declare value Array public)\n(sys/declare value PoolByteArray public)\n(sys/declare value PoolIntArray public)\n(sys/declare value PoolRealArray public)\n(sys/declare value PoolStringArray public)\n(sys/declare value PoolVector2Array public)\n(sys/declare value PoolVector3Array public)\n(sys/declare value PoolColorArray public)\n\n;;; Polymorphic functions\n\n(defn len (x)\n  (cond\n    ((= x nil) 0)\n    ((sys/instance-direct? x Cons)\n     (let ((result 0))\n       (while (sys/instance-direct? x Cons)\n         (set result (+ result 1))\n         (set x x:cdr))\n       result))\n    (#t ((literally len) x))))\n\n;;; List functions\n\n(defn list (&rest args)\n  (sys/call-magic LIST)\n  ;; The argument list may be shared with some other code, but `list`\n  ;; should always allocate a new list, so defensively copy the\n  ;; argument list.\n  (list/copy args))\n\n(defn list/copy (xs)\n  (list/map (lambda (x) x) xs))\n\n(defn cons (a b)\n  (Cons:new a b))\n\n(defn snoc (a b)\n  (append a (list b)))\n\n(defn init (a)\n  (cond\n    ((sys/instance-direct? a:cdr Cons) (cons a:car (init a:cdr)))\n    (#t nil)))\n\n(defn last (a)\n  (cond\n    ((sys/instance-direct? a:cdr Cons) (last a:cdr))\n    (#t a:car)))\n\n(defn list->array (list)\n  (let ((arr []))\n    (while (sys/instance-direct? list Cons)\n      (arr:push_back list:car)\n      (set list list:cdr))\n    arr))\n\n(defn array->list (arr)\n  (let ((outer (cons () ())))\n    (let ((curr outer))\n      (for elem arr\n        (set curr:cdr (cons elem ()))\n        (set curr curr:cdr))\n      outer:cdr)))\n\n(defn list/elt (list n)\n  (list/tail list n):car)\n\n(defn list/fold (f xs &opt x)\n  (cond\n    ((= x nil)\n     (set x xs:car)\n     (set xs xs:cdr)))\n  (while (/= xs nil)\n    (set x (funcall f x xs:car))\n    (set xs xs:cdr))\n  x)\n\n(defn list/map (f xs)\n  (let ((outer (cons nil nil)))\n    (let ((curr outer))\n      (while (/= xs nil)\n        (set curr:cdr (cons (funcall f xs:car) nil))\n        (set curr curr:cdr)\n        (set xs xs:cdr))\n      outer:cdr)))\n\n(defn list/filter (p xs)\n  (let ((outer (cons nil nil)))\n    (let ((curr outer))\n      (while (/= xs nil)\n        (cond\n          ((funcall p xs:car)\n           (set curr:cdr (cons xs:car nil))\n           (set curr curr:cdr)))\n        (set xs xs:cdr))\n      outer:cdr)))\n\n(defn list/find (f xs &opt default)\n  (while (/= xs nil)\n    (cond\n      ((funcall f xs:car) (return xs:car)))\n    (set xs xs:cdr))\n  default)\n\n(defn list/reverse (arg)\n  (let ((rev nil))\n    (while (/= arg nil)\n      (set rev `(,arg:car . ,rev))\n      (set arg arg:cdr))\n    rev))\n\n(defn append (&rest args)\n  (cond\n    ((= args nil) nil)\n    (#t\n     (let ((outer (cons nil nil)))\n       (let ((curr outer))\n         (while (/= args:cdr nil)\n           (let ((inner-value args:car))\n             (while (/= inner-value nil)\n               (set curr:cdr (cons inner-value:car nil))\n               (set curr curr:cdr)\n               (set inner-value inner-value:cdr)))\n           (set args args:cdr))\n         (set curr:cdr args:car)\n         outer:cdr)))))\n\n(defn list/tail (list k)\n  (for _i (range k)\n    (set list list:cdr))\n  list)\n\n(defn sys/qq-smart-list (a)\n  (cond\n    ((instance? a GDLisp:BaseArray) (array->list a))\n    (#t a)))\n\n;;; Array functions\n\n(defn elt (arr n)\n  (sys/call-magic ARRAY-SUBSCRIPT)\n  (elt arr n))\n\n(defn set-elt (x arr n)\n  (sys/call-magic ARRAY-SUBSCRIPT-ASSIGNMENT)\n  (set-elt x arr n))\n\n(defn member? (value arr)\n  (sys/call-magic ARRAY-MEMBER-CHECK)\n  (member? value arr))\n\n(defn array/fold (f xs &opt x)\n  (let ((starting-index 0))\n    (cond\n      ((= x nil)\n       (set x (elt xs 0))\n       (set starting-index 1)))\n    (for i (range starting-index (len xs))\n      (set x (funcall f x (elt xs i))))\n    x))\n\n(defn array/map (f xs)\n  (let ((result []))\n    (for i (len xs)\n      (result:push_back (funcall f (elt xs i))))\n    result))\n\n(defn array/filter (p xs)\n  (let ((result []))\n    (for i (len xs)\n      (cond\n        ((funcall p (elt xs i))\n         (result:push_back (elt xs i)))))\n    result))\n\n(defn array/reverse (arr)\n  (let ((len (len arr))\n        (result (arr:duplicate)))\n    (for i (range len)\n      (set (elt result i) (elt arr (- len i 1))))\n    result))\n\n(defn array/find (f arr &opt default)\n  (for elem arr\n    (cond\n      ((funcall f elem) (return elem))))\n  default)\n\n;;; Higher-order functions\n\n(defn funcall (f &rest args)\n  (apply f args))\n\n;; funcall alias used in IIFEs. The user can shadow funcall (though\n;; it's a bad idea), but shadowing names in sys/ is undefined behavior.\n(defn sys/funcall (f &rest args)\n  (apply f args))\n\n(defn apply (f &rest args)\n  (let ((args1 (init args))\n        (args2 (last args)))\n    (cond\n      ((sys/instance-direct? f Function) (f:call_funcv (append args1 args2)))\n      (#t (push-error \"Attempt to call non-function\")))))\n\n;;; Array functions\n\n(defn array/concat (&rest arrays)\n  (cond\n    ((= (len arrays) 0) [])\n    (#t (apply #'+ arrays))))\n\n;;; Vector functions\n\n(defn vector/map (f arg &rest args)\n  (flet ((-x (v) v:x)\n         (-y (v) v:y)\n         (-z (v) v:z))\n    (let ((args (cons arg args)))\n      (cond\n        ((instance? arg GDLisp:Vector2) (Vector2 (apply f (list/map #'-x args))\n                                                 (apply f (list/map #'-y args))))\n        ((instance? arg GDLisp:Vector3) (Vector3 (apply f (list/map #'-x args))\n                                                 (apply f (list/map #'-y args))\n                                                 (apply f (list/map #'-z args))))\n        (#t (push-error \"Attempted vector/map on non-vector\"))))))\n\n;;; Node functions\n\n(defclass SignalAdaptor (Reference) private\n  (defvar function)\n\n  (defn _init (@function))\n\n  (defn invoke (a b c d e f) sys/nullargs\n    ;; Identify the first non-null argument.\n    (let ((arglist (cond\n                     ((/= f ()) (list a b c d e f))\n                     ((/= e ()) (list a b c d e))\n                     ((/= d ()) (list a b c d))\n                     ((/= c ()) (list a b c))\n                     ((/= b ()) (list a b))\n                     ((/= a ()) (list a))\n                     (#t (list)))))\n      (apply @function arglist))))\n\n(defn get-signals-meta (obj) private\n  (cond\n    ((obj:has-meta \"__gdlisp_signals\") (obj:get-meta \"__gdlisp_signals\"))\n    (#t (let ((new-meta {\"__key\" 0})) (obj:set-meta \"__gdlisp_signals\" new-meta) new-meta))))\n\n(defn connect>> (obj signal-name function)\n  (let ((function (SignalAdaptor:new function)))\n    (obj:connect signal-name function \"invoke\")\n    (let ((meta (get-signals-meta obj)))\n      (let ((key (dict/elt meta \"__key\")))\n        (set (dict/elt meta key) function)\n        (set (dict/elt meta \"__key\") (+ (dict/elt meta \"__key\") 1))\n        key))))\n\n(defn connect1>> (obj signal-name function)\n  (let ((key nil)\n        (weakref (weakref obj)))\n    (flet ((oneshot-function (&rest args)\n             (apply function args)\n             (let ((obj (weakref:get-ref)))\n               (cond\n                 (obj (disconnect>> obj signal-name key))))))\n      (set key (connect>> obj signal-name #'oneshot-function)))))\n\n(defn disconnect>> (obj signal-name index)\n  (let ((meta (get-signals-meta obj)))\n    (meta:erase index)))\n\n;;; Miscellaneous simple functions\n\n(defn bool (x)\n  ;; Like the GDScript primitive but works for all types, not just\n  ;; strings and numbers.\n  (cond\n    (x #t)\n    (#t #f)))\n\n(defn vector (x y &opt z)\n  (sys/call-magic VECTOR)\n  (cond\n    ((= z ()) V{x y})\n    (#t V{x y z})))\n\n(defn array (&arr xs)\n  (sys/call-magic ARRAY)\n  xs)\n\n(defn dict (&arr xs)\n  (sys/call-magic DICT)\n  (let ((resulting-dict {}))\n    (for i (range 0 (len xs) 2)\n      (let ((k (elt xs i))\n            (v (elt xs (+ i 1))))\n        (set (elt resulting-dict k) v)))\n    resulting-dict))\n\n(defn NodePath (s)\n  (sys/call-magic NODEPATH-SYNTAX)\n  ((literally NodePath) s))\n\n(defn not (x)\n  (sys/call-magic BOOLEAN-NOT)\n  (not x))\n\n(defn intern (a)\n  (cond\n    ((member? a GDLisp:__gdlisp_Global_symbol_table) (elt GDLisp:__gdlisp_Global_symbol_table a))\n    (#t (set (elt GDLisp:__gdlisp_Global_symbol_table a) (Symbol:new a)))))\n\n(defn gensym (&opt prefix)\n  (cond\n    ((= prefix ()) (Symbol:new (GDLisp:__gdlisp_Global_name_generator:generate)))\n    (#t (Symbol:new (GDLisp:__gdlisp_Global_name_generator:generate_with prefix)))))\n\n(defn sys/get-node (obj path)\n  (sys/call-magic GET-NODE-SYNTAX)\n  (obj:get-node path))\n\n(defn instance? (value type)\n  (cond\n    ((sys/instance-direct? type GDLispSpecialType) (type:satisfies? value))\n    (#t (sys/instance-direct? value type))))\n\n(defn sys/instance-direct? (value type)\n  (sys/call-magic DIRECT-INSTANCE-CHECK)\n  (sys/instance-direct? value type))\n\n(sys/declare function typeof (value) public)\n\n(defn convert (what type)\n  (cond\n    ((sys/instance-direct? type PrimitiveType) ((literally convert) what type:primitive-value))\n    (#t ((literally convert) what type))))\n\n(defn dict/elt (dict n)\n  (sys/call-magic DICT-SUBSCRIPT)\n  (dict/elt dict n))\n\n(defn set-dict/elt (x dict n)\n  (sys/call-magic DICT-SUBSCRIPT-ASSIGNMENT)\n  (set-dict/elt x dict n))\n\n(defn dict/find (f dict &opt default)\n  (for key dict\n    (cond\n      ((funcall f key (dict/elt dict key)) (return key))))\n  default)\n\n;;; Math operators\n\n(defn + (&rest args)\n  (sys/call-magic ADDITION)\n  (cond\n    ((sys/instance-direct? args Cons)\n     (let ((result args:car))\n       (set args args:cdr)\n       (while (sys/instance-direct? args Cons)\n         (set result (+ result args:car))\n         (set args args:cdr))\n       result))\n    (#t 0)))\n\n(defn * (&rest args)\n  (sys/call-magic MULTIPLICATION)\n  (let ((result 1))\n    (while (sys/instance-direct? args Cons)\n      (set result (* result args:car))\n      (set args args:cdr))\n    result))\n\n(defn - (x &rest args)\n  (sys/call-magic SUBTRACTION)\n  (cond\n    ((sys/instance-direct? args Cons)\n     (let ((result x))\n       (while (sys/instance-direct? args Cons)\n         (set result (- result args:car))\n         (set args args:cdr))\n       result))\n    (#t (- x))))\n\n(defn / (x &rest args)\n  (sys/call-magic DIVISION)\n  (cond\n    ((sys/instance-direct? args Cons)\n     (let ((result x))\n       (while (sys/instance-direct? args Cons)\n         (set result (/ result args:car))\n         (set args args:cdr))\n       result))\n    (#t (/ x))))\n\n(defn mod (x y)\n  (sys/call-magic MODULO)\n  (mod x y))\n\n(defn min (&rest args)\n  (sys/call-magic MIN-FUNCTION)\n  (cond\n    ((sys/instance-direct? args Cons)\n     (let ((result args:car))\n       (set args args:cdr)\n       (while (sys/instance-direct? args Cons)\n         (set result (min result args:car))\n         (set args args:cdr))\n       result))\n    (#t (literally INF))))\n\n(defn max (&rest args)\n  (sys/call-magic MAX-FUNCTION)\n  (cond\n    ((sys/instance-direct? args Cons)\n     (let ((result args:car))\n       (set args args:cdr)\n       (while (sys/instance-direct? args Cons)\n         (set result (max result args:car))\n         (set args args:cdr))\n       result))\n    (#t (- (literally INF)))))\n\n(defn gcd (&rest args)\n  (list/fold #'binary-gcd args 0))\n\n(defn lcm (&rest args)\n  (list/fold #'binary-lcm args 1))\n\n(defn binary-gcd (a b) private\n  (while (/= b 0)\n    (let ((tmp a))\n      (set a b)\n      (set b (mod tmp b))))\n  a)\n\n(defn binary-lcm (a b) private\n  (/ (* a b) (binary-gcd a b)))\n\n;;; Comparison operators\n\n(defn = (x &rest args)\n  (sys/call-magic EQUAL)\n  (while (sys/instance-direct? args Cons)\n    (cond\n      ((= x args:car) ())\n      (#t (return #f)))\n    (set x args:car)\n    (set args args:cdr))\n  #t)\n\n(defn < (x &rest args)\n  (sys/call-magic LESS-THAN)\n  (while (sys/instance-direct? args Cons)\n    (cond\n      ((< x args:car) ())\n      (#t (return #f)))\n    (set x args:car)\n    (set args args:cdr))\n  #t)\n\n(defn > (x &rest args)\n  (sys/call-magic GREATER-THAN)\n  (while (sys/instance-direct? args Cons)\n    (cond\n      ((> x args:car) ())\n      (#t (return #f)))\n    (set x args:car)\n    (set args args:cdr))\n  #t)\n\n(defn <= (x &rest args)\n  (sys/call-magic LESS-THAN-OR-EQUAL)\n  (while (sys/instance-direct? args Cons)\n    (cond\n      ((<= x args:car) ())\n      (#t (return #f)))\n    (set x args:car)\n    (set args args:cdr))\n  #t)\n\n(defn >= (x &rest args)\n  (sys/call-magic GREATER-THAN-OR-EQUAL)\n  (while (sys/instance-direct? args Cons)\n    (cond\n      ((>= x args:car) ())\n      (#t (return #f)))\n    (set x args:car)\n    (set args args:cdr))\n  #t)\n\n(defn /= (x &rest args)\n  (sys/call-magic NOT-EQUAL)\n  (let ((outer (cons x args)))\n    (while (sys/instance-direct? outer Cons)\n      (let ((inner outer:cdr))\n        (while (sys/instance-direct? inner Cons)\n          (cond\n            ((/= outer:car inner:car) ())\n            (#t (return #f)))\n          (set inner inner:cdr)))\n      (set outer outer:cdr)))\n  #t)\n\n(defn equal? (x &rest args)\n  (while (sys/instance-direct? args Cons)\n    (cond\n      ((bin-equal x args:car) ())\n      (#t (return #f)))\n    (set x args:car)\n    (set args args:cdr))\n  #t)\n\n(defn bin-equal (a b) private\n  (cond\n    ((cond ((instance? a GDLisp:BaseArray) (instance? b GDLisp:BaseArray)) (#t #f))\n     (array-equal a b))\n    ((cond ((instance? a GDLisp:Dictionary) (instance? b GDLisp:Dictionary)) (#t #f))\n     (dict-equal a b))\n    ((cond ((instance? a Cons) (instance? b Cons)) (#t #f))\n     (cons-equal a b))\n    ((cond ((instance? a GDLisp:Number) (instance? b GDLisp:Number)) (#t #f))\n     (= a b))\n    ((= (GDLisp:typeof a) (GDLisp:typeof b))\n     (= a b))\n    (#t #f)))\n\n(defn array-equal (a b) private\n  (cond\n    ((/= (len a) (len b)) #f)\n    (#t (let ((i 0)\n              (upper (len a)))\n          (while (< i upper)\n            (cond ((not (bin-equal (elt a i) (elt b i))) (return #f)))\n            (set i (+ i 1)))\n          #t))))\n\n(defn dict-equal (a b) private\n  (cond\n    ((/= (len a) (len b)) #f)\n    (#t (for key (a:keys)\n          (cond ((not (bin-equal (elt a key) (elt b key))) (return #f))))\n        #t)))\n\n(defn cons-equal (a b) private\n  (cond\n    ((bin-equal a:car b:car) (bin-equal a:cdr b:cdr))\n    (#t #f)))\n\n;;; GDScript built-ins that we use unmodified\n\n;; Note: These all repeat the name of the function. By default,\n;; `sys/declare` will refuse to declare a name that conflicts with a\n;; GDScript language keyword (adding an `_` to the name\n;; automatically). But in our case, we want these names to represent\n;; the GDScript keywords for real, so we override this using the\n;; explicit `sys/declare` naming syntax and force it to use the name\n;; anyway.\n\n(sys/declare superfunction (int int) (a) public)\n(sys/declare superfunction (randomize randomize) () public)\n(sys/declare superfunction (randi randi) () public)\n(sys/declare superfunction (randf randf) () public)\n(sys/declare superfunction (rand-range rand-range) (a b) public)\n(sys/declare superfunction (clamp clamp) (a b c) public)\n(sys/declare superfunction (abs abs) (a) public)\n(sys/declare superfunction (get-global-mouse-position get-global-mouse-position) () public) ; TODO Definitely want to wrap this (and all of the mouse functions) in a nice namespace or module or something\n(sys/declare superfunction (push-error push-error) (a) public)\n(sys/declare superfunction (push-warning push-warning) (a) public)\n(sys/declare superfunction (load load) (a) public)\n(sys/declare superfunction (acos acos) (a) public)\n(sys/declare superfunction (asin asin) (a) public)\n(sys/declare superfunction (atan atan) (a) public)\n(sys/declare superfunction (atan2 atan2) (a b) public)\n(sys/declare superfunction (cos cos) (a) public)\n(sys/declare superfunction (cosh cosh) (a) public)\n(sys/declare superfunction (sin sin) (a) public)\n(sys/declare superfunction (sinh sinh) (a) public)\n(sys/declare superfunction (tan tan) (a) public)\n(sys/declare superfunction (tanh tanh) (a) public)\n(sys/declare superfunction (ceil ceil) (a) public)\n(sys/declare superfunction (char char) (a) public)\n(sys/declare superfunction (exp exp) (a) public)\n(sys/declare superfunction (floor floor) (a) public)\n(sys/declare superfunction (sqrt sqrt) (a) public)\n(sys/declare superfunction (fmod fmod) (a b) public)\n(sys/declare superfunction (fposmod fposmod) (a b) public)\n(sys/declare superfunction (posmod posmod) (a b) public)\n(sys/declare superfunction (sign sign) (a) public)\n(sys/declare superfunction (ord ord) (a) public)\n(sys/declare superfunction (hash hash) (a) public)\n(sys/declare superfunction (get-stack get-stack) () public)\n(sys/declare superfunction (is-nan is-nan) (a) public)\n(sys/declare superfunction (is-inf is-inf) (a) public)\n(sys/declare superfunction (is-equal-approx is-equal-approx) (a b) public)\n(sys/declare superfunction (is-zero-approx is-zero-approx) (a) public)\n(sys/declare superfunction (inverse-lerp inverse-lerp) (a b c) public)\n(sys/declare superfunction (lerp lerp) (a b c) public)\n(sys/declare superfunction (lerp-angle lerp-angle) (a b c) public)\n(sys/declare superfunction (pow pow) (a b) public)\n(sys/declare superfunction (stepify stepify) (a b) public)\n(sys/declare superfunction (step-decimals step-decimals) (a) public)\n(sys/declare superfunction (seed seed) (a) public)\n(sys/declare superfunction (rand-seed rand-seed) (a) public)\n(sys/declare superfunction (deg2rad deg2rad) (a) public)\n(sys/declare superfunction (rad2deg rad2deg) (a) public)\n(sys/declare superfunction (db2linear db2linear) (a) public)\n(sys/declare superfunction (linear2db linear2db) (a) public)\n(sys/declare superfunction (is-instance-valid is-instance-valid) (a) public)\n(sys/declare superfunction (log log) (a) public)\n(sys/declare superfunction (wrapf wrapf) (a b c) public)\n(sys/declare superfunction (wrapi wrapi) (a b c) public)\n(sys/declare superfunction (print-stack print-stack) () public)\n(sys/declare superfunction (round round) (a) public)\n(sys/declare superfunction (cartesian2polar cartesian2polar) (a b) public)\n(sys/declare superfunction (polar2cartesian polar2cartesian) (a b) public)\n(sys/declare superfunction (range-lerp range-lerp) (a b c d e) public)\n(sys/declare superfunction (move-toward move-toward) (a b c) public)\n(sys/declare superfunction (nearest-po2 nearest-po2) (a) public)\n(sys/declare superfunction (instance-from-id instance-from-id) (a) public)\n(sys/declare superfunction (parse-json parse-json) (a) public)\n(sys/declare superfunction (to-json to-json) (a) public)\n(sys/declare superfunction (validate-json validate-json) (a) public)\n(sys/declare superfunction (dict2inst dict2inst) (a) public)\n(sys/declare superfunction (inst2dict inst2dict) (a) public)\n(sys/declare superfunction (str2var str2var) (a) public)\n(sys/declare superfunction (var2str var2str) (a) public)\n(sys/declare superfunction (weakref weakref) (a) public)\n(sys/declare superfunction (ease ease) (a b) public)\n(sys/declare superfunction (funcref funcref) (a b) public)\n(sys/declare superfunction (type-exists type-exists) (a) public)\n(sys/declare superfunction (smoothstep smoothstep) (a b c) public)\n\n;; Note that, in the spirit of internal consistency with the type\n;; names, we do name some of these functions differently. For\n;; instance, the GDScript function `bool` is mapped to the GDLisp\n;; function `Bool`, for consistency with the `Bool` type.\n\n(sys/declare superfunction (Bool bool) (a) public)\n(sys/declare superfunction (Int int) (a) public)\n(sys/declare superfunction (Float float) (a) public)\n(sys/declare superfunction (String String) (a) public)\n(sys/declare superfunction (AABB AABB) (a b) public)\n(sys/declare superfunction (RID RID) (a) public)\n(sys/declare superfunction (Dictionary Dictionary) (a) public)\n(sys/declare superfunction (Array Array) (a) public)\n(sys/declare superfunction (PoolColorArray PoolColorArray) (a) public)\n(sys/declare superfunction (PoolByteArray PoolByteArray) (a) public)\n(sys/declare superfunction (PoolIntArray PoolIntArray) (a) public)\n(sys/declare superfunction (PoolRealArray PoolRealArray) (a) public)\n(sys/declare superfunction (PoolVector2Array PoolVector2Array) (a) public)\n(sys/declare superfunction (PoolVector3Array PoolVector3Array) (a) public)\n\n(sys/declare superfunction (Vector2 Vector2) (a b) public)\n(sys/declare superfunction (Vector3 Vector3) (a b c) public)\n\n(sys/min-godot-version 3050000\n  (sys/declare superfunction (deep-equal deep-equal) (a b) public))\n\n;;; Varargs functions\n\n;; (See https://github.com/Mercerenies/gdlisp/issues/79 for details on\n;; why we have to wrap these ourselves)\n\n(defn str (x &arr args)\n  (sys/call-magic VARARG-STR)\n  (let ((result (str x)))\n    (for arg args\n       (set result (+ result (str arg))))\n    result))\n\n(defn printerr (&arr args)\n  (sys/call-magic VARARG-PRINTERR)\n  (let ((result \"\"))\n    (for arg args\n       (set result (+ result (str arg))))\n    (printerr result)))\n\n(defn printraw (&arr args)\n  (sys/call-magic VARARG-PRINTRAW)\n  (let ((result \"\"))\n    (for arg args\n       (set result (+ result (str arg))))\n    (printraw result)))\n\n(defn print-debug (&arr args)\n  (sys/call-magic VARARG-PRINTDEBUG)\n  (let ((result \"\"))\n    (for arg args\n       (set result (+ result (str arg))))\n    (print-debug result)))\n\n(defn print (&arr args)\n  (sys/call-magic VARARG-PRINT)\n  (let ((result \"\"))\n    (for arg args\n       (set result (+ result (str arg))))\n    (print result)))\n\n(defn prints (&arr args)\n  (sys/call-magic VARARG-PRINTS)\n  (let ((result \"\")\n        (first #t))\n    (for arg args\n       (set result (+ result (cond (first \"\") (#t \" \")) (str arg)))\n       (set first #f))\n    (print result)))\n\n(defn printt (&arr args)\n  (sys/call-magic VARARG-PRINTT)\n  (let ((result \"\")\n        (first #t))\n    (for arg args\n       (set result (+ result (cond (first \"\") (#t \"\\t\")) (str arg)))\n       (set first #f))\n    (print result)))\n\n(defn range (a &opt b c)\n  (sys/call-magic VARARG-RANGE)\n  (cond\n    ((= b nil) (range a))\n    ((= c nil) (range a b))\n    (#t (range a b c))))\n\n(defn Color8 (a b c &opt d)\n  (sys/call-magic VARARG-COLOR8)\n  (cond\n    ((= d nil) (Color8 a b c))\n    (#t (Color8 a b c d))))\n\n(defn ColorN (a &opt b)\n  (sys/call-magic VARARG-COLORN)\n  (cond\n    ((= b nil) (ColorN a))\n    (#t (ColorN a b))))\n\n(defn Rect2 (a b &opt c d) ; TODO Not a perfect translation of the pair of overloads provided\n  (sys/call-magic VARARG-RECT2)\n  (cond\n    ((= c nil) (Rect2 a b))\n    (#t (Rect2 a b c d))))\n\n(defn Transform2D (a &opt b c)\n  (sys/call-magic VARARG-TRANSFORM2D)\n  (cond\n    ((= b nil) (Transform2D a))\n    ((= c nil) (Transform2D a b))\n    (#t (Transform2D a b c))))\n\n(defn Plane (a b &opt c d)\n  (sys/call-magic VARARG-PLANE)\n  (cond\n    ((= c nil) (Plane a b))\n    ((= d nil) (Plane a b c))\n    (#t (Plane a b c d))))\n\n(defn Quat (a &opt b c d) ; TODO Not a perfect translation of the overloads provided\n  (sys/call-magic VARARG-QUAT)\n  (cond\n    ((= b nil) (Quat a))\n    ((= c nil) (Quat a b))\n    (#t (Quat a b c d))))\n\n(defn Basis (a &opt b c)\n  (sys/call-magic VARARG-BASIS)\n  (cond\n    ((= b nil) (Basis a))\n    ((= c nil) (Basis a b))\n    (#t (Basis a b c))))\n\n(defn Transform (a &opt b c d) ; TODO Not a perfect translation of the overloads provided\n  (sys/call-magic VARARG-TRANSFORM)\n  (cond\n    ((= b nil) (Transform a))\n    ((= c nil) (Transform a b))\n    (#t (Transform a b c d))))\n\n(defn Color (a &opt b c d) ; TODO Not a perfect translation of the overloads provided\n  (sys/call-magic VARARG-COLOR)\n  (cond\n    ((= b nil) (Color a))\n    ((= d nil) (Color a b c))\n    (#t (Color a b c d))))\n\n(defn bytes2var (a &opt b)\n  (sys/call-magic VARARG-BYTES2VAR)\n  (cond\n    ((= b nil) (bytes2var a))\n    (#t (bytes2var a b))))\n\n(defn var2bytes (a &opt b)\n  (sys/call-magic VARARG-VAR2BYTES)\n  (cond\n    ((= b nil) (var2bytes a))\n    (#t (var2bytes a b))))\n\n;; TYPE_* Constants\n\n;;; Built-In Macros\n\n(defmacro or (&rest args)\n  (let ((args (list/reverse args)))\n    (cond\n      (args\n       (let ((result `((#t ,args:car))))\n         (set args args:cdr)\n         (while (/= args nil)\n           (set result `((,args:car) . ,result))\n           (set args args:cdr))\n         `(cond . ,result)))\n      (#t #f))))\n\n(defmacro and (&rest args)\n  (let ((args (list/reverse args)))\n    (cond\n      (args\n       (let ((result `((#t ,args:car))))\n         (set args args:cdr)\n         (while (/= args nil)\n           (set result `(((not ,args:car) #f) . ,result))\n           (set args args:cdr))\n         `(cond . ,result)))\n       (#t #t))))\n\n(defmacro let* (vars &rest body)\n  (cond\n    ((= vars nil) `(progn ,.body))\n    (#t `(let (,vars:car)\n           (let* ,vars:cdr ,.body)))))\n\n(defmacro defvars (&rest args)\n  (let ((arr []))\n    (while (/= args nil)\n      (arr:push_back (list 'defvar args:car))\n      (set args args:cdr))\n    `(progn ,.arr)))\n\n(defmacro when (cnd &rest args)\n  `(cond\n     (,cnd (progn ,.args))))\n\n(defmacro unless (cnd &rest args)\n  `(cond\n     (,cnd ())\n     (#t (progn ,.args))))\n\n(defmacro if (cnd t &opt f)\n  `(cond\n     (,cnd ,t)\n     (#t ,f)))\n\n(defmacro update (field updater)\n  (cond\n    ((not (instance? updater Cons)) (set updater (list updater))))\n  (set updater:cdr (cons field updater:cdr))\n  `(set ,field ,updater))\n\n(defmacro yield* (arg)\n  (let ((symbol (gensym \"_yield\")))\n    `(let ((,symbol ,arg))\n       (while (and (instance? ,symbol GDScriptFunctionState) ((unquote symbol):is-valid))\n         (yield)\n         (set ,symbol ((unquote symbol):resume)))\n       ,symbol)))\n\n(defmacro quit ()\n  ;; Just a small helper macro to make it easier to exit the game.\n  ;; Mainly useful in the REPL.\n  '((GDLisp:get-tree):quit))\n\n(defmacro this-file ()\n  '(sys/special-ref this-file))\n\n(defmacro this-filename ()\n  '(sys/special-ref this-filename))\n\n(defmacro this-true-filename ()\n  '(sys/special-ref this-true-filename))\n\n;; TODO Document the semantics of this macro and what preconditions\n;; are necessary for it to be safe to use.\n(defmacro contextual-load (arg)\n  `(load (sys/context-filename ,arg)))\n\n(defmacro deflazy (name value &rest modifiers)\n  (let ((fn-name (gensym \"_lazy\"))\n        (this-file (gensym \"_this_file\"))\n        (value-var (gensym \"_value\"))\n        (meta-name (\"__gdlisp_Lazy_{}\":format [(gensym):__gdlisp_contents] \"{}\"))) ; TODO Find a better way to convert symbol to string than accessing a, theoretically, private field\n    `(progn\n       (defn ,fn-name ()\n         (let ((,this-file (this-file)))\n           (if ((unquote this-file):has-meta ,meta-name)\n               ((unquote this-file):get-meta ,meta-name)\n               (let ((,value-var ,value))\n                 ((unquote this-file):set-meta ,meta-name ,value-var)\n                 ,value-var))))\n       (define-symbol-macro ,name (list (list 'access-slot (list 'contextual-load (this-true-filename)) ',fn-name)) ,.modifiers))))\n\n(defmacro defobject (name parent &opt visibility &rest body)\n  (cond\n    ((= visibility nil)\n     (set visibility 'public))\n    ((not (instance? visibility Symbol))\n     (set body (cons visibility body)) ; It's not a modifier, so it's part of the body\n     (set visibility 'public))\n    ((= visibility 'public)\n     nil)\n    ((= visibility 'private)\n     nil)\n    (#t\n     (set body (cons visibility body)) ; It's not a modifier, so it's part of the body\n     (set visibility 'public)))\n  `(deflazy ,name (new ,parent ,.body) ,visibility))\n\n(defmacro list/for (var list &rest body)\n  (let ((iter (gensym)))\n    `(let ((,iter ,list)\n           (,var ()))\n       (while (/= ,iter ())\n         (set ,var (access-slot ,iter car))\n         (set ,iter (access-slot ,iter cdr))\n         ,.body))))\n\n(defmacro -> (arg &rest forms)\n  (cond\n    ((= forms nil) arg)\n    (#t (let ((first (cond ((instance? forms:car Cons) forms:car) (#t (list forms:car))))\n              (rest forms:cdr))\n          (let ((new-arg (cons first:car (cons arg first:cdr))))\n            `(-> ,new-arg ,.rest))))))\n\n(defmacro ->> (arg &rest forms)\n  (cond\n    ((= forms nil) arg)\n    (#t (let ((first (cond ((instance? forms:car Cons) forms:car) (#t (list forms:car))))\n              (rest forms:cdr))\n          (let ((new-arg (snoc first arg)))\n            `(->> ,new-arg ,.rest))))))\n\n(defmacro as-> (arg var &rest forms)\n  (cond\n    ((= forms nil) arg)\n    (#t (let ((first forms:car)\n              (rest forms:cdr))\n          `(as-> (let ((,var ,arg)) ,first) ,var ,.rest)))))\n"
  },
  {
    "path": "MacroServer/TestLoadedFile.gd",
    "content": "# Copyright 2023 Silvio Mayolo\n#\n# This file is part of GDLisp.\n#\n# GDLisp is free software: you can redistribute it and/or modify it\n# 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# GDLisp is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n# This file is used exclusively for testing purposes and is not used\n# in compilation of actual GDScript code.\n\nstatic func example():\n    return \"Test succeeded\"\n"
  },
  {
    "path": "MacroServer/main.gd",
    "content": "# Copyright 2023 Silvio Mayolo\n#\n# This file is part of GDLisp.\n#\n# GDLisp is free software: you can redistribute it and/or modify it\n# 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# GDLisp is distributed in the hope that it will be useful, but\n# WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n# General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextends Node\n\nvar peer = null\nvar loaded_files = null\n\n\nfunc _ready():\n    loaded_files = []\n\n    var port_number = int(OS.get_environment(\"GDLISP_PORT_NUMBER\"))\n\n    peer = StreamPeerTCP.new()\n    peer.big_endian = true\n    peer.connect_to_host(\"127.0.0.1\", port_number)\n\n\nfunc _process(_delta):\n    if peer.get_available_bytes() > 0:\n        var json_result = JSON.parse(peer.get_string())\n        if json_result.error == OK:\n            var payload = json_result.result\n            run_command(payload)\n        else:\n            push_error(\"Invalid JSON \" + json_result.error_string)\n            peer.put_string(failed_response(ERR_INVALID_DATA, \"Invalid JSON \" + json_result.error_string))\n\n\nfunc failed_response(error_code, error_string = \"\"):\n    var response = {\n        \"error_code\": error_code,\n        \"error_string\": error_string,\n        \"response_string\": \"\"\n    }\n    return JSON.print(response)\n\n\nfunc successful_response(response_string):\n    var response = {\n        \"error_code\": OK,\n        \"error_string\": \"\",\n        \"response_string\": response_string\n    }\n    return JSON.print(response)\n\n\nfunc run_command(payload):\n    var cmd = payload['command']\n    var args = payload['args']\n    match cmd:\n        \"quit\":\n            peer.put_string(successful_response(\"Acknowledged\\nQuitting...\"))\n            get_tree().quit()\n        \"ping\":\n            peer.put_string(successful_response(\"pong\"))\n        \"eval\":\n            var input = args[0]\n            var result = eval(input)\n            peer.put_string(successful_response(pretty(result)))\n        \"exec\":\n            var input = args[0]\n            var result = exec(input)\n            peer.put_string(successful_response(pretty(result)))\n        \"load\":\n            var input = args[0]\n            var idx = len(loaded_files)\n            loaded_files.push_back(load(input))\n            peer.put_string(successful_response(pretty(idx)))\n\nfunc eval(input):\n    return exec(\"    return \" + input)\n\n# Funny hack, thanks Godot Q&A! :)\n#\n# https://godotengine.org/qa/339/does-gdscript-have-method-to-execute-string-code-exec-python?show=362#a362\nfunc exec(input):\n    var script = GDScript.new()\n    script.set_source_code(\"func exec(MAIN):\\n\" + input)\n    script.reload()\n\n    var obj = Reference.new()\n    obj.set_script(script)\n\n    return obj.exec(self)\n\n\n# I'll probably end up migrating this to GDLisp.gd proper at some\n# point, but for now, here it is.\nfunc pretty(value):\n    if value == null:\n        return \"()\"\n    elif value is bool and value:\n        return \"#t\"\n    elif value is bool and not value:\n        return \"#f\"\n    elif value is int or value is float:\n        return str(value)\n    elif value is Vector2:\n        return \"V{{} {}}\".format([pretty(value.x), pretty(value.y)], \"{}\")\n    elif value is Vector3:\n        return \"V{{} {} {}}\".format([pretty(value.x), pretty(value.y), pretty(value.z)], \"{}\")\n    elif value is Array:\n        var s = \"[\"\n        var first = true\n        for x in value:\n            if not first:\n                s += \" \"\n            s += pretty(x)\n            first = false\n        return s + \"]\"\n    elif value is Dictionary:\n        var s = \"{\"\n        var first = true\n        for k in value:\n            if not first:\n                s += \" \"\n            s += pretty(k) + \" \" + pretty(value[k])\n            first = false\n        return s + \"}\"\n    elif value is String:\n        var s = \"\\\"\"\n        for x in value:\n            match x:\n                \"\\n\":\n                    s += \"\\\\n\"\n                \"\\t\":\n                    s += \"\\\\t\"\n                \"\\r\":\n                    s += \"\\\\r\"\n                \"\\a\":\n                    s += \"\\\\a\"\n                \"\\b\":\n                    s += \"\\\\b\"\n                \"\\f\":\n                    s += \"\\\\f\"\n                \"\\v\":\n                    s += \"\\\\v\"\n                \"\\\"\":\n                    s += \"\\\\\\\"\"\n                \"\\\\\":\n                    s += \"\\\\\\\\\"\n                _:\n                    s += x\n        return s + \"\\\"\"\n    elif value.has_meta(\"__gdlisp_Primitive_Cons\"):\n        return \"(\" + _pretty_list(value) + \")\"\n    elif value.has_meta(\"__gdlisp_Primitive_Symbol\"):\n        return value.__gdlisp_contents\n    else:\n        return str(value)\n\n\nfunc _pretty_list(value):\n    # The same logic as `crate::sxp::ast::fmt_list` to make list\n    # output pretty.\n    if value.cdr == null:\n        return pretty(value.car)\n    elif typeof(value.cdr) == TYPE_OBJECT and value.cdr.has_meta(\"__gdlisp_Primitive_Cons\"):\n        return pretty(value.car) + \" \" + _pretty_list(value.cdr)\n    else:\n        return pretty(value.car) + \" . \" + pretty(value.cdr)\n"
  },
  {
    "path": "MacroServer/main.tscn",
    "content": "[gd_scene load_steps=2 format=2]\n\n[ext_resource path=\"res://main.gd\" type=\"Script\" id=1]\n\n[node name=\"main\" type=\"Node\"]\nscript = ExtResource( 1 )\n"
  },
  {
    "path": "MacroServer/project.godot",
    "content": "\nconfig_version=4\n\n[application]\n\nrun/main_scene=\"res://main.tscn\"\n\n[autoload]\n\nGDLisp=\"*res://GDLisp.gd\"\n"
  },
  {
    "path": "README.md",
    "content": "[![GitHub Actions: Ubuntu](https://github.com/mercerenies/gdlisp/actions/workflows/test-Godotv3.5-Ubuntu.yml/badge.svg)](https://github.com/Mercerenies/gdlisp/actions/workflows/test-Godotv3.5-Ubuntu.yml)\n[![GitHub Actions: Ubuntu](https://github.com/mercerenies/gdlisp/actions/workflows/test-Godotv3.5-Mono-Ubuntu.yml/badge.svg)](https://github.com/Mercerenies/gdlisp/actions/workflows/test-Godotv3.5-Mono-Ubuntu.yml)\n[![GitHub Actions: Ubuntu](https://github.com/mercerenies/gdlisp/actions/workflows/test-Godotv3.4-Ubuntu.yml/badge.svg)](https://github.com/Mercerenies/gdlisp/actions/workflows/test-Godotv3.4-Ubuntu.yml)\n[![GitHub Actions: Ubuntu](https://github.com/mercerenies/gdlisp/actions/workflows/test-Godotv3.3.3-Ubuntu.yml/badge.svg)](https://github.com/Mercerenies/gdlisp/actions/workflows/test-Godotv3.3.3-Ubuntu.yml)\n\n# GDLisp\n\nLisp for the [Godot](https://godotengine.org/) platform! This language\naims to be a Lisp dialect which compiles to GDScript.\n\nThis project is built using [Cargo](https://doc.rust-lang.org/cargo/)\nwith [Rake](https://ruby.github.io/rake/) as a wrapper for custom\nbuild scripts. Use `rake test` to run the test suite, and use `rake\nrun` to compile stdin input to GDScript.\n\nThe current version of GDLisp is tested against Godot 3.5 and expects\na command called `godot` to be on your system's path which points to\nthe Godot executable.\n\nCheck out the [official\ndocumentation](https://gdlisp.readthedocs.io/en/stable/) for a\n\"Getting Started\" guide!\n\nGDLisp is not affiliated with or endorsed by the Godot project in any\nofficial capacity. This is an independent project.\n\n## Features\n\n* Support for standard GDScript functionality, including function\n  declarations, class declarations, signals, etc.\n* The expression-based semantics we all love from Lisp\n* Lambdas / anonymous functions as well as anonymous classes\n* Support for compile-time macros that run in the GDLisp compiler\n  itself\n\n## FAQs\n\n### Can I use GDLisp today?\n\nAbsolutely! GDLisp is production-ready, and I encourage everyone to\ntry it out and provide any feedback on the issue tracker.\n\n### What Godot versions is GDLisp compatible with?\n\nGDLisp works with Godot 3.x. The next major release of GDLisp will be\nfully compatible with Godot 4, though there's no definite timeline on\nthat as yet.\n\n### Can I use GDLisp and GDScript in the same project?\n\nYou certainly can! GDLisp constructs compile in a straightforward way\nto existing Godot concepts. Classes and functions written in GDLisp\ncan be used from GDScript, and vice versa.\n\n## License\n\nGDLisp is distributed under the terms of the GNU General Public\nLicense version 3 or later. For more details, see `COPYING`.\n\nAs a special exception to the GNU General Public License, the GDLisp\nsupport file `GDLisp.lisp` can be used and redistributed without any\nrestrictions.\n"
  },
  {
    "path": "Rakefile",
    "content": "\nrequire 'logger'\nrequire 'fileutils'\n\nif Gem.win_platform?\n  # Needed to be able to create symlinks on Windows.\n  require 'win32/file'\nend\n\n$logger = Logger.new($stdout)\n\nrelease_flag =\n  if ENV['GDLISP_RELEASE']\n    ['--release']\n  else\n    []\n  end\n\ntask default: %w[run]\n\ntask :clippy do |t, args|\n  sh 'cargo', 'clippy', *args\nend\n\ntask :doc do |t, args|\n  ENV['RUSTDOCFLAGS'] ||= ''\n  ENV['RUSTDOCFLAGS'] += ' -D warnings'\n  sh 'cargo', 'doc', *args\nend\n\ntask :build_rs do |t, args|\n  sh 'cargo', 'build', *release_flag\nend\n\ntask build: :build_rs do |t, args|\n  sh 'cargo', 'run', *release_flag, '--', '--compile-stdlib'\n  cp 'GDLisp.gd', 'MacroServer/GDLisp.gd'\n  cp_r 'MacroServer', 'target/debug'\n  cp 'GDLisp.msgpack', 'target/debug'\n  cp_r 'MacroServer', 'target/debug/deps'\n  cp 'GDLisp.msgpack', 'target/debug/deps'\n  cp_r 'MacroServer', 'target/release'\n  cp 'GDLisp.msgpack', 'target/release'\n  cp_r 'MacroServer', 'target/release/deps'\n  cp 'GDLisp.msgpack', 'target/release/deps'\n  if release_flag.include? '--release'\n    mkdir_p 'bin/'\n    File.delete('bin/gdlisp') if File.exist?('bin/gdlisp')\n    File.symlink('../target/release/gdlisp', 'bin/gdlisp')\n  end\nend\n\ntask run: :build do |t, args|\n  sh 'cargo', 'run', *release_flag, *args\nend\n\ntask test: :build do |t, args|\n  sh 'cargo', 'test', *release_flag, *args\nend\n\ntask :clean do |t, args|\n  sh 'cargo', 'clean', *args\nend\n"
  },
  {
    "path": "build.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate lalrpop;\n\nfn main() {\n    lalrpop::process_root().unwrap();\n}\n"
  },
  {
    "path": "doc/README.md",
    "content": "\n# GDLisp Official Documentation\n\nThis is the official documentation for the GDLisp language.\n\n * [`internal/`](internal/) - Information useful for contributors to\n   this repository. Developers *using* GDLisp should not need the\n   information here.\n"
  },
  {
    "path": "doc/internal/CompilationStages.md",
    "content": "\n# Compilation Stages\n\n```mermaid\nflowchart LR\n    subgraph AST\n      direction TB\n      Parse[Parse .lisp file into AST]\n    end\n    subgraph IR\n      direction TB\n      LoadIR[Load AST into IR data structures]\n      Scopes[Check for Scope Conflicts]\n      Loops[\"Validate Looping Constructs (Break/Continue)\"]\n      LoadIR-->Scopes-->Loops\n    end\n    subgraph GDScript\n      direction TB\n      Bind[Bind all top-level declarations in a global symbol table]\n      Const[Check that any expressions in constant position are actually constant]\n      Compile[Compile IR into GDScript syntax representation]\n      Optimize[Run GDScript-Level Optimizations]\n      Output[Output GDScript to .gd file]\n      Bind-->Const-->Compile-->Optimize-->Output\n    end\n    AST-->IR-->GDScript\n```\n\nThis document details the stages of the GDLisp Compiler.\n\nGDLisp source code begins in a `.lisp` source file. The parser loads\nthat into an abstract syntax tree (AST). At this point, the code is\n*only* being viewed as a sequence of S-expressions that has no further\nsemantic meaning.\n\nNext, the AST is loaded into a specially-designed intermediate\nrepresentation (IR). During loading into IR, any macros that are\nencountered are evaluated and interpolated. Crucially, after the code\nis loaded into its IR representation, no more macro expansion will\never need to happen. The IR understands basic notions, such as the\ndifference between an expression and a declaration, as well as control\nflow constructs like `cond` statements and assignment statements. It\ndoes *not* understand more detailed scoping rules regarding local\nvariables.\n\nOnce the IR is loaded, some validation is performed on the code in its\nIR form.\n* All scopes are checked for name conflicts. While name shadowing is\n  allowed (i.e. declaring a local function with the same name as a\n  global one, or a local function with the same name as a local\n  function in a strictly outer scope), declaring the same variable or\n  function twice in the same scope is forbidden. Additionally, the\n  top-level scope can declare at most one `main` class.\n* Any loop control constructs are checked. That is, all uses of\n  `break` and `continue` are audited to ensure that they make sense.\n  `break` and `continue` can only be used inside of loops, and they\n  cannot be used if there is a closure (such as a lambda) between the\n  control construct and the loop declaration.\n\nNext, the IR is analyzed, and a global symbol table is created. Any\ntop-level declarations have their names bound in the global symbol\ntable. This ensures that, during compilation, functions can access\nfunctions declared anywhere in the file.\n\nWith the symbol table, any expressions in constant position are\nchecked to make sure they are actually constant. This includes `const`\ndeclarations, the right-hand-side of `enum` values, and the inside of\n`export` clauses.\n\nThen the IR is compiled into an internal representation of GDScript.\nThis is basically GDScript source code, but represented as an abstract\nsyntax tree manipulable within the GDLisp engine. This compilation\ntakes into consideration the global symbol table and checks that any\nreferenced names (local or global) make sense in that context.\n\nOnce the GDScript source is built, some optimizations are performed on\nit to eliminate dead code, simplify convoluted constructs, and\ngenerally make the code better and faster. This step is specifically\ndisabled during unit and integration testing, except for those\nspecifically designed to test optimizations.\n\nFinally, the resulting GDScript source is stringified and written out\nto a `.gd` file.\n"
  },
  {
    "path": "doc/internal/README.md",
    "content": "\n# Internal GDLisp Documentation\n\n * [Compilation Stages](CompilationStages.md)\n"
  },
  {
    "path": "doc/readthedocs/Makefile",
    "content": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line, and also\n# from the environment for the first two.\nSPHINXOPTS    ?=\nSPHINXBUILD   ?= sphinx-build\nSOURCEDIR     = .\nBUILDDIR      = _build\n\n# Put it first so that \"make\" without argument is like \"make help\".\nhelp:\n\t@$(SPHINXBUILD) -M help \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n\n.PHONY: help Makefile\n\n# Catch-all target: route all unknown targets to Sphinx using the new\n# \"make mode\" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).\n%: Makefile\n\t@$(SPHINXBUILD) -M $@ \"$(SOURCEDIR)\" \"$(BUILDDIR)\" $(SPHINXOPTS) $(O)\n"
  },
  {
    "path": "doc/readthedocs/conf.py",
    "content": "# Configuration file for the Sphinx documentation builder.\n#\n# For the full list of built-in configuration values, see the documentation:\n# https://www.sphinx-doc.org/en/master/usage/configuration.html\n\n# -- Project information -----------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information\n\nproject = 'GDLisp'\ncopyright = '2022-2023, Mercerenies'\nauthor = 'Mercerenies'\n\n# -- General configuration ---------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration\n\nextensions = ['sphinxcontrib.mermaid']\n\ntemplates_path = ['_templates']\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\nhighlight_language = 'scheme'\n\n# -- Options for HTML output -------------------------------------------------\n# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output\n\n#html_theme = 'alabaster'\nhtml_theme = \"sphinx_rtd_theme\"\nhtml_static_path = ['_static']\n"
  },
  {
    "path": "doc/readthedocs/index.rst",
    "content": ".. GDLisp documentation master file, created by\n   sphinx-quickstart on Thu Dec  1 12:29:12 2022.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nWelcome to GDLisp's documentation!\n==================================\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents:\n\n   tutorial/index.rst\n   reference/index.rst\n"
  },
  {
    "path": "doc/readthedocs/make.bat",
    "content": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build\r\n)\r\nset SOURCEDIR=.\r\nset BUILDDIR=_build\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.https://www.sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "doc/readthedocs/reference/classes.rst",
    "content": "\n.. _classes:\n\nClasses and Objected-Oriented Programming\n=========================================\n\nGDLisp, like GDScript, is an object-oriented programming language.\nGDLisp implements a similar object inheritance mechanism to GDScript,\nallowing you to define classes which inherit from exactly one\nsuperclass and to create instances of those classes.\n\nClasses are declared at the top-level of a module as follows. Classes\nmay *not* be nested within one another.\n\n::\n\n  (defclass ClassName (ParentClassName)\n    body ...)\n  (defclass ClassName (ParentClassName) public-or-private\n    body ...)\n\nThe class name is a symbol literal. By convention, class names in\nGodot are written in ``CamelCase``, though this is not a requirement.\nThe parent class is a symbol literal whose value is a class known at\ncompile-time. The parent class name may optionally be followed by a\n:ref:`visibility modifier <visibility-modifiers>`. Class names are\nplaced in the value namespace, similar to constants and enums.\n\nThe parent class name may be omitted by simply replacing it with the\nempty list ``()``. In this case, the parent class is assumed to be the\nbuilt-in type ``Reference``. For example, the following class\nimplicitly has parent class ``Reference``.\n\n::\n\n  (defclass MyNewReferenceType ()\n    body ...)\n\nAfter the class name, the class body consists of zero or more class\ndeclarations. These are somewhat similar to normal module-level\ndeclarations, but there are some forms that only make sense inside of\nclasses, and there are some subtle differences between the two, so\nit's best to treat the two scopes as distinct.\n\nMacro expansion takes place inside of class declaration scopes, just\nlike it does at the top level of a module.\n\nInside of classes, there are four valid types of declarations:\n``defsignal``, ``defconst``, ``defvar``, and ``defn``. Additionally,\n:ref:`progn <progn>` forms can appear in class bodies and behave\nidentically to the same forms at the top-level.\n\nClass Constants\n---------------\n\n``defconst`` inside of classes works identically to the ``defconst``\ntop-level form. It defines a constant inside the class scope, whose\nvalue is known at compile-time. See :ref:`constants` for more details\non how this declaration type works.\n\nClass constants are considered \"static\". That is, it is not necessary\nto construct an instance in order to access a class constant. Class\nconstants can be accessed on the class directly *or* on an instance,\nand the syntax to do so uses ``access-slot`` (or, more conveniently,\nthe ``:`` colon syntax sugar). Given the following class, both\n``Foo:A`` and ``(Foo:new):A`` will evaluate to ``42``.\n\n::\n\n  (defclass Foo ()\n    (defconst A 42))\n\nClass Signals\n-------------\n\n::\n\n  (defsignal signal-name)\n  (defsignal signal-name (args ...))\n\nSignals are the only values defined in the signal namespace. A signal\nis defined by ``defsignal``, followed by the name of the signal, and\nthen followed by a :ref:`simple lambda list <simple-lambda-lists>`. If\nthe argument list is omitted, then it defaults to ``()``.\n\nInstance Variables\n------------------\n\n::\n\n  (defvar var-name)\n  (defvar var-name initial-value)\n\nInstance variables are defined using ``defvar``. An instance variable\nis a name that exists on *instances* of the class, not on the class\nitself. GDLisp has no concept of \"static\" instance variables.\n\nInstance variables can be accessed on instances of a class via the\n``access-slot`` syntax (more conveniently written using the infix\n``:`` operator). Given an instance ``my-player`` of type ``Player``,\nthe ``health`` field on this instance can be accessed via\n``my-player:health`` (equivalently, ``(access-slot my-player\nhealth)``).\n\nAn instance variable may optionally be followed by its initial value.\nIf provided, the initial value will be set at the very beginning of\nthe class' constructor, immediately after calling the superclass\nconstructor. If not provided, the initial value of the variable shall\nbe the null ``()`` object.\n\n.. Note:: GDLisp, like GDScript, has no notion of static instance\n          variables. All instance variables are scoped to a particular\n          instance.\n\nInitialization Time\n^^^^^^^^^^^^^^^^^^^\n\nBy default, instance variables for which an initial value is given are\ninitialized at object construction time, after the parent ``_init`` is\ncalled but before the current class' ``_init`` is executed. It is\noften useful to initialize instance variables when a node is first\nadded to the scene tree. To this end, a ``defvar`` for which an\ninitial value is provided can optionally be succeeded by the\n``onready`` symbol.\n\n::\n\n  (defvar var-name initial-value onready)\n\nA variable indicated in this way will have its value set immediately\nbefore the ``_ready`` method of the class is invoked, when the node is\nadded to the scene tree.\n\nClass and Instance Functions\n----------------------------\n\n::\n\n  (defn function-name (args ...)\n    body ...)\n\nClass-level functions are declared similarly to module-level\nfunctions, using the ``defn`` keyword, followed by the function name,\nthen a list of formal arguments, and finally the function body.\nClass-level functions take formal arguments as a :ref:`simple lambda\nlist <simple-lambda-lists>`, which means functions inside of a class\ndo *not* support optional or variable arguments.\n\nA function defined inside of a class is called on instances of the\nclass, using an ``access-slot`` form as the head of an S-expression in\nan expression context. That is, given an object ``foo``, the\nexpression ``(foo:bar 1 2 3)`` (or, written out in full,\n``((access-slot foo bar) 1 2 3)``) will invoke the instance function\ncalled ``bar`` on the object ``foo``, calling it with three arguments:\n``1``, ``2``, and ``3``.\n\nInside the body of an instance function, the argument names are bound\nwithin a local scope, similar to a module function. Additionally, the\nspecial variable name ``self`` is bound to the instance on which the\nfunction was invoked. The body expressions of the function are\nevaluated in order, and the final expression is returned. If the\nfunction has no body, then the null ``()`` object is returned. Like\nwith module functions, instance functions can be exited early with the\n``return`` special form.\n\nStatic Functions\n^^^^^^^^^^^^^^^^\n\n::\n\n  (defn function-name (args ...) static\n    body ...)\n\nA function may be marked as static by placing the keyword ``static``\nkeyword after the function's formal argument list. A static function\ncan be invoked on *either* an instance or the class itself using the\n``:`` (equivalently, ``access-slot``) forms to call the function. In\neither case, a static function behaves like an instance function\nexcept that ``self`` is never bound inside the function.\n\n.. _constructor-functions:\n\nConstructor Functions\n^^^^^^^^^^^^^^^^^^^^^\n\n::\n\n  (defn _init (args ...)\n    body ...)\n\nThe function called ``_init`` is special. This is the function which\nwill be invoked when a new instance of the class should be constructed\nvia ``new``. Any arguments passed to the class' ``new`` function will\nbe forwarded onto ``_init``.\n\n``_init`` takes a *constructor lambda list*, which permits a special\nform of syntax unique to constructors, rather than a simple lambda\nlist. See :ref:`constructor-lambda-lists` for details. ``_init`` can\nnever be static. Finally, ``_init`` never returns a value. The\n``return`` special form may still be used to exit the constructor\nearly, but its argument will be ignored.\n\nThe first expression in the body of a constructor function can be of\nthe form ``(super args ...)``, i.e. a proper list whose first element\nis the literal name ``super``. This will cause the constructor\nfunction to invoke the parent class' constructor with the arguments\ngiven. This must be the first expression in a constructor function. If\nan explicit ``super`` call is not supplied, then the parent class'\nconstructor will be called implicitly with no arguments.\n\n.. _getter-and-setter:\n\nGetter and Setter Functions\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n::\n\n  (defn (get field-name) ()\n    body ...)\n  (defn (set field-name) (arg)\n    body ...)\n\nRather than a literal symbol, the function name of a ``defn`` can be\none of the special forms ``(get field-name)`` or ``(set field-name)``\nwhere ``field-name`` is an arbitrary literal symbol. These define,\nrespectively, a getter and a setter function. Getters and setters can\nnever be static.\n\nA getter function, defined with the name ``(get field-name)``, must\ntake zero arguments. It will be invoked when the corresponding field\n(in the value namespace) is accessed on an instance of the class. That\nis, a getter method ``(get health)`` will be invoked on a class\n``Player`` if we have an instance of the class ``my-player`` and\nattempt to access the field ``my-player:health``.\n\nA setter function, defined with the name ``(set field-name)``, must\ntake exactly one argument. It will be invoked when the corresponding\nfield is *assigned to* with the ``set`` special form. That is, a\nfunction ``(set health)`` will be invoked on a class ``Player`` if we\nhave an instance of the class ``my-player`` and write ``(set\nmy-player:health some-value)``. The sole argument to a setter function\nis the right-hand side of the ``set`` special form. A setter function\nnever returns a value. Setters can be exited early with the ``return``\nspecial form, but the value returned will be ignored.\n\nSetters and getters for the same field may be defined on the same\nclass. It is an error to define an instance variable (via ``defvar``)\nand a setter or a getter for the same field name on the same class.\n\nSetters and getters are compatible with GDScript, in the sense that\nattempts to access or set the field from GDScript will also trigger\nthe getter or setter, respectively.\n\nSuperclass Calls\n^^^^^^^^^^^^^^^^\n\nWithin a non-static instance function, a special form of syntax is\navailable.\n\n::\n\n  (super:method-name args ...)\n\nAttempting to call a method on the literal symbol ``super`` will\ninvoke the method of the given name on the current instance, but\nconsidering only functions defined in the parent class or above.\n\nThis syntax only makes sense inside of instance functions in a class.\nThe behavior is undefined if this ``super`` call syntax is used in a\nsetter, getter, or constructor. ``super`` is *not* a value in the\nvalue namespace, and it is not permitted to assign ``super`` (on its\nown) to a variable or use it in some way other than the syntax shown.\n\nMain Classes\n------------\n\nIn GDScript, a single source file maps to a defined class. Functions\non a source file are, unless marked static, functions on *instances*\nof that class. GDLisp works differently. A GDLisp source file is a\nmodule, and it may *contain* one or more classes, but it is not itself\na class. These classes contained in a GDLisp source file will compile\nto *inner classes* in the resulting GDScript source file.\n\nHowever, there are good reasons to have control over this \"top-level\"\nclass in Godot. Packed scenes will always refer to a file's top-level\nclass, not to inner classes. So GDLisp provides a mechanism to define\na particular class that should be treated as the \"main\" class.\n\n::\n\n  (defclass ClassName (ParentClassName) main\n    body ...)\n\n\nAfter the class' parent name and before the class' body, the symbol\n``main`` can be written to indicate that this class is the module's\n\"main\" class. If your class has a visibility modifier, then the\n``main`` modifier can be written before or after the visibility\nmodifier (though a private ``main`` class makes very little sense).\n\nDesignating a class as the \"main\" class does not change how you refer\nto this class in GDLisp. It is still a class name defined on the\nmodule in the value namespace, just like any other class, and it will\nstill be instantiated, imported, and used in the exact same way. The\n``main`` designator does affect how the class is compiled, though.\nRather than compiling to an inner class, the main class compiles to\nthe top-level class of the GDScript file.\n\nThere are several limitations.\n\n* There can be at most one ``main`` class in a file.\n\n* There must be no conflicts between names defined inside the ``main``\n  class and names defined at the module level. That is, if a constant\n  is defined at the module level, then there must be no constants or\n  instance variables inside the class with the same name (up to\n  normalization). Likewise, if a function or macro is defined at the\n  top-level, then there must be no instance functions (static or\n  otherwise) with the same name (again, up to normalization).\n\nVisibility Inside a Class\n-------------------------\n\nA class name can, like most module declarations, be declared\n``public`` or ``private``. However, the elements *inside* of a class\nhave no visibility modifiers. There is no way to define private fields\nor instance functions in GDLisp. Everything defined inside of a class\nis presumed public.\n\nName Normalization Within Classes\n---------------------------------\n\nName normalization works slightly differently inside of classes. The\nrules for *how* names are normalized within classes are the same as at\nthe module level (see :ref:`name-normalization`). However, the\ndifference is in how names are resolved.\n\nAt the module level, a name must be referred to in the exact same way\nas it was defined. That is, if you define a function called\n``foo-bar``, then you must call it as ``(foo-bar ...)``. Even though,\nat runtime, the resulting GDScript function will be called ``foo_bar``\n(with an underscore in place of the dash), GDLisp will not allow you\nto call the function as ``(foo_bar ...)``.\n\nInside of classes, the rules are much more lenient, owing to Godot's\ndynamic nature. You may access fields or call functions on classes and\ninstances using *any* name that normalizes to the same name that was\nused to define the function or field.\n\nAs a consequence, you can call built-in GDScript instance functions\nusing the conventions of GDLisp, so you can get a child node from a\nnode by writing ``(parent-node:get-node \"ChildNodeName\")``. This will\nnormalize to a function call to ``get_node``, which is defined by\nGodot.\n"
  },
  {
    "path": "doc/readthedocs/reference/command-line.rst",
    "content": "\nThe GDLisp Command Line Tool\n============================\n\nThe core interface to the GDLisp compiler and programming language is\nthe command line tool ``gdlisp``. This tool is written mostly in Rust,\nwith a small portion written in GDScript for bootstrapping purposes.\n\n.. _building:\n\nBuilding\n--------\n\n``gdlisp`` is built using Rake, the Ruby task management tool. The\nfollowing dependencies must be explicitly installed before building\n``gdlisp``.\n\n* The ``rake`` command line tool must be installed, as it is necessary\n  to run rakefiles.\n\n* The target version of Godot must be available on the target system\n  and must be available on the system path under the name ``godot``\n  (case sensitive).\n\n* If building on Windows, you will also need the `win32-file gem\n  <https://www.rubydoc.info/gems/win32-file>`_.\n\nOnce the dependencies are installed, to build the release version of\nGDLisp, it should suffice to run\n\n.. code-block:: bash\n\n   $ GDLISP_RELEASE=1 rake build\n\n.. Note:: If you're building in a non-POSIX shell, you may have to\n          initialize the environment variable ``GDLISP_RELEASE`` in\n          some other way.\n\nAfter a successful build for release purposes, an executable will be\nplaced at ``./bin/gdlisp``. At this point, if you plan to use GDLisp\nlong-term, then you may wish to place that file on your system path.\n\nThe Rakefile\n^^^^^^^^^^^^\n\nThe Rakefile for this tool provides the following tasks.\n\n``rake build``\n    Builds the entire project, including all dependencies.\n\n``rake clean``\n    Cleans up any build artifacts.\n\n``rake run``\n    Builds and then runs the ``gdlisp`` program.\n\n``rake build_rs``\n    Builds only the Rust dependencies, not the Godot dependencies.\n\n``rake test``\n    Runs the full GDLisp test suite.\n\n``rake clippy``\n    Runs the Rust linter Clippy against the codebase.\n\n``rake doc``\n    Generates the internal documentation of the codebase. That is\n    *not* the page you're reading right now but consists of Rustdoc\n    pages detailing the inner workings of the source code.\n\nWhen building the software, the build suite defaults to compiling in\n*debug mode*, which disables certain optimizations and produces better\ninternal stack traces in GDLisp. To make a build for release purposes,\ndefine the ``GDLISP_RELEASE`` environment variable to any value.\n\nRunning\n-------\n\nOnce you have the ``gdlisp`` command line tool, you may run it with\n``--help`` to see the available options. Generally speaking, users of\nGDLisp will invoke the program in the following ways.\n\n* Invoking ``gdlisp`` with no arguments will open a REPL instance.\n\n* Invoking ``gdlisp`` with the name of one or more ``.lisp`` filenames\n  will compile all of those files from GDLisp into GDScript.\n\n* Invoking ``gdlisp`` with the name of a directory will recursively\n  search that directory, compiling all ``.lisp`` files into GDScript\n  files. This is most useful if given the root directory of a Godot\n  project (i.e. the folder containing your ``project.godot`` file).\n\nThe REPL\n--------\n\nInvoking ``gdlisp`` with no arguments drops you into a read-eval-print\nloop (or REPL, for short), where you can run arbitrary GDLisp\ndeclarations or expressions and see the output.\n\n.. _support-file:\n\nThe GDLisp Support File\n-----------------------\n\nGDLisp has a single-file support library that **must** be included in\nany project that uses GDLisp source code. This file defines all of the\nbuilt-in functions and values available to GDLisp programmers, as well\nas providing some scaffolding necessary for the generated code as\nwell.\n\nIn a project that needs to utilize GDLisp source code, you must\ninclude the ``GDLisp.gd`` file. This file is bootstrapped from\n``GDLisp.lisp`` and is produced into the root project directory as\npart of the build process above.\n\n1. Copy and paste the ``GDLisp.gd`` file into the root of your project\n   directory.\n\n2. Create a Godot autoload (sometimes called a singleton) for this\n   file in your project. The autoload must be called ``GDLisp`` (with\n   that exact capitalization). All GDLisp source files will assume\n   that this global variable exists.\n\n.. Warning:: Some versions of Godot try to choose the capitalization\n             for your autoloads in a different way. Specifically,\n             Godot may suggest the name ``GdLisp`` (with a lowercase\n             ``d``) as a default name. The name of the autoload must\n             be ``GDLisp``, with that *exact* capitalization, or\n             GDLisp generated source files will not load.\n"
  },
  {
    "path": "doc/readthedocs/reference/datatypes.rst",
    "content": "\nBasic Datatypes\n===============\n\nGDLisp compiles to GDScript and runs on the Godot engine, so the core\ndatatypes for GDLisp are the same as those of GDScript. That is,\nGDLisp defines the following core types, identical to their\nrepresentations in Godot.\n\nFor more details about these basic types, see the `GDScript\ndocumentation\n<https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html>`_.\nThey are listed here for completeness.\n\n* A ``Null`` type with a single value. The single value of this type\n  is written as ``()``, or ``nil``. (Note that ``()`` is the literal\n  representation of the null value, whereas ``nil`` is merely a global\n  constant which is *defined* to be ``()``)\n\n* A ``Bool`` type with two values, written as ``#f`` for false and\n  ``#t`` for true.\n\n* An ``Int`` type for 64-bit signed integers.\n\n* A ``Float`` type representing double-precision floating point\n  values.\n\n* A ``String`` type consisting of UTF-8 strings.\n\n* The ``Vector2`` and ``Vector3`` types, with literals written\n  enclosed in ``V{`` and ``}``. Both types of vectors are written in\n  this way; they are distinguished by the number of arguments to the\n  syntactic form.\n\n* ``Rect2``, ``Transform2D``, ``Plane``, ``Quat``, ``AABB``,\n  ``Basis``, ``Transform``, ``Color``, ``NodePath``, and ``RID`` all\n  behave identically to GDScript. There is no literal syntax for any\n  of these types.\n\n.. _cons-cell:\n\nCons Cells\n----------\n\nGDLisp defines a ``Cons`` datatype. ``Cons`` values are constructed\nusing the built-in function ``cons`` and store two fields: a ``car``\nand a ``cdr``, both accessible by public field access on the cell.\n\nCons cells are used to build up singly-linked lists to represent the\nabstract syntax tree of your code during macro expansion.\n\nArrays\n------\n\nGDLisp supports all of the built-in Godot array types, including the\nbase ``Array`` and all of its strongly-typed companions. Array\nliterals are written in square brackets ``[`` and ``]``, or\nequivalently are constructed with the ``array`` function.\n\nDictionaries\n------------\n\nGDLisp supports the built-in Godot ``Dictionary`` type, which maps\nkeys to values. Dictionaries are constructed using the ``dict``\nfunction, or equivalently enclosed in curly braces ``{`` and ``}``.\nThe elements of a dictionary are *not* delimited by colons or commas;\nthey are merely written side by side. For example, ``{key1 value1 key2\nvalue2}``.\n\nFunctions\n---------\n\n``Function`` is the type of first class functions in GDLisp. Functions\nare defined using top-level declaration forms such as ``defn`` or by\nthe ``lambda`` or ``function`` special forms. Calling functions is\ndone with the built-in ``funcall`` or its fully general companion\n``apply``.\n\n.. _cell-type:\n\nCells\n-----\n\nA ``Cell`` is a simple type that has a single public field:\n``contents``. A cell is constructed directly by the ``Cell``\nconstructor (``(Cell:new value)``). Cells are seldom used directly in\nGDLisp code but are frequently used behind the scenes to implement\nclosures.\n\nSymbols\n-------\n\nThe ``Symbol`` data type represents atomic symbols in GDLisp. There is\nno equivalent to this in base Godot (though it is similar in principle\nto the Godot 4 type ``StringName``). A ``Symbol`` has a textual\nrepresentation, similar to a string. Symbols are most commonly\nconstructed by simply quoting a symbol literal, though they can also\nbe constructed from strings using ``intern``, and unique, unused\nsymbols can be constructed with ``gensym``.\n\nThe Object Hierarchy\n--------------------\n\nLike GDScript, GDLisp supports an object-oriented style of\nprogramming. GDLisp expands upon Godot's built-in inheritance\nhierarchy, introducing a partial ordering on all types.\n\n.. mermaid::\n\n   graph BT\n       Nothing-->Symbol-->Reference-->Object-->AnyRef-->Any\n       Nothing-->Node-->Object\n       Nothing-->String-->AnyVal-->Any\n       Nothing-->Int-->Number-->AnyVal\n       Nothing-->Float-->Number\n       Nothing-->Array-->BaseArray-->AnyVal\n       Nothing-->Pool...Array-->BaseArray\n   Nothing-->Prim[\"Primitive types\"]-->AnyVal\n\nAt the top of the hierarchy is the ``Any`` type, which contains all\nGDLisp objects, whether they inherit from the ``Object`` class or are\nprimitives.\n\nBelow that, the two types ``AnyRef`` and ``AnyVal`` partition the\nspace of objects into two. ``AnyRef`` contains all of the object types\nand ``AnyVal`` contains all of the Godot primitives.\n\nOn the ``AnyVal`` side, all of the primitive Godot types are provided\nas-is as subtypes of ``AnyVal``. Additionally, two new types are\nprovided. ``Number`` is the lowest common supertype of ``Int`` and\n``Float``, and ``BaseArray`` is a supertype of all of the array types\nin Godot (the generic ``Array`` as well as all of the strongly-typed\n\"Pool\" array types).\n\nBelow ``AnyRef`` is ``Object``, the root of the Godot object\nhierarchy. In the current implementation of GDLisp, ``AnyRef`` and\n``Object`` are equivalent as types, but this may change in the future\nif an alternative object hierarchy is added.\n\nBelow ``Object``, all of the class types available in Godot fit into\nthe hierarchy in the same way they do in GDScript. In particular,\nuser-defined classes can subclass ``Object``, ``Node``, ``Reference``,\nor any of the other types freely.\n\nFinally, ``Nothing`` is the unique bottom type of the hierarchy.\n``Nothing`` is a subtype of *every* type, and there is no value which\nis a member of the ``Nothing`` type. Note carefully: Not even the\nspecial null value ``()`` is a member of the ``Nothing`` type;\n``Nothing`` is uninhabited *by design*.\n"
  },
  {
    "path": "doc/readthedocs/reference/expressions.rst",
    "content": "\nExpressions\n===========\n\nExpressions are the forms that make up the body of functions and the\ninitial value of variables and constants. Expressions are the basic\nunit of \"work\" in a GDLisp program.\n\nNote that GDLisp is an expression-oriented language, which means that\nthere is no distinction between statements and expressions in GDLisp.\nEven though we may colloquially refer to the ``if`` special form as an\n\"if statement\", it is an expression like any other. This commitment to\nexpressions allows for better composability, as any expression can be\nnested inside of any other.\n\nThere are several different types of expressions, all of which are\ndetailed below.\n\nAtoms\n-----\n\nThere are several S-expressions which compile natively to themselves.\nThese include the null object ``()``, integer literals, floating-point\nliterals, string literals, and the Boolean constants ``#t`` and\n``#f``. Note that this explicitly does *not* include symbol literals,\nwhich are evaluated differently. See :ref:`expr-variable-names` below.\n\n.. _expr-variable-names:\n\nVariable Names\n--------------\n\nA bare symbol appearing in expression context is treated as a variable\nname. All variables in GDLisp are lexically-scoped. That is, any\nvariable is declared to be either scoped to the current module or to a\nnarrower scope, such as a ``let`` block or a function body. It is an\nerror to refer to a variable name that does not exist.\n\nA bare symbol in expression context always refers to a name in the\nvalue namespace. Variable names can be *shadowed*, which means that a\nbare symbol always refers to the name in the *narrowest scope* for\nwhich it is defined.\n\nNote that bare symbols appearing in the code are subject to macro\nexpansion for :ref:`symbol macros <symbol-macros>`.\n\nFunction Calls\n--------------\n\nA proper, nonempty list will be interpreted in one of several ways,\ndepending on the value of the first term (or head) of the list. The\nhead of a function call can be one of three things. It can be a symbol\nliteral, a ``literally`` form, or an ``access-slot`` form.\n\nOrdinary Calls\n^^^^^^^^^^^^^^\n\nAn ordinary call is one whose head is a symbol literal. Ordinary calls\ncan resolve to one of several forms, depending on the value of the\nhead.\n\nFirst, if the value of the head is the start of one of GDLisp's\n:ref:`special forms <expr-special-forms>`, then the call is treated as\na special form, even if there is a function name in the current scope\nwhich would shadow that special form. These special forms are baked\ninto the GDLisp compiler and are the primary bootstrapping tool on\nwhich we build up syntax. There is no way to define new special forms\nin GDLisp.\n\nIf the head is not the start of one of the special forms, then it must\nbe a valid name in the function namespace of the current lexical\nscope. Like values, names in the function namespace are always\naccessed starting from the innermost scope and falling back to outer\nscopes, which makes names in the function namespace subject to\nshadowing as well.\n\nIf the name refers to a macro, then :ref:`macro expansion <macros>`\noccurs. If the name refers to an ordinary function, then the call\ncompiles to a runtime call to the function with the given name.\n\nLiteral Calls\n^^^^^^^^^^^^^\n\nA literal call is a call whose head is the form ``(literally name)``,\nwhere ``name`` is an arbitrary symbol. ``literally`` calls are like\n:ref:`literal forms <expr-literal-forms>`, except that they are\nfunction calls rather than values. That is, a literal call always\nexpands to a call to the function with the given name in the current\nscope in GDScript, without regard to whether or not that call is valid\nor makes sense. ``literally`` calls never undergo macro expansion and\nare never considered special forms.\n\n.. Warning:: All of the same caveats that apply to literal forms apply\n             to literal calls, and they should be used with caution.\n\nMethod Calls\n^^^^^^^^^^^^\n\nA method call is a call whose head is of the form ``(access-slot expr\nname)``, where ``expr`` is an arbitrary expression and ``name`` is a\nliteral symbol. In cases where the operator precedence is not\nambiguous, these ``access-slot`` forms are usually written using the\n``:`` notation as ``expr:name``. That is, to call the method named\n``bar`` on an object called ``foo``, with three arguments, we would\nwrite ``(foo:bar 1 2 3)``, which is syntax sugar for ``((access-slot\nfoo bar) 1 2 3)``.\n\nThe first argument to ``access-slot`` is an arbitrary expression, on\nwhich the method will be called, and ``name`` will be :ref:`normalized\n<name-normalization>` to the name of the method to call on the object.\n\nGDLisp does *not* validate the name of the method or that it exists on\nthe object referred to by the expression.\n\nIf the first argument to ``access-slot`` is the literal name\n``super``, then the method call is a :ref:`super call\n<expr-super-calls>`.\n\n.. _expr-super-calls:\n\nSuper Calls\n\"\"\"\"\"\"\"\"\"\"\"\n\n::\n\n   (super:method-name args ...)\n\nA method call where the left-hand side of the ``access-slot`` call is\nthe literal symbol ``super`` is a super call. A super call can only\noccur in a context where the ``self`` name exists (i.e. inside of a\nclass body) and will call the method with the given name, using\n``self`` as the target of the call, but only considering methods\ndefined in the superclass of the current class body.\n\nNote that calls to a superclass' *constructor* are handled specially.\nSpecifically, they are *not* written as ``super:_init``, and in fact\nthey are not even expressions in the strictest sense of the word. A\ncall to a superclass' constructor is a special part of the ``_init``\ndefinition syntax. For more details, see :ref:`constructor-functions`.\n\n.. _expr-special-forms:\n\nSpecial Forms\n-------------\n\nSpecial forms are elements of the GDLisp syntax that have special\nmeanings baked into the compiler. Special forms can be thought of as\nsimilar to macros but more primitive, the building blocks on which\nGDLisp syntax is constructed. There are 25 special forms in GDLisp.\n\n``access-slot`` Forms\n^^^^^^^^^^^^^^^^^^^^^\n\n::\n\n   expr:field\n   (access-slot expr field)\n\nThe ``access-slot`` form, usually written using the infix ``:``\nnotation, accesses a field on an object. That is, ``expr`` is\nevaluated, and then the slot with the name ``field`` (which must be a\nsymbol literal) is returned from the object referenced by the\nexpression.\n\nThe field name is not validated. That is, GDLisp makes no effort to\nensure that the name ``field`` is a field that exists on the type of\n``expr``. There is one exception to this rule. If ``expr`` is the\nliteral name of an enumeration (a la ``defenum``) whose definition is\nstatically known, GDLisp will validate that the name ``field`` is\nactually a defined enumeration constant on that type.\n\n.. _expr-assert:\n\n``assert`` Forms\n^^^^^^^^^^^^^^^^\n\n::\n\n   (assert condition)\n   (assert condition message)\n\nThe ``assert`` special form evaluates the condition and message as\nexpressions. If ``condition`` is true, then the code proceeds as\nplanned. If ``condition`` is false, then an error is generated, using\n``message`` in the error message if provided.\n\n``assert`` special forms are only used when the resulting Godot\nruntime is in debug mode. In release mode, these forms will be\nignored, and their arguments will not even be evaluated. As such,\narguments which have side effects should generally not be given to\n``assert``.\n\n``break`` Forms\n^^^^^^^^^^^^^^^\n\n::\n\n   (break)\n\n``break`` is a special form that can only be used inside of loop\ncontexts. ``break`` exits the current loop and continues immediately\nafter the loop body.\n\n``cond`` Forms\n^^^^^^^^^^^^^^\n\n::\n\n    (cond clauses ...)\n\nA ``cond`` form is the most basic form of conditional in GDLisp.\n``cond`` takes zero or more clauses. Each clause's conditional portion\nis evaluated in turn. If the conditional is true, then the clause's\nbody portion is evaluated and returned. Otherwise, the next clause\nis tried. If all clauses are exhausted, then the null object ``()`` is\nreturned.\n\nEach clause must be a proper list containing one or more elements. If\nthe list contains at least two elements, then the first element is the\nconditional term and the rest form the body of the clause. The body is\ntreated as though it is inside a ``progn``, so the last expression\nwill be returned. If the list contains only one element, then that\nelement is *both* the condition and the body of the clause, and it\nwill only be evaluated once.\n\nFor example,\n\n::\n\n   (cond\n     ((foo1) (bar1) (baz1))\n     ((foo2) (bar2)))\n\nThis is a ``cond`` form consisting of two clauses. When this form is\nevaluated, first, we will call the function ``foo1`` with no\narguments. If that function returns a truthy value, then we call\n``bar1`` and then ``baz1``, using the latter as the result of the\nwhole ``cond`` form. If ``foo1`` returns a falsy value, then we try\n``foo2``. If ``foo2`` evaluates to a truthy value, then ``bar2`` is\nevaluated and its result is returned. Otherwise, ``()`` is returned as\na default value.\n\nAs an example of the one-argument clause form, consider\n\n::\n\n   (cond\n     ((my-dict:get \"x\"))\n     ((my-dict:get \"y\"))\n     ((my-dict:get \"z\")))\n\nAssuming ``my-dict`` is a dictionary object, this expression will\nattempt to get the keys ``x``, ``y``, and then ``z`` from the\ndictionary in order, returning the first one which exists and is\ntruthy. If none satisfy the condition, then ``()`` is returned as a\ndefault value.\n\nNote that if all you want is a simple if statement with a \"true\" and a\n\"false\" case, then the :ref:`if macro <macro-if>` may be more\nconvenient for your use case.\n\n.. Tip:: A common idiom is to make the condition of the last clause be\n         the literal ``#t`` true object. This acts as a sort of \"else\"\n         clause, triggering unconditionally if all of the other\n         branches fail.\n\n``continue`` Forms\n^^^^^^^^^^^^^^^^^^\n\n::\n\n   (continue)\n\n``continue`` is a special form that can only be used inside of loop\ncontexts. ``continue`` exits the current loop iteration and continues\nthe next iteration of the loop.\n\n.. _expr-flet:\n\n``flet`` Forms\n^^^^^^^^^^^^^^\n\n::\n\n   (flet (clauses ...) body ...)\n\nAn ``flet`` form is similar to a ``let`` form except that it binds\nfunctions in the function namespace, rather than arbitrary values in\nthe value namespace. Specifically, an ``flet`` form creates a new\nlocal scope and defines zero or more functions in that local scope.\nThen the body is executed in that scope and its final value returned.\n\nEach function clause takes the following form.\n\n::\n\n   (name (args ...) body ...)\n\n``name`` is a symbol literal indicating the name of the local\nfunction, ``args`` is an :ref:`ordinary lambda list\n<ordinary-lambda-lists>`, and ``body`` is the body of the function.\nWhen the function called ``name`` is invoked inside of the ``flet``\nform's body, the given arguments will be bound and the body will be\nexecuted in a new lexical scope, cloned from the scope in which\n``flet`` itself was defined.\n\nNote that the bodies of the local functions are evaluated in a scope\ncloned from the one in which the ``flet`` form was defined, not the\ninner scope created by ``flet``. That is, the local function bodies\ndefined by an ``flet`` do not have access to each other's names or to\ntheir own. For a version of ``flet`` that does have such access, see\n:ref:`expr-labels`.\n\nThe bodies of ``flet`` local functions can create :ref:`closures\n<expr-capture>`. ``flet`` creates a loop barrier between the enclosing\nscope and the clauses of the ``flet`` form. This means that a\n``break`` or ``continue`` expression inside of a clause of the\n``flet`` cannot be used to control a loop that began outside of the\nclauses. This constraint does not exist for the body of the ``flet``,\nonly the clauses.\n\n``for`` Forms\n^^^^^^^^^^^^^\n\n::\n\n   (for var iterable body ...)\n\nA ``for`` form is the second of the two most basic looping constructs\nin GDLisp. The first argument to ``for`` must be a literal symbol,\nthen the other arguments are arbitrary expressions. First,\n``iterable`` is evaluated once, and it must evaluate to an array\n(including pool arrays), string, or dictionary object. Then a new\nlexical scope is created. Then ``body`` is run in that lexical scope,\nonce for each element of the iterable object. At each loop iteration,\nthe variable ``var`` is bound to the current value. ``for`` forms\nalways return the null object ``()``.\n\nFor arrays, a ``for`` form iterates over each element of the array.\nFor dictionaries, a ``for`` form iterates over each *key* of the\ndictionary, consistent with Python's semantics for the same. For\nstrings, a ``for`` form iterates over each character (as a\nsingle-character string) of the string.\n\nA ``for`` loop defines a loop context for its body. This means that\n``break`` and ``continue`` can be used in the body of a ``for`` loop.\n\n.. Warning:: Note that the behavior is undefined if the result of\n             ``iterable`` is not an array, dictionary, or string.\n             Currently, ``for`` loops in GDLisp compile to ``for``\n             loops in GDScript, which means some legacy GDScript\n             behavior (such as iterating over numerical literals) may\n             work, but this behavior may change in the future, so it\n             is always recommended to explicitly call ``range`` if the\n             intent is to iterate up to a number.\n\n.. _expr-function:\n\n``function`` Forms\n^^^^^^^^^^^^^^^^^^\n\n::\n\n   #'name\n   (function name)\n\nThe ``function`` special form is used to take a function that exists\nin the function namespace and convert it into a first-class value. The\n``function`` form is often abbreviated using the (equivalent) syntax\n``#'name``.\n\nThe ``name`` argument must be a symbol, and it must be a valid name in\nthe function namespace of the current lexical scope. A function object\nis created (as a value) which, when called, invoked the function with\nthe given name, forwarding all arguments.\n\nThe name can refer to a function defined at module scope or to a local\nfunction defined in the current scope. In the latter case, the local\nfunction will be kept alive (by reference semantics) until the\nfunction object constructed by this ``function`` form is discarded.\nThat is, it is permitted to have a reference to a local function which\noutlives the scope of that local function's binding.\n\n``function`` *cannot* be used to create references to instance\nmethods. Explicit ``lambda`` expressions must be used to do so.\n\n.. Warning:: The target name of a ``function`` form must be the name\n             of a valid function. If the name refers to a macro, then\n             the behavior is undefined.\n\n.. _expr-labels:\n\n``labels`` Forms\n^^^^^^^^^^^^^^^^\n\n::\n\n   (labels (clauses ...) body ...)\n\n``labels`` works nearly identically to ``flet`` and carries the exact\nsame syntax. However, whereas an ``flet`` form evaluates its local\nfunction bodies in the enclosing scope of the ``flet`` block, a\n``labels`` form evaluates its function bodies in the inner scope of\nthe ``labels`` block itself. This means that the functions defined in\na ``labels`` block have access to each other and to their own name,\nallowing them to be recursive or mutually recursive.\n\nThe bodies of ``labels`` local functions can create :ref:`closures\n<expr-capture>`. ``labels`` creates a loop barrier between the\nenclosing scope and the clauses of the ``labels`` form. This means\nthat a ``break`` or ``continue`` expression inside of a clause of the\n``labels`` cannot be used to control a loop that began outside of the\nclauses. This constraint does not exist for the body of the\n``labels``, only the clauses.\n\n.. _expr-lambda:\n\n``lambda`` Forms\n^^^^^^^^^^^^^^^^\n\n::\n\n   (lambda (args ...) body ...)\n\nA ``lambda`` form defines a local function without giving it a name.\nThe argument list ``args`` is an :ref:`ordinary lambda list\n<ordinary-lambda-lists>`. A new function object is created, which\nexists as a value (hence, can be assigned to variables in the value\nnamespace or passed as an argument to a function).\n\nWhen the function created by this form is invoked, a new lexical scope\nis created, which is cloned from the lexical scope in which the\n``lambda`` was first defined. Then the arguments are bound and the\nbody is run, just like any other function.\n\nThe ``lambda`` body can create :ref:`closures <expr-capture>`.\n``lambda`` creates a loop barrier between the enclosing scope and the\nbody of the ``lambda`` form. This means that a ``break`` or\n``continue`` expression inside of the body of the ``lambda`` cannot be\nused to control a loop that began outside of the body.\n\n``let`` Forms\n^^^^^^^^^^^^^\n\n::\n\n   (let (clauses ...) body ...)\n\n``let`` is the most basic form of local variable binding in GDLisp. A\n``let`` form creates a new lexical scope in which zero or more local\nvariables are bound, and then runs ``body`` in that local scope. The\nvalue of the final expression of ``body`` is returned, or ``()`` if\n``body`` is empty.\n\nEach variable clause takes one of the following forms.\n\n::\n\n   var-name\n   (var-name initial-value ...)\n\nIn the second (and most general) form, a variable clause takes the\nform of a proper list whose first element is a literal symbol\nindicating the name of the variable to declare. The remaining elements\nare evaluated to determine the variable's initial value. Note\ncarefully: the ``initial-value`` expressions are evaluated in the\n*outer* scope, not in the newly-created scope that the variable is\nbeing declared in. This means that, in a ``let`` statement which\ndeclares multiple variables, none of the variables have access to each\nother during initialization, even those declared later in the same\nblock. The ``initial-value`` block is treated as a ``progn`` block, so\nif the block is empty then ``()`` is used as the variable's initial\nvalue.\n\nA variable name ``var-name`` that appears on its own (that is, a\nsymbol literal *not* contained in a sublist) is treated as\n``(var-name)`` and will initialize the variable to ``()``.\n\n``let`` always binds in the value namespace. To bind in the function\nin the function namespace, see :ref:`expr-flet` and\n:ref:`expr-labels`.\n\n.. _expr-literal-forms:\n\n``literally`` Forms\n^^^^^^^^^^^^^^^^^^^\n\n::\n\n   (literally variable-name)\n\nA ``literally`` form is a backdoor through the GDLisp scoping system.\nThe sole argument to ``literally`` must be a symbol literal.\n``(literally x)`` will be translated into the variable name ``x`` in\nthe resulting GDScript code. This will be done **without any\nconsideration** to whether or not ``x`` is a valid variable name.\nGDLisp will not check that the name is defined, or what scope it is\ndefined in. GDLisp will merely assume that you know what you're doing\nand pass the name through.\n\nThe name ``variable-name`` given to this form will undergo a partial\nform of :ref:`name normalization <name-normalization>`. Specifically,\n``variable-name`` will be escaped in the same way as an ordinary\nvariable name, with the exception that GDScript reserved words will\nnot be prefixed with an underscore.\n\nCare must be taken when using ``literally``. Since GDLisp does not\nperform any semantic analysis on the given name, it cannot guarantee\nthat the name is valid, or even syntactically makes sense in GDScript\nin the case of keywords. Additionally, names referenced inside of\n``literally`` will not have closures created for them if they occur\ninside of a ``lambda`` or other closure-producing construct. This can\nresult in difficult-to-debug situations that GDLisp cannot handle.\n\nThe primary intended use case for ``literally`` is to port future\nGDScript functions to GDLisp without having to wait on official\nsupport from the GDLisp compiler. If a future iteration of Godot adds\na function called ``frobnicate`` to the global namespace, then you can\ncall that function by using the name ``(literally frobnicate)``, even\nif the version of the GDLisp compiler you're using is not aware that\nsuch a function exists.\n\n``macrolet`` Forms\n^^^^^^^^^^^^^^^^^^\n\n::\n\n   (macrolet (clauses ...) body ...)\n\nA ``macrolet`` form is syntactically identical to an ``flet``.\nHowever, whereas ``flet`` binds functions in the function namespace of\nthe current scope, ``macrolet`` binds *macros* in the same namespace.\nThe macros defined by a ``macrolet`` are only defined inside of the\n``body`` scope. During that scope, those names are subject to macro\nexpansion.\n\n``macrolet`` creates a loop barrier between the enclosing scope and\nthe clauses of the ``macrolet`` form. This means that a ``break`` or\n``continue`` expression inside of a clause of the ``macrolet`` cannot\nbe used to control a loop that began outside of the clause. This\nconstraint does not exist for the body of the ``macrolet``.\n\n.. Warning:: The clauses of a ``macrolet`` form **cannot** create\n             closures. If a locally-defined macro depends on a local\n             variable or function defined in an enclosing scope, then\n             the behavior is undefined.\n\n``new`` Forms\n^^^^^^^^^^^^^\n\n::\n\n   (new Superclass body ...)\n   (new (Superclass args ...) body ...)\n\nThe ``new`` form constructs a new local *anonymous* class. That is,\n``new`` is to ``defclass`` as ``lambda`` is to ``defn``. The\nnewly-defined class is not given a name, and the only instances of\nthat class are those created by this particular ``new`` form.\n\nWhen this form is evaluated, an instance of a subclass of\n``Superclass`` is constructed. The body of this subclass shall be\n``body``, which can consist of zero or more :ref:`class declarations\n<classes>`, with the exception that it is illegal to define static\nmethods in an anonymous class. The constructor of this class shall be\ninvoked with ``args``, or with zero arguments if the non-parameterized\nversion of ``new`` is used.\n\nThe body of a ``new`` statement is capable of creating :ref:`closures\n<expr-capture>`.\n\n``new`` creates a loop barrier between the enclosing scope and the\nbody of the ``new`` form. This means that a ``break`` or ``continue``\nexpression inside of the body of the ``new`` cannot be used to control\na loop that began outside of the body.\n\n.. Attention:: ``new`` is *not* a general-purpose constructor.\n               Programmers used to Java or C# may be used to prefixing\n               type names with ``new`` to construct ordinary instances\n               of the type. That is not how object construction works\n               in GDLisp. To construct ordinary instances of some\n               class, call the method ``new`` on that class, such as\n               ``(ClassName:new 1 2 3)``. The ``new`` special form is\n               only intended to be used when behavior (such as\n               instance variables or methods) is being added\n               anonymously to the class for this instance alone.\n\n.. _expr-preload:\n\n``preload`` Forms\n^^^^^^^^^^^^^^^^^\n\n::\n\n   (preload name)\n\n``preload`` is a special form which takes a single string literal as\nargument. The string literal must be the name of a file which can be\nimported, using the Godot ``res://`` notation. ``preload`` works like\nthe built-in function ``load`` but performs the act of loading at\ncompile-time. It is an error if the pathname does not point to a file.\n\n.. Tip:: In GDLisp, most ``preload`` calls should be replaced with\n         ``use`` directives. See :ref:`imports` for details.\n         ``preload`` can be used in situations (such as macro\n         expansion) where the name being loaded may not be known at\n         definition time but will be known before compilation is\n         complete.\n\n.. _expr-progn:\n\n``progn`` Forms\n^^^^^^^^^^^^^^^\n\n::\n\n   (progn args ...)\n\nA ``progn`` form evaluates each of its arguments in order and returns\nthe final argument. ``progn`` is a useful way to insert multiple\nexpressions which have side effects in a context, such as the\nright-hand side of a ``defvar``, that only accepts one expression.\n\nAn empty ``progn`` silently returns ``()``, the null object.\n\n.. Note:: ``progn`` can also be used in declaration (or class\n          declaration) context. See :ref:`progn` for details.\n\n``quasiquote`` Forms\n^^^^^^^^^^^^^^^^^^^^\n\n::\n\n   `s-expression\n   (quasiquote s-expression)\n\nA ``quasiquote`` form refuses to evaluate its argument and returns the\nS-expression representing it, similar to ``quote``. However,\n``unquote`` and ``unquote-spliced`` have special meaning inside of\n``quasiquote`` forms. See :ref:`quoting` for more details.\n\n``quote`` Forms\n^^^^^^^^^^^^^^^\n\n::\n\n   's-expression\n   (quote s-expression)\n\nA ``quote`` form refuses to evaluate its argument and returns the\nS-expression representing it verbatim. See :ref:`quoting` for more\ndetails. Note that a ``quote`` form is usually written abbreviated as\n``'s-expression``.\n\n``return`` Forms\n^^^^^^^^^^^^^^^^\n\n::\n\n   (return expr)\n\nEvaluates the expression and then returns that expression immediately\nfrom the enclosing function or instance method.\n\n.. _expr-set:\n\n``set`` Forms\n^^^^^^^^^^^^^\n\n::\n\n   (set variable value)\n\n``set`` is the basic form of variable and name assignment in GDLisp.\nIt can do several different things, depending on the nature of the\n``variable`` portion of the form.\n\nVariable Assignment\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nIf ``variable`` is a literal symbol, then it is interpreted as a\nvariable name in the value namespace. The variable pointed to by that\nname is assigned a new value, namely the result of evaluating\n``value``. The variable must be mutable, or a compile error will be\nissued.\n\nThe value that was assigned is returned from the ``set`` form.\n\nField Assignment\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nIf ``variable`` is of the form ``(access-slot object target)``, where\n``object`` is an arbitrary expression and ``target`` is a literal\nsymbol, then the assignment will modify an instance field on the\ninstance to which ``object`` evaluates. The field's name shall be\n``target``, after :ref:`name normalization <name-normalization>`. This\ncan trigger :ref:`setter functions <getter-and-setter>`.\n\nThe value that was assigned is returned from the ``set`` form. It is\nunspecified whether this will invoke a getter function on the class,\nif one exists.\n\nDelegated Assignment\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nIf ``variable`` is a proper list of the form ``(head args ...)`` where\n``head`` is a literal symbol that is *not* ``access-slot``, then the\nassignment is a delegated assignment. The form\n\n::\n\n   (set (some-function args ...) value)\n\nwill compile into the function call\n\n::\n\n   (set-some-function value args ...)\n\nThat is, a ``set`` on a function call will compile to a call to the\nfunction whose name is the former function with ``set-`` prepended to\nit. The right-hand side of the assignment will be the first argument\npassed to the delegated function. The return value of the function is\nreturned from the ``set`` form, so by convention a function intended\nto be used in this way should return the assigned value.\n\n``symbol-macrolet`` Forms\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\n::\n\n   (symbol-macrolet (clauses ...) body ...)\n\nA ``symbol-macrolet`` clause binds local macros, just like\n``macrolet``, but the former binds *symbol* macros, which are subject\nto macro expansion when a literal symbol is used.\n\nEach clause is of the form\n\n::\n\n   (name value)\n\nBoth parts are mandatory. ``name`` is the name of the symbol macro\n(which will be bound in the value namespace). ``value`` is the\nexpression which should be run to evaluate the macro.\n\n``symbol-macrolet`` creates a loop barrier between the enclosing scope\nand the clauses of the ``symbol-macrolet`` form. This means that a\n``break`` or ``continue`` expression inside of a clause of the\n``symbol-macrolet`` cannot be used to control a loop that began\noutside of the clause. This constraint does not exist for the body of\nthe ``symbol-macrolet``.\n\n.. Warning:: Like ``macrolet``, the clauses of a ``symbol-macrolet``\n             **cannot** create closures. It is undefined behavior to\n             write a local symbol macro that depends on a local\n             variable or function defined in an enclosing scope.\n\n``unquote`` Forms\n^^^^^^^^^^^^^^^^^\n\n::\n\n   ,expr\n   (unquote expr)\n\nAn ``unquote`` form can only be used inside of a ``quasiquote`` form.\nIt is an error for this special form to appear in an expression\ncontext.\n\n``unquote-spliced`` Forms\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\n::\n\n   ,.expr\n   (unquote-spliced expr)\n\nAn ``unquote-spliced`` form can only be used inside of a\n``quasiquote`` form. It is an error for this special form to appear in\nan expression context.\n\n``while`` Forms\n^^^^^^^^^^^^^^^\n\n::\n\n   (while condition body ...)\n   (while condition)\n\nA ``while`` form is one of the two most basic forms of looping in\nGDLisp. A ``while`` form takes a condition and then zero or more\nexpressions forming a body as arguments. The ``while`` loop iterates\nzero or more times. At each iteration, the loop runs the condition\nfirst. If the condition is falsy, then the loop exits immediately.\nOtherwise, the body runs, and then the loop starts over.\n\nA ``while`` loop always returns the null object ``()``. It is possible\nto have a ``while`` loop where the body is empty, in which case, the\ncondition is evaluated multiple times until it returns a falsy value.\nThis can be used to emulate the \"do ... while\" construct seen in some\nprogramming languages, where the condition is evaluated at the end of\nthe body, rather than the beginning. That is, to emulate such a\nconstruct in GDLisp, consider\n\n::\n\n   (while (progn\n     body ...\n     condition))\n\nA ``while`` loop defines a loop context, for both its condition and\nits body. This means that ``break`` and ``continue`` can be used in\neither the condition or the body.\n\n.. _expr-yield:\n\n``yield`` Forms\n^^^^^^^^^^^^^^^\n\n::\n\n   (yield)\n   (yield object signal)\n\nThe ``yield`` special form behaves similarly to the GDScript function\nof the same name. Called with zero arguments, ``yield`` halts the\ncurrent function and returns a function state object from the\nfunction. That function state object has a ``:resume`` method which\nwill return to the halted function at the same point it was yielded\nfrom.\n\nIf ``yield`` is called with two arguments, then both arguments are\nevaluated. The first is treated as an object and the second shall\nevaluate to a string which is the name of a signal on the given\nobject. The function still halts and returns a state object, just as\nif ``yield`` was called with no arguments. However, if the given\nobject ``object`` ever fires the signal called ``signal``, then the\nfunction resumes automatically, without an explicit call to\n``:resume``.\n\nIt is an error to call ``yield`` with exactly one argument.\n\nNote that ``yield`` is a special form, not a function, despite it\nevaluating its arguments in applicative order. Functions in GDLisp\nmust satisfy `η-reduction\n<https://en.wikipedia.org/wiki/Lambda_calculus#%CE%B7-reduction>`_.\nThat is, in order for ``yield`` to be a function, it would have to be\nthe case that ``yield`` and ``(lambda (&rest args) (apply #'yield\nargs))`` are equivalent functions. This is not true for ``yield``,\nsince the former, when called, will halt the current function, whereas\nthe latter will halt an inner function and return a (somewhat useless)\nfunction state object that resumes at the end of the inner function.\n\n.. _expr-capture:\n\nClosures and Name Capture\n-------------------------\n\nSeveral special forms create *closures*. A closure is a nested scope\nthat can outlive its containing scope. We call the variables which are\nplaced inside a closure for such forms *captures*. This is perfectly\nacceptable. If a variable is defined in a local scope and then\ncaptured by a ``lambda`` or other special form, then that variable\nwill remain in existence for as long as the ``lambda`` object exists.\n\nFunction objects always have ``Reference`` semantics, which means that\na function object (created with ``lambda`` or ``function``) will be\nfreed when the last reference to it is freed. This ensures that\nclosures created in this way are freed promptly. Custom objects\ncreated with ``new`` will follow the semantics of their superclass\neventual (``Reference`` subclasses will have reference semantics,\nwhile ``Object`` and ``Node`` subclasses will have to be freed\nexplicitly), so some care must be taken in those situations to prevent\na memory leak.\n\nA closure is a read-write binding to a variable name. That means that\nthe values in a closure are captured by *reference*, not by value. If\nthe inside of a closure (such as a ``lambda``) modifies a captured\nvariable, then the enclosing scope (and, by extension, any other\nclosure that captured the same variable) will be able to see that\nchange.\n\nFor example, this lambda will return one number higher each time it's\ncalled.\n\n::\n\n   (let ((accum 0)) (lambda () (set accum (+ 1 accum))))\n\nWe can call this function, for example, as follows.\n\n::\n\n   (defn create-counter ()\n     (let ((accum 0))\n       (lambda () (set accum (+ 1 accum)))))\n\n   (defn _ready ()\n     (let ((counter (create-counter)))\n       (print (funcall counter))   ; Prints 1\n       (print (funcall counter))   ; Prints 2\n       (print (funcall counter)))) ; Prints 3\n\nThe variable ``accum`` is captured by the ``lambda`` *by reference*,\nso when we modify the variable, that modification is reflected in\nfuture calls to the ``lambda``, since there is truly only one copy of\nthat variable.\n\nForms that define local macros can never capture local variables or\nfunctions from an enclosing scope.\n"
  },
  {
    "path": "doc/readthedocs/reference/imports.rst",
    "content": "\n.. _imports:\n\nImport Directives\n=================\n\nGDLisp uses the special ``use`` directive to import names and\nresources from other files. Although it is technically possible to\nimport resources in the GDScript way (by defining constants whose\nvalues are ``preload`` calls), it is not recommended.\n\nThere are three different forms to the ``use`` directive, all of which\nbegin with the symbol ``use``, followed by the path name as a literal\nstring.\n\n::\n\n  ;; Qualified import\n  (use \"res://filename.lisp\" as alias-name)\n\n  ;; Open import\n  (use \"res://filename.lisp\" open)\n\n  ;; Explicit import\n  (use \"res://filename.lisp\" (...))\n\nThe path name is always a string and follows the same rules as Godot\npaths. See `File system\n<https://docs.godotengine.org/en/stable/tutorials/scripting/filesystem.html>`_\nfor details.\n\nImporting names involves adding names (in either of the value or\nfunction namespaces) to the module's lexical scope. Names added by a\n``use`` directive are always imported as ``private``, and hence cannot\nbe transitively imported into other modules through the current one.\n\nQualified Imports\n-----------------\n\n::\n\n  (use \"res://filename.lisp\")\n  (use \"res://filename.lisp\" as alias-name)\n\nA qualified import takes the form of either the ``use`` symbol\nfollowed by the path name and nothing else, or the ``use`` symbol\nfollowed by ``as`` and then followed by the desired alias name. If the\nalias name is not provided, then the default alias shall be the name\nof the file, without the path or the file extension. So the import\n``\"res://foo/bar/baz.lisp`` would have a default alias name of\n``foo/bar/baz``. Remember that forward slashes are valid in\nidentifiers in GDLisp.\n\nEvery public identifier in the target module will be imported into the\ncurrent scope, in the appropriate namespace. The name in the current\nscope shall be the import's alias name, followed by a forward slash,\nfollowed by the identifier's name in the source module. As an example,\nsuppose the module ``MyModule.lisp`` defines a public function called\n``get-name``. Then the directive ``(use \"res://MyModule.lisp\" as\nMySpecialModule)`` would import that function into the current scope\nas ``MySpecialModule/get-name``.\n\nNon-GDLisp Imports\n^^^^^^^^^^^^^^^^^^\n\nThe ``use`` directive is used for both importing other GDLisp files,\nas well as for importing GDScript files, scenes, textures, and\nessentially anything you would use ``preload`` for in GDScript.\nHowever, the GDLisp compiler can only parse other GDLisp files, so any\nnon-GDLisp file must be imported using the qualified import, not an\nexplicit or open import. This restriction may be loosened in the\nfuture.\n\nIn the case of a non-GDLisp file, the alias name itself is introduced\ninto the module's lexical scope in the value namespace. In the case of\na GDScript file, the name refers to the top-level class defined in\nthat file. In the case of other resources, the name refers to the\nresource itself.\n\nOpen Imports\n------------\n\n::\n\n   (use \"res://filename.lisp\" open)\n\nAn open import is similar to a qualified import except without the\nqualifier. An open import introduces all of the public names from the\ntarget module into the current scope, keeping their names identical to\nthe original names from the source module.\n\nExplicit Imports\n----------------\n\n::\n\n  (use \"res://filename.lisp\" (...))\n\nAn explicit import does *not* import all of the public names into the\ncurrent scope. Instead, it imports only the names specified. Each\nelement of the list that follows the pathname identifies a single name\nto be imported. The most general form for these elements is\n\n::\n\n   (source-name namespace as destination-name)\n\nwhere ``source-name`` and ``destination-name`` are arbitrary\nidentifiers, and ``namespace`` is one of ``value`` or ``function``.\nThis imports the name ``source-name`` from the given namespace into\nthe current module scope (in the same namespace), giving it the name\n``destination-name``.\n\nThe ``as destination-name`` portion may be omitted and, if left out,\nwill be assumed to be the same as the ``source-name``. The\n``namespace`` may also be omitted if unambiguous. If the namespace is\nomitted, then it will be inferred from the available names in the\nsource module. If there is *both* a function and a value with that\nname in the source module, then an error will be issued. Finally, if\nboth the alias name and the namespace are omitted, then the symbol for\nthe name must be passed on its own, not inside of a sublist. In\nsummary, the four possible forms of a named import are\n\n::\n\n   (source-name namespace as destination-name)\n   (source-name namespace) ;; destination-name implied to be source-name\n   (source-name as destination-name) ;; namespace is inferred from context\n   source-name ;; namespace is inferred, and destination-name is implied to be source-name\n"
  },
  {
    "path": "doc/readthedocs/reference/index.rst",
    "content": "\n.. _comprehensive:\n\nReference Documentation\n=======================\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents:\n\n   parser.rst\n   datatypes.rst\n   source-file.rst\n   lambda-lists.rst\n   macros.rst\n   classes.rst\n   imports.rst\n   expressions.rst\n   quoting.rst\n   command-line.rst\n   standard-library/index.rst\n"
  },
  {
    "path": "doc/readthedocs/reference/lambda-lists.rst",
    "content": "\nLambda Lists\n============\n\nFunctions, macros, and constructors in GDLisp take arguments. As part\nof their declaration syntax, any of these declarations must declare\nthe collection of *formal arguments* that they take. These formal\narguments take the form of a *lambda list*, which is a special\nsub-language that communicates what arguments are permitted to the\nGDLisp compiler.\n\nBroadly speaking, a lambda list is a list of symbols indicating\nvariable names to bind arguments to. As part of this list of symbols,\nspecial directives beginning with an ampersand ``&`` can change the\nbehavior of the following arguments, such as making required arguments\noptional.\n\nNote that a name beginning with an ampersand in a lambda list is\n*always* treated as a special directive. It is an error to use a\nspecial directive that does not exist, or to use a directive in a\ncontext that disallows it. As a consequence, it is not possible to\ndeclare arguments in a lambda list whose names begin with an\nampersand. It is generally not recommended to declare variable names\nbeginning with an ampersand at all for this reason.\n\nIt is an error to list the same name twice in a lambda list. Likewise,\nit is an error to list the same special directive twice, unless\notherwise stated.\n\nA special directive generally only lasts until the next special\ndirective. Unless otherwise stated, directives do not \"stack\".\n\nThere are different types of lambda lists, depending on the context in\nwhich it appears.\n\n.. _simple-lambda-lists:\n\nSimple Lambda Lists\n-------------------\n\nThe simplest kind of lambda list is called, fittingly, a *simple\nlambda list*. A simple lambda list is a collection of zero or more\nrequired arguments, with no special directives or alternative forms\nallowed. That is, a simple lambda list is always a simple list of\nliteral symbols.\n\nSimple lambda lists are used in the declaration of class functions and\nsignals, which do not support more advanced argument parsing.\n\nExamples::\n\n  ()\n  (arg1 arg2)\n  (foo bar baz)\n\n.. _ordinary-lambda-lists:\n\nOrdinary Lambda Lists\n---------------------\n\nOrdinary lambda lists are the most common kind of lambda list in\nGDLisp, used for module-level functions and macros, as well as lambda\nexpressions.\n\nAn ordinary lambda list is a list of symbol names of arguments. An\nordinary lambda list accepts several special directives. All special\ndirectives are optional. If multiple directives are provided, then\n``&opt`` must be the first of them, and only one of ``&rest`` or\n``&arr`` can be provided.\n\n* Any arguments that appear before any directives are treated as\n  *required parameters*. It is an error to call a function or macro\n  with fewer arguments than the number of required parameters in its\n  lambda list.\n\n* Any arguments that appear after the ``&opt`` directive are optional\n  parameters. If too few arguments are provided and the rest of the\n  parameters are optional, then any optional parameters that have not\n  been bound receive a default value of ``()``, the null object.\n\n* If the ``&rest`` directive is used, it must be followed by a single\n  name, which indicates the name of the function's \"rest\" argument.\n  After binding any required and optional arguments, all remaining\n  arguments to the function are collected into a list and passed as\n  the function's \"rest\" argument. If there are no extra arguments,\n  then the empty list ``()`` is passed. Note that it is impossible to\n  call a function which declares a ``&rest`` argument with \"too many\"\n  arguments, as all extras will be collected into one variable.\n\n* The ``&arr`` directive works similarly to ``&rest``. It must be\n  followed by a single name. All remaining arguments, after binding\n  required and optional arguments, will be collected and passed at the\n  \"arr\" argument, but in this case they will be collected into an\n  array rather than a list.\n\nExamples::\n\n  ()\n  (arg1 arg2)\n  (required-arg1 required-arg2 &opt optional-arg1 optional-arg2)\n  (&rest all-args)\n  (&arr all-args-as-array)\n  (required-arg &opt optional-arg &rest all-remaining-args)\n\n.. _constructor-lambda-lists:\n\nConstructor Lambda Lists\n------------------------\n\nA constructor lambda list is a lambda list used to indicate the\narguments to a class' ``_init`` constructor. A constructor lambda list\nis similar to a simple lambda list but allows for one additional\nfeature.\n\nA constructor lambda list consists of a list of arguments. Each\nargument can be a symbol, similar to a simple lambda list. However,\narguments can also take the special form ``@name`` (or, written\nliterally, ``(access-slot self name)``), where ``name`` is any valid\nsymbol identifier. An argument of this form will *not* be bound to a\nlocal variable when the constructor is called. Instead, the value\npassed for that argument will be bound to the instance variable with\nthe given name on ``self``. If no such instance variable exists, then\nan error will be issued at runtime.\n\nThis allows a very compact representation of classes whose\nconstructors merely initialize fields.\n\n.. code-block::\n\n   (defclass Enemy (Node)\n     (defvars name attack-points defense-points)\n     (defn _init (@name @attack-points @defense-points)))\n\nExamples::\n\n  (foo bar)\n  (x y z)\n  (@x @y @z)\n  (some-ordinary-arg @some-instance-var)\n"
  },
  {
    "path": "doc/readthedocs/reference/macros.rst",
    "content": "\n.. _macros:\n\nMacros\n======\n\nMacros are the key feature that separates a Lisp dialect from many\nconventional programming languages. A macro is a mechanism to\ndynamically generate code during compilation.\n\nMacros are subdivided into two categories in GDLisp: functional macros\nand symbol macros. Functional macros are far more common, so this\ndocumentation will often refer to them simply as \"macros\" with the\n\"functional\" part understood.\n\nA (functional) macro declaration looks similar to a function\ndeclaration, except that the former uses ``defmacro`` rather than\n``defn``.\n\n.. code-block::\n\n   (defmacro macro-name (args ...)\n      body ...)\n   (defmacro macro-name (args ...) public-or-private\n      body ...)\n\nA macro's argument list takes the form of a :ref:`ordinary lambda list\n<ordinary-lambda-lists>`, which means that macros can take optional\nand variable arguments.\n\nWhen a macro declaration is encountered, all names that it references,\neither directly or indirectly, must always be fully defined. That is,\nwhile two functions can mutually depend on each other, regardless of\nthe order in which they're defined in a file, a macro cannot depend on\na function or constant defined *later* in the current file.\n\nMacros are defined immediately, and are available to the compiler. The\nname of the macro is bound, in the function namespace, to the given\nmacro object for the current module. A macro is a function-like object\nthat is invoked during compilation.\n\nSpecifically, whenever the compiler is expecting a declaration or an\nexpression and encounters a proper, nonempty list ``(foo args ...)``,\nit will first check whether it is aware of a macro defined in the\ncurrent scope with the name ``foo``. This process is referred to as\n*macro expansion*.\n\nIf there is a macro with that name, then rather than interpreting the\ncode as an expression or declaration, the macro will be invoked with\nthe given arguments. Note carefully that the macro's arguments are\npassed in *unevaluated*. That is, if there is a macro called\n``example`` and it is called as ``(example arg)``, then the literal\nsymbol ``arg`` will be passed in, *not* the value of a variable called\n``arg``. Likewise, if it is called as ``(example (+ 1 1))``, then the\nliteral three-element list ``(+ 1 1)`` will be passed in, not the\nnumber ``2``.\n\nA macro must return an S-expression, which will replace the macro call\nin the current position of the code. If a macro is called in\ndeclaration context, then the result of the macro call will be treated\nas a declaration. If a macro is called in expression context, then the\nresult of the macro call will be treated as an expression.\n\nThe result of a macro call is itself subject to macro expansion. A\nmacro call can return an S-expression which itself calls another\nmacro, or even the same macro recursively. The behavior is undefined\nif a macro exhibits infinitely recursive behavior. That is, the\nfollowing macro will exhibit undefined behavior if it is ever invoked.\n\n::\n\n  (defmacro recursive ()\n    '(recursive))\n\n.. _symbol-macros:\n\nSymbol Macros\n-------------\n\n.. code-block::\n\n   (define-symbol-macro macro-name value)\n   (define-symbol-macro macro-name value public-or-private)\n\nThe second kind of macro is a symbol macro. Symbol macros work like\nfunctional macros except that they occupy the value namespace, not the\nfunction namespace.\n\nWhen a symbol macro is declared, it binds a name in the value\nnamespace of the current module. Note that symbol macros cannot take\narguments. The same caveats with regards to definedness apply for\nsymbol macros: all names referenced (directly or indirectly) by a\nsymbol macro must be fully available at compile-time, when the symbol\nmacro is first defined.\n\nMacro expansion for symbol macros occurs when a symbol literal ``foo``\nis evaluated in declaration or expression context. In this case,\nbefore compiling the symbol literally, GDLisp checks whether a symbol\nmacro with the given name exists in the current scope. If it does,\nthen that symbol macro's body is evaluated in a new lexical scope, and\nthe return value of the symbol macro is used in place of the symbol\nliteral. Like functional macros, symbol macros can expand recursively\ninto other macro calls.\n\nNote that the \"body\" of a symbol macro is a single expression, not a\ncollection of zero or more expressions. Symbol macros are designed to\nhave the same syntax as ``defconst``. To write a symbol macro whose\nbody consists of multiple statements, wrap the body in the ``progn``\nspecial form.\n\nTechnical Limitations\n---------------------\n\nMacros can reference other GDLisp source files freely. However, due to\ntechnical limitations, macros cannot currently interface directly with\nGDScript source files or other resource types (such as packed scenes\nor textures). This limitation may be lifted in the future.\n\nAdditionally, care must be taken if files are dynamically loaded via\nthe ``load`` function. GDLisp performs name mangling during macro\nexpansion in order to consistently load macros into the runtime. The\nGDLisp compiler understands ``use`` directives and ``preload`` calls\n(both of which must refer to a statically-known filename) and will\ntranslate these names accordingly, but GDLisp will not attempt to\ntranslate the argument to a ``load`` function. The built-in macro\n``contextual-load`` can be helpful to perform such dynamic loading\ninside of macros.\n"
  },
  {
    "path": "doc/readthedocs/reference/parser.rst",
    "content": "\n.. _parser:\n\nThe GDLisp Parser\n=================\n\nThe GDLisp language is a Lisp dialect, with a few extensions to make\nobject-oriented programming using Godot types more straightforward.\n\nListed below is a full description of the GDLisp grammar using\n`Extended Backup-Naur form\n<https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form>`_.\n\nGDLisp source code is always written in UTF-8. GDLisp supports the\nfull breadth of Unicode code points, so unless otherwise stated,\n\"character\" refers to a valid Unicode character.\n\n.. code-block:: ebnf\n\n   prefixed-expr = [ prefix ], expr ;\n   prefix = \"'\" | \"#'\" | \"`\" | \",\" | \",.\" ;\n   expr = literal | list-expr | array-expr |\n          dict-expr | vector-expr |\n          nested-name | nested-node-path |\n          self-name | self-node-path ;\n   literal = \"#t\" | \"#f\" | integer | float | string | symbol ;\n   list-expr = \"(\", \")\" | \"(\", prefixed-expr, {prefixed-expr}, [\".\" prefixed-expr], \")\" ;\n   array-expr = \"[\", {prefixed-expr}, \"]\" ;\n   dict-expr = \"{\", {prefixed-expr, prefixed-expr}, \"}\" ;\n   vector-expr = \"V{\", prefixed-expr, prefixed-expr, [prefixed-expr], \"}\" ;\n   nested-name = expr, \":\", symbol ;\n   nested-node-path = expr, \":\", node-path ;\n   self-name = \"@\", symbol ;\n   self-node-path = node-path ;\n   integer = ? integer literal ? ;\n   float = ? floating point literal ? ;\n   string = ? string literal ? ;\n   symbol = ? symbol literal ? ;\n   node-path = ? node path literal ? ;\n\nGDLisp code, like any other Lisp dialect, is built up of zero or more\n`S-expressions <https://en.wikipedia.org/wiki/S-expression>`_, where\nan S-expression is defined to be an (optionally prefixed) form of any\nof the following.\n\n* A literal expression\n* A list\n* An array\n* A dictionary\n* A (2D or 3D) vector\n* A nested name or node path\n* A self name or node path\n\nUnless otherwise stated, an arbitrary amount of whitespace is allowed\nbetween adjacent tokens in the above EBNF grammar. \"Whitespace\" is,\nhere, defined as any Unicode character with the ``White_Space=yes``\nproperty.\n\nLiterals\n--------\n\n.. code-block:: ebnf\n\n   literal = \"#t\" | \"#f\" | integer | float | string | symbol ;\n   integer = ? integer literal ? ;\n   float = ? floating point literal ? ;\n   string = ? string literal ? ;\n   symbol = ? symbol literal ? ;\n   node-path = ? node path literal ? ;\n\nLiterals in GDLisp are integers, floating point values, strings,\nsymbols, or node path literals.\n\nInteger Literals\n^^^^^^^^^^^^^^^^\n\nAn integer literal consists of an optional sign (``+`` or ``-``)\nfollowed by one or more ASCII digits (``0`` to ``9``). The following\nare valid integer literals: ``0``, ``+56``, ``-9``, ``10000``,\n``00900``. Leading zeroes in an integer literal are ignored. Unlike in\nC, the presence of a leading zero does *not* cause the subsequent\nnumber to be interpreted as ASCII.\n\nFloating Point Literals\n^^^^^^^^^^^^^^^^^^^^^^^\n\nA floating point literal is an expression which matches the following\nregular expression\n\n.. code-block:: text\n\n   [+-]?[0-9]+(\\.[0-9]+)?([eE][+-]?[0-9]+)?\n\nand is *not* a valid integer literal.\n\nString Literals\n^^^^^^^^^^^^^^^\n\nA string literal is a sequence of zero or more characters enclosed in\nquotation marks ``\"``. Inside the quotation marks is a sequence of\nindividual string characters, where each individual character is one\nof\n\n* Any Unicode character other than a backslash ``\\`` or a quotation\n  mark ``\"``.\n* A valid escape sequence beginning in a backslash ``\\``.\n\nEscape Sequences\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nAn escape sequence in GDLisp begins with a backslash and consists of\none or more characters indicating what character the sequence should\nbe translated to in the resulting code. GDLisp supports most of the\ncommonly-used escape sequences found in C-style languages.\n\n* ``\\n`` translates to a newline (``0x0A``)\n* ``\\t`` translates to a horizontal tab (``0x09``)\n* ``\\r`` translates to a carriage return (``0x0D``)\n* ``\\a`` translates to the 'alert' character (``0x07``)\n* ``\\b`` translates to the backspace character (``0x08``)\n* ``\\f`` translates to a form feed (``0x0C``)\n* ``\\v`` translates to a vertical tab (``0x0B``)\n* ``\\\"`` translates to a literal quotation mark (``0x22``)\n* ``\\'`` translates to a literal apostrophe (``0x27``)\n* ``\\\\`` translates to a literal backslash (``0x5C``)\n\nFinally, a backslash followed by a ``u`` is a Unicode literal. Unicode\nliterals can be represented in two forms: basic and extended.\n\nA basic Unicode literal consists of ``\\u`` followed by exactly four\nvalid hexadecimal characters. The four characters are interpreted as a\nnumber in base 16 and must point to a valid Unicode code point. Note\nthat there are characters outside of the basic multilingual plane\n(such as emoji) that cannot be represented in this way. For such\ncharacters, the extended form is provided.\n\nAn extended Unicode literal consists of ``\\u`` followed by a\ncurly-brace-enclosed list of at least one hexadecimal character. The\ncharacters in the list are interpreted as a number in base 16 and must\npoint to a valid Unicode code point.\n\nA backslash followed by any other character in a string literal is an\nerror.\n\nSymbol Literals\n^^^^^^^^^^^^^^^\n\nSymbols are the cornerstone of a Lisp program and are used as variable\nand function names. These are, generally speaking, the valid\nidentifiers in a Lisp program.\n\n.. code-block:: ebnf\n\n   symbol = starting-char, { following-char }, { qualifier } ;\n   qualifier = \".\", following-char, { following-char } ;\n\nA symbol consists of a starting character, followed by zero or more\nfollowing characters, then subsequently followed by zero or more\nqualifiers. A qualifier consists of a dot followed by one or more\nfollowing characters.\n\nThe starting character of a symbol literal can be any of the following.\n\n* An ASCII letter\n* Any of the following: ``_~+=-\\/!%^&*<>?``\n* Any non-ASCII character which falls into the Unicode categories L,\n  Mn, Nl, No, S, Pc, Pd, or Po.\n\nA \"following\" character can be any starting character, a valid ASCII\nnumber, or any non-ASCII character in the Unicode category N.\n\nThe following are examples of valid identifiers in GDLisp: ``foo``,\n``bar``, ``satisfies?``, ``set-element``, ``list/map``,\n``com.mercerenies.gdlisp``.\n\n.. Note:: Unlike in some Lisp dialects, symbols in GDLisp are **case\n          sensitive**. That is, ``foo`` and ``Foo`` are distinct\n          symbols that refer to distinct variables or functions.\n\nNode Path Literals\n^^^^^^^^^^^^^^^^^^\n\nA node path literal is the primary means of accessing nodes in the\nscene tree whose names are known at compile-time. A node path literal\nconsists of a dollar sign ``$`` followed by either a quoted string\nliteral or a sequence of one or more of the following:\n* An ASCII letter or number\n* Any of the following: ``_~+=-\\/!$%^&*<>?``\n\nNote that only ASCII characters are allowed in the non-quoted node\npath form. To include Unicode characters in a node path, it is\nnecessary to quote the path.\n\nLists\n-----\n\n.. code-block:: ebnf\n\n   list-expr = \"(\", \")\" | \"(\", prefixed-expr, {prefixed-expr}, [\".\" prefixed-expr], \")\" ;\n\nIn GDLisp, the fundamental unit of composition is a *cons cell*,\nsometimes called a *pair*. A cons cell consists of two elements,\nconventionally referred to as the *car* and the *cdr*, separated by a\ndot and enclosed in parentheses.\n\n.. code-block:: text\n\n  (a . b)\n\nBy convention, lists are built up as a singly-linked list, using cons\ncells as the links. The car of each is the first element, or \"head\",\nof the list, and the cdr of each is the rest of the list, or \"tail\".\nThe end of the list is denoted with the special \"null\" atom,\nindicating by a pair of parentheses with nothing in between.\n\nThis convention is so widely used in Lisp programs that the syntax\nsupports it directly. That is, a sequence of of one or more\nS-expressions, followed by the dotted end of the list, is interpreted\nas a list whose final cdr is the rightmost term. Concretely, the\nfollowing are equivalent.\n\n.. code-block:: text\n\n  (a b c . d)\n  (a . (b . (c . d)))\n\nSimilarly, if the dotted terminator is left off, it is assumed to be\nthe special ``()`` null object, so the following are equivalent.\n\n.. code-block:: text\n\n  (a b c d)\n  (a . (b . (c . d . ())))\n\nAn S-expression can be viewed as a *dotted list*, consisting of a\nleading list of values (in the ``car`` portion of cons cells)\nterminated by an arbitrary non-cons value as the final ``cdr``. For\ninstance, the S-expression ``(a b c . d)`` (or, equivalently, ``(a .\n(b . (c . d)))`` would be viewed as a dotted list consisting of the\nelements ``a``, ``b``, and ``c``, terminated by ``d``.\n\nWe call a dotted list which is terminated by the ``()`` null object a\n*proper list*. All lists appearing unquoted in GDLisp source code must\nbe proper lists.\n\nArrays\n------\n\n.. code-block:: ebnf\n\n   array-expr = \"[\", {prefixed-expr}, \"]\" ;\n\nLike in GDScript, a GDLisp array is a general-purpose random-access\ndata structure. Array literals are written as a proper list whose\nfirst element is the symbol ``array``. For convenience, zero or more\nexpressions wrapped in square brackets will desugar to this syntax.\nNote that, unlike in GDScript, elements in an array literal are *not*\nseparated by commas.\n\nExamples:\n\n.. code-block:: text\n\n    [] ==> (array)\n    [1 2 3 4] ==> (array 1 2 3 4)\n\nDictionaries\n------------\n\n.. code-block:: ebnf\n\n   dict-expr = \"{\", {prefixed-expr, prefixed-expr}, \"}\" ;\n\nA dictionary expression is a collection of an even number of\nexpressions, enclosed in curly braces. Like array literals, dictionary\nliterals are not a new form of syntax but are instead mere sugar for\nsomething that can be expressed with only basic S-expressions. The\nfirst element of each pair of expressions is a key and the second is a\nvalue in the resulting dictionary. It is an error to have a\nbrace-enclosed collection of an *odd* number of expressions. A\ndictionary literal desugars to a proper list whose first element is\nthe symbol ``dict``.\n\nExamples:\n\n.. code-block:: text\n\n    {} ==> (dict)\n    {1 2 3 4} ==> (dict 1 2 3 4)\n\n\nVectors\n-------\n\n.. code-block:: ebnf\n\n   vector-expr = \"V{\", prefixed-expr, prefixed-expr, [prefixed-expr], \"}\" ;\n\nVectors are used in Godot to represent objects in 2D or 3D space. A\nvector in GDLisp is constructed using the ``vector`` built-in\nfunction. Since vectors are so ubiquitous, the syntax ``V{ ... }`` is\nprovided, which desugars to a call to the ``vector`` function with the\ngiven arguments.\n\nNested Names\n------------\n\n.. code-block:: ebnf\n\n   nested-name = expr, \":\", symbol ;\n   nested-node-path = expr, \":\", node-path ;\n   self-name = \"@\", symbol ;\n   self-node-path = node-path ;\n\nGodot is built on an single-inheritance, messaging-passing\nobject-oriented paradigm. This means that it's very common to call a\nfunction *on* an object, not just an independent function that exists\nin the abstract.\n\nIn GDLisp, the equivalent to the GDScript \"dot\" operator is the\n``access-slot`` built-in special form. That is, ``foo.bar`` in\nGDScript would be translated to the GDLisp ``(access-slot foo bar)``.\nThis is relatively awkward and verbose to write all the time, so\nseveral shortcuts are provided. These are purely syntax sugar and are\n*not* new data structures in the abstract syntax tree of the\nlanguage.\n\nThe following translations are made.\n\n1. ``foo:bar``, with a literal colon in the middle, is translated to\n   ``(access-slot foo bar)``. The left-hand side can be any\n   (non-prefixed) expression, and the right-hand side must be a symbol\n   literal. Note that a prefix in front of the left-hand side will be\n   parsed at a lower precedence than the ``:``, so ``'a:b`` will be\n   parsed as ``(quote (access-slot a b))``, not ``(access-slot\n   (quote a) b)``. If the precedence is not to your liking, then you\n   can always write out the S-expressions by hand rather than using\n   the syntax sugar, and sometimes this is necessary in complex macro\n   expansions.\n\n2. ``foo:$bar`` translates to a function call equivalent to\n   ``(foo:get-node \"bar\")``. Written in full generality, this is\n   ``((access-slot foo get-node) \"bar\")``. Internally, the *actual*\n   expression calls a GDLisp built-in function that allows for better\n   optimization potential, but the effect is the same as long as your\n   custom nodes do not override ``get-node``. The right-hand side must\n   be a node path literal, either quoted or unquoted.\n\n3. ``@bar`` translates to ``(access-slot self bar)``, or equivalently\n   ``self:bar``.\n\n4. A node path literal on its own ``$bar`` translates to a function\n   call equivalent to ``((access-slot self get-node) \"bar\")``. As with\n   the nested form, ``@$bar`` does not *literally* translate to the\n   latter, instead factoring through a GDLisp middleman for\n   optimization purposes.\n\nPrefixes\n--------\n\n.. code-block:: ebnf\n\n   prefixed-expr = [ prefix ], expr ;\n   prefix = \"'\" | \"#'\" | \"`\" | \",\" | \",.\" ;\n\nExpressions, in general, can have a single prefix applied to them.\nEach prefix is mere syntax sugar for some slightly more complicated\nS-expression form. The semantics of these forms are explained in\nfuture sections, but the translations are defined here.\n\n* ``'foo`` translates to ``(quote foo)``\n\n* ``#'foo`` translates to ``(function foo)``\n\n* ```foo`` translates to ``(quasiquote foo)``\n\n* ``,foo`` translates to ``(unquote foo)``\n\n* ``,.foo`` translates to ``(unquote-spliced foo)``\n\nComments\n--------\n\nThere are two types of comments in GDLisp. Both are ignored entirely\nby the implementation and will *not* be present in the compiled\nGDScript code.\n\nLine comments begin with a semicolon ``;`` and continue until the next\ncarriage return or linefeed character, or until the end of the file.\nConventionally, line comments which occupy the *entire* line will be\nwritten with two semicolons, so it's common to see a pair of\nsemicolons denote a line comment. But this is merely a convention and\nis, as far as the GDLisp parser is concerned, an irrelevant\ndistinction.\n\nBlock comments begin with ``#|`` and continue until the next ``|#``.\nNote that block comments cannot be nested, so additional leading\nsequences of ``#|`` inside a block comment will be ignored, not paired\noff against matching delimiters.\n"
  },
  {
    "path": "doc/readthedocs/reference/quoting.rst",
    "content": "\n.. _quoting:\n\nQuoting and Quasiquoting\n========================\n\nQuoting an S-expression delays its evaluation. That is, normally in\nGDLisp, a symbol or proper list that appears in expression context\nwill have special semantics, whether that involves expanding macros,\napplying functions, accessing local variables, or any number of other\nrules. However, if you actually intend to explicitly construct a list\nor symbol as-is and have access to that data structure as a runtime\nvalue, you must quote the S-expression.\n\nThis is done by calling the special form ``quote`` with one argument,\nas ``(quote some-s-expression)``. This can be abbreviated to an\napostrophe followed by the expression: ``'some-s-expression``.\n\nAll literals evaluate to themselves when quoted. This includes\nBooleans (``#t`` and ``#f``), integers, floats, strings, symbols, and\nthe null object. Aside from symbols (when are treated specially when\nquoted), the quote operator is a no-op on literals. That is, the\nfollowing are all equivalent when evaluated.\n\n::\n\n   '1 == 1\n   '1.0 == 1.0\n   '#t == #t\n   '#f == #f\n   '() == ()\n   '\"abc\" == \"abc\"\n\nA list evaluates, likewise, to the list itself when quoted. So while\n``(foo bar)`` is a function call in expression context, ``'(foo bar)``\n(or equivalently ``(quote (foo bar))``) is a proper list at runtime,\nwhose first element is the symbol ``foo`` and whose second is the\nsymbol ``bar``. Note that this also highlights another property of\nquoting, namely that quoting an expression is contagious on the inner\nexpressions. Since the outer list in ``'(foo bar)`` is quoted, the\nnames ``foo`` and ``bar`` are never evaluated as variable names\neither.\n\nYou may also use dotted list notation to construct lists with\nnon-``()`` endings. For instance, ``'(1 2 . 3)`` will return a runtime\ncons object whose car is ``1``. The cdr of this object is another cons\nobject whose car is ``2`` and whose cdr is ``3``.\n\nQuasiquoting\n------------\n\nOften, fully quoting with ``quote`` is sufficient. However, there are\noften situations, especially during macro expansion, where an\nS-expression should be interpreted mostly literally but with a few\nvalues interjected at runtime. This is where quasiquoting comes in.\n\nAn S-expression is quasiquoted with the ``quasiquote`` special form,\nabbreviated with a single prefix backtick `````. ``quasiquote``\nbehaves like ``quote`` except that it interprets the forms ``unquote``\nand ``unquote-spliced`` in a special way.\n\nAn ``unquote`` form (or, equivalently, a prefix ``,``) inside of a\n``quasiquote`` effectively reverses the ``quasiquote``. The expression\ninside of the ``unquote`` is evaluated and *interpolated* into the\nlist or expression being constructed by ``quasiquote``.\n\nAn ``unquote-spliced`` form (equivalently, a prefix ``,.``) works like\nan ``unquote``, except that it can only be used inside a *list* within\na ``quasiquote`` and will flatten itself inside the enclosing list.\nConsider the following examples.\n\n::\n\n   `(1 2 ,(list 3 4) 5)\n\nThis evaluates to ``(1 2 (3 4) 5)``. The ``unquote`` (written with a\ncomma) causes the inner expression to be evaluated using the usual\nGDLisp rules, which produces a list that is then inserted, as a single\nelement, into the surrounding list. On the other hand,\n\n::\n\n   `(1 2 ,.(list 3 4) 5)\n\nThis evaluates to ``(1 2 3 4 5)``, because ``unquote-spliced``\n(written as ``,.``) flattens the result of evaluating the inner\nexpression into the outer list.\n\nThe expression within an ``unquote-spliced`` can evaluate to a list or\na Godot array. If it evaluates to any other datatype, then a runtime\nerror will be issued when the offending expression is interpolated.\n\nNested Quasiquotes\n^^^^^^^^^^^^^^^^^^\n\n.. Warning:: Nested quasiquotes are an experimental feature, whose\n             behavior may change in a future version of GDLisp. Use\n             with caution.\n\nQuasiquotes can be nested. If a ``quasiquote`` appears inside of\nanother ``quasiquote``, then it effectively cancels off with one\n``unquote`` or ``unquote-spliced`` on the inner list. That is,\n\n::\n\n   ``,a\n\nThis expression will not evaluate ``a`` as an expression. It will\nreturn the constant list ``(quasiquote (unquote a))``. On the other\nhand,\n\n::\n\n   ``,,a\n\nThis *will* evaluate the inner ``unquote`` but not the outer one, so\nif ``a`` has value ``1``, then this will return ``(quasiquote (unquote\n1))``.\n"
  },
  {
    "path": "doc/readthedocs/reference/source-file.rst",
    "content": "\nStructure of a GDLisp Source File\n=================================\n\nA GDLisp source file is, by convention, written with a ``.lisp`` file\nextension. GDLisp source files will be compiled into GDScript source\nfiles (``.gd``) in a one-to-one fashion, with the resulting compiled\nGDScript file taking the same filename as the input file, merely with\nthe extension changed. So, for example, ``Player.lisp`` would be\ncompiled to ``Player.gd``. GDLisp source files are *always* encoded in\nUTF-8.\n\nA GDLisp source file defines a module. Syntactically, a GDLisp source\nfile consists of zero or more S-expressions. With the exception of\nquoted and quasiquoted terms, all lists appearing in GDLisp source\ncode must be *proper lists*. These top-level S-expressions will be\ninterpreted as declarations and will be compiled together to form a\ntop-level GDScript class body.\n\nThere are six types of declarations: ``defn``, ``defmacro``,\n``defconst``, ``defclass``, ``defenum``, and ``define-symbol-macro``.\nAdditionally, there is one pseudo-declaration, called ``progn``, which\nis treated specially in this context. Finally, ``use`` directives\nappear in declaration context, despite not being declarations\nthemselves.\n\nMacros are discussed in :ref:`macros`. Classes are discussed in\n:ref:`classes`. ``use`` directives are discussed in :ref:`imports`.\nThe other declaration forms are discussed below.\n\nNamespaces\n----------\n\nAll GDLisp declarations fall into one of three *namespaces*. The\nnamespaces are intentionally kept distinct, and it is possible to\ndefine the same name in all three namespaces in a given scope.\n\n* The value namespace consists of variables and constants and their\n  values. The value of a variable or constant can be a first-class\n  function object (reified as a value), as well as any other type of\n  object in GDLisp. Names in the value namespace are declared at the\n  top-level by ``defconst``, ``defclass``, ``defenum``, and\n  ``define-symbol-macro``. Names can be *locally* introduced to the\n  value namespace through several special forms, most prominently\n  ``let``.\n\n* The function namespace consists of functions that have been\n  explicitly bound to a function name. Functions are called by simply\n  indicating the name of the function as the head of an S-expression\n  in the source code. A name in the function namespace is *always*\n  bound to a value of function type or to a macro. At the top-level,\n  names in the function namespace are declared by ``defn`` and\n  ``defmacro``. Names can be *locally* introduced to the function\n  namespace through several special forms, such as ``flet`` and\n  ``labels``.\n\n* The final namespace is the signal namespace. This namespace only\n  exists inside the scope of a class declaration, never at the\n  top-level of a module. The signal namespace is the namespace which\n  contains signals *within* a class defined by ``defsignal``. Classes\n  are discussed later.\n\nFunctions\n---------\n\n.. code-block::\n\n   (defn function-name (args ...)\n      body ...)\n   (defn function-name (args ...) public-or-private\n      body ...)\n\nA function is the fundamental unit of code execution in GDLisp. A\nfunction defined at the top-level of a module compiles to a static\nfunction in the resulting GDScript file.\n\nA function declaration introduces a name into the function namespace\nfor the current module. When the function is called, the arguments\nwill be bound to the formal arguments of the function in a new lexical\nscope, and the body of the function will be executed, in order, in\nthat lexical scope. The final expression of the function body is\nalways returned from the function, but it is also possible to return\nearly using the ``return`` special form. If a function body is empty,\nthen the function is a no-op which silently returns the null object\n``()``.\n\nThe list of formal arguments is an :ref:`ordinary lambda list\n<ordinary-lambda-lists>`. When a function is called, the number of\narguments passed to the function is validated at compile-time, and an\nerror is issued if the count is incompatible with the function's\nlambda list.\n\nCompilation of Functions\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nA GDLisp function translates to a GDScript function with its name\n:ref:`normalized <name-normalization>`. No guarantees are currently\nmade about the GDScript signature of the resulting function if\noptional or variable arguments are used. However, if the function only\naccepts *required* arguments, then the resulting function is\nguaranteed to compile to a function that accepts exactly the same\nnumber of required arguments. Therefore, if you intend to call a\nGDLisp function from GDScript, then the GDLisp function should take\nonly required arguments, in order to maximize compatibility.\n\n.. _constants:\n\nConstants\n---------\n\n.. code-block::\n\n   (defconst constant-name constant-value)\n   (defconst constant-name constant-value public-or-private)\n\nA constant is a top-level immutable name which binds to a value. The\nvalue of a constant must be an expression whose value is known at\ncompile time.\n\nThe notion of a *constant expression* is recursively defined as\n\n* The name of another constant.\n\n* A literal value. (Due to Godot limitations, it is not currently\n  possible to assign *symbol* literals to constants. This limitation\n  will hopefully be lifted in a future version of GDLisp.)\n\n* A ``progn`` block with zero terms, or a ``progn`` block with exactly\n  one term which is a constant expression.\n\n* A built-in function call that performs basic arithmetic, such as\n  ``+`` or ``abs``, which is called with only constant expressions as\n  arguments.\n\n* An array or dictionary literal consisting of only constant\n  expressions as elements.\n\n* A field access ``foo:bar``, where ``foo`` is the name of an enum and\n  ``bar`` is a valid option for that enum.\n\n* A call to the ``preload`` special form.\n\nA constant declaration defines a name in the value namespace for the\ncurrent module.\n\nEnumerations\n------------\n\n.. code-block::\n\n   (defenum enum-name entries ...)\n   (defenum enum-name public-or-private entries ...)\n\nAn enumeration is a scoped namespace containing a finite number of\nvalues, useful for representing a collection of choices.\n\nEach entry in the enumeration can be specified either as a symbol\nliteral or as a two-element list, where the first element is the\nsymbol literal and the second is the constant expression indicating\nthe value of the enum entry.\n\nExamples::\n\n   (defenum PlayerChoice\n      ATTACK DEFEND HEAL PASS)\n\n   (defenum Color\n     (RED 0) (GREEN 1) (BLUE 2))\n\nAn enumeration defines a name in the value namespace. This name\nbehaves like a GDLisp object and has fields defined corresponding to\nthe names indicated in the entries. In the first example above,\n``PlayerChoice`` is a value for which ``PlayerChoice:ATTACK``,\n``PlayerChoice:DEFEND``, ``PlayerChoice:HEAL``, and\n``PlayerChoice:PASS`` are all distinct integer values. In the second\nexample above, ``Color`` is a value, and ``Color:RED`` is the value 0,\n``Color:GREEN`` is the value 1, and ``Color:BLUE`` is the value 2. If\nprovided, the value of an enumeration constant must be an integer.\n\n.. _progn:\n\nThe ``progn`` Directive\n-----------------------\n\n.. code-block::\n\n   (progn body ...)\n\n``progn`` is a special sort of directive, in that it can be used as a\ndeclaration *or* an expression. In declaration context, it takes zero\nor more declarations and evaluates them in order in the *current*\nscope, as though the ``progn`` wasn't even there.\n\nA ``progn`` directive is never useful directly in a file in\ndeclaration context, since it would be easier and more readable to\nsimply place the declarations at the top-level. However, it is useful\nin macro expansions, when a macro wishes to define multiple\ndeclarations but must evaluate to a *single* declaration S-expression.\n\n.. _visibility-modifiers:\n\nVisibility Modifiers\n--------------------\n\nSeveral module-level declarations take an optional visibility\nmodifier. A visibility modifier, if provided, must either be the\nsymbol ``private`` or the symbol ``public``. If a visibility modifier\nis not provided, it is always assumed to be ``public``.\n\nA name with public visibility can be accessed from anywhere. Any other\nmodule is free to import the name and reference, call, or instantiate\nit at their liberty.\n\nA name with private visibility is only directly usable within the\ncurrent module. The current module can freely use the name, but it is\nan error to attempt to import the name in *another* module.\n\n.. _name-normalization:\n\nName Normalization\n------------------\n\nGDLisp is far more lenient than GDScript when it comes to identifiers.\nIn particular, GDLisp allows several non-standard characters such as\n``-`` and ``?`` in identifiers, as well as Unicode characters.\nAdditionally, GDLisp does not have a notion of \"keywords\", and it's\nperfectly kosher to define a variable called ``if`` or ``while``\n(though it may confuse the readers of your code).\n\nWhen the GDLisp compiler translates your code into GDScript, it must\nconvert these identifiers into valid GDScript identifiers. The exact\ntranslation rules are an implementation detail that may change in\nfuture releases of GDLisp, but some guarantees are made in order to\nmaximize compatibility.\n\n* Any name which is a valid GDScript identifier and *not* a GDScript\n  keyword will be left unchanged. So ``foo``, ``foobar``,\n  ``player_health1``, and ``i`` will all be left untouched by the\n  GDLisp compiler.\n\n* An ASCII arrow ``->`` in a name will be translated to ``_to_``. This\n  allows a name like ``array->list`` to translate into GDScript as\n  ``array_to_list``.\n\n* A dash ``-`` that is *not* part of an ASCII arrow is translated to\n  an underscore ``_``. So a conventional Lisp function like\n  ``create-player`` will have its name translated into the GDScript\n  function ``create_player``.\n\n* A question mark ``?`` at the *end* of a name will be translated to\n  ``is_`` at the beginning. For instance, a Lisp predicate called\n  ``positive-number?`` will be translated to the GDScript function\n  ``is_positive_number``.\n\n* If the name is a reserved word in GDScript, then an underscore will\n  be prefixed, so ``if`` translates to ``_if`` when used as a variable\n  name.\n\n* Any other characters, or a ``?`` that is not at the end of a name,\n  will translate in an implementation-defined way.\n\n.. Warning:: It is undefined behavior to define two names in the same\n   scope and namespace that will normalize to the same name under\n   these rules. So, for example, it is undefined behavior to define\n   functions called ``foo-bar`` and ``foo_bar`` in the same scope,\n   since these names will both translate to ``foo_bar``.\n\nReserved Names\n--------------\n\nWhile GDLisp allows the programmer to define variables and functions\nwith a very broad set of identifier names, some names are specifically\nreserved for use by the GDLisp compiler. GDLisp programs should\n*never* define any names, in any namespace, that conflict with a\nGDLisp reserved name. The compiler may or may not flag such behavior.\n\n* The names ``self``, ``super``, and ``GDLisp`` are reserved.\n\n* All names beginning with ``sys/`` (including the forward slash) or\n  ``__gdlisp`` are reserved.\n\nFurther, names defined in the ``sys/`` namespace are strictly reserved\nfor *internal* use and are an implementation detail of the compiler.\nGDLisp programmers should never directly invoke such functions or\nreference such values, and the behavior of those names may change at\nany time, even in a maintenance release.\n\nOrder of Definition\n-------------------\n\nGenerally speaking, classes and functions defined in a GDLisp module\ncan reference each other freely and can be defined in any order.\nHowever, there is an important exception to this rule, and that is\nmacros.\n\nWhen a macro ``foo`` is defined, whether by ``defmacro``,\n``define-symbol-macro``, ``macrolet``, or ``symbol-macrolet``, that\nmacro and all of its dependencies must be *fully* defined. That is,\nevery function that ``foo`` calls and every constant, enum, or class\nname that ``foo`` references must already be defined in a preloaded\nfile or *earlier* in the current file, and the same must be\nrecursively true of all of the names referenced by the macro ``foo``.\nThis is a very strong constraint which is necessary to allow the macro\nto be loaded during compilation.\n"
  },
  {
    "path": "doc/readthedocs/reference/standard-library/functions.rst",
    "content": "\nBuilt-in Functions\n==================\n\nAll of the GDLisp global functions are documented here. These\nfunctions are available (in the function namespace) at the top-level\nof every GDLisp file.\n\nNote that most of the `GDScript built-in functions\n<https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html>`_\nare available unmodified in GDLisp as well. All names in GDScript\nwhich contain an underscore have their underscores replaced with\ndashes in GDLisp, so ``get_stack`` translates to ``get-stack`` in\nGDLisp for instance. The exceptions to this are:\n\n* ``assert``, ``preload``, and ``yield`` are not functions and are\n  instead special forms in GDLisp. See :ref:`expr-assert`,\n  :ref:`expr-preload`, and :ref:`expr-yield`, respectively.\n\n* ``typeof`` in GDLisp behaves differently. It is documented at\n  :ref:`function-typeof`.\n\n* ``convert`` has been wrapped to support GDLisp synthetic types. See\n  :ref:`function-convert`.\n\n* ``len`` has been wrapped in GDLisp to support GDLisp lists in\n  addition to strings, arrays, and dictionaries. See\n  :ref:`function-len`.\n\n* ``bool`` has been extended to support all types. See\n  :ref:`function-bool`.\n\n``*``\n-----\n\n::\n\n   (defn * (&rest args)\n     ...)\n\nMultiplies all of the arguments (which must be numerical or vectors)\ntogether. If given no arguments, returns the integer 1.\n\n``+``\n-----\n\n::\n\n   (defn + (&rest args)\n     ...)\n\nAdds all of the arguments (which must be numerical or vectors)\ntogether. If given no arguments, returns the integer 0.\n\nTo concatenate strings in GDLisp, use the built-in Godot function\n``str``, which accepts multiple strings and concatenates them\ntogether.\n\n``-``\n-----\n\n::\n\n   (defn - (x &rest args)\n     ...)\n\nSubtracts all of the arguments from the first in order. If given only\none argument, returns its negation. That is, ``(- x)`` is equivalent\nto ``(- 0 x)``.\n\n``/``\n-----\n\n::\n\n   (defn / (x &rest args)\n     ...)\n\nDivides all of the arguments out of the first in order. If given only\none argument, returns its reciprocal. That is, ``(/ x)`` is equivalent\nto ``(/ 0 x)``.\n\n``/=``\n------\n\n::\n\n   (defn /= (x &rest args)\n     ...)\n\nReturns true if *none* of the arguments are equal to each other. Note\nthat since ``/=`` is non-transitive, this is different than just\nchecking whether *adjacent* arguments are equal. That is, ``(/= 1 2\n1)`` is *false* even though no adjacent elements are equal.\n\n``<``\n-----\n\n::\n\n   (defn < (x &rest args)\n     ...)\n\nReturns true if each argument (in order) is less than the following\nelement, according to their natural ordering.\n\n``<=``\n------\n\n::\n\n   (defn <= (x &rest args)\n     ...)\n\nReturns true if each argument (in order) is less than or equal to the\nfollowing element, according to their natural ordering.\n\n``=``\n-----\n\n::\n\n   (defn = (x &rest args)\n     ...)\n\nChecks whether each argument is equal to the argument following it.\n\n``>``\n-----\n\n::\n\n   (defn > (x &rest args)\n     ...)\n\nReturns true if each argument (in order) is greater than the following\nelement, according to their natural ordering.\n\n``>=``\n------\n\n::\n\n   (defn >= (x &rest args)\n     ...)\n\nReturns true if each argument (in order) is greater than or equal to\nthe following element, according to their natural ordering.\n\n``append``\n----------\n\n::\n\n   (defn append (&rest args)\n     ...)\n\nAppends all of the lists together, returning a list which contains all\nof the elements of each argument, in the same order they appeared.\n\nThe resulting list will share structure with the final argument to\n``append``. That is, the cons cells leading up to the final argument\nto ``append`` will *not* be rebuilt. Additionally, the final argument\nto ``append`` can be an improper list, while all of the other\narguments must be proper lists.\n\n.. _function-apply:\n\n``apply``\n-----------\n\n::\n\n   (defn apply (f &rest args)\n     ...)\n\nCalls the function object ``f`` with the given arguments, returning\nthe result of the function call. At least one argument must be\nsupplied, and the final argument is treated as a list of arguments,\nnot an individual one.\n\nFor a version of ``apply`` without the \"variable argument\" behavior,\nsee :ref:`function-funcall`.\n\nThe difference between this function and ``funcall`` is that the\nformer treats its final argument as a list and prepends the others\nonto it. Concretely, all of the following will sum the numbers from\none to four, returning ten::\n\n  (+ 1 2 3 4)\n  (funcall #'+ 1 2 3 4)\n  (apply #'+ 1 2 3 4 ())\n  (apply #'+ '(1 2 3 4))\n  (apply #'+ 1 2 '(3 4))\n\n``array``\n----------\n\n::\n\n   (defn array (&rest xs)\n     ...)\n\nConstructs a array of the arguments given. The ``[ ... ]`` syntax\ndesugars to a call to this function.\n\n``array->list``\n---------------\n\n::\n\n   (defn array->list (arr)\n     ...)\n\nConverts a Godot array or pooled array into a proper list.\n\n``array/concat``\n----------------\n\n::\n\n    (defn array/concat (&rest arrays)\n      ...)\n\nConcatenates all of the arrays in order, producing a new array. The\nexisting arrays are not modified. If given an empty argument list,\nreturns an empty array.\n\n\n.. _function-array-filter:\n\n``array/filter``\n----------------\n\n::\n\n   (defn array/filter (p xs)\n     ...)\n\nApplies the unary predicate ``p`` to each element of the array ``xs``\nand returns a filter of all elements for which the predicate returned\ntruthy. Does not modify ``xs``.\n\n.. _function-array-find:\n\n``array/find``\n----------------\n\n::\n\n   (defn array/find (p arr &opt default)\n     ...)\n\nApplies the unary predicate ``p`` to each element of the array\n``arr``. Returns the first element of the array for which the\npredicate returned true. If no element returns true, then ``default``\nis returned. This function will short-circuit and stop calling ``p``\nas soon as a match is found.\n\n.. _function-array-fold:\n\n``array/fold``\n--------------\n\n::\n\n   (defn array/fold (f xs &opt x)\n     ...)\n\nA left-fold over an array. ``f`` shall be a function of two arguments,\n``xs`` shall be a Godot array, and ``x`` (if supplied) shall be a\nnon-null starting value.\n\nThe array is traversed from the beginning to the end. At each element,\nthe call ``(funcall f acc element)`` is made, where ``acc`` is the\nvalue we've accumulated so far and ``element`` is the current element.\nThe return value of that function call is used as the new value of\n``acc``. At the end, ``acc`` is returned.\n\nThe initial value of ``acc`` is ``x`` if supplied. If ``x`` is not\nsupplied, then the initial value is the first element of the array,\nand iteration begins at the second. If ``x`` is not supplied and the\narray is empty, then an error is produced.\n\n.. _function-array-map:\n\n``array/map``\n-------------\n\n::\n\n   (defn array/map (f xs)\n     ...)\n\nApplies the unary function ``f`` to each element of the array ``xs``\nand returns a new array of the returned values. Does not modify\n``xs``.\n\n``array/reverse``\n-----------------\n\n::\n\n   (defn array/reverse (arg)\n     ...)\n\nReturns a new array containing all of the same elements as the input\narray ``arg`` but in reverse order. Does not mutate ``arg``.\n\n.. _function-bool:\n\n``bool``\n--------\n\n::\n\n   (defn bool (x)\n     ...)\n\nNormalizes the argument to Boolean. Returns ``#t`` if ``x`` is truthy,\nor ``#f`` otherwise.\n\n``cons``\n--------\n\n::\n\n   (defn cons (a b)\n     ...)\n\nConstructs a fresh cons cell, with the first argument as car and the\nsecond as cdr.\n\n``connect>>``\n-------------\n\n::\n\n   (defn connect>> (obj signal-name function)\n     ...)\n\nConnects the signal on ``obj`` whose name is the string\n``signal-name`` to the given function. This function is similar to the\nbuilt-in method ``connect`` on Godot objects, but the former is\ndesigned to work on GDLisp first-class functions, such as those\nconstructed with :ref:`expr-lambda` or :ref:`expr-function`.\n\nThis function returns a unique identifier which can be passed to\n:ref:`function-disconnect` to disconnect the signal.\n\n.. Warning:: Due to current limitations in the implementation, signals\n             connected in this way can only accept up to six\n             arguments. If you need more than six arguments, use the\n             built-in Godot method ``Object.connect`` instead.\n\n``connect1>>``\n--------------\n\n::\n\n   (defn connect1>> (obj signal-name function)\n      ...)\n\nConnects the signal on ``obj`` whose name is the string\n``signal-name`` to the given function. The connection will be dropped\nas soon as it's been called once.\n\nReturns an identifier that can be passed to :ref:`function-disconnect`\nto disconnect the signal.\n\n.. Warning:: Like ``connect>>``, functions connected with this\n             function can only accept up to six arguments.\n\n.. _function-convert:\n\n``convert``\n-----------\n\n::\n\n   (defn convert (what type)\n     ...)\n\nConverts the value ``what`` into the type ``type``, according to the\nGodot rules for the GDScript function of the same name. ``type`` can\nbe a primitive type object (such as ``Int``) or a type enumeration\nconstant (such as ``Type:INT``).\n\n``dict``\n----------\n\n::\n\n   (defn dict (&rest xs)\n     ...)\n\nConstructs a dictionary, where each even-indexed argument is a key\npaired with the value immediately after it. of the arguments given.\nThe ``{ ... }`` syntax desugars to a call to this function. The\nbehavior is undefined if this function is called with an odd number of\narguments.\n\n``dict/elt``\n------------\n\n::\n\n   (defn dict/elt (dict k)\n     ...)\n\nGets the value corresponding to key ``k`` of the dictionary ``dict``.\n\n``dict/find``\n-------------\n\n::\n\n   (defn dict/find (p dict &opt default)\n     ...)\n\nIterates over the dictionary ``dict`` in element insertion order. For\neach key-value pair of the dictionary, the predicate ``p`` is called\nwith two arguments: the key and the value. Returns the first *key* for\nwhich the predicate returned true, or ``default`` if no match is\nfound. This function will short-circuit and stop calling ``p`` as soon\nas a match is found.\n\n.. _function-disconnect:\n\n``disconnect>>``\n----------------\n\n::\n\n   (defn disconnect>> (obj signal-name index)\n     ...)\n\nDisconnects a signal that was connected using ``connect>>`` or\n``connect1>>``. The index shall be the return value of the function\nthat was used to make the connection.\n\nNote that signals connected with Godot's built-in ``connect`` method\nshould be disconnected, correspondingly, with the\n``Object.disconnect`` method. This function is only designed to\ndisconnect signals made using the GDLisp functions ``connect>>`` and\n``connect1>>``.\n\n``equal?``\n----------\n\n::\n\n   (defn equal? (x &rest args)\n\nReturns whether each value is equal to the one after it, following\ndata structures recursively. This function is similar to the built-in\nGodot function ``deep_equal``, but ``equal?`` works on more data\ntypes. Specifically, ``equal?`` will recursively follow arrays (of any\ntype), dictionaries, and GDLisp lists.\n\n``elt``\n-------\n\n::\n\n   (defn elt (x y)\n     ...)\n\nGets the element from the data structure. ``(elt x y)`` is guaranteed\nto compile to the Godot ``x[y]``, hence can be used on arrays,\ndictionaries, and any other data structure that the subscript operator\nis compatible with.\n\n.. _function-funcall:\n\n``funcall``\n-----------\n\n::\n\n   (defn funcall (f &rest args)\n     ...)\n\nCalls the function object ``f`` with the given arguments, returning\nthe result of the function call. Note that, since function objects do\nnot know their own function signature at compile-time, the number of\narguments cannot be validated at compile time when using ``funcall``.\nAn error will be issued at runtime, if the argument count does not\nmatch up.\n\nExample::\n\n  (let ((my-function (lambda (x y) (+ x y))))\n    (funcall my-function 10 15)) ; Returns 25\n\nFor a version of ``funcall`` that takes a list of arguments rather\nthan individual arguments, see :ref:`function-apply`.\n\n``gcd``\n-------\n\n::\n\n   (defn gcd (&rest args)\n     ...)\n\nReturns the greatest common divisor of the arguments, or 0 if no\narguments are given.\n\n``gensym``\n----------\n\n::\n\n   (defn gensym (&opt prefix)\n     ...)\n\nReturns a new symbol object whose name has not appeared in the GDLisp\nsource code that the compiler has encountered up to this point and\nthat has not been interned dynamically with ``intern``.\n\nIf ``prefix`` is provided, then it must be a string and the returned\nsymbol will have a name that begins with ``prefix``. If not, then a\ndefault prefix will be chosen for you.\n\n``init``\n--------\n\n::\n\n   (defn init (a)\n     ...)\n\nReturns a new list which consists of all of the elements of the\noriginal list ``a`` *except* the final one. ``a`` can be a proper or\nimproper list, and in either case the terminator is ignored (it is not\nconsidered the final element, even if it is nontrivial), and the\nresulting list shall be proper.\n\n``a`` must be nonempty.\n\n``instance?``\n-------------\n\n::\n\n   (defn instance? (value type)\n     ...)\n\nGiven a value and a type object, returns whether or not that value is\nan instance of that type. The type object can be any of the following.\n\n* A GDScript or GDLisp class.\n* The name of a built-in Godot object type, such as ``Node`` or\n  ``Spatial``.\n* A Godot primitive type, such as ``Int``.\n* A GDLisp abstract type, such as ``BaseArray`` or ``AnyVal``.\n\n``intern``\n----------\n\n::\n\n   (defn intern (s)\n     ...)\n\nReturns a symbol object whose name is ``s``. If the given symbol\nalready exists in the global symbol table, then the *same* object (up\nto ``=`` comparison) is returned. Otherwise, a new symbol object is\ncreated and returned.\n\n``last``\n--------\n\n::\n\n   (defn last (a)\n     ...)\n\nReturns the final element of ``a``, which must be a nonempty list\n(proper or improper). The terminator of an improper list is ignored\n(it is not considered the final element, even if it is nontrivial).\n\n``lcm``\n-------\n\n::\n\n   (defn lcm (&rest args)\n     ...)\n\nReturns the least common multiple of the arguments, or 1 if no\narguments are given.\n\n.. _function-len:\n\n``len``\n-------\n\n::\n\n   (defn len (x)\n     ...)\n\nReturns the length of the list, array, dictionary, or string object.\n\n``list``\n--------\n\n::\n\n   (defn list (&rest args)\n     ...)\n\nReturns a proper list containing all of the arguments, in the same\norder.\n\n``list->array``\n---------------\n\n::\n\n   (defn list->array (list)\n     ...)\n\nConverts a proper list into a Godot array.\n\n``list/elt``\n------------\n\n::\n\n   (defn list/elt (list n)\n     ...)\n\nGiven a list and an index, returns the element of the list at that\n0-indexed position. Produces an error if the index is out of bounds.\n\n``list/copy``\n-------------\n\n::\n\n   (defn list/copy (list)\n     ...)\n\nReturns a new list containing the same elements as ``list`` in the\nsame order. The two lists will not share any structure.\n\n``list/filter``\n---------------\n\n::\n\n   (defn list/filter (p xs)\n     ...)\n\nApplies the unary predicate ``p`` to each element of ``xs`` and\nreturns a list of all elements for which the predicate returned\ntruthy. The returned list shares no structure with the argument list.\n\n``list/find``\n----------------\n\n::\n\n   (defn list/find (p xs &opt default)\n     ...)\n\nApplies the unary predicate ``p`` to each element of the proper list\n``xs``. Returns the first element of the list for which the predicate\nreturned true. If no element returns true, then ``default`` is\nreturned. This function will short-circuit and stop calling ``p`` as\nsoon as a match is found.\n\n``list/fold``\n-------------\n\n::\n\n   (defn list/fold (f xs &opt x)\n     ...)\n\nA left-fold over a list. ``f`` shall be a function of two arguments,\n``xs`` shall be a proper list, and ``x`` (if supplied) shall be a\nnon-null starting value.\n\nThe list is traversed from the beginning to the end. At each list\nelement, the call ``(funcall f acc element)`` is made, where ``acc``\nis the value we've accumulated so far and ``element`` is the current\nelement. The return value of that function call is used as the new\nvalue of ``acc``. At the end, ``acc`` is returned.\n\nThe initial value of ``acc`` is ``x`` if supplied. If ``x`` is not\nsupplied, then the initial value is the first element of the list, and\niteration begins at the second. If ``x`` is not supplied and the list\nis empty, then an error is produced.\n\n``list/map``\n------------\n\n::\n\n   (defn list/map (f xs)\n     ...)\n\nApplies the unary function ``f`` to each element of the list ``xs``\nand returns a new list of the result values.\n\n``list/reverse``\n----------------\n\n::\n\n   (defn list/reverse (arg)\n     ...)\n\nReturns a list containing all of the same elements as ``arg`` but in\nreverse order. Does not mutate ``arg``.\n\n``list/tail``\n-------------\n\n::\n\n   (defn list/tail (list k)\n     ...)\n\nReturns the ``k``\\ th cdr of ``list``. That is, returns a tail of the\nlist with the first ``k`` elements removed. The resulting list shares\nstructure with ``list``.\n\n``max``\n-------\n\n::\n\n   (defn max (&rest args)\n     ...)\n\nReturns the maximum of all of the arguments given, or ``- INF`` if\ngiven no arguments.\n\n``member``\n----------\n\n::\n\n   (defn member? (value structure)\n     ...)\n\nReturns whether or not the value is a member of the structure. This is\nguaranteed to compile to the Godot ``in`` infix operator and has the\nsame semantics.\n\n``min``\n-------\n\n::\n\n   (defn min (&rest args)\n     ...)\n\nReturns the minimum of all of the arguments given, or ``INF`` if given\nno arguments.\n\n``mod``\n-------\n\n::\n\n   (defn mod (x y)\n     ...)\n\nReturns the (integer) modulo of ``x`` by ``y``. As in Godot, to take a\nfloating-point modulo, use the built-in GDScript function ``fmod``.\n\n``NodePath``\n------------\n\n::\n\n   (defn NodePath (string)\n     ...)\n\nConstructs a ``NodePath`` object containing the given string.\n\n``not``\n-------\n\n::\n\n   (defn not (x)\n     ...)\n\nReturns a proper Boolean (i.e. either ``#t`` or ``#f``) of the\n*opposite* truthiness to ``x``. So if ``x`` is truthy then this\nfunction returns ``#f``, and if ``x`` is falsy then this function\nreturns ``#t``.\n\n``set-dict/elt``\n----------------\n\n::\n\n   (defn set-dict/elt (x dict k)\n     ...)\n\nSets the value corresponding to key ``k`` of the dictionary ``dict``\nto ``x``. If the key does not exist in the dictionary, then it is\ninserted.\n\n``set-elt``\n-----------\n\n::\n\n   (defn set-elt (x y z)\n     ...)\n\nSets the element from the data structure. ``(set-elt x y z)`` is guaranteed\nto compile to the Godot ``y[z] = x``, hence can be used on arrays,\ndictionaries, and any other data structure that the subscript operator\nis compatible with.\n\n``snoc``\n--------\n\n::\n\n   (defn snoc (a b)\n     ...)\n\nAppends an element ``b`` to the end of a proper list ``a``. The list\n``a`` is not modified, and the returned list is a newly-constructed\none.\n\n.. _function-typeof:\n\n``typeof``\n----------\n\n::\n\n   (defn typeof (value)\n     ...)\n\nGiven a value, return a type object representing its type. Note that\nthis is **not** the same as the GDScript ``typeof`` function, which\nreturns an integer constant. The object returned by the GDLisp\n``typeof`` function is an object representing the value's most\nspecific type, which can be passed to ``instance?`` for instance\nchecks.\n\n* In the case of a primitive value (i.e. an instance of a subtype of\n  ``AnyVal``), a special type object is returned. This type object can\n  only be passed to ``instance?`` and does not have any public fields\n  or methods.\n\n* In the case of an object whose type is defined in GDLisp or\n  GDScript, the script resource or inner class resource is returned.\n\n* In the case of a direct instance of a Godot native type like\n  ``Node``, the relevant native type object is returned.\n\n``vector``\n----------\n\n::\n\n   (defn vector (x y &opt z)\n     ...)\n\nConstructs a vector of two or three dimensions with the given\narguments. The ``V{ ... }`` syntax desugars to calls to this function.\n\n``vector/map``\n--------------\n\n::\n\n   (defn vector/map (f arg &rest args)\n     ...)\n\nApplies a function to each component of the 2D or 3D vectors. In its\nsimplest form (with two arguments), ``vector/map`` produces a new\nvector of the same dimension as the input, where ``f`` has been\napplied to the X and Y (and Z, if 3D) coordinates of the input vector.\n\nIf given more than two arguments, the arguments must all be of the\ndimension. ``f`` will be applied to the X components of *all* input\nvectors at once, then to the Y components, then to the Z components\n(if needed), producing a single new vector.\n\nExample::\n\n  (vector/map #'+ V{1 2} V{100 200}) ; Returns V{101 202}\n"
  },
  {
    "path": "doc/readthedocs/reference/standard-library/index.rst",
    "content": "\nThe GDLisp Standard Library\n===========================\n\nThe GDLisp standard library is provided at runtime by the\n``GDLisp.lisp`` support file. See :ref:`support-file` for details on\nhow to ensure that your project can access this file at runtime.\n\nAll names defined in the GDLisp standard library are available at the\nglobal scope in every GDLisp source file.\n\n.. toctree::\n   :maxdepth: 1\n   :caption: Contents:\n\n   values.rst\n   functions.rst\n   macros.rst\n   types.rst\n"
  },
  {
    "path": "doc/readthedocs/reference/standard-library/macros.rst",
    "content": "\nBuilt-in Macros\n===============\n\nAll of the global macros available in GDLisp are documented here.\n\n``->``\n------\n\n::\n\n   (defmacro -> (arg &rest forms)\n     ...)\n\nFirst-argument threading. Evaluates ``arg``. Then, for each form in\n``forms``, passes the accumulated argument as the first argument to\nthe given list (shifting the other arguments over). Any argument which\nis a symbol is treated as a one-element list.\n\nExamples::\n\n  (-> a b c) ; Expands to (c (b a))\n  (-> a (b 1) c (d 2 3)) ; Expands to (d (c (b a 1)) 2 3)\n\n``->>``\n-------\n\n::\n\n   (defmacro ->> (arg &rest forms)\n     ...)\n\nLast-argument threading. Evaluates ``arg``. Then, for each form in\n``forms``, passes the accumulated argument as the last argument to the\ngiven list. Any argument which is a symbol is treated as a one-element\nlist.\n\nExamples::\n\n  (->> a b c) ; Expands to (c (b a))\n  (->> a (b 1) c (d 2 3)) ; Expands to (d 2 3 (c (b 1 a)))\n\n``and``\n-------\n\n::\n\n   (defmacro and (&rest args)\n     ...)\n\nShort-circuiting conjunction operator. Returns the first falsy value\nout of its arguments, evaluating as few arguments as necessary to do\nso. Returns ``#t`` if given no arguments.\n\n``as->``\n--------\n\n::\n\n   (defmacro as-> (arg var &rest forms)\n     ...)\n\nArbitrary threading macro. Evaluates ``arg``. Then evaluates the first\nof ``forms`` with ``var`` bound to ``arg``. The next form in ``forms``\nis evaluated with the result of the prior bound to ``var``, and so on.\nThe result of the final form is returned.\n\nExample::\n\n  ;; Equivalent:\n  (as-> (foo) v (bar 2 v 3) (baz 1 v 4 5))\n  (baz 1 (bar 2 (foo) 3) 4 5)\n\n``contextual-load``\n-------------------\n\n::\n\n   (defmacro contextual-load (filename)\n     ...)\n\nExpands to a ``load`` call to the file with the name ``filename``.\nHowever, unlike a plain ``load`` call, ``contextual-load`` will be\nunderstood during macro expansion and translated to an appropriate\nvirtual filename. Thus, ``load`` should not be used directly in macro\nbodies, as it will fail when run during macro expansion.\n\n.. Caution:: While the GDLisp macro engine understands how to\n             translate names into virtual filenames inside a\n             ``contextual-load``, it will NOT attempt to trace\n             dependencies into the corresponding file. So\n             ``contextual-load`` is *only* safe to use on a filename\n             which has already been loaded in the current scope. For\n             instance, ``(contextual-load (this-true-filename))`` is\n             always safe, since the current file is always (trivially)\n             loaded.\n\n``deflazy``\n-----------\n\n::\n\n   (defmacro deflazy (name value &rest modifiers)\n     ...)\n\n``deflazy`` defines the name ``name`` in the value namespace of the\ncurrent scope. That name, when it is evaluated for the *first* time,\nwill evaluate ``value`` and return it. The value will then be cached\nand returned when ``name`` is evaluated in the future.\n\nThe only valid modifiers are ``public`` and ``private``, which set the\nvisibility of the defined name. New modifiers may be added in the\nfuture.\n\n``defobject``\n-------------\n\n::\n\n   (defmacro defobject (name parent &opt visibility &rest body)\n     ...)\n\nDefines a new subclass of ``parent``, whose visibility is\n``visibility`` (or public, if not provided) and whose class body is\n``body``. This subclass only has one instance, which is lazily\ninitialized to the name ``name``. The first time ``name`` is\nevaluated, the instance is constructed and returned. On future\nevaluations of ``name``, the same instance will be returned.\n\n.. _macro-defvars:\n\n``defvars``\n-----------\n\n::\n\n   (defmacro defvars (&rest args)\n     ...)\n\n``defvars`` expands into multiple ``defvar`` blocks with no\ninitializers. That is, ``(defvars a b c)`` is equivalent to\n\n::\n\n   (defvar a)\n   (defvar b)\n   (defvar c)\n\nThere is no way to include initializers or ``onready`` modifiers with\nthis macro. For such use cases, call ``defvar`` directly.\n\n.. _macro-if:\n\n``if``\n------\n\n::\n\n   (defmacro if (cond true-case &opt false-case)\n     ...)\n\nEvaluates ``cond``. If it's true, then evalautes and returns\n``true-case``. If ``cond`` is false, then evaluates and returns\n``false-case``. If not provided, ``false-case`` defaults to ``()``.\n\n.. _macro-let-star:\n\n``let*``\n--------\n\n::\n\n   (defmacro let* (vars &rest body)\n     ...)\n\n``let*`` is equivalent to ``let`` except that each variable clause in\na ``let*`` is evaluated in sequence and has access to the variables\ndeclared before it in the same ``let*`` block. That is,\n\n::\n\n   (let* ((a 1)\n          (b (+ a 1)))\n     b)\n\nis equivalent to\n\n::\n\n   (let ((a 1))\n     (let ((b (+ a 1)))\n       b))\n\nand will return ``2``. Attempting to do the same with a single ``let``\nblock will result in the ``a`` variable not being in scope during\ninitialization of ``b``.\n\n``list/for``\n------------\n\n::\n\n   (defmacro list/for (var list &rest body)\n     ...)\n\nEquivalent to the ``for`` special form, but works on lists rather than\narrays.\n\n``var`` is a symbol name and ``list`` is an expression that evaluates\nto a list. ``list`` is evaluated, and then ``body`` is run once per\nlist element in a local scope where ``var`` is defined to be the\ncurrent list element. Always returns ``()``.\n\n``or``\n------\n\n::\n\n   (defmacro or (&rest args)\n     ...)\n\nShort-circuiting disjunction operator. Returns the first truthy value\nout of its arguments, evaluating as few arguments as necessary to do\nso. Returns ``#f`` if given no arguments.\n\n``quit``\n--------\n\n::\n\n   (defmacro quit ()\n     ...)\n\nExpands to a call to the ``quit`` method on the scene tree. This is\nmost commonly used in the REPL, where ``(quit)`` will quickly and\neasily exit the REPL.\n\n``this-file``\n-------------\n\n::\n\n   (defmacro this-file ()\n     ...)\n\n``(this-file)`` is an expression which will load the current file.\n``this-file`` can be used in macro contexts to dynamically load the\ncurrent file. Equivalent to ``(load (this-filename))``.\n\n``this-filename``\n-----------------\n\n::\n\n   (defmacro this-filename ()\n     ...)\n\n``(this-filename)`` evaluates to a string consisting of the path to\nthe current file being compiled (as a ``.gd`` file). In macro\nexpansion, ``(this-filename)`` will expand to the virtual filename of\nthe file, suitable to be used *during* macro expansion.\n\n``this-true-filename``\n----------------------\n\n::\n\n   (defmacro this-true-filename ()\n     ...)\n\n``(this-true-filename)`` evaluates to a string consisting of the path\nto the current file being compiled (as a ``.gd`` file). In macro\nexpansion, ``(this-true-filename)`` will expand to the *real* runtime\nfilename of the file. This filename is *not* suitable to load during\nmacro expansion but it should be used in situations where a macro is\nattempting to expand *into* a ``load`` call which will happen at\nruntime.\n\n.. _macro-unless:\n\n``unless``\n----------\n\n::\n\n   (defmacro unless (cond &rest body)\n     ...)\n\nShorthand syntax for an ``if`` block which *only* has an ``else``\npart. If ``cond`` is false, evaluates and returns the body. If\n``cond`` is true, returns ``()``.\n\n``update``\n----------\n\n::\n\n   (defmacro update (field updater)\n     ...)\n\nConvenient shorthand for updating a field. ``(update x (foo y z))``\nexpands to ``(set x (foo x y z))``. That is, the value ``field`` is\ntaken and passed as the first argument to the ``updater`` (with the\nother arguments, if any, shifted one to the right), and then the\nresult is put back into the place ``x``. ``x`` can be anything that is\nvalid on the left-hand side of a ``set``. See :ref:`expr-set` for more\ndetails on the valid argument forms.\n\nIf ``updater`` is a symbol rather than a proper list, then it is\nwrapped in a one-element list before expanding.\n\nExamples::\n\n  (update a (+ 1)) ; Adds 1 to the variable a\n  (update b -) ; Sets b equal to its negative\n  (update player:position (* 2)) ; Multiplies the position field on player by 2\n  (update (elt x 0) (/ 2)) ; Divides the first element of the array x by 2\n\n.. _macro-when:\n\n``when``\n--------\n\n::\n\n   (defmacro when (cond &rest body)\n     ...)\n\nShorthand syntax for an ``if`` block which has no ``else`` part. If\n``cond`` is true, evaluates and returns the body. If ``cond`` is\nfalse, returns ``()``.\n\n``yield*``\n----------\n\n::\n\n   (defmacro yield* (arg)\n     ...)\n\nIf ``arg`` is a function call which yields and produces a\n``GDScriptFunctionState`` object, then this macro yields the *current*\nfunction as well. When the current function is resumed, then so too\nshall the inner function. When the inner function terminates and\nreturns normally, the result of the inner function is returned from\n``yield*``. Effectively, ``yield*`` propagates a ``yield`` from\n``arg`` to the caller.\n\nExample::\n\n  (defn foo ()\n    (print 2)\n    (yield)\n    (print 4)\n    (yield)\n    (print 6))\n\n  (defn bar ()\n    (print 1)\n    (yield* (foo))\n    (print 7))\n\n  (let ((x (bar)))\n    (print 3)\n    (set x (x:resume))\n    (print 5)\n    (x:resume)\n    (print 8))\n\nThis code will print the numbers from 1 to 8 in order. Note that the\nactual \"yielding\" is done in ``foo``, but when we resume from our\n``let`` block, we resume *through* the ``bar`` function.\n\n.. Warning:: ``yield*`` should only be used to yield from functions\n             that used the 0-argument form of ``yield``. If a function\n             uses the 2-argument ``yield`` and then is resumed by a\n             signal, the intermediate ``yield*`` object will be left\n             in an un-resumable state.\n"
  },
  {
    "path": "doc/readthedocs/reference/standard-library/types.rst",
    "content": "\nTypes and Classes\n=================\n\nThese are all of the class and enumeration constants defined at the\ntop-level in GDLisp.\n\nAll of `the classes defined by Godot\n<https://docs.godotengine.org/en/stable/classes/index.html>`_ are\navailable in GDLisp. Any such classes not listed below are available\nwithout modification.\n\nGodot classes that are singletons use the following convention: The\nsingleton object is available in GDLisp as-is, and the type that\nrepresents it is available preceded with an underscore. So, for\nexample, ``Engine`` is a global constant in GDLisp that refers to the\nsingleton object whose type is ``_Engine``.\n\nGodot Enumerations\n------------------\n\nThe global enumerations defined in `Enumerations\n<https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enumerations>`_\nhave been included with modifications. The common prefix for each\nenumeration type has been removed from the name of the constant and\nreplaced with an appropriate typename. So, for example, the name\n``KEY_SHIFT``, which refers to the shift key, is referred to in GDLisp\nas ``Key:SHIFT``. The following name translations take place\n\n* ``BUTTON_`` constants belong in the ``Mouse`` enum.\n* ``CORNER_`` constants belong in the ``Corner`` enum.\n* ``ERR_`` constants (and the ``OK`` and ``FAILED`` constants) belong\n  in the ``Err`` enum.\n* ``HALIGN_`` constants belong in the ``HAlign`` enum.\n* ``JOY_`` constants belong in the ``Joy`` enum.\n* ``KEY_`` constants belong in the ``Key`` enum.\n* ``KEY_MASK_`` constants belong in the ``KeyMask`` enum.\n* ``MARGIN_`` constants belong in the ``Margin`` enum.\n* ``METHOD_FLAG_`` constants (and the ``METHOD_FLAGS_DEFAULT``\n  constant) belong in the ``MethodFlag`` enum.\n* ``MIDI_MESSAGE_`` constants belong in the ``MidiMessage`` enum.\n* ``OP_`` constants belong in the ``Op`` enum.\n* ``PROPERTY_HINT_`` constants belong in the ``PropertyHint`` enum.\n* ``PROPERTY_USAGE_`` constants belong in the ``PropertyUsage`` enum.\n* ``TYPE_`` constants belong in the ``Type`` enum.\n* ``VALIGN_`` constants belong in the ``VAlign`` enum.\n* ``HORIZONTAL`` and ``VERTICAL`` go in their own enum called\n  ``Orientation``.\n\nGodot Primitive Types\n---------------------\n\nThe 27 Godot primitive types are also available in GDLisp. In GDLisp,\nthese types are real type objects and can be used as the right-hand\nside of an ``instance?`` call. Since they are real objects in the\nGDLisp ecosystem, they can also be assigned to variables and passed as\nfunction arguments. Additionally, the names of the GDLisp primitive\ntypes are normalized to be consistent with other class names.\nSpecifically,\n\n* ``Null`` refers to the Godot type whose constant is ``TYPE_NIL`` and\n  whose only value is the null object ``()``.\n\n* ``Bool`` refers to the Godot type ``bool`` whose constant is\n  ``TYPE_BOOL``.\n\n* ``Int`` refers to the Godot type ``int`` whose constant is\n  ``TYPE_INT``.\n\n* ``Float`` refers to the Godot type ``float`` whose constant is\n  ``TYPE_REAL``.\n\n* ``String``, ``Vector2``, ``Rect2``, ``Vector3``, ``Transform2D``,\n  ``Plane``, ``Quat``, ``AABB``, ``Basis``, ``Transform``, ``Color``,\n  ``NodePath``, ``RID``, ``Object``, ``Dictionary``, ``Array``,\n  ``PoolByteArray``, ``PoolIntArray``, ``PoolStringArray``,\n  ``PoolVector2Array``, ``PoolVector3Array``, and ``PoolColorArray``\n  are available in GDLisp and refer to the Godot primitive types of\n  the same name.\n\n``_GDLisp``\n-----------\n\n::\n\n   (defclass _GDLisp (Node)\n     ...)\n\nThe class of the singleton GDLisp support object. The behavior of the\nprogram is undefined if an attempt is made to construct any additional\ninstances of this type.\n\n``Any``\n-------\n\n::\n\n   (defclass Any (<no-parent>)\n     ...)\n\nThe type of all values in GDLisp. Every single value is an value of\nthis type.\n\n``AnyRef``\n----------\n\n::\n\n   (defclass AnyRef (Any)\n     ...)\n\nThe type of all object values in GDLisp.\n\n``AnyVal``\n----------\n\n::\n\n   (defclass AnyVal (<no-parent>)\n     ...)\n\nThe type of all primitive values in GDLisp. All values which are not\ninstances of ``AnyRef`` are instances of ``AnyVal``.\n\n``BaseArray``\n-------------\n\n::\n\n   (defclass BaseArray (AnyVal)\n     ...)\n\nThe common supertype of all array types in GDLisp, including ``Array``\nitself and the seven strictly-typed pool array types.\n\n``Cell``\n--------\n\n::\n\n   (defclass Cell (Reference)\n     (defvar contents)\n     (defn _init (contents)\n       ...))\n\nThe type of simple cells containing one single value. ``Cell`` has a\nsingle public mutable field, ``contents``, and a constructor of one\nargument.\n\nSee also :ref:`cell-type`.\n\n``ConnectFlags``\n----------------\n\n::\n\n   (defenum ConnectFlags\n     (DEFERRED ...)\n     (PERSIST ...)\n     (ONESHOT ...)\n     (REFERENCE_COUNTED ...))\n\nThis enumeration makes global the `ConnectFlags enumeration\n<https://docs.godotengine.org/en/stable/classes/class_object.html#enumerations>`_\nin Godot.\n\n``Cons``\n--------\n\n::\n\n   (defclass Cons (Reference)\n     (defvar car)\n     (defvar cdr)\n\n     (defn _init (car cdr)\n       ...)\n\n     (defn (get caar) ()\n       ...)\n\n     (defn (get cadr) ()\n       ...)\n\n     (defn (get cdar) ()\n       ...)\n\n     (defn (get cddr) ()\n       ...)\n\n     (defn (get caaar) ()\n       ...)\n\n     (defn (get caadr) ()\n       ...)\n\n     (defn (get cadar) ()\n       ...)\n\n     (defn (get caddr) ()\n       ...)\n\n     (defn (get cdaar) ()\n       ...)\n\n     (defn (get cdadr) ()\n       ...)\n\n     (defn (get cddar) ()\n       ...)\n\n     (defn (get cdddr) ()\n       ...))\n\nThe type of pairs, or cons cells, in GDLisp. Cons cells have two\npublic mutable fields, called ``car`` and ``cdr``. Additionally,\ngetters are defined for various nestings of ``car`` and ``cdr`` up to\nthree layers deep. For instance, ``cons-cell:caddr`` is equivalent to\n``cons-cell:car:cdr:cdr``.\n\nSee also :ref:`cons-cell`.\n\n``Function``\n------------\n\n::\n\n   (defclass Function (Reference)\n     ...)\n\nThe type of GDLisp function objects which have been reified into the\nvalue namespace. These are valid arguments to ``funcall`` and\n``apply``.\n\n``Nothing``\n-----------\n\n::\n\n   (defclass Nothing (<all-parents>)\n     ...)\n\nThe bottom of the type hierarchy. There are not, and never will be,\nvalues of this type in GDLisp.\n\n``Notification``\n----------------\n\n::\n\n   (defenum Notification\n     (POSTINITIALIZE ...)\n     (PREDELETE ...))\n\nAn enumeration representing the Godot notification constants `defined\non Object\n<https://docs.godotengine.org/en/stable/classes/class_object.html#constants>`_.\n\n``Number``\n----------\n\n::\n\n   (defclass Number (AnyVal)\n     ...)\n\nThe type of numbers in GDLisp. Integers and floating-point numbers are\nboth instances of this type.\n\n``Symbol``\n----------\n\n::\n\n   (defclass Symbol (Reference)\n     ...)\n\nThe type of symbols at runtime in GDLisp.\n"
  },
  {
    "path": "doc/readthedocs/reference/standard-library/values.rst",
    "content": "\nGlobal Values\n=============\n\nConstant values defined in the GDLisp value namespace at global scope\nare documented here.\n\nNote that all constants defined in the global scope for use in\nGDScript are also available (unmodified) in GDLisp. That includes all\nof the following:\n\n* `@GDScript Constants <https://docs.godotengine.org/en/stable/classes/class_%40gdscript.html#constants>`_\n\n* `@GlobalScope Constants <https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#constants>`_\n\n* `@GlobalScope Enumerations <https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#enumerations>`_\n\nAdditionally, the constants defined on the root ``Object`` type are\nall exposed globally in GDLisp. These include: ``CONNECT_DEFERRED``,\n``CONNECT_PERSIST``, ``CONNECT_ONESHOT``,\n``CONNECT_REFERENCE_COUNTED``, ``NOTIFICATION_POSTINITIALIZE``, and\n``NOTIFICATION_PREDELETE``.\n\n``GDLisp``\n----------\n\n::\n\n   (defconst GDLisp ...)\n\nThe top-level constant called ``GDLisp`` refers to the GDLisp node\nitself. That is, the top-level name ``GDLisp`` can be used to\nreference the singleton object that represents the ``GDLisp.lisp``\nsupport library. It is generally not necessary to refer to this object\ndirectly, since GDLisp does the name resolution for you, but the name\nis available if needed.\n\n``GODOT-VERSION``\n-----------------\n\n::\n\n   (defconst GODOT-VERSION ...)\n\nThis constant is defined to be an integer value representing the Godot\nversion that ``GDLisp.lisp`` was compiled with, as follows\n\n.. code-block:: text\n\n   GODOT-VERSION = 1,000,000 * major-version + 10,000 * minor-version + 100 * patch-version\n\nSo, for instance, Godot 3.4.1 would be represented by the\n``GODOT-VERSION`` constant ``3040100``. These version integers are\nconstructed in such a way that two version integers can be compared\nusing the standard (numerical) comparison operators, to check if the\nversion of Godot is newer or older than some set value.\n\nNote that this checks the version of Godot that ``GDLisp.lisp`` was\ncompiled with, not the one being used at runtime. To get the version\nof Godot that is currently *running*, use the built-in Godot function\n`get_version_info\n<https://docs.godotengine.org/en/stable/classes/class_engine.html#class-engine-method-get-version-info>`_.\n\n``nil``\n-------\n\n::\n\n   (defconst nil ())\n\nThe value ``nil`` is defined to be the special null object ``()``.\n\n"
  },
  {
    "path": "doc/readthedocs/requirements.txt",
    "content": "sphinx==5.2.2\nsphinx_rtd_theme==1.1.1\nsphinxcontrib-mermaid==0.7.1\n"
  },
  {
    "path": "doc/readthedocs/tutorial/classes.rst",
    "content": "\nClasses in GDLisp\n=================\n\nGDLisp prides itself on being a functional language, capable of\nmanipulating functions as first-class objects and providing powerful\nabstraction techniques to support common functional paradigms. But\nGDLisp targets the Godot platform, which is designed with an\nobject-oriented paradigm in mind. GDLisp fully supports this paradigm\nand provides mechanisms for working with classes, both those built-in\nto Godot and those from GDScript source files, as well as for\ndeclaring new classes in GDLisp proper.\n\nUsing Classes\n-------------\n\nEvery built-in GDScript class is available in GDLisp as a global name.\nThis includes classes deriving from ``Object``, like ``Node``,\n``Spatial``, and ``Control``, as well as typenames that are considered\nprimitive in Godot, such as ``Int`` and ``Vector2``.\n\nTo access a field on a class or an object, put a colon ``:`` after the\nclass or object name and follow it by the name of the constant or\nvariable. For example::\n\n  Vector2:ZERO\n  Transform:FLIP_X\n  Quat:IDENTITY\n  some-timer-node:paused\n  some-material:render_priority\n\nLikewise, to call a method on an object or a class, use the colon\nsyntax as the first term of an S-expression.\n\n::\n\n   (Reference:new)\n   (Engine:get_singleton \"MySingleton\")\n   (some-node:get_path)\n   (my-object:free)\n\nDeclaring Classes\n-----------------\n\nClasses can be declared inside a module using the following syntax.\n\n::\n\n   (defclass ClassName (ParentClassName)\n     body ...)\n\nNote that ``ParentClassName`` must always be a (possibly qualified)\nclass *name*. Unlike GDScript, GDLisp does not allow string paths to\nbe used as the name of a parent class.\n\nAs with functions and other declarations, ``defclass`` can be suffixed\nwith the symbol ``private`` to make the class invisible outside of the\ncurrent module.\n\n::\n\n   (defclass ClassName (ParentClassName) private\n     body ...)\n\nInside the class, several types of declarations are supported. Note\nthat declarations inside a class are always public, as GDLisp cannot\nenforce access restrictions on class members.\n\nSignal Declarations\n^^^^^^^^^^^^^^^^^^^\n\n::\n\n   (defsignal signal_name (args ...))\n\nThe ``defsignal`` form declares the existence of a signal with the\ngiven name and argument list. Note that signals are conventionally\nnamed in ``snake_case``, not ``train-case``, for maximum compatibility\nwith Godot signals. If the argument list is empty, it can be omitted.\n\nThe argument list, if provided, must be a :ref:`simple lambda list\n<simple-lambda-lists>`. That means that the ``&opt``, ``&rest``, and\n``&arr`` directives are not supported.\n\nConstant Declarations\n^^^^^^^^^^^^^^^^^^^^^\n\n::\n\n   (defconst ConstantName value)\n\nConstants inside a class work identically to those at the\nmodule-level, with the exception that a class-scoped ``defconst``\ncannot be marked ``private``. Note that ``defconst`` is evaluated at\nclass scope, so ``self`` does *not* exist in the right-hand side of a\n``defconst``.\n\nVariable Declarations\n^^^^^^^^^^^^^^^^^^^^^\n\n::\n\n   (defvar var-name initial-value)\n\nVariables declared inside a class function as instance variables. When\nan instance of the class is initialized, it will receive an instance\nvariable with the given name. ``initial-value`` is optional, but if\nprovided it will be used as the initial value of the instance variable\nat initialization time. ``initial-value`` can contain arbitrary\nexpressions and can use ``self``.\n\n::\n\n   (defvar var-name initial-value onready)\n\nIf the ``defvar`` form is followed by the ``onready`` keyword, then\nthe initial value will be applied during the ``_ready`` method (when\nthe node is added to the tree) instead of the ``_init`` method (when\nthe object is constructed). The ``onready`` modifier makes no sense on\nobjects that are not nodes.\n\n.. Note:: If you're wondering where the GDScript ``setget`` keyword\n          is, GDLisp doesn't have ``setget``. GDLisp implements proxy\n          properties in a different way. See\n          :ref:`getters-and-setters` below.\n\nExport Modifiers\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nA variable declaration in a class can be followed by an ``export``\nform, which will be compiled into a GDScript ``export`` clause. This\nprovides certain information to the Godot editor indicating what\nvalues are acceptable for the instance variable.\n\nExamples::\n\n  (defvar player-hp 10 (export int))\n  (defvar character-name \"Alice\" (export String \"Alice\" \"Bob\" \"Charlie\"))\n  (defvar background-color Color:red (export Color))\n  (defvar player-node (export NodePath))\n\nThe ``defvars`` Macro\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nIf you're simply declaring several instance variables in a row and do\nnot need to provide initial values for any of them, you may use the\n:ref:`macro-defvars` macro. ``defvars`` takes any number of instance\nvariable names and declares those variables, without initial values.\n``defvars`` does not support ``export`` or ``onready``.\n\n::\n\n   (defvars player-hp character-name background-color)\n\nInstance Functions\n^^^^^^^^^^^^^^^^^^\n\nThe main lifeblood of a class is its instance methods, which are\ndeclared using a similar ``defn`` syntax to module functions.\n\n::\n\n   (defn method-name (args ...)\n     body ...)\n\nLike signal declarations, instance function declarations take a simple\nlambda list, which means modifiers such as ``&opt``, ``&arr``, and\n``&rest`` are not allowed in this context.\n\nInside the instance method body, the variable ``self`` is available\nand refers to the current instance of the class. Note that GDLisp does\n*not* implicitly insert ``self`` in any context. That is, a bare name\nlike ``example`` will *always* refer to a statically-scoped local\nvariable or module constant with the name ``example``, even inside a\nclass. To refer to the instance variable with that name, you must\nexplicitly write ``self:example``. The syntax sugar ``@example`` is\nprovided, which desugars to ``self:example``.\n\nMethods may be marked ``static``, to indicate that they should be\ncalled on the class itself rather than an instance.\n\n::\n\n   (defn method-name (args ...) static\n     body ...)\n\nInside a static method, the name ``self`` is *not* bound.\n\n.. Tip:: You probably shouldn't be using the ``static`` modifier very\n         often. Usually, when you find yourself writing a ``static``\n         class method, that method would be better written as a\n         top-level module function instead. ``static`` is provided\n         mainly for compatibility with GDScript.\n\nSuper Calls\n\"\"\"\"\"\"\"\"\"\"\"\n\nInside an instance method, you may use the special syntax\n``(super:method-name ...)`` to invoke the method called\n``method-name`` on the *superclass* of the current class.\n``method-name`` need not be the name of the currently-executing method\n(though it usually will be, in practice). Note that ``super`` on its\nown is *not* a variable, so attempting to assign ``super`` to a local\nvariable or pass it as a function argument will fail.\n\nConstructors\n\"\"\"\"\"\"\"\"\"\"\"\"\n\nClass constructors in GDLisp are a bit special and have some\nadditional syntax to accommodate that. A constructor is called\n``_init`` and is declared using ``defn`` like any other instance\nfunction. Constructors do not return values, though ``(return nil)``\ncan still be used to exit the constructor early.\n\n::\n\n   (defn _init (args ...)\n     body ...)\n\nA class constructor cannot be made ``static``. Inside the body of the\nconstructor, if you wish to call the superclass' constructor, you may\ndo so by calling the function ``super`` as the *first* expression in\nthe constructor.\n\n::\n\n   (defn _init (args ...)\n     (super args ...)\n     body ...)\n\nThe ``super`` call, if present, *must* be the first expression in the\nconstructor body.\n\nThe argument list to a constructor supports a special syntax unique to\nconstructors. An ``@`` sign can be placed before the name of an\nargument.\n\n::\n\n   (defn _init (@foo @bar))\n\nIn this case, ``foo`` and ``bar`` are *not* local variables to the\nconstructor. Instead, the values given to those parameters are\nassigned directly to instance variables on the class itself.\nEssentially, the above is equivalent to\n\n::\n\n   (defn _init (foo bar)\n     (set @foo foo)\n     (set @bar bar))\n\nNote that the automatic assignment happens after any ``super``\nconstructor call.\n\n.. _getters-and-setters:\n\nGetters and Setters\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nGetters and setters are special instance methods that look like\nordinary instance variables, from a caller's perspective.\n\nGetters are declared using ``defn`` with a special method name of the\nform ``(get ...)``. A getter method must be non-static and cannot take\nany arguments.\n\n::\n\n   (defn (get variable-name) ()\n     body ...)\n\nWhen a user of the class attempts to get the instance variable\n``variable-name`` from the class, the method ``(get variable-name)``\nwill be called instead, and its result will be used as the value of\nthe expression.\n\nLikewise, setters are declared using a ``(set ...)`` name. A setter\nmethod must be non-static and must take exactly one argument. Like a\nconstructor, a setter does not return any values, though it can exit\nearly with ``(return nil)``.\n\n::\n\n   (defn (set variable-name) (value)\n     body ...)\n\nThe setter will be invoked when a caller attempts to ``set`` the given\ninstance variable.\n\n::\n\n   (set my-instance:variable-name value)\n\nThe same variable name can be used for a setter and a getter. The name\nof a setter/getter cannot coincide with the name of an actual,\nconcrete ``defvar`` instance variable on the class. Setters and\ngetters are fully compatible with GDScript, in that a caller from\nGDScript who attempts to get or set the given variable will correctly\ninvoke the getter or setter function, respectively.\n\nIf your goal is to wrap a ``defvar`` with some validation or some\nactions, a common idiom is to precede the ``defvar`` with an\nunderscore and then use the property outside the class.\n\nExample::\n\n  (defclass Player (Node2D)\n    (defsignal hp_changed)\n\n    (defvar _hp 10)\n    (defvar max_hp 10)\n\n    (defn (get hp) ()\n      ;; Simply return the instance variable\n      @_hp)\n\n    (defn (set hp) (x)\n      ;; Make sure the HP value is in bounds\n      (set @_hp (clamp x 0 @max_hp))\n      ;; Let everyone know the value has changed\n      (@emit_signal \"hp_changed\")))\n\nMain Classes\n------------\n\nGodot is based around the idea that every file is also a class, in\nsome form or another. In GDLisp, this is not the case. However, it is\noften useful to designate one class in a GDLisp module as the\n\"primary\" class of that module, so that Godot can link scenes and\nother resources up to it in the editor.\n\nFor this reason, GDLisp classes can be marked with the ``main``\nmodifier.\n\n::\n\n   (defclass ClassName (ParentClassName) main\n     body ...)\n\nEvery file can have at most one ``main`` class, and the ``main`` class\ncannot be private. From the perspective of GDLisp modules, the\n``main`` modifier changes nothing. A GDLisp module will still import\nthe class name with ``use`` just like any other name. However, in the\nresulting GDScript file, the ``main`` class will be compiled to the\ntop-level class of the file, rather than a nested class. This allows\nthe editor to bind the ``main`` class to a packed scene or other\nresource.\n\nSignals\n-------\n\nGodot communicates between nodes and other objects using signals. For\nthe most part, GDLisp supports signals and connections just like\nGDScript::\n\n  (my-object:connect \"signal\" target-object \"_target_method_name\")\n  (my-object:disconnect \"signal\" target-object \"_target_method_name\")\n\nHowever, GDLisp also offers some convenience functions to deal with\ncommon use cases.\n\n::\n\n   (connect>> my-object \"signal\" (lambda () ...))\n\nThe built-in function ``connect>>`` connects a signal to a lambda\nfunction. When the signal is fired, the lambda function will be called\nand can accept the arguments of the signal. As a first-class function,\nthe lambda is free to close around any local variables (including\n``self``, if applicable) in the current scope, so you should never\nhave to explicitly bind variables of a signal connection when using\n``connect>>``.\n\nGDLisp also provides a function ``connect1>>``, which takes the same\narguments but connects a signal as if using the one-shot flag. That\nis, the connection removes itself after firing once.\n\nBoth ``connect>>`` and ``connect1>>`` return a unique value to\nidentify the connection. To disconnect a signal that was connected in\nthis way, use ``disconnect>>``, which takes the return value of\n``connect>>`` or ``connect1>>`` and disconnects it.\n\n.. Note:: You may freely intermix GDLisp-style ``connect>>`` calls and\n          Godot ``connect`` method calls in the same program. However,\n          ``disconnect>>`` should only be used on connections\n          established with ``connect>>`` or ``connect1>>``, while the\n          built-in Godot method ``disconnect`` should be used for\n          those established with ``connect`` or through the Godot UI.\n"
  },
  {
    "path": "doc/readthedocs/tutorial/control-flow.rst",
    "content": "\nBasic Control Flow\n==================\n\nLet's jump right in and take a look at some GDLisp code. Once you've\n:ref:`built GDLisp <building>`, you should have an executable called\n``gdlisp``. If you invoke this executable with no arguments, you'll be\nplaced in a read-eval-print loop, or REPL for short. In this\ninteractive interpreter, you can type GDLisp expressions or\ndeclarations and see the results live. This interpreter is backed by a\nrunning Godot instance, so you also have access to the full breadth of\nbuilt-in Godot types and functions, like ``Node`` and ``sqrt``.\n\nHello, world!\n-------------\n\nLet's start with everybody's favorite program.\n\n::\n\n   (print \"Hello, world!\")\n\nIf you type this into the REPL and hit enter, you'll see \"Hello,\nworld!\" printed to the screen. This calls the built-in function\n``print`` with one string argument, namely the string \"Hello, world!\".\nCongratulations! You've just written your first line of GDLisp.\n\nIf you just ran this, you'll notice that, in addition to the string,\nthe interpreter also printed out a stray set of parentheses ``()``.\nThis is normal and is for good reasons. In GDLisp, every expression\nreturns a value, even those like ``print`` that are usually only\ncalled for their side effects. Since ``print`` has no meaningful\nreturn value, it returns the nil value as a default, which is written\nas ``()``. So when you see ``()``, you can read that as \"nil\".\n\n.. Note:: You might wonder why the null value prints out as ``()``,\n          rather than as the word \"nil\". This is because ``()`` is\n          used as a placeholder for the empty list and is used as the\n          final cdr cell in a proper list. You can read more about how\n          lists are actually stored in GDLisp at :ref:`parser`.\n\nLet's do some math.\n\n::\n\n   (+ 1 1)\n\nThis will, naturally, add one and one together and return the result:\ntwo. Operators like ``+`` and ``-`` are written in prefix notation in\nGDLisp, in contrast to GDScript and most non-Lisp languages, which use\ninfix notation. In fact, ``+`` isn't an operator at all; it's just a\nfunction like ``print``. GDLisp is very liberal in what it considers a\nvalid function name, and ``+`` is a perfectly valid identifier.\n\nThe ``+`` function also supports variable arguments. The following\nwill add all four numbers together and print the total::\n\n   (+ 1 2 3 4)\n\nSince everything is written as prefix function calls, there's never\nany ambiguity about operator precedence.\n\n::\n\n   (* 3 (+ 1 1) (+ 1 10 (* 2 4)))\n\nAll of the common mathematical operators work as functions in exactly\nthe way you'd expect. ``+`` is for addition, ``*`` is for\nmultiplication, ``-`` is for subtraction, and ``/`` is for division.\nAll of the operators support variable arguments, with associativity to\nthe left when it matters (so, for instance ``(/ a b c)`` is equivalent\nto ``(/ (/ a b) c)``, not ``(/ a (/ b c))``).\n\nComments\n--------\n\nGDLisp line comments start with a semicolon.\n\n::\n\n   ; This is a line comment\n\nBy convention, a single semicolon is used to annotate a line of code,\nand a pair of semicolons is used when a line comment stands on its own\nline.\n\n::\n\n   ;; This is a standalone line comment.\n   (+ 1 1) ; This comment annotates other code.\n\nBlock comments begin with ``#|`` and are terminated by ``|#``. Block\ncomments cannot be nested.\n\nData Types\n----------\n\nGDLisp supports the same basic data types as GDScript, albeit with\ndifferent syntax.\n\nThe GDLisp equivalent of Godot's \"null\" value is called \"nil\" and is\nwritten in GDLisp as ``()``. GDLisp also defines a global constant\ncalled ``nil``, which evaluates to ``()``.\n\nThe Boolean values \"true\" and \"false\" are written, respectively, as\n``#t`` and ``#f``.\n\nIntegers and floats are written in GDLisp in the same way as in\nGDScript.\n\nStrings are written in GDLisp using double quotes ``\"...\"``. Strings\n*cannot* be written using single quotes, as the single quote character\nis used for something different in GDLisp. Aside from that, all of the\nusual escape sequences work. Additionally, GDLisp supports two methods\nof escaping Unicode values in strings. The first consists of ``\\u``\nfollowed by exactly four hexadecimal digits, exactly like GDScript's\nsyntax for the same. The second consists of ``\\u{...}``, with any\nnumber of hexadecimal digits enclosed in the curly braces. This syntax\ncan be used to represent *any* Unicode code point, including those\noutside the basic multilingual plane.\n\nConditionals\n------------\n\nIf Expressions\n^^^^^^^^^^^^^^\n\nThe most basic conditional in GDLisp is the :ref:`macro-if` macro.\n\n::\n\n   (if some-conditional true-case false-case)\n\n``if`` is a macro that takes three arguments. The first argument is\nthe conditional, which is evaluated according to Godot's rules of\ntruthiness. That is, zero, false, the empty string, and other\nintuitively \"empty\" objects are considered false.\n\n::\n\n   (if (> x 0)\n     (print \"x is positive!\")\n     (print \"x is not positive!\"))\n\nThis expression tests whether the variable ``x`` is positive and, in\neither case, prints out an appropriate message. Note that ``>``, like\n``+``, is a perfectly valid identifier in GDLisp and is in fact a\nsimple, built-in function just like ``+``.\n\nUp to this point, we've been calling these forms \"expressions\"\nsomewhat informally, but it's important to note that even control\nforms like ``if`` are expressions in GDLisp, whereas in a language\nlike GDScript or Python ``if`` would be considered a statement. Put\nmore colloquially, ``if`` is really no different than any other\nexpression and can also return values. So we could've written our\n``print`` like so.\n\n::\n\n   (print (if (> x 0) \"x is positive!\" \"x is not positive!\"))\n\nOr we could have returned the resulting string from a function, or\nassigned it to a variable, or any number of other things. GDLisp does\nnot distinguish between statements and expressions. Anything that can\nbe used in expression context returns a value.\n\nThe true and false branches of the ``if`` statement are each\nindividual arguments, which means each branch expects a single\nexpression. If you need to perform multiple actions inside one of the\nbranches, you can use the :ref:`expr-progn` special form.\n\n::\n\n    (if (> x 0)\n      (progn (print \"x is positive!\")\n             (print \"Have a lovely day :)\"))\n      (print \"x is not positive!\"))\n\n``progn`` is a special form that takes zero or more arguments,\nevaluates each of them in order, and then returns the value of the\nlast one.\n\nFinally, the \"false\" branch is optional and will default to the nil\nexpression ``()``.\n\n::\n\n   (if (> x 0)\n      (progn (print \"x is positive!\")\n             (print \"Have a lovely day :)\")))\n\nThough if your intention is to execute some code conditionally for its\nside effects, you might find the macros :ref:`macro-when` and\n:ref:`macro-unless` more useful.\n\nCond Expressions\n^^^^^^^^^^^^^^^^\n\nExtending our ``if`` expression from above, we might consider adding\nanother case to distinguish between negative numbers and zero.\n\n::\n\n   (if (> x 0)\n     (print \"x is positive!\")\n     (if (< x 0)\n       (print \"x is negative!\")\n       (print \"x is zero!\")))\n\nNesting additional ``if`` branches in an \"else\" block is a very common\npattern. Languages like Python and GDScript accommodate this with an\n``elif`` keyword. GDLisp accommodates this pattern with the ``cond``\nexpression, the general-purpose conditional dispatch form. ``cond``\ntakes the following form.\n\n::\n\n   (cond (conditional1 branch1) (conditional2 branch2) ...)\n\nThat is, the word ``cond`` is followed by several lists. Each list\nconsists of a conditional expression, similar to the conditional\nportion of ``if``, followed by one or more expressions. When\nevaluated, the ``cond`` form evaluates each conditional. When it\nencounters one that's true, it stops, evaluates that branch, and\nreturns the result. Our ``if`` above can be translated as follows::\n\n  (cond\n    ((> x 0) (print \"x is positive!\"))\n    ((< x 0) (print \"x is negative!\"))\n    (#t (print \"x is zero!\")))\n\nIf none of the branches match, then ``cond`` silently returns ``()``,\nthough in many cases (including our example above), it's common to\ninclude a final \"catch-all\" clause whose conditional is the literal\ntrue ``#t`` value.\n\nLike ``if``, ``cond`` is an expression and returns values, so the\nentire ``cond`` expression can be assigned to a variable or passed as\na function argument.\n\n.. Note:: Incidentally, ``cond`` is the only primitive conditional\n          expression in GDLisp. ``if``, ``when``, ``unless``, ``and``,\n          and ``or`` are all macros built on top of ``cond``, while\n          ``cond`` is baked into the compiler.\n\nComparisons\n^^^^^^^^^^^\n\nWe've already seen the ``<`` and ``>`` functions, which compare values\nfor, respectively, the less-than and greater-than relations. The\n``<=`` and ``>=`` functions also exist for non-strict comparisons.\nFinally, ``=`` is used for equality and ``/=`` is used for inequality.\nComparison, ordering, and equality semantics are equivalent to the\ncorresponding Godot operators.\n\nAll of these functions accept variable arguments and are applied\ntransitively to their arguments. Concretely, that means that ``(< a b\nc d)`` is true if and only if ``a`` is less than ``b``, ``b`` is less\nthan ``c``, and ``c`` is less than ``d``. The other comparison\noperators work similarly.\n\nOf particular note is ``/=``, which is unique among the comparison\noperators in that it is not transitive. ``(/= a b c d)`` is true if\nand only if *none* of the arguments are equal. It compares every\npossible pairing of arguments, not just the adjacent ones. More\nconcretely, ``(/= 1 2 1 3)`` is false, since one is equal to itself.\n\nSince the ``=`` function obeys Godot's built-in equality semantics, it\ncompares dictionaries by reference. The GDLisp function ``equal?``\nworks like ``=`` but for deep equality. ``equal?`` is similar to the\nGDScript function ``deep_equal``, except that the former accepts\nvariable arguments and applies the equality relationship transitively.\n\nLooping Expressions\n-------------------\n\nLike GDScript, GDLisp provides constructs for repeating code.\n\nWhile Loops\n^^^^^^^^^^^\n\nThe simplest looping construct is a ``while`` loop. To print all of\nthe numbers from 10 down to 0, we can write::\n\n   (let ((x 10))\n     (while (> x 0)\n       (print x)\n       (set x (- x 1))))\n\nThe ``while`` form takes a conditional expression as its first\narguments, and the loop body, which can consist of zero or more\nexpressions, follows. When a ``while`` loop is evaluated, the\nconditional is evaluated. If the conditional is true, then the body is\nevaluated and the process repeats. If the conditional is false, then\nthe body is skipped. ``while`` loops always return ``()``.\n\nGDLisp has no direct equivalent of a \"do-while\" loop from other\nlanguages. However, the construct is easy enough to mimic. Since the\nconditional part of a ``while`` loop can be *any* expression (even a\n``progn`` form), we can simply place the entire loop body inside the\nconditional part and leave the body empty.\n\n::\n\n   (let ((x 10))\n     (while (progn (print x)\n                   (set x (- x 1))\n                   (> x 0))))\n\nFor Loops\n^^^^^^^^^\n\nFor loops in GDLisp work exactly as they do in GDScript.\n\n::\n\n   (for i (range 10)\n     (print x))\n\n``for`` takes a variable name, an iterable object (such as an array or\na string), and a body. It evaluates the body once for each element of\nthe iterable, with the variable bound to that element at each\niteration. Like ``while``, a ``for`` loop always returns ``()``.\n\nBreaking and Continuing\n^^^^^^^^^^^^^^^^^^^^^^^\n\nThe built-in special forms ``(break)`` and ``(continue)`` work like\nthe equivalent GDScript keywords. ``(break)`` can be used inside of a\nloop and, when evaluated, will immediately exit the loop.\n``(continue)`` can be used inside a loop and jumps back to the\nbeginning of the loop, beginning the *next* iteration of the loop (or\nexiting the loop, if we were on the final iteration).\n\nHow NOT to Loop\n^^^^^^^^^^^^^^^\n\nNow that we've seen the two basic looping constructs in GDLisp, it's\nimportant to emphasize that there are often alternatives. In an\nimperative language like GDScript or Java, most iteration is done, as\na matter of course, with ``for`` or ``while``. However, GDLisp is a\nfunctional programming language, and as such it provides several\nhigher-order functions designed to capture common looping and\niteration patterns. This section aims to provide an incomplete summary\nof some of the useful functions that can be used to replace common\nlooping patterns.\n\nTransforming each Element of an Array\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nConsider this GDScript code.\n\n.. code-block:: gdscript\n\n   var new_array = []\n   for element in old_array:\n       new_array.push_back(element * 2)\n   return new_array\n\nThis block of code takes an array ``old_array``, doubles each element,\nand returns a new array containing the doubles. This pattern of\napplying an operation to each element of an array is captured by the\n:ref:`function-array-map` function. The idiomatic way to write the\nabove code in GDLisp is\n\n::\n\n   (array/map (lambda (x) (* x 2)) old-array)\n\nSumming or Combining all Elements of an Array\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nThis GDScript code sums the elements of an array.\n\n.. code-block:: gdscript\n\n   var total = 0\n   for element in old_array:\n       total += element\n   return total\n\nThe pattern of accumulating all of the elements of an array using some\nbinary function is called a *fold*, and it can be done in GDLisp with\n:ref:`function-array-fold`.\n\n::\n\n   (array/fold #'+ old-array 0)\n\nDiscarding some Elements of an Array\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nThe following GDScript code takes an array and keeps only the elements\nwhich are positive.\n\n.. code-block:: gdscript\n\n   var new_array = []\n   for element in old_array:\n       if element > 0:\n           new_array.push_back(element)\n   return new_array\n\nThis pattern is captured by the :ref:`function-array-filter` function.\n\n::\n\n   (array/filter (lambda (x) (> x 0)) old-array)\n\nSearching an Array for some Matching Element\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nThis GDScript code searches an array, in order, for an element\nsatisfying a particular condition, returning the first match.\n\n.. code-block:: gdscript\n\n   for element in old_array:\n       if element % 10 == 0:\n           return element\n   return null\n\nThis can be done in GDLisp with :ref:`function-array-find`.\n\n::\n\n   (array/find (lambda (x) (= (mod x 10) 0)) old-array)\n\n``array/find`` also optionally accepts a third argument, which\ndefaults to ``()`` and is returned if no match is found.\n\nLocals\n------\n\nLocal Variables\n^^^^^^^^^^^^^^^\n\nAs we start to write larger blocks of code, it will become convenient\nto store the results of intermediate expressions in local variables.\nWe've already seen examples that use the most basic primitive:\n``let``.\n\n::\n\n   (let ((variable-name1 initial-value1)\n         (variable-name2 initial-value2) ...)\n     ...)\n\nThe ``let`` form takes a list of variable clauses and then a body. The\ngiven variables are declared and bound to their initial values. Then\nthe body is evaluated in a scope where the local variables exist.\n\nYou can declare any number of variables in a ``let`` clause.\nCrucially, the variables' scope is bound strictly to the ``let``\nblock, so as soon as the body of the ``let`` finishes evaluating, the\nvariables are no longer accessible. This is in contrast to GDScript,\nwhere a ``var`` declaration implicitly lasts until the end of the\n*enclosing* scope. In GDLisp, a local variable always defines its own\nscope when it's declared.\n\nExample::\n\n  (let ((a 1) (b 2))\n    (+ a b)) ; 3\n\nNote that when a ``let`` block has multiple variable clauses, the\nclauses are evaluated in parallel.\n\n::\n\n   (let ((x 1) (y (+ x 1))) y)\n\nIn this example, the ``x`` from the first clause is *not* in scope\nwhen the ``y`` clause is evaluated. If there's an ``x`` from an\n*enclosing* scope available, then ``y`` will be set to that variable\nplus one. If not, this code will produce an error at compile-time.\n\nIt's common to want several variable bindings to be evaluated in\norder, such as when initializing several pieces of data to perform\nsome calculations. For this use case, GDLisp provides the\n:ref:`macro-let-star` macro.\n\n::\n\n   (let* ((x 1) (y (+ x 1))) y) ; 2\n\n``let*`` works exactly like ``let`` and shares all of the same syntax,\nexcept that the variables in a ``let*`` are bound sequentially, with\neach subsequent variable having access to all of the prior ones. In\neffect, a ``let*`` form is equivalent to several nested ``let`` forms,\neach having only one variable clause.\n\nTo change the value of an existing local variable, use ``set``.\n\n::\n\n   (let ((x 1))\n     (print x) ; 1\n     (set x (+ x 1))\n     (print x)) ; 2\n\nNote that local variables, even mutable local variables, fully support\nclosures. That is, if a ``lambda`` or other local function construct\ncloses around a local variable, then both the closure function and the\nenclosing scope can modify the variable, and both scopes will reflect\nthe change.\n\nLocal Functions\n^^^^^^^^^^^^^^^\n\nSimilar to ``let``, GDLisp provides the ``flet`` primitive for\ndeclaring one or more local functions. This is most useful when you\nhave a private function that is only needed inside one function.\n\n::\n\n   (flet ((normalize-name (name) ((name:capitalize):substr 0 10)))\n     (print \"Hello, \" (normalize-name first-name) (normalize-name last-name))\n     (print \"You have an unread message from \" (normalize-name caller-name)))\n\nIn this hypothetical example, we need to print some messages involving\npeople's names. For consistency in our hypothetical UI, we want all\nnames to be capitalized in a particular way and to truncate their\nlength to ten characters. We can define the function to normalize\nthese names as a local function with ``flet`` and then call it as many\ntimes as we want inside the ``flet`` block. Outside the block, the\nfunction doesn't exist.\n\n``labels`` is a variant of ``flet`` with the same syntax but more\npowerful binding semantics. ``labels`` evaluates its function bodies\nin a scope where all of the function clauses already exist. This\nallows your local helper functions to be recursive or to depend on\neach other in any order.\n\n.. Tip:: If you're declaring a local function with the intent of\n         passing it (as an argument) to a higher-order function like\n         ``array/map`` or ``array/filter``, you'll have better luck\n         using an ordinary ``let`` and a ``lambda``, since you need to\n         reify the function as a value anyway. ``flet`` and ``labels``\n         are intended for situations where you want to locally *call*\n         the function inside the scope.\n"
  },
  {
    "path": "doc/readthedocs/tutorial/index.rst",
    "content": "\nGetting Started\n===============\n\nWelcome to GDLisp, the Lisp dialect designed to compile to GDScript!\nIf you're interested in learning how to write GDLisp code, you're in\nthe right place.\n\nThis tutorial is targeted at game developers who use Godot and are\ninterested in learning about GDLisp. No prior knowledge of Lisp is\nassumed. However, I do assume familiarity with the Godot toolchain and\nwith the scene tree model. If you're a first-time user of Godot, I\nrecommend starting out with GDScript. The official Godot documentation\nhas `an excellent tutorial\n<https://docs.godotengine.org/en/stable/getting_started/introduction/index.html#toc-learn-introduction>`_\nfor folks new to the engine.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Contents:\n\n   lisping.rst\n   control-flow.rst\n   modules.rst\n   classes.rst\n   macros.rst\n   what-now.rst\n"
  },
  {
    "path": "doc/readthedocs/tutorial/lisping.rst",
    "content": "\nIntroduction to Lisp\n====================\n\nThe term \"Lisp\" encompasses a broad collection of programming\nlanguages dating back to the 1960s. This section aims to describe the\nLisp ecosystem in a broad sense and to talk about the parts of GDLisp\nthat are like other Lisp dialects, without going into too much detail\non the Godot-specific parts. If you've used another Lisp dialect in\nthe past, you can likely skim this section or skip it entirely.\n\nWhat is Lisp?\n-------------\n\nLisp dialects are homoiconic, functional programming languages with a\ndistinctive syntax. If you've seen Lisp code in the past, you'll\nprobably recognize it offhand.\n\n::\n\n    (let ((x 1) (y 1))\n      (if (> x 0) (+ x y) y))\n\nThis is a GDLisp expression. It declares two local variables: ``x``\nand ``y``. Both variables get the initial value of 1. Then we check\nwhether or not ``x`` is greater than zero. If so, we add ``x`` and\n``y`` together, and if not, we simply return ``y``.\n\nAside from some minor syntax sugar, every piece of GDLisp syntax is\nshown in the above snippet of code. Specifically, a GDLisp source file\nis made up of zero or more *S-expressions*. An S-expression, or\nsymbolic expression, is defined recursively to be either a *literal*\n(such as a number, a symbol, or a string) or a list of zero or more\nS-expressions wrapped in parentheses.\n\n.. Note:: This is a slight oversimplification. Strictly speaking, an\n          S-expression is either a literal or a *cons cell*, which may\n          or may not form a proper list, but in practice most cons\n          cells written in a GDLisp program are proper lists. For a\n          full description of the syntax of GDLisp, see :ref:`parser`.\n\nFor example, ``3`` is an S-expression representing, naturally, the\nnumber 3. ``(10 20)`` is an S-expression which is a list. That list\ncontains two literals: 10 and 20. Lists can be nested, such as ``((9)\n8)``. This S-expression is a list of two elements. The first element\nis *itself* a list of one element, while the second is a literal.\n\nThis is the entirety of the GDLisp syntax. Every construct in GDLisp\ncan be built up from these basic rules. There is some syntax sugar on\ntop of these constructs to make common idioms (such as calling methods\non objects, or getting Godot nodes by name) easier.\n\nThis raises the natural question: What do we *do* with this syntax?\nEvery GDLisp expression is read in the same way. Strings and numbers\nevaluate to themselves, so ``3``, when evaluated, returns the\nnumerical value 3. Symbols, which are unquoted barewords, evaluate to\nthe value of the variable with the given name. In the ``let`` snippet\nabove, ``x`` and ``y`` are symbols, and inside the ``let`` block, they\nevaluate to the current value of the variables ``x`` and ``y``,\nrespectively. Finally, lists can act as function calls, macro calls,\nor special forms. We'll dive more into the specifics of those later\non, but the basic idea is that an S-expression which is a list takes\nthe form\n\n::\n\n   (name arg1 arg2 ...)\n\nwhere ``name`` is usually a symbol. The function or other form with\nthe given name will be *called* with the given arguments.\n\nWhy Lisp?\n---------\n\nSo why should we care? Our syntax has been reduced to two simple\nrules, in contrast to languages like Python which require complex\nparsers to even understand the language. This gives us some powerful\nintrospection capabilities, and none more so than *macros*.\n\nIf you've been programming in GDScript or another programming language\nfor very long, you've likely encountered *functions*. When we write\n``example(a, b, c)`` in GDScript, what happens is this: Godot invokes\na function called ``example`` with the arguments ``a``, ``b``, and\n``c``. But that's not all that happens. First, ``a``, ``b``, and ``c``\nhave to be evaluated. They might be variables, or function calls of\ntheir own, or something more complicated.\n\nThe same is true in GDLisp, with some caveats. If there exists a\nfunction called ``example`` in the current scope, then the above\nGDScript call is equivalent to ``(example a b c)``. Remember that this\nis an S-expression of length 4. The first element indicates the\nfunction's name and the other three are its arguments.\n\nHowever, GDLisp also supports *macros*. A macro is like a function,\nbut whereas a function takes runtime values and returns a value,\nmacros operate on code. That is, macros take input in the form of\nsource code and produce new code. Calling a macro is done in the same\nway as calling a function, so if ``example`` happened to be a *macro*\nin the current scope, then ``(example a b c)`` would invoke the macro\nwith three arguments. However, those three arguments would *not* be\nevaluated. Instead, they would literally be passed, as S-expressions\nrepresenting pieces of syntax, to ``example``. ``example`` then\nreturns a new S-expression that will be evaluated in place of the\noriginal macro call.\n\nLet's see a more concrete example. GDLisp already provides a macro\ncalled ``and``, but let's pretend for the moment that it does not.\nLet's write a function called ``and`` that takes two Boolean arguments\nand returns true if and only if both arguments are true.\n\n::\n\n   (defn and (a b)\n     (if a\n       b\n       #f))\n\nLet's break this down for a moment. ``defn`` is used to declare\nfunctions, so this defines a *function* (not a macro) called ``and``.\nThe function takes two arguments ``a`` and ``b``. Inside the function,\nif ``a`` is true, then we return ``b``, and if ``a`` is false, then we\nreturn false (represented by the literal ``#f``). Note that we never\nactually use the word ``return`` here. In GDLisp, the last expression\nof a function is automatically returned.\n\nThis function has a problem, though. In most programming languages,\nthe Boolean operators ``and`` and ``or`` exhibit `Short-circuit\nevaluation <https://en.wikipedia.org/wiki/Short-circuit_evaluation>`_.\nThat is, if ``a`` happens to be false, then the whole expression is\nfalse, regardless of the value of ``b``, so there's no sense in even\nevaluating the latter. But our current function doesn't do that. By\ndefinition, functions evaluate all of their arguments before even\ntrying to execute.\n\nWhat we want is a special sort of function-like object that *doesn't*\nevaluate its arguments and instead produces some code that does what\nwe want. And that's exactly what a macro is. Let's rewrite this\nfunction to be a macro instead.\n\n::\n\n   (defmacro and (a b)\n     `(if ,a\n        ,b\n        #f))\n\nThat's it. That's the macro. We've changed ``defn`` to ``defmacro`` to\nindicate our intent to write a macro, and we've sprinkled some funny\ncommas and backticks in the code.\n\nNow our macro takes two unevaluated arguments ``a`` and ``b``, and it\nproduces an ``if`` statement. The backtick starts a :ref:`quasiquote\n<quoting>`, which is a sort of fancy template. The quasiquote is\ninterpreted literally, without evaluating anything inside of it, until\nwe encounter an *unquote* expression, indicated by the comma, which\ninterpolates a value into the resulting expression. This is sort of\nlike string interpolation in Python or Javascript, but it works for\narbitrary S-expressions, not just strings.\n\n.. Note:: We're not introducing new fundamental syntax here. We're\n          just introducing some syntax sugar. The backtick and comma\n          expand to, respectively, the words ``quasiquote`` and\n          ``unquote``. So, written without any syntax sugar, the macro\n          declaration above is equivalent to::\n\n            (defmacro and (a b)\n              (quasiquote (if (unquote a)\n                            (unquote b)\n                            #f)))\n\n          but the former is quite a bit more readable.\n\nSo the true benefit of Lisp is this. Since our code is merely data, we\ncan write functions which operate on code and produce more code, and\nwe write those functions in the same language that we would write\nordinary value-based functions as well. These \"code functions\" are\ncalled macros. As we delve further into the GDLisp programming\nlanguage, we'll encounter several built-in macros. In fact, ``and`` is\na macro built-in to the standard library (though the implementation is\na bit more complex than our example here, as the standard library\nmacro accounts for arbitrary numbers of arguments).\n\nFunctional Programming\n----------------------\n\nFunctional programming is a paradigm that puts emphasis on treating\nfunctions as first-class citizens of the language and being able to\npass around functions and construct new functions from existing ones\nusing combinators. GDLisp is designed to support a functional style of\nprogramming. GDScript, on its own, has some basic facilities for\nfunctional programming, but they can be obtuse. For example, `FuncRef\n<https://docs.godotengine.org/en/stable/classes/class_funcref.html>`_\ncan be used to wrap an object and a method name into a callable\nobject. However, FuncRef cannot create closures around local\nvariables. Likewise, there's no way to declare a local function that's\nonly needed for a few lines.\n\nIn GDLisp, all of these restrictions are lifted. Functions are\nfirst-class values in the language. To convert a function into a value\nthat can be assigned to a variable, the ``function`` primitive can be\nused.\n\n::\n\n   (defn addone (a)\n     (+ a 1))\n\n   (function addone)\n\nHere, ``addone`` is a function that can be called. However, being a\nfunction, it can't very well be passed to another function as an\nargument. But by wrapping the name in the ``(function ...)`` special\nform, we convert it into a callable object. This is a common enough\noperation that it, like quasiquoting above, has a shortcut syntax.\nNamely, ``#'addone`` is equivalent to ``(function addone)``. To call a\nfunction that has been wrapped in this way, we use\n:ref:`function-funcall`.\n\n::\n\n   (funcall (function addone) 10) ; Returns 11\n\nBut that's not all. GDLisp also fully supports *closures*. The\n``lambda`` primitive constructs a local function which has full access\nto its enclosing scope, including any local variables, as well as the\ncurrent ``self`` object.\n\nSuppose, as a random example, that we have an array of numbers and we\nwant to add some constant to each element of that array, producing a\nnew array of values. In GDScript\n\n.. code-block:: gdscript\n\n   var old_array = [1, 2, 3, 4, 5]\n   var my_constant = 10\n   var new_array = []\n   for x in old_array:\n       new_array.push_back(x + my_constant)\n   return new_array\n\nIf we find ourselves doing this sort of transformation a lot, we might\nwish to capture the behavior in a \"for-each\" sort of function that\napplies a FuncRef to each element of a list.\n\n.. code-block:: gdscript\n\n   func map(old_array, f):\n       var new_array = []\n       for x in old_array:\n           new_array.push_back(f.call_func(x))\n       return new_array\n\nBut we can't really easily apply this to our \"add a constant\"\nsituation, since FuncRef can't easily capture additional values like\n``my_constant``. We can create a sort of FuncRef-like object that\n*does* capture these values and use that instead.\n\n.. code-block:: gdscript\n\n   class AddConstant:\n       var constant\n\n       func _init(c):\n           constant = c\n\n       func call_func(arg):\n           return arg + constant\n\n    var old_array = [1, 2, 3, 4, 5]\n    var my_constant = 10\n    return map(old_array, AddConstant.new(10))\n\nBut this is a lot of extra code to capture the intuitive notion of\n\"add a constant to a number\".\n\nIn GDLisp, this transformation is already built-in and is called\n:ref:`function-array-map`. And if we want to use a local variable, we\ncan simply create a ``lambda`` that automatically captures that local\nvariable.\n\n::\n\n   (let ((old-array [1 2 3 4 5])\n         (my-constant 10))\n     (array/map (lambda (x) (+ x my-constant)) old-array))\n\n``lambda`` constructs a local function that, when called, adds\n``my-constant`` to the argument. Then ``array/map`` applies that to\neach element of our array.\n\nFunctional programming is all about breaking a problem down into\nmanageable pieces. We've taken the complex problem of \"add some number\nto each element of an array\" and broken it down into two easy pieces:\n\"do something to each element of an array\" and \"add a constant to a\nnumber\". The first piece was built-in under the name ``array/map``,\nand the second was a simple ``lambda`` expression.\n"
  },
  {
    "path": "doc/readthedocs/tutorial/macros.rst",
    "content": "\nMacros\n======\n\nMacros are the centerpiece of any Lisp. Macros provide a mechanism for\nmodifying the code of your program using other program code. A macro\ndeclaration looks like a function declaration but is declared using\n``defmacro`` instead of ``defn``.\n\n::\n\n   (defmacro macro-name (args ...)\n     ...)\n\nMacros can only be declared at module scope, not inside of a class.\nWhen a macro is *called*, its arguments are not evaluated. Instead, it\nis given the syntax of the arguments directly and should return the\nsyntax of a new GDLisp expression to use in its place.\n\nThis is best demonstrated with some examples. ``when`` is a macro\nbuilt into GDLisp that evaluates a conditional and then runs the code\ninside if (and only if) the conditional is true. Essentially, ``when``\nis an ``if`` block that does not have an \"else\" branch and for which\nthe \"true\" case can be longer than one expression.\n\nIf GDLisp did not provide this macro, we could write it as follows::\n\n  (defmacro when (condition &rest body)\n    `(if ,condition\n       (progn ,.body)))\n\nLet's break this down, as it uses several new features. When the macro\nis called, it must be called with at least one argument. The first\nargument, as an abstract syntax tree, will be assigned to\n``condition``, and the rest will be accumulated into a list and passed\nas ``body``. Inside the macro, we use a new form of syntax called\n*quasiquoting*.\n\nAny GDLisp expression can be *quoted*. By calling the ``quote``\nspecial form, or equivalently putting a single quotation mark ``'``\nbefore it, evaluation of the expression is delayed. For literals like\nstrings or numbers, this has no effect, as strings and numbers are\nself-evaluating forms. For lists and symbols, quoting the value\nreturns a reified representation of the value. That is, ``(if c t f)``\nis an ``if`` expression that will evaluate ``c`` and then branch, but\n``'(if c t f)`` evaluates to a literal list of four elements, all of\nwhich are symbols in this example.\n\nQuoting is useful when we have a constant expression that we wish to\nreify at runtime. But often, especially with macros, we wish to have a\n*mostly* constant expression with some unknown values interpolated in.\nFor this, we use *quasiquoting*. You can think of quasiquoting as\nbeing sort of like string interpolation in Python or Ruby, but for\narbitrary expressions, not just strings.\n\nA quasiquote begins with the backtick `````, which expands to the\n``(quasiquote ...)`` special form. Inside the quasiquote, everything\nis interpreted literally with the following two exceptions.\n\n* ``(unquote ...)`` blocks (equivalently, a prefix comma ``,``) will\n  be evaluated and spliced as an element into the result.\n* ``(unquote-spliced ...)`` blocks (equivalently, a prefix ``,.``)\n  will be evaluated and concatenated into the result. The enclosing\n  context must be either a list or an array.\n\nTo see the difference, consider the following::\n\n  ;; Assume the variable x has the value (2 3 4)\n  `(1 x 5)   ; No unquote, evaluates to (1 x 5)\n  `(1 ,x 5)  ; Regular unquote, evaluates to (1 (2 3 4) 5)\n  `(1 ,.x 5) ; Spliced unquote, evaluates to (1 2 3 4 5)\n\nLooking back at our ``when`` example::\n\n  (defmacro when (condition &rest body)\n    `(if ,condition\n       (progn ,.body)))\n\nThe ``when`` macro, when called, returns an S-expression whose head is\n``if``. The first argument to ``if`` is our condition. Then the second\nargument is a ``progn`` which runs the entire body (as a list of\nexpressions) if the condition is true.\n\nWe could write its opposite, ``unless``, in a similar way::\n\n  (defmacro unless (condition &rest body)\n    `(if (not ,condition)\n       (progn ,.body)))\n\nMacros also work in declaration context, both at module scope and\ninside of a class. We've already seen one such macro: ``defvars``.\n\n::\n\n   (defmacro defvars (&rest args)\n     (let ((var-decls (list/map (lambda (name) `(defvar ,name)) args)))\n       `(progn ,.var-decls)))\n\nNote that the ``let`` block is *not* part of the expanded code. The\n``let`` block is code that's run when the macro is called and is used\nto *compute* the macro expansion, which is a ``progn`` consisting of\nseveral ``defvar`` special forms in a row.\n\n.. Note:: You may be wondering how ``progn`` works here, since it's an\n          *expression* that evaluates expressions in order, while\n          ``defvar`` is clearly a declaration that only makes sense at\n          class or module scope.\n\n          The answer is that ``progn`` is actually deep magic. It's\n          even more special than other \"special\" forms, in that\n          ``progn`` is the one thing in GDLisp that is valid\n          fully-expanded in both expression or declaration context (or\n          both, incidentally, but that can only occur in the command\n          line REPL).\n\nGDLisp provides several macros built-in, which you'll grow accustomed\nto using as a matter of course (as mentioned before, ``if`` itself is\na macro, written in terms of ``cond``). For end-user games, you might\nnever write ``defmacro`` at all, but it's an invaluable tool for\nlibrary authors who wish to do advanced code generation. And even in a\nrunnable game, you might find yourself automating the boring bits of\ncode generation from time to time as well.\n"
  },
  {
    "path": "doc/readthedocs/tutorial/modules.rst",
    "content": "\nFunctions and Modules\n=====================\n\nNow that we have some basic tools to do calculations and store\nvariables in GDLisp, it's time to talk about how to write independent\nGDLisp source files.\n\nModules\n-------\n\nThis is one of the key places that GDLisp differs from GDScript, and\nit's important to understand the distinction. In GDScript, every\nsource file you write represents a GDScript resource, effectively a\nclass in Python terminology. A GDLisp source file does *not*\nautomatically produce an instantiable class. There are ways to declare\na class, and we will discuss those in the next section. But a GDLisp\nsource file has a one-to-one correspondence with a *module*. A module\nis a collection of named classes, functions, and constants that can be\nimported and used in other modules.\n\nFunctions\n---------\n\nWe've already seen how to declare local functions inside of a block of\ncode, either as a reified value with ``lambda`` or as a scoped local\nname with ``flet`` or ``labels``. At the top-level of a module, named\nfunctions are declared with the ``defn`` declaration form.\n\n::\n\n   (defn function-name (args ...)\n     body ...)\n\nGDLisp functions are, by default, public to the module, which means\nother modules are free to import and use the function. Private\nmodule-level functions can be declared with the ``private`` modifier::\n\n   (defn function-name (args ...) private\n     body ...)\n\nA function declared in this way can be used freely inside the current\nmodule but cannot be imported or used in another module.\n\nTo call a function, simply use its name as the first symbol of an\nS-expression::\n\n  (function-name arg1 arg2 arg3 ...)\n\nThe formal argument list of a function supports optional and variadic\narguments. To declare a function with optional arguments, use the\n``&opt`` directive::\n\n   (defn compare-strings (a b &opt case-sensitive)\n     (if case-sensitive\n       (a:casecmp_to b)\n       (a:nocasecmp_to b)))\n\nThis declares a function ``compare-strings`` that accepts two or three\narguments. If the third argument ``case-sensitive`` is not supplied,\nit defaults to ``()`` (which is falsy).\n\nVariadic arguments can be declared with ``&rest`` or ``&arr``. The\nformer groups all extra arguments into a GDLisp list, and the latter\ngroups all extra arguments into a Godot array.\n\n::\n\n   (defn make-array (&arr args)\n     args)\n\n   (make-array 1 2 3 4 5) ; Produces the array [1 2 3 4 5]\n\nGDLisp standalone function calls always have their argument count\nvalidated at compile-time. If a function is called with the wrong\nnumber of arguments, the GDLisp compiler will emit an error.\n\nNote that the same argument directives ``&opt``, ``&rest``, and\n``&arr`` can be applied to local functions declared with ``flet``,\n``labels``, or ``lambda``. The only difference is that in the case of\n``lambda``, the function is reified into an object, so the argument\nvalidation happens at runtime rather than compile-time.\n\nConstants\n---------\n\nConstants are declared with ``defconst``. Like functions, constants\nare public by default but can be made private to the module.\n\nExamples::\n\n  (defconst DAYS_IN_YEAR 365)\n  (defconst DEFAULT_PLAYER_NAME \"Steve\")\n  (defconst EPOCH 1970 private) ; Private constant\n\n.. Important:: Unlike GDScript, GDLisp does **not** use constants to\n               load resources and other scripts. GDLisp has a\n               dedicated import syntax for loading other files,\n               whether those files are resources, source files, or\n               packed scenes.\n\nEnumerations\n------------\n\nEnumerations are declared with ``defenum``.\n\n::\n\n  (defenum Color RED YELLOW BLUE)\n\nThis defines an enumeration constant called ``Color`` with three\nelements: ``Color:RED``, ``Color:GREEN``, and ``Color:BLUE``. Note\nthat we refer to the elements of an enumeration with a colon ``:``, as\nopposed to a dot ``.`` like we would in GDScript. We'll see this\nsyntax again when we discuss classes.\n\nEnum elements can optionally be given specific numerical values. For\nexample::\n\n  (defenum NUMBERS\n    (ONE 1)\n    (TEN 10)\n    (ONE_HUNDRED 100))\n\nAs with constants and functions, an enumeration can be made private to\nthe enclosing module.\n\n::\n\n  (defenum NUMBERS private\n    (ONE 1)\n    (TEN 10)\n    (ONE_HUNDRED 100))\n\nImporting Modules\n-----------------\n\nModules are of little use if they can't be imported and reused in\nother modules. In GDScript, we load other resources, including other\nGDScript source files, with the ``preload`` function, as follows.\n\n.. code-block:: gdscript\n\n   const MyScript = preload(\"res://MyScript.gd\")\n   const MySprite = preload(\"res://MySprite.png\")\n   const MyScene = preload(\"res://MyScene.tscn\")\n\nThis is still technically possible to do in GDLisp, but it's not\nidiomatic, and it complicates macro expansion. GDLisp has a special,\nand very versatile, ``use`` directive that's designed for importing\ndata from other files.\n\nImporting Resources\n^^^^^^^^^^^^^^^^^^^\n\nImporting non-GDLisp resources is simple.\n\n::\n\n   (use \"res://MySprite.png\" as MySprite)\n   (use \"res://MyScene.png\" as MyScene)\n\nAfter the keyword ``use``, we write the path of the resource, using\nthe same ``res://`` syntax as a GDScript ``preload``. Then we specify\nhow we'd like to name the resource in the current scope. The above\nsnippet defines two constants in the current scope: ``MySprite`` and\n``MyScene``.\n\nNote that names imported into a module are *not* transitively\nre-exported, so while our hypothetical module above has access to the\nnames ``MySprite`` and ``MyScene``, other modules that *import* our\nmodule cannot import those names from it.\n\nIf you don't specify an alias for the import, one will be chosen for\nyou based on the name of the resource. The following two lines are\nequivalent::\n\n   (use \"res://Example/MySprite.png\" as MySprite)\n   (use \"res://Example/MySprite.png\")\n\nThis is also how source files written in GDScript are imported into\nGDLisp. The entire GDScript resource is imported as a single name\nwhich the GDLisp module can access.\n\nImporting Other GDLisp Modules\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nImporting GDLisp modules works a bit differently. A GDLisp module is a\ncollection of functions and other names, not just a single runtime\nresource. If we use the same import syntax as above::\n\n   (use \"res://MyModule.lisp\" as MyModule)\n\nThen every public name from the module ``MyModule`` will be imported\ninto the current module, with the prefix ``MyModule`` added.\nConcretely, suppose ``MyModule.lisp`` contained these names::\n\n   (defn a-function () ...)\n\n   (defn another-function () ...)\n\n   (defconst MY_FIRST_CONSTANT 1)\n   (defconst MY_SECOND_CONSTANT 2 private)\n\nThen the ``use`` directive given above would have the following\neffects:\n\n* The function ``a-function`` is imported into the current scope,\n  under the name ``MyModule/a-function``.\n\n* The function ``another-function`` is imported into the current\n  scope, under the name ``MyModule/another-function``.\n\n* The constant ``MY_FIRST_CONSTANT`` is imported into the current\n  scope, under the name ``MyModule/MY_FIRST_CONSTANT``.\n\n* The constant ``MY_SECOND_CONSTANT`` is *not* imported, as the name\n  is private and not visible to external modules.\n\nAs with non-GDLisp resources, if an alias is not specified, then one\nwill be chosen for you based on the full path of the module.\n\nExplicit Imports\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nInstead of providing a prefix to insert before all names from a\nmodule, you may instead specify explicitly which names you want to\nimport (without a prefix) from the module.\n\n::\n\n   (use \"res://MyModule.lisp\" (a-function MY_FIRST_CONSTANT))\n\nIn this example, the function ``a-function`` and the constant\n``MY_FIRST_CONSTANT`` will be imported as-is into the current scope\n(with no prefix), and ``another-function`` will not be imported at\nall.\n\nAliases can be provided, for any explicit imports.\n\n::\n\n   (use \"res://MyModule.lisp\" ((a-function as an-external-function)\n                               (MY_FIRST_CONSTANT as CONSTANT)))\n\nFinally, if you want to import all of the (public) names from a\nmodule, without any prefix, you may replace the explicit import list\nwith the word ``open``.\n\n::\n\n   (use \"res://MyModule.lisp\" open)\n\nGDLisp.lisp\n-----------\n\nIf you're compiling your own GDLisp modules, there's one more\ndependency you need to know about. All of the functions and constants\nthat are defined in GDLisp are provided by a support library, called\n``GDLisp.lisp``. This support library is included in the GDLisp\npackage you downloaded and is automatically compiled into\n``GDLisp.gd`` when you build the GDLisp compiler. ``GDLisp.gd`` must\nbe included as an autoloaded singleton (with the name ``GDLisp``) in\nany project that uses GDLisp code.\n\nTo include this in your project, simply copy the ``GDLisp.gd`` file\ninto your project's root directory and add it in the autoloads list\nunder your project settings, ensuring that the name of the autoload is\n``GDLisp`` (this is case-sensitive). **All** GDLisp modules implicitly\nassume that this autoload is available, so you **must** include it in\nany project that includes GDLisp code.\n"
  },
  {
    "path": "doc/readthedocs/tutorial/what-now.rst",
    "content": "\nWhat's Next\n===========\n\nThis has been a whirlwind tour of the basic features of GDLisp. More\ncomprehensive documentation is also available at :ref:`comprehensive`.\n\nGDLisp is available for use today. I encourage you to try it out in\nyour next Godot project. You can report any issues you encounter at\nour `official Github repository\n<https://github.com/Mercerenies/gdlisp/>`_.\n\nGood luck, and happy coding!\n"
  },
  {
    "path": "minimal.txt",
    "content": "\nCommand to build Godot with minimal modules, for testing:\n\n> scons platform=server tools=yes target=release_debug disable_3d=no disable_advanced_gui=no module_bmp_enabled=no module_bullet_enabled=no module_camera_enabled=no module_csg_enabled=no module_cvtt_enabled=no module_dds_enabled=no module_denoise_enabled=no module_enet_enabled=no module_etc_enabled=no module_fbx_enabled=no module_freetype_enabled=yes module_gltf_enabled=no module_gridmap_enabled=no module_hdr_enabled=no module_jpg_enabled=no module_jsonrpc_enabled=no module_lightmapper_cpu_enabled=no module_mbedtls_enabled=no module_minimp3_enabled=no module_mobile_vr_enabled=no module_mono_enabled=no module_ogg_enabled=no module_opensimplex_enabled=no module_opus_enabled=no module_pvr_enabled=no module_raycast_enabled=no module_recast_enabled=no module_regex_enabled=no module_squish_enabled=no module_stb_vorbis_enabled=no module_svg_enabled=no module_tga_enabled=no module_theora_enabled=no module_tinyexr_enabled=no module_upnp_enabled=no module_vhacd_enabled=no module_visual_script_enabled=no module_vorbis_enabled=no module_webm_enabled=no module_webp_enabled=no module_webrtc_enabled=no module_websocket_enabled=no module_webxr_enabled=no module_xatlas_unwrap_enabled=no\n\nGDScript and GDNative modules are required. Tools are required to be\nable to query the API. Freetype, 3D, and advanced GUI are required for\ntools. Everything else is disabled.\n"
  },
  {
    "path": "src/command_line.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! This module provides functionality to parse command line\n//! arguments.\n//!\n//! Generally speaking, the entrypoint to this module will be\n//! [`parse_args`].\n\nuse getopts::{Options, ParsingStyle};\n\nconst PROGRAM_DESCRIPTION: &str =\n  r#\"The GDLisp compiler. If invoked with filenames, compiles the GDLisp sources\nat the given path. If given a directory, compiles all GDLisp source files in the\nGodot project at that directory. If given *no* file or directory arguments, invokes\nthe GDLisp REPL.\"#;\n\n/// This structure contains information about the command line\n/// arguments passed to the compiler. It is usually constructed via\n/// [`parse_args`].\n#[derive(Debug, Clone, Default)]\npub struct CommandLineArgs {\n\n  /// The input file provided, or `None` if none was provided.\n  pub input_file: Option<String>,\n\n  /// Whether `--help` was provided.\n  pub help_message: bool,\n\n  /// Whether the internal `--compile-stdlib` was provided.\n  pub compile_stdlib_flag: bool,\n\n  /// Whether the `--legacy-repl` command was provided.\n  pub legacy_repl_flag: bool,\n\n}\n\nimpl CommandLineArgs {\n\n  /// Construct a new defaulted [`CommandLineArgs`].\n  pub fn new() -> CommandLineArgs {\n    CommandLineArgs::default()\n  }\n\n  /// Construct a [`CommandLineArgs`] which indicates that the user\n  /// would like to see the help message.\n  pub fn help() -> CommandLineArgs {\n    let mut inst = CommandLineArgs::new();\n    inst.help_message = true;\n    inst\n  }\n\n}\n\n/// The [`Options`] which are used for parsing GDLisp command line\n/// arguments.\npub fn options() -> Options {\n  let mut opts = Options::new();\n  opts\n    .parsing_style(ParsingStyle::FloatingFrees)\n    .long_only(false)\n    .optflag(\"\", \"help\", \"Display usage information\")\n    .optflag(\"\", \"compile-stdlib\", \"Compile the GDLisp standard library\")\n    .optflag(\"\", \"legacy-repl\", \"Run the old-style GDLisp REPL which compiles rather than executing (legacy)\");\n  opts\n}\n\n/// Parse the arguments and return an appropriate [`CommandLineArgs`]\n/// instance.\n///\n/// If any parsing error occurs, then [`CommandLineArgs::help()`] is\n/// returned instead. If you would like to do your own error-handling,\n/// consider calling [`options`] directly.\npub fn parse_args(args: &[String]) -> CommandLineArgs {\n  match options().parse(args) {\n    Err(_) => {\n      CommandLineArgs::help()\n    }\n    Ok(parsed) => {\n      let mut result = CommandLineArgs::new();\n\n      result.help_message = parsed.opt_present(\"help\");\n      result.compile_stdlib_flag = parsed.opt_present(\"compile-stdlib\");\n      result.legacy_repl_flag = parsed.opt_present(\"legacy-repl\");\n      result.input_file = parsed.free.first().cloned();\n\n      result\n    }\n  }\n}\n\n/// Helper function to show the GDLisp compiler help message.\npub fn show_help_message(program: &str) {\n  let opts = options();\n  let brief = format!(\"Usage: {} [FILE] [options]\\n\\n{}\", program, PROGRAM_DESCRIPTION);\n  print!(\"{}\", opts.usage(&brief));\n}\n"
  },
  {
    "path": "src/compile/args.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! This module provides convenience functions for checking the length\n//! of [`Vec`] vectors of values.\n//!\n//! This module is *not* a handler for the concept of an \"argument\n//! list\" on a GDLisp function; for that, use [`crate::ir::arglist`]\n//! or [`crate::gdscript::arglist`], depending on whether you're\n//! concerned about the GDLisp or GDScript representation of an\n//! argument list. This module, instead, is for situations where the\n//! *compiler* is expecting some predetermined number of arguments,\n//! and we'd like a convenient way to check that the argument count is\n//! correct and return an error otherwise.\n\nuse super::error::{GDError, GDErrorF};\nuse crate::pipeline::source::SourceOffset;\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::literal::Literal;\nuse crate::ir::special_form::access_slot::ACCESS_SLOT_FORM_NAME;\n\nuse std::fmt;\n\n/// `Expecting` specifies how many arguments are expected. The two\n/// bounds are always inclusive. If a function has no upper bound,\n/// then [`usize::MAX`] can be used.\n///\n/// An `Expecting` instance where `maximum < minimum` will accept no\n/// argument lists and always produce an error. No effort is made in\n/// this module to prevent the construction of such instances.\n///\n/// See also [`ExpectedShape`], which deals with the expected shape of\n/// a single argument, rather than the number of arguments.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub struct Expecting {\n  pub minimum: usize,\n  pub maximum: usize,\n}\n\n/// `ExpectedShape` specifies the type of argument that is expected.\n///\n/// Whereas [`Expecting`] concerns itself with the number of\n/// arguments, this enum concerns itself with the shape of an\n/// individual argument.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum ExpectedShape {\n  /// A variable declaration clause for a let expression.\n  VarDecl,\n  /// A macro declaration clause in a macrolet expression.\n  MacroDecl,\n  /// A function declaration clause in an flet expression or similar.\n  FnDecl,\n  /// A valid \"extends\" clause for a class declaration.\n  SuperclassDecl,\n  /// A special reference name. See\n  /// [`crate::ir::special_form::special_ref_form`].\n  SpecialRefValue,\n  /// A symbol literal.\n  Symbol,\n  /// A string literal.\n  String,\n  /// An integer literal.\n  Integer,\n  /// The empty list, as a literal.\n  EmptyList,\n  /// Equivalent to `EmptyList` but issues an error message of the\n  /// form \"expected end of list\". This shape should be used in cases\n  /// where the full list is not syntactically empty but the tail is\n  /// expected to be, for clarity.\n  EndOfList,\n  /// A nonempty list literal.\n  NonemptyList,\n  /// A list literal containing a single element.\n  SingletonList,\n  /// A second argument to `yield`. The GDScript `yield` function is\n  /// unique in that it has two optional arguments, but if one is\n  /// supplied then the other becomes required. This special value\n  /// provides an error message specific to that unique case.\n  YieldArg,\n  /// A valid instance function name, either a symbol or an expression\n  /// of the form `(set name)` or `(get name)`, where `name` is an\n  /// arbitrary symbol.\n  InstanceFnName,\n  /// A symbol, or a pair of symbols as a 2-element list.\n  SymbolOrPairOfSymbols,\n  /// The `access-slot` literal name.\n  AccessSlotName,\n}\n\nimpl Expecting {\n\n  /// `Expecting` instance which expects no arguments at all.\n  pub const NONE: Expecting = Expecting { minimum: 0, maximum: 0 };\n\n  /// Convenience function for constructing general `Expecting`\n  /// values.\n  pub fn new(minimum: usize, maximum: usize) -> Expecting {\n    Expecting { minimum, maximum }\n  }\n\n  /// Synonym for [`Expecting::new`].\n  pub fn between(minimum: usize, maximum: usize) -> Expecting {\n    Expecting::new(minimum, maximum)\n  }\n\n  /// An `Expecting` which demands a specific number of arguments.\n  pub fn exactly(value: usize) -> Expecting {\n    Expecting::new(value, value)\n  }\n\n  /// An `Expecting` with no upper bound.\n  pub fn at_least(minimum: usize) -> Expecting {\n    Expecting::new(minimum, usize::MAX)\n  }\n\n  /// An `Expecting` with no lower bound.\n  pub fn at_most(maximum: usize) -> Expecting {\n    Expecting::new(0, maximum)\n  }\n\n  /// Check that the number of arguments is within the bounds\n  /// specified by `self`.\n  pub fn contains(&self, args_count: usize) -> bool {\n    args_count >= self.minimum && args_count <= self.maximum\n  }\n\n  /// Check that `self.contains(args_count)`, and if not, raise an\n  /// appropriate [`GDErrorF::WrongNumberArgs`] with the given `name`\n  /// and `pos`.\n  pub fn validate_amount(&self, name: &str, pos: SourceOffset, args_count: usize) -> Result<(), GDError> {\n    if self.contains(args_count) {\n      Ok(())\n    } else {\n      Err(GDError::new(GDErrorF::WrongNumberArgs(String::from(name), *self, args_count), pos))\n    }\n  }\n\n  /// Validate against the length of a slice.\n  ///\n  /// Equivalent to `self.validate_amount(name, pos, slice.len())`.\n  pub fn validate<T>(&self, name: &str, pos: SourceOffset, slice: &[T]) -> Result<(), GDError> {\n    self.validate_amount(name, pos, slice.len())\n  }\n\n}\n\nimpl ExpectedShape {\n\n  /// Extracts a [`Literal::Symbol`], or reports an error if the\n  /// [`AST`] is not a symbol.\n  pub fn extract_symbol(form_name: &str, ast: AST) -> Result<String, GDError> {\n    let pos = ast.pos;\n    match ast.value {\n      ASTF::Atom(Literal::Symbol(s)) => Ok(s),\n      _ => Err(GDError::new(GDErrorF::InvalidArg(form_name.to_owned(), ast, ExpectedShape::Symbol), pos)),\n    }\n  }\n\n  /// Extracts a [`Literal::String`], or reports an error if the\n  /// [`AST`] is not a string literal.\n  pub fn extract_string(form_name: &str, ast: AST) -> Result<String, GDError> {\n    let pos = ast.pos;\n    match ast.value {\n      ASTF::Atom(Literal::String(s)) => Ok(s),\n      _ => Err(GDError::new(GDErrorF::InvalidArg(form_name.to_owned(), ast, ExpectedShape::String), pos)),\n    }\n  }\n\n  /// Extracts a [`Literal::Int`], or reports an error if the [`AST`]\n  /// is not an integer literal.\n  pub fn extract_i32(form_name: &str, ast: AST) -> Result<i32, GDError> {\n    let pos = ast.pos;\n    match ast.value {\n      ASTF::Atom(Literal::Int(n)) => Ok(n),\n      _ => Err(GDError::new(GDErrorF::InvalidArg(form_name.to_owned(), ast, ExpectedShape::Integer), pos)),\n    }\n  }\n\n  /// Validates that the vector is in fact empty. If it is, this\n  /// function returns `()` harmlessly. If it is nonempty, then this\n  /// function produces an appropriate error about the expected shape\n  /// with [`ExpectedShape::EmptyList`].\n  ///\n  /// This function takes a `&[&AST]` to be compatible with the output\n  /// of [`DottedExpr`](crate::sxp::dotted::DottedExpr).\n  pub fn validate_empty(form_name: &str, lst: &[&AST], pos: SourceOffset) -> Result<(), GDError> {\n    if lst.is_empty() {\n      Ok(())\n    } else {\n      // Note: Report error as `lst[0].pos`, since that's where the proof of nonempty-ness started.\n      let err_pos = lst[0].pos;\n      let lst: Vec<_> = lst.iter().map(|x| (*x).to_owned()).collect();\n      Err(GDError::new(GDErrorF::InvalidArg(form_name.to_owned(), AST::list(lst, pos), ExpectedShape::EmptyList), err_pos))\n    }\n  }\n\n  /// Equivalent to [`validate_empty`](ExpectedShape::validate_empty)\n  /// but produces [`ExpectedShape::EndOfList`] as error instead.\n  ///\n  /// This function takes a `&[&AST]` to be compatible with the output\n  /// of [`DottedExpr`](crate::sxp::dotted::DottedExpr).\n  pub fn validate_end_of_list(form_name: &str, lst: &[&AST], pos: SourceOffset) -> Result<(), GDError> {\n    if lst.is_empty() {\n      Ok(())\n    } else {\n      // Note: Report error as `lst[0].pos`, since that's where the proof of nonempty-ness started.\n      let err_pos = lst[0].pos;\n      let lst: Vec<_> = lst.iter().map(|x| (*x).to_owned()).collect();\n      Err(GDError::new(GDErrorF::InvalidArg(form_name.to_owned(), AST::list(lst, pos), ExpectedShape::EndOfList), err_pos))\n    }\n  }\n\n}\n\nimpl fmt::Display for Expecting {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    if self.minimum > self.maximum {\n      write!(f, \"(no valid call)\")\n    } else if self.minimum == self.maximum {\n      write!(f, \"exactly {}\", self.minimum)\n    } else if self.maximum == usize::MAX {\n      write!(f, \"at least {}\", self.minimum)\n    } else if self.minimum == usize::MIN {\n      write!(f, \"at most {}\", self.maximum)\n    } else {\n      write!(f, \"{} to {}\", self.minimum, self.maximum)\n    }\n  }\n}\n\nimpl fmt::Display for ExpectedShape {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      ExpectedShape::VarDecl => write!(f, \"variable declaration\"),\n      ExpectedShape::MacroDecl => write!(f, \"macro declaration\"),\n      ExpectedShape::FnDecl => write!(f, \"function declaration\"),\n      ExpectedShape::SuperclassDecl => write!(f, \"superclass declaration\"),\n      ExpectedShape::SpecialRefValue => write!(f, \"special reference value\"),\n      ExpectedShape::Symbol => write!(f, \"symbol\"),\n      ExpectedShape::String => write!(f, \"string\"),\n      ExpectedShape::Integer => write!(f, \"integer\"),\n      ExpectedShape::EmptyList => write!(f, \"empty list\"),\n      ExpectedShape::EndOfList => write!(f, \"end of list\"),\n      ExpectedShape::NonemptyList => write!(f, \"nonempty list\"),\n      ExpectedShape::SingletonList => write!(f, \"singleton list\"),\n      ExpectedShape::YieldArg => write!(f, \"additional argument (yield takes 0 or 2 arguments)\"),\n      ExpectedShape::InstanceFnName => write!(f, \"instance function name\"),\n      ExpectedShape::SymbolOrPairOfSymbols => write!(f, \"symbol or 2-element list of symbols\"),\n      ExpectedShape::AccessSlotName => write!(f, \"the literal symbol '{}'\", ACCESS_SLOT_FORM_NAME),\n    }\n  }\n}\n\n/// Panic if the vector is nonempty. This method is provided for\n/// symmetry with [`one`], [`two`], and [`three`].\n///\n/// This is intended to be used as a convenient destructuring method\n/// *after* a call to [`Expecting::validate`].\npub fn zero<T>(args: Vec<T>) {\n  assert!(!args.is_empty(), \"Assertion violated in gdlisp::compile::args::zero\");\n}\n\n/// Get the single element from the vector, panicking if the length is\n/// wrong.\n///\n/// This is intended to be used as a convenient destructuring method\n/// *after* a call to [`Expecting::validate`].\npub fn one<T>(mut args: Vec<T>) -> T {\n  assert!(args.len() == 1, \"Assertion violated in gdlisp::compile::args::one\");\n  args.pop().expect(\"Internal error in gdlisp::compile::args\")\n}\n\n/// Get the two elements from the argstor, panicking if the length is\n/// wrong.\n///\n/// This is intended to be used as a convenient destructuring method\n/// *after* a call to [`Expecting::validate`].\npub fn two<T>(mut args: Vec<T>) -> (T, T) {\n  assert!(args.len() == 2, \"Assertion violated in gdlisp::compile::args::two\");\n  let y = args.pop().expect(\"Internal error in gdlisp::compile::args\");\n  let x = args.pop().expect(\"Internal error in gdlisp::compile::args\");\n  (x, y)\n}\n\n/// Get the three elements from the argstor, panicking if the length is\n/// wrong.\n///\n/// This is intended to be used as a convenient destructuring method\n/// *after* a call to [`Expecting::validate`].\npub fn three<T>(mut args: Vec<T>) -> (T, T, T) {\n  assert!(args.len() == 3, \"Assertion violated in gdlisp::compile::args::three\");\n  let z = args.pop().expect(\"Internal error in gdlisp::compile::args\");\n  let y = args.pop().expect(\"Internal error in gdlisp::compile::args\");\n  let x = args.pop().expect(\"Internal error in gdlisp::compile::args\");\n  (x, y, z)\n}\n"
  },
  {
    "path": "src/compile/body/builder.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Builders for GDScript code.\n\nuse crate::gdscript::decl::{self, Decl};\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::class_extends::ClassExtends;\n\n/// A builder for an entire GDScript source file.\n#[derive(Clone)]\npub struct CodeBuilder {\n  toplevel: decl::TopLevelClass,\n}\n\n/// A builder for a sequence of GDScript statements.\n///\n/// This structure also retains a list of \"helper\" declarations\n/// necessary for the statements to run correctly. These helper\n/// declarations should eventually be hoisted to the top-level scope\n/// containing the statements. These declarations include things like\n/// inner classes for implementing lambdas and other advanced GDLisp\n/// features.\n#[derive(Clone, Default)]\npub struct StmtBuilder {\n  body: Vec<Stmt>,\n  helpers: Vec<Decl>,\n}\n\n/// Trait for builder structures which can have declarations added to\n/// them in some meaningful way.\n///\n/// This trait allows [`StmtBuilder::build_into`] to fold neatly into\n/// another [`StmtBuilder`] or into any other kind of builder (such as\n/// [`CodeBuilder`]) in a nice, uniform way.\npub trait HasDecls {\n\n  /// Adds a declaration to the builder.\n  fn add_decl(&mut self, decl: Decl);\n\n  /// Adds several declarations to the builder.\n  fn add_decls(&mut self, decls: impl IntoIterator<Item=Decl>) {\n    for decl in decls {\n      self.add_decl(decl);\n    }\n  }\n\n}\n\nimpl CodeBuilder {\n\n  /// Construct a new builder for a class which extends the given\n  /// class.\n  ///\n  /// The builder begins representing a class with no declarations and\n  /// no name.\n  pub fn new(extends: ClassExtends) -> CodeBuilder {\n    CodeBuilder {\n      toplevel: decl::TopLevelClass {\n        name: None,\n        extends: extends,\n        body: vec!(),\n      }\n    }\n  }\n\n  /// Give the class referenced by this builder a name, i.e. a\n  /// `class_name` header in GDScript.\n  ///\n  /// If the class already has a name, it is overwritten.\n  pub fn named(&mut self, name: String) {\n    self.toplevel.name = Some(name);\n  }\n\n  /// Change what the class extends.\n  pub fn extends(&mut self, extends: ClassExtends) {\n    self.toplevel.extends = extends;\n  }\n\n  /// Consume this builder and produce a top-level class declaration.\n  pub fn build(self) -> decl::TopLevelClass {\n    self.toplevel\n  }\n\n}\n\nimpl HasDecls for CodeBuilder {\n  fn add_decl(&mut self, decl: Decl) {\n    self.toplevel.body.push(decl);\n  }\n}\n\nimpl StmtBuilder {\n\n  /// A new builder containing no statements.\n  pub fn new() -> StmtBuilder {\n    StmtBuilder::default()\n  }\n\n  /// Append a single statement to the builder.\n  pub fn append(&mut self, stmt: Stmt) {\n    self.body.push(stmt);\n  }\n\n  /// Append a collection of statements to the builder in order.\n  pub fn append_all(&mut self, stmts: &mut dyn Iterator<Item=Stmt>) {\n    for stmt in stmts {\n      self.append(stmt)\n    }\n  }\n\n  /// Append a declaration to the builder's collection of helper\n  /// declarations.\n  pub fn add_helper(&mut self, decl: Decl) {\n    self.helpers.push(decl);\n  }\n\n  /// Consume the builder and produce its statements and necessary\n  /// helper declarations.\n  ///\n  /// The caller is responsible for ensuring that the helper\n  /// declarations are safely transmitted to the enclosing scope.\n  /// Often, [`StmtBuilder::build_into`] can be used to do this automatically.\n  pub fn build(self) -> (Vec<Stmt>, Vec<Decl>) {\n    (self.body, self.helpers)\n  }\n\n  /// Consume the builder, passing any helper declarations onto the\n  /// subsequent builder.\n  ///\n  /// This is more useful than [`StmtBuilder::build`] if you have access to the\n  /// builder (often, but not necessary, a `StmtBuilder`) representing\n  /// the enclosing scope. The helper declarations from `self` are\n  /// added (via [`HasDecls::add_decl`]) to `other`, and the\n  /// statements from `self` are returned.\n  pub fn build_into(self, other: &mut impl HasDecls) -> Vec<Stmt> {\n    let (body, helpers) = self.build();\n    for h in helpers {\n      other.add_decl(h);\n    }\n    body\n  }\n\n}\n\nimpl HasDecls for StmtBuilder {\n  fn add_decl(&mut self, decl: Decl) {\n    self.add_helper(decl);\n  }\n}\n"
  },
  {
    "path": "src/compile/body/class_initializer.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Builders for class initializers.\n\nuse crate::gdscript::decl::{Decl, DeclF, ClassDecl, InitFnDecl, FnDecl, Static};\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::arglist::ArgList;\nuse crate::gdscript::library::READY_NAME;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::util::find_or_else_mut;\nuse crate::pipeline::source::SourceOffset;\nuse super::builder::{StmtBuilder, HasDecls};\nuse super::synthetic_field::{SyntheticField, Getter, Setter};\nuse super::super_proxy::SuperProxy;\nuse super::class_scope::DirectClassScope;\n\nuse std::mem;\nuse std::collections::HashMap;\n\n/// A builder for a GDScript class.\n///\n/// This builder allows certain pieces of functionality for the class,\n/// such as `_init` and `_ready`, to build up over the course of\n/// compilation and eventually build it into the resulting class at\n/// the end.\n#[derive(Default, Clone)]\npub struct ClassBuilder {\n  /// The builder for `_init`.\n  init_builder: StmtBuilder,\n  /// The builder for `_ready`.\n  ready_builder: StmtBuilder,\n  /// The builder for any synthetic fields generated as a result of\n  /// `get` and `set` declarations.\n  synthetic_fields: Vec<SyntheticField>,\n  /// The builder for any supermethod proxies generated as a result of\n  /// `super` method calls. Note that a `super` constructor call is an\n  /// entirely different mechanism which is parsed into the syntax\n  /// earlier than other `super` method calls and is not included\n  /// here.\n  super_proxies: Vec<SuperProxy>,\n  /// Any declarations which have been absorbed by another builder.\n  /// This field exists for compatibility with [`HasDecls`] so that\n  /// this builder can be composed with others easily.\n  other_helpers: Vec<Decl>,\n}\n\n/// This is the eventual result of a [`ClassBuilder`]. It contains\n/// information that can be used to modify a [`ClassDecl`].\n#[derive(Default, Clone, Debug)]\npub struct ClassInit {\n  /// Statements to be prepended to the class' `_init` method.\n  init: Vec<Stmt>,\n  /// Statements to be prepended to the class' `_ready` method.\n  ready: Vec<Stmt>,\n  /// Proxy fields to be generated with appropriate `setget`\n  /// declarations.\n  synthetic_fields: Vec<SyntheticField>,\n  /// Proxy methods to be generated for supermethod calls.\n  super_proxies: Vec<SuperProxy>,\n}\n\n/// The time that an instance variable should be initialized.\n#[derive(Clone, Copy, PartialEq, Eq, Debug)]\npub enum InitTime {\n  /// The variable is initialized during `_init()`, i.e. when the\n  /// instance itself is first constructed.\n  Init,\n  /// The variable is initialized during `_ready()`, i.e. when the\n  /// instance is added to the scene tree.\n  Ready,\n}\n\nimpl ClassBuilder {\n\n  /// A new, empty initializer builder. Equivalent to\n  /// `ClassBuilder::default()`.\n  pub fn new() -> ClassBuilder {\n    ClassBuilder::default()\n  }\n\n  /// Returns the builder object for either `_init` or `_ready`,\n  /// depending on the initialization time argument.\n  pub fn builder_for(&mut self, init_time: InitTime) -> &mut StmtBuilder {\n    match init_time {\n      InitTime::Init => &mut self.init_builder,\n      InitTime::Ready => &mut self.ready_builder,\n    }\n  }\n\n  /// Declares a getter for the given proxy field. If a getter already\n  /// exists, this method overwrites it and returns the old one.\n  ///\n  /// Note: This method takes the field name as argument, not the\n  /// getter name. The field name will be prefixed appropriately (as\n  /// per [`Getter::method_name`]) to determine the field name.\n  pub fn declare_getter_for(&mut self, field_name: String) -> Option<String> {\n    let method_name = Getter::method_name(&field_name);\n    for field in &mut self.synthetic_fields {\n      if field.name == field_name {\n        return mem::replace(&mut field.getter, Some(method_name));\n      }\n    }\n    // Name wasn't found at all, so add a new one to the end.\n    self.synthetic_fields.push(SyntheticField {\n      name: field_name,\n      getter: Some(method_name),\n      setter: None,\n    });\n    None\n  }\n\n  /// Declares a setter for the given proxy field. If a setter already\n  /// exists, this method overwrites it and returns the old one.\n  ///\n  /// Note: This method takes the field name as argument, not the\n  /// setter name. The field name will be prefixed appropriately (as\n  /// per [`Setter::method_name`]) to determine the field name.\n  pub fn declare_setter_for(&mut self, field_name: String) -> Option<String> {\n    let method_name = Setter::method_name(&field_name);\n    for field in &mut self.synthetic_fields {\n      if field.name == field_name {\n        return mem::replace(&mut field.setter, Some(method_name));\n      }\n    }\n    // Name wasn't found at all, so add a new one to the end.\n    self.synthetic_fields.push(SyntheticField {\n      name: field_name,\n      getter: None,\n      setter: Some(method_name),\n    });\n    None\n  }\n\n  /// Declares a supermethod proxy which will delegate to the\n  /// supermethod with the given name. Returns the name of the new\n  /// proxy method we've generated.\n  pub fn declare_super_proxy(&mut self, gen: &mut FreshNameGenerator, super_name: String, args: usize, pos: SourceOffset) -> String {\n    let proxy = SuperProxy::generate(gen, super_name, args, pos);\n    let name = proxy.name.clone();\n    self.super_proxies.push(proxy);\n    name\n  }\n\n  /// Incorporates all of the supermethod proxies from the given\n  /// direct class scope.\n  pub fn declare_proxies_from_scope(&mut self, scope: DirectClassScope) {\n    self.super_proxies.extend(scope.into_proxies());\n  }\n\n  /// This method takes any of the synthetic fields defined on the\n  /// builder which have either a getter *or* a setter but not both\n  /// and fills out the missing accessor method with one that produces\n  /// an error. After calling this method, the current builder will\n  /// contain only complete synthetic fields, i.e. all of them have\n  /// both a getter and a setter.\n  pub fn fill_out_synthetic_fields(&mut self, decl: &mut ClassDecl, pos: SourceOffset) {\n    for field in &mut self.synthetic_fields {\n      // If getter is none, generate a synthetic one.\n      if field.getter.is_none() {\n        let method_name = Getter::method_name(&field.name);\n        let method = ClassBuilder::implied_getter(&field.name, &method_name, pos);\n        decl.body.push(Decl::new(DeclF::FnDecl(Static::NonStatic, method), pos));\n        field.getter = Some(method_name);\n      }\n      // If setter is none, generate a synthetic one.\n      if field.setter.is_none() {\n        let method_name = Setter::method_name(&field.name);\n        let method = ClassBuilder::implied_setter(&field.name, &method_name, pos);\n        decl.body.push(Decl::new(DeclF::FnDecl(Static::NonStatic, method), pos));\n        field.setter = Some(method_name);\n      }\n    }\n  }\n\n  fn implied_getter(field_name: &str, method_name: &str, pos: SourceOffset) -> FnDecl {\n    let error_string = format!(\"Cannot access nonexistent field '{}'\", field_name);\n    FnDecl {\n      name: method_name.to_owned(),\n      args: ArgList::empty(),\n      body: vec!(\n        Stmt::expr(Expr::simple_call(\"push_error\", vec!(Expr::from_value(error_string, pos)), pos)),\n      )\n    }\n  }\n\n  fn implied_setter(field_name: &str, method_name: &str, pos: SourceOffset) -> FnDecl {\n    let arglist = ArgList::required(vec!(String::from(\"_unused\")));\n    let error_string = format!(\"Cannot assign to nonexistent field '{}'\", field_name);\n    FnDecl {\n      name: method_name.to_owned(),\n      args: arglist,\n      body: vec!(\n        Stmt::expr(Expr::simple_call(\"push_error\", vec!(Expr::from_value(error_string, pos)), pos)),\n      )\n    }\n  }\n\n  /// Builds the builder into a [`ClassInit`].\n  pub fn build(self) -> (ClassInit, Vec<Decl>) {\n    let mut helpers = self.other_helpers;\n\n    // Initializer\n    let (init, mut init_helpers) = self.init_builder.build();\n    helpers.append(&mut init_helpers);\n\n    // Ready\n    let (ready, mut ready_helpers) = self.ready_builder.build();\n    helpers.append(&mut ready_helpers);\n\n    let initializer = ClassInit {\n      init,\n      ready,\n      synthetic_fields: self.synthetic_fields,\n      super_proxies: self.super_proxies,\n    };\n\n    (initializer, helpers)\n  }\n\n  /// Builds the builder into a [`ClassInit`], passing any helper\n  /// declarations onto the enclosing builder. See\n  /// [`StmtBuilder::build_into`] for a summary of why this method\n  /// might be preferred over [`ClassBuilder::build`].\n  pub fn build_into(self, other: &mut impl HasDecls) -> ClassInit {\n    let (body, helpers) = self.build();\n    for h in helpers {\n      other.add_decl(h);\n    }\n    body\n  }\n\n}\n\nimpl HasDecls for ClassBuilder {\n\n  fn add_decl(&mut self, decl: Decl) {\n    self.other_helpers.push(decl);\n  }\n\n}\n\nimpl Default for InitTime {\n\n  /// [`InitTime::Init`] is the \"default\" initialization time, if no\n  /// modifiers are applied.\n  fn default() -> InitTime {\n    InitTime::Init\n  }\n\n}\n\nimpl ClassInit {\n\n  /// Applies the initializer information to the given class\n  /// declaration. The class declaration is mutated in-place. If an\n  /// error occurs, then the class declaration is left in a valid but\n  /// unspecified state.\n  pub fn apply(mut self, class: &mut ClassDecl, pos: SourceOffset) -> Result<(), GDError> {\n\n    // Initializer\n    if !self.init.is_empty() {\n      let initializer = ClassInit::find_initializer(&mut class.body);\n      self.init.append(&mut initializer.body);\n      initializer.body = self.init;\n    }\n\n    // Ready\n    if !self.ready.is_empty() {\n      let ready = ClassInit::find_ready(&mut class.body);\n      self.ready.append(&mut ready.body);\n      ready.body = self.ready;\n    }\n\n    // Proxy fields for getters and setters\n    ClassInit::apply_proxy_fields(self.synthetic_fields, class, pos)?;\n\n    // Super-call proxy methods\n    for method in self.super_proxies {\n      let fn_decl = FnDecl::from(method);\n      class.body.push(Decl::new(DeclF::FnDecl(Static::NonStatic, fn_decl), pos));\n    }\n\n    Ok(())\n  }\n\n  /// Generates all of the proxy fields associated with the synthetic\n  /// field collection. In case of a conflict with an existing (non-proxy)\n  /// field, an error is raised. In that case, the class declaration\n  /// is left in a valid but unspecified state.\n  fn apply_proxy_fields(fields: Vec<SyntheticField>, class: &mut ClassDecl, pos: SourceOffset) -> Result<(), GDError> {\n    let existing_fields = ClassInit::all_var_decls(class);\n\n    for field in fields {\n      let var_decl = field.into_field();\n      if let Some(pos) = existing_fields.get(&*var_decl.name) {\n        return Err(GDError::new(GDErrorF::FieldAccessorConflict(var_decl.name), *pos));\n      }\n      class.body.push(Decl::new(DeclF::VarDecl(var_decl), pos));\n    }\n\n    Ok(())\n  }\n\n  /// Returns a collection of all of the variable declaration names in\n  /// the given class.\n  fn all_var_decls(class: &ClassDecl) -> HashMap<String, SourceOffset> {\n    let mut acc = HashMap::new();\n    for decl in &class.body {\n      if let DeclF::VarDecl(var_decl) = &decl.value {\n        acc.insert(var_decl.name.to_owned(), decl.pos);\n      }\n    }\n    acc\n  }\n\n  /// Find the initializer function defined on the current class. If\n  /// no initializer is defined, then an empty one is created,\n  /// appended to the class, and returned.\n  pub fn find_initializer(decls: &mut Vec<Decl>) -> &mut InitFnDecl {\n    let init = find_or_else_mut(decls, ClassInit::empty_initializer, |d| matches!(d.value, DeclF::InitFnDecl(_)));\n    if let DeclF::InitFnDecl(decl) = &mut init.value {\n      decl\n    } else {\n      panic!(\"Internal error in ClassInit::find_initializer\")\n    }\n  }\n\n  fn empty_initializer() -> Decl {\n    Decl::new(DeclF::InitFnDecl(InitFnDecl {\n      args: ArgList::empty(),\n      super_call: vec!(),\n      body: vec!(),\n    }), SourceOffset(0))\n  }\n\n  /// Find the _ready function defined on the current class. If no\n  /// _ready function is defined, then an empty one is created,\n  /// appended to the class, and returned.\n  pub fn find_ready(decls: &mut Vec<Decl>) -> &mut FnDecl {\n    let decl = find_or_else_mut(decls, ClassInit::empty_ready, |d| matches!(&d.value, DeclF::FnDecl(_, f) if f.name == READY_NAME));\n    if let DeclF::FnDecl(_, fdecl) = &mut decl.value {\n      fdecl\n    } else {\n      panic!(\"Internal error in ClassInit::find_ready\")\n    }\n  }\n\n  fn empty_ready() -> Decl {\n    Decl::new(DeclF::FnDecl(Static::NonStatic, FnDecl {\n      name: READY_NAME.to_string(),\n      args: ArgList::empty(),\n      body: vec!(),\n    }), SourceOffset(0))\n  }\n\n}\n"
  },
  {
    "path": "src/compile/body/class_scope.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Exposes the [`ClassScope`] trait and its various common\n//! implementations.\n\n// TODO Not a fan of all of the `dyn ClassScope` in this file. Can we\n// remove at least *some* of the indirection?\n\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::compile::symbol_table::local_var::LocalVar;\nuse crate::compile::names::lisp_to_gd;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::gdscript::expr::Expr;\nuse crate::pipeline::source::SourceOffset;\nuse super::super_proxy::SuperProxy;\n\n/// A class scope for situations where we're not inside a class. Any\n/// attempt to call a superclass method in this scope will fail.\n#[derive(Clone, Debug)]\npub struct OutsideOfClass;\n\n/// A simple wrapper around a [`ClassScope`] which delegates to the\n/// value it's borrowing.\npub struct ClassScopeMut<'a>(pub &'a mut dyn ClassScope);\n\n/// A class scope for situations where we're *directly* inside of a\n/// class, i.e. we're inside of a class and there's no closure\n/// strictly between the current position and the class declaration in\n/// the scope hierarchy.\n#[derive(Clone, Debug, Default)]\npub struct DirectClassScope {\n  super_proxies: Vec<SuperProxy>,\n}\n\n/// A class scope within a closure that is transitively contained\n/// inside a class. This maintains a mutable reference to a\n/// [`DirectClassScope`] for the purposes of accessing a common\n/// supermethod proxy list with other closures of the same class.\npub struct ClosedClassScope<'a>(pub &'a mut DirectClassScope);\n\n/// A `ClassScope` implementor keeps track of which class (if any)\n/// we're currently compiling inside. This is used to track what\n/// should be called when we invoke a `super` method.\npub trait ClassScope {\n\n  /// Compiles the necessary code to make a supermethod call to the\n  /// GDLisp name `super_name`, and then returns an expression which\n  /// will perform the call.\n  fn super_call(&mut self,\n                gen: &mut FreshNameGenerator,\n                self_binding: &LocalVar,\n                super_name: String,\n                args: Vec<Expr>,\n                pos: SourceOffset)\n                -> Result<Expr, GDError>;\n\n  /// Returns a new `ClassScope` which will handle supermethod calls\n  /// inside of a closure within the current scope.\n  fn closure_mut(&mut self) -> Box<dyn ClassScope + '_>;\n\n}\n\nimpl DirectClassScope {\n\n  /// A new, empty `DirectClassScope`. Equivalent to\n  /// `DirectClassScope::default()`.\n  pub fn new() -> DirectClassScope {\n    DirectClassScope::default()\n  }\n\n  /// Returns a vector of superclass proxy methods, in an unspecified\n  /// order.\n  pub fn into_proxies(self) -> Vec<SuperProxy> {\n    self.super_proxies\n  }\n\n  /// Adds a new supermethod proxy to the current scope.\n  pub fn add_proxy(&mut self, proxy: SuperProxy) {\n    self.super_proxies.push(proxy);\n  }\n\n}\n\nimpl ClassScope for OutsideOfClass {\n\n  fn super_call(&mut self,\n                _gen: &mut FreshNameGenerator,\n                _self_binding: &LocalVar,\n                super_name: String,\n                _args: Vec<Expr>,\n                pos: SourceOffset)\n                -> Result<Expr, GDError> {\n    // Always fail.\n    Err(GDError::new(GDErrorF::BadSuperCall(super_name), pos))\n  }\n\n  fn closure_mut(&mut self) -> Box<dyn ClassScope + '_> {\n    Box::new(ClassScopeMut(self))\n  }\n\n}\n\nimpl<'a> ClassScope for ClassScopeMut<'a> {\n\n  fn super_call(&mut self,\n                gen: &mut FreshNameGenerator,\n                self_binding: &LocalVar,\n                super_name: String,\n                args: Vec<Expr>,\n                pos: SourceOffset)\n                -> Result<Expr, GDError> {\n    // Delegate to inner\n    self.0.super_call(gen, self_binding, super_name, args, pos)\n  }\n\n  fn closure_mut(&mut self) -> Box<dyn ClassScope + '_> {\n    // *shrug* Just another delegator. Kind of silly but gets the job\n    // done.\n    Box::new(ClassScopeMut(self))\n  }\n\n}\n\nimpl ClassScope for DirectClassScope {\n\n  fn super_call(&mut self,\n                _gen: &mut FreshNameGenerator,\n                _self_binding: &LocalVar,\n                super_name: String,\n                args: Vec<Expr>,\n                pos: SourceOffset)\n                -> Result<Expr, GDError> {\n    Ok(Expr::super_call(&lisp_to_gd(&super_name), args, pos))\n  }\n\n  fn closure_mut(&mut self) -> Box<dyn ClassScope + '_> {\n    // Create a closure scope with access to self.\n    Box::new(ClosedClassScope(self))\n  }\n\n}\n\nimpl<'a> ClassScope for ClosedClassScope<'a> {\n\n  fn super_call(&mut self,\n                gen: &mut FreshNameGenerator,\n                self_binding: &LocalVar,\n                super_name: String,\n                args: Vec<Expr>,\n                pos: SourceOffset)\n                -> Result<Expr, GDError> {\n    let proxy = SuperProxy::generate(gen, super_name, args.len(), pos);\n    let method_name = proxy.name.clone();\n\n    self.0.add_proxy(proxy);\n\n    Ok(Expr::call(Some(self_binding.expr(pos)), &method_name, args, pos))\n  }\n\n  fn closure_mut(&mut self) -> Box<dyn ClassScope + '_> {\n    // Delegate to self; having two nested closures is no different\n    // than having one closure for these purposes.\n    Box::new(ClassScopeMut(self))\n  }\n\n}\n"
  },
  {
    "path": "src/compile/body/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helpers for working with bodies of code.\n\npub mod builder;\npub mod class_initializer;\npub mod class_scope;\npub mod synthetic_field;\npub mod super_proxy;\n"
  },
  {
    "path": "src/compile/body/super_proxy.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Exposes the [`SuperProxy`] type, for construction of instance\n//! methods that delegate to a superclass method.\n\nuse crate::pipeline::source::SourceOffset;\nuse crate::gdscript::decl::FnDecl;\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse crate::compile::names::lisp_to_gd;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::compile::special_form::lambda::simple_arg_names;\n\n/// A supermethod proxy is a method on the current class that, when\n/// invoked, simply delegates to a call of a given method on a\n/// superclass.\n#[derive(Default, Clone, Debug)]\npub struct SuperProxy {\n  /// The name of the proxy method.\n  pub name: String,\n  /// The name of the superclass method to delegate to.\n  pub super_name: String,\n  /// The total number of arguments to be passed to the superclass\n  /// method.\n  pub args: usize,\n  /// The position in the code where the construction of this proxy\n  /// became necessary.\n  pub pos: SourceOffset,\n}\n\nimpl SuperProxy {\n\n  /// The prefix used to generate names for superclass proxy methods.\n  pub const PROXY_NAME: &'static str = \"__gdlisp_super\";\n\n  /// Generates a superclass proxy method for the method with the\n  /// given (GDLisp) name and argument count. The provided name\n  /// generator is used to come up with a unique name for the proxy\n  /// method.\n  pub fn generate(gen: &mut FreshNameGenerator, super_name: String, args: usize, pos: SourceOffset) -> SuperProxy {\n    let name = gen.generate_with(SuperProxy::PROXY_NAME);\n    let super_name = lisp_to_gd(&super_name);\n    SuperProxy { name, super_name, args, pos }\n  }\n\n}\n\nimpl From<SuperProxy> for FnDecl {\n\n  fn from(proxy: SuperProxy) -> FnDecl {\n\n    let proxy_params = simple_arg_names(proxy.args);\n    let call_args: Vec<_> = proxy_params.all_args_iter().map(|name| Expr::var(name, proxy.pos)).collect();\n\n    let body = vec!(\n      Stmt::return_stmt(Expr::super_call(&proxy.super_name, call_args, proxy.pos), proxy.pos),\n    );\n    FnDecl {\n      name: proxy.name,\n      args: proxy_params,\n      body: body,\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/compile/body/synthetic_field.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`SyntheticField`] type, for generating proxy fields\n//! on GDScript classes.\n\nuse crate::gdscript::decl::{FnDecl, VarDecl, Setget};\nuse crate::gdscript::arglist::ArgList;\nuse crate::gdscript::stmt::Stmt;\n\n/// A synthetic field will be generated on the resulting class with\n/// the given getter and setter. Both parts of the synthetic field are\n/// optional.\n#[derive(Default, Clone, Debug)]\npub struct SyntheticField {\n  /// The name of the field.\n  pub name: String,\n  /// The name of the getter method for the field, if present.\n  pub getter: Option<String>,\n  /// The name of the setter method for the field, if present.\n  pub setter: Option<String>,\n}\n\n/// A getter method is a zero-argument GDScript function which will\n/// return the value of the proxy field.\n#[derive(Clone, Debug)]\npub struct Getter {\n  name: String,\n  body: Vec<Stmt>,\n}\n\n/// A setter method is a one-argument GDScript function which will set\n/// the value of the proxy field.\n#[derive(Clone, Debug)]\npub struct Setter {\n  name: String,\n  argument_name: String,\n  body: Vec<Stmt>,\n}\n\nimpl SyntheticField {\n\n  pub fn new() -> SyntheticField {\n    SyntheticField::default()\n  }\n\n  /// Converts the synthetic field into a variable declaration. The\n  /// resulting variable has no default value and no modifiers other\n  /// than the `setget` modifier.\n  pub fn into_field(self) -> VarDecl {\n    VarDecl::simple(self.name).setget(Setget {\n      setter: self.setter,\n      getter: self.getter,\n    })\n  }\n\n}\n\nimpl Getter {\n\n  /// Construct a getter from a method name and a method body.\n  pub fn new(name: String, body: Vec<Stmt>) -> Getter {\n    Getter { name, body }\n  }\n\n  /// By convention, the name of a GDLisp getter is produced using a\n  /// set prefix to distinguish it. Given a proxy field name, this\n  /// function returns the conventional getter name for that field.\n  pub fn method_name(field_name: &str) -> String {\n    format!(\"__gdlisp_get_{}\", field_name)\n  }\n\n}\n\nimpl Setter {\n\n  /// Construct a setter from a method name, an argument name, and a method body.\n  pub fn new(name: String, argument_name: String, body: Vec<Stmt>) -> Setter {\n    Setter { name, argument_name, body }\n  }\n\n  /// By convention, the name of a GDLisp setter is produced using a\n  /// set prefix to distinguish it. Given a proxy field name, this\n  /// function returns the conventional setter name for that field.\n  pub fn method_name(field_name: &str) -> String {\n    format!(\"__gdlisp_set_{}\", field_name)\n  }\n\n}\n\nimpl From<Getter> for FnDecl {\n\n  fn from(getter: Getter) -> FnDecl {\n    FnDecl {\n      name: getter.name,\n      args: ArgList::empty(),\n      body: getter.body,\n    }\n  }\n\n}\n\nimpl From<Setter> for FnDecl {\n\n  fn from(setter: Setter) -> FnDecl {\n    FnDecl {\n      name: setter.name,\n      args: ArgList::required(vec!(setter.argument_name)),\n      body: setter.body,\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/compile/constant.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Functionality for checking whether an expression is actually an\n//! allowable constant expression.\n//!\n//! For the moment, what's allowable as a constant expression is\n//! *extremely* conservative. Even things like if-statements where all\n//! arguments are constant are disallowed, as we have no way to\n//! compile that on the GDScript side. Long term, it would be nice to\n//! support things like that which are constant \"in spirit\" and work\n//! around GDScript limitations. But for now, we're being super\n//! strict.\n\nuse super::error::{GDError, GDErrorF};\nuse super::symbol_table::SymbolTable;\nuse super::symbol_table::local_var::{LocalVar, ValueHint};\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir::expr::{Expr as IRExpr, ExprF as IRExprF, BareName, CallTarget};\nuse crate::ir::decl::{ClassInnerDecl, ClassInnerDeclF, Decl, DeclF};\nuse crate::ir::literal::Literal;\nuse crate::ir::scope::decl::on_each_lambda_class;\n\nuse phf::phf_set;\n\npub const CONSTANT_GDSCRIPT_FUNCTIONS: phf::Set<&'static str> = phf_set! {\n  \"NodePath\", \"bool\", \"int\", \"float\", \"String\", \"str\", \"Rect2\", \"AABB\", \"RID\", \"Dictionary\",\n  \"Array\", \"PoolColorArray\", \"PoolByteArray\", \"PoolIntArray\", \"PoolRealArray\", \"PoolStringArray\",\n  \"PoolVector2Array\", \"PoolVector3Array\", \"Vector2\", \"Vector3\", \"Transform2D\", \"Plane\",\n  \"Quat\", \"Basis\", \"Transform\", \"Color\",\n};\n\npub fn validate_all_constant_scopes(decls: &[Decl], table: &SymbolTable) -> Result<(), GDError> {\n\n  // Check all top-level constant and enum declarations, and delegate for top-level classes\n  for decl in decls {\n    match &decl.value {\n      DeclF::ConstDecl(const_decl) => {\n        validate_const_expr(&const_decl.name, &const_decl.value, table)?;\n      }\n      DeclF::EnumDecl(enum_decl) => {\n        for (_, rhs) in &enum_decl.clauses {\n          if let Some(rhs) = rhs {\n            validate_const_expr(&enum_decl.name, rhs, table)?;\n          }\n        }\n      }\n      DeclF::ClassDecl(class_decl) => {\n        validate_constant_names_in_class(&class_decl.decls, table)?;\n      }\n      _ => {}\n    }\n  }\n\n  // Check all lambda classes\n  on_each_lambda_class(decls, |cls| {\n    validate_constant_names_in_class(&cls.decls, table)\n  })?;\n\n  Ok(())\n}\n\nfn validate_constant_names_in_class(inner_decls: &[ClassInnerDecl], table: &SymbolTable) -> Result<(), GDError> {\n  for decl in inner_decls {\n    match &decl.value {\n      ClassInnerDeclF::ClassConstDecl(const_decl) => {\n        validate_const_expr(&const_decl.name, &const_decl.value, table)?;\n      }\n      ClassInnerDeclF::ClassVarDecl(var_decl) => {\n        if let Some(export) = &var_decl.export {\n          for arg in &export.args {\n            // As a special exception, we allow any literal symbols\n            // appearing here, since they can reference things like\n            // `int` freely. (TODO Just generally make exports fit\n            // better with the rest of GDLisp)\n            if !(matches!(&arg.value, IRExprF::BareName(_))) {\n              validate_const_expr(&var_decl.name, arg, table)?;\n            }\n          }\n        }\n      }\n      _ => {}\n    }\n  }\n  Ok(())\n}\n\npub fn is_const_expr(expr: &IRExpr, table: &SymbolTable) -> bool {\n  validate_const_expr(\"UNUSED NAME IN is_const_expr\", expr, table).is_ok()\n}\n\npub fn validate_const_expr(name: &str, expr: &IRExpr, table: &SymbolTable) -> Result<(), GDError> {\n  match &expr.value {\n    IRExprF::BareName(var) => {\n      match var {\n        BareName::Plain(var_name) => {\n          validate_const_var_name(name, var_name, table, expr.pos)\n        }\n        BareName::Atomic(_) => {\n          // AtomicName is explicitly opting out of GDLisp's safety\n          // checks, so we'll let it through and just trust the\n          // programmer.\n          Ok(())\n        }\n      }\n    }\n    IRExprF::Literal(lit) => {\n      if let Literal::Symbol(_) = lit {\n        non_constant_error(name, expr.pos)\n      } else {\n        Ok(())\n      }\n    }\n    IRExprF::Progn(body) => {\n      match &body[..] {\n        [] => {\n          // Empty progn, compiles to null.\n          Ok(())\n        }\n        [single_term] => {\n          // Single-term progn, compiles to the inside.\n          validate_const_expr(name, single_term, table)\n        }\n        _ => {\n          // Multiple terms will require multiple statements, forbid\n          // it.\n          non_constant_error(name, expr.pos)\n        }\n      }\n    }\n    IRExprF::CondStmt(_) => {\n      // Note: We always compile CondStmt to full-form multi-line \"if\"\n      // statements first. Sometimes, the optimizer might simplify\n      // them down to ternary expressions, but that's an optimization\n      // detail. As far as we're concerned, it's a statement and is\n      // non-const.\n      //\n      // If the architecture changes and we start compiling to ternary\n      // directly, then this constraint will loosen.\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::WhileStmt(_, _) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::ForStmt(_, _, _) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Call(object, function_name, args) => {\n      validate_const_call(name, object, function_name, args, table, expr.pos)\n    }\n    IRExprF::Let(_, _) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::FunctionLet(_, _, _) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Lambda(_, _) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::FuncRef(_) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Assign(_, _) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Quote(_) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::FieldAccess(lhs, _name) => {\n      if is_name_of_enum(lhs, table) {\n        Ok(())\n      } else {\n        non_constant_error(name, expr.pos)\n      }\n    }\n    IRExprF::LambdaClass(_) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Yield(_) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Assert(_, _) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Return(_) => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Break => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Continue => {\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::SpecialRef(_) => {\n      Ok(())\n    }\n    IRExprF::ContextualFilename(_) => {\n      // This resolves completely at compile-time and, as far as\n      // GDScript is concerned, is just a constant string.\n      Ok(())\n    }\n    IRExprF::Split(_, _) => {\n      // Split specifically requires a temporary variable in order to\n      // work, which we can't do in a constant expression\n      non_constant_error(name, expr.pos)\n    }\n    IRExprF::Preload(_) => {\n      Ok(())\n    }\n  }\n}\n\nfn validate_const_var_name(name: &str, var_name: &str, table: &SymbolTable, pos: SourceOffset) -> Result<(), GDError> {\n  let var = table.get_var(var_name).ok_or_else(|| non_constant_error::<()>(name, pos).unwrap_err())?;\n  if var.is_valid_const_expr() {\n    Ok(())\n  } else {\n    non_constant_error(name, pos)\n  }\n\n}\n\nfn validate_const_call(name: &str, object: &CallTarget, function_name: &str, args: &[IRExpr], table: &SymbolTable, pos: SourceOffset) -> Result<(), GDError> {\n  match object {\n    CallTarget::Super | CallTarget::Object(_) => {\n      // Always disallowed.\n      return non_constant_error(name, pos);\n    }\n    CallTarget::Atomic => {\n      // `Atomic` is explicitly opting out of GDLisp's safety checks,\n      // so we'll let the name through and just trust the programmer.\n      // We'll still check the arguments though.\n    }\n    CallTarget::Scoped => {\n      // Check the name to make sure it's a constant enough function.\n      validate_scoped_const_call(name, function_name, args.len(), table, pos)?;\n    }\n  }\n  for arg in args {\n    validate_const_expr(name, arg, table)?;\n  }\n  Ok(())\n}\n\nfn validate_scoped_const_call(name: &str, function_name: &str, arg_count: usize, table: &SymbolTable, pos: SourceOffset) -> Result<(), GDError> {\n  let (function, magic) = table.get_fn(function_name).ok_or_else(|| non_constant_error::<()>(name, pos).unwrap_err())?;\n  if magic.is_default() {\n    // If the call magic passes through to the implementation, then we\n    // need to look at the function and see if it's sufficiently const\n    // for GDScript's tastes.\n    if function.can_be_called_as_const() {\n      Ok(())\n    } else {\n      non_constant_error(name, pos)\n    }\n  } else {\n    // If there's call magic, use that to determine const-ness.\n    if magic.can_be_called_as_const(arg_count) {\n      Ok(())\n    } else {\n      non_constant_error(name, pos)\n    }\n  }\n}\n\nfn is_name_of_enum(lhs: &IRExpr, table: &SymbolTable) -> bool {\n  if let Some(lhs) = lhs.as_plain_name() {\n    if let Some(LocalVar { value_hint: Some(ValueHint::Enum(_)), .. }) = table.get_var(lhs) {\n      // Note: We don't care if the name we're referencing on the enum\n      // is correct or not here. If we're subscripting an enum, then\n      // it's fine. If the name is bad, then the compiler will catch\n      // it in the next phase and we'll throw a much more accurate\n      // `NoSuchEnumValue` (rather than `NotConstantEnough`).\n      return true;\n    }\n  }\n  false\n}\n\nfn non_constant_error<T>(name: &str, pos: SourceOffset) -> Result<T, GDError> {\n  Err(GDError::new(GDErrorF::NotConstantEnough(name.to_owned()), pos))\n}\n"
  },
  {
    "path": "src/compile/error.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Errors that can occur as a result of GDLisp code.\n\nuse super::args::{Expecting, ExpectedShape};\nuse crate::sxp;\nuse crate::sxp::ast::AST;\nuse crate::ir::expr::{Expr as IRExpr};\nuse crate::ir::arglist::error::{ArgListParseError, ArgListParseErrorF};\nuse crate::ir::identifier::{Id, Namespace, ClassNamespace};\nuse crate::ir::decl::DuplicateMainClassError;\nuse crate::ir::import::{ImportDeclParseError, ImportNameResolutionError};\nuse crate::ir::modifier::{ParseError as ModifierParseError, ParseErrorF as ModifierParseErrorF};\nuse crate::ir::scope::error::ScopeError;\nuse crate::ir::loops::error::LoopPrimitiveError;\nuse crate::ir::depends::DependencyError;\nuse crate::compile::symbol_table::local_var::VarNameIntoExtendsError;\nuse crate::runner::macro_server::response;\nuse crate::pipeline::source::{SourceOffset, Sourced};\n\nuse std::fmt;\nuse std::error::Error;\n\n/// This type captures all errors that can occur during compilation of\n/// GDLisp code.\n///\n/// This type does *not* include I/O errors, which are considered to\n/// be outside of GDLisp's purview. This type also excludes parsing\n/// errors, for which LALRPOP provides its own error types. See\n/// [`crate::pipeline::error`] for an error type which includes this\n/// one and is more general.\n#[derive(PartialEq, Eq, Debug)]\npub enum GDErrorF {\n  /// A `DottedListError` indicates that a dotted list, such as `(1 2\n  /// . 3)`, was encountered where a proper list, such as `(1 2 3)`,\n  /// was expecting. `DottedListError` is almost always constructed\n  /// via a [`From`] conversion from\n  /// [`sxp::dotted::TryFromDottedExprError`].\n  DottedListError,\n  /// An error during parsing of an argument list. The sole argument\n  /// to this constructor stores further information about what went\n  /// wrong.\n  ArgListParseError(ArgListParseError),\n  /// An error during parsing of an import declaration. The sole\n  /// argument to this constructor stores further information about\n  /// what went wrong.\n  ImportDeclParseError(ImportDeclParseError),\n  /// Only a handful of `AST` shapes are valid callable objects, most\n  /// namely symbols but also certain shapes of proper lists. If any\n  /// non-callable `AST` is ever put into a position where it would be\n  /// the head of a function call, then a `CannotCall` error shall be\n  /// issued, with that `AST` as argument.\n  CannotCall(AST),\n  /// A function, macro, special form, or any other callable object\n  /// was called with a different number of arguments than was\n  /// expected.\n  ///\n  /// This error includes the name of the function, the expected\n  /// (minimum and maximum) number of arguments, and the actual number\n  /// of arguments provided.\n  WrongNumberArgs(String, Expecting, usize),\n  /// A function, macro, special form, or any other callable object\n  /// was expecting a value of a particular type or shape but received\n  /// something incompatible.\n  ///\n  /// This error includes the name of the callable object, the faulty\n  /// argument that was passed, and a description of the sort of\n  /// argument that was expected.\n  InvalidArg(String, AST, ExpectedShape),\n  /// A name was referenced in the variable namespace, but no such\n  /// name was found in the symbol table.\n  NoSuchVar(String),\n  /// A name was referenced in the function namespace, but no such\n  /// name was found in the symbol table.\n  NoSuchFn(String),\n  /// An enumeration constant was subscripted, but the subscripted\n  /// name does not exist in the enumeration.\n  ///\n  /// Note that this is a fairly basic check and can be circumvented\n  /// with relative ease. This error is designed to catch the most\n  /// obvious of typos and is not even close to a full static\n  /// typechecker for enumeration values.\n  NoSuchEnumValue(String, String),\n  /// A call magic entity was referenced, but no entity with that name\n  /// exists.\n  ///\n  /// In general, users of GDLisp should not be dealing directly with\n  /// call magic, so if you see this error in real code, please\n  /// consider reporting a bug to the compiler's issue tracker.\n  NoSuchMagic(String),\n  /// An `AST` appeared in declaration context, but its head could not\n  /// be interpreted as a known declaration type.\n  ///\n  /// This error is only produced after any macro expansion is\n  /// attempted, so if you were expecting to call a macro, then\n  /// consider double-checking the name of the macro.\n  UnknownDecl(AST),\n  /// An `unquote` expression appeared outside of a `quasiquote`.\n  UnquoteOutsideQuasiquote,\n  /// An `unquote-spliced` expression appeared outside of a\n  /// `quasiquote`.\n  UnquoteSplicedOutsideQuasiquote,\n  /// An `unquote-spliced` expression appeared within a `quasiquote`\n  /// but in a context where splicing does not make sense, such as in\n  /// the cdr of a dotted list or as a part of a dictionary literal.\n  BadUnquoteSpliced(AST),\n  /// The current preload resolver or pipeline failed to resolve the\n  /// file with the given path.\n  NoSuchFile(String),\n  /// An import declaration includes an explicit named import whose\n  /// namespace is ambiguous.\n  ///\n  /// Normally, when importing particular names in a `use` statement,\n  /// the namespace will be inferred. If either a function or a value\n  /// exists with that name, then the matching namespace will be used\n  /// in either case. However, if *both* a function and a value exist\n  /// with the same name in the target module, then the import is\n  /// ambiguous. Such imports can be disambiguated by specifying the\n  /// namespace explicitly as follows.\n  ///\n  /// ```text\n  /// (use \"res://Example.lisp\" ((my-example-value value) (my-example-function function)))\n  /// (use \"res://Example.lisp\" ((my-example-value value as aliased-value) (my-example-function function as aliased-function)))\n  /// ```\n  AmbiguousNamespace(String),\n  /// The constant variable with the given name was initialized with a\n  /// value that does not meet the GDLisp requirements for a constant.\n  ///\n  /// Note that GDLisp is fairly strict in what is considers a valid\n  /// constant. In particular, if your intention is to import an\n  /// external file, GDScript users would declare a constant whose\n  /// value is initialized to the result of a `preload` call, but in\n  /// GDLisp, we use the special `use` declaration for this. Generally\n  /// speaking, constants should be used for simple atomic values,\n  /// such as numbers and strings, not for aliasing complex data from\n  /// another source.\n  NotConstantEnough(String),\n  /// An attempt was made to assign to a non-assignable variable, such\n  /// as a constant or the special `self` value in a class.\n  CannotAssignTo(String),\n  /// An attempt was made to extend a value which cannot be extended\n  /// as a class.\n  CannotExtend(VarNameIntoExtendsError),\n  /// An `export` declaration was used on a variable in an inner\n  /// class.\n  ///\n  /// `export` declarations only make sense on the main class of a\n  /// file, as they are used for interfacing with existing Godot\n  /// tooling which assumes the class is the main class.\n  ExportOnInnerClassVar(String),\n  /// This error is issued if the type of import declaration was\n  /// incorrect for the given file. Namely, explicit and open import\n  /// lists are only allowed for GDLisp source files, not for GDScript\n  /// source files or other resource types.\n  InvalidImportOnResource(String),\n  /// An error occurred in communication with the Godot macro server.\n  GodotServerError(response::Failure),\n  /// A constructor `_init` function was labeled as static.\n  /// Constructor functions can never be static.\n  StaticConstructor,\n  /// A static method, or other non-instance entity such as a\n  /// constant, was declared on a lambda class (i.e. a class\n  /// constructed in an expression via the `new` special form).\n  ///\n  /// There's no philosophical reason a lambda class cannot have\n  /// static methods or constants, but due to various awkward\n  /// implementation issues, together with the questionable utility of\n  /// such a feature, the feature was outright banned. See Issue #30\n  /// for more background.\n  StaticOnLambdaClass(String),\n  /// An error during parsing of a modifier or modifier list. The sole\n  /// argument to this constructor stores further information about\n  /// what went wrong.\n  ModifierParseError(ModifierParseError),\n  /// A macro call was encountered during a minimalist compilation.\n  ///\n  /// Generally, users should never be performing minimalist\n  /// compilations on their own files, as this is an advanced feature\n  /// intended for bootstrapping the standard library. As such, if you\n  /// encounter this error, consider reporting a bug to GDLisp.\n  MacroInMinimalistError(String),\n  /// A macro call was encountered before the macro was defined.\n  ///\n  /// While GDLisp functions can be, generally speaking, called from\n  /// before they're defined (subject to some constraints), macros\n  /// are, for deeply technical reasons, only available after the\n  /// point in the file where they've been defined. This error will\n  /// arise if a macro call is performed at a point in the file above\n  /// the point where the `defmacro` is actually written.\n  MacroBeforeDefinitionError(String),\n  /// Two or more main classes were declared in the same file.\n  ///\n  /// A file can have at most one main class. On the GDScript side,\n  /// main classes will be compiled into the overarching class\n  /// representing the file, and all other classes will get compiled\n  /// into inner classes within that main class. If there's no main\n  /// class, then a minimal stub inheriting from `Node` will be filled\n  /// in in GDScript.\n  DuplicateMainClass,\n  /// A `sys/context-filename` call failed to resolve (using the\n  /// current preload resolver) the current filename.\n  ///\n  /// If you ever encounter this error, please report it as a bug.\n  ContextualFilenameUnresolved,\n  /// Two or more constructors were defined in the same class or\n  /// lambda class.\n  DuplicateConstructor,\n  /// The same name was declared twice in the same namespace and scope.\n  DuplicateName(ClassNamespace, String),\n  /// A getter was declared with an invalid argument list or modifier.\n  BadGetterArguments(String),\n  /// A setter was declared with an invalid argument list or modifier.\n  BadSetterArguments(String),\n  /// A field and either a getter or setter with the same name were\n  /// declared in the same scope.\n  FieldAccessorConflict(String),\n  /// A `super` call was attempted in a situation where it makes no\n  /// sense (such as outside of class scope).\n  BadSuperCall(String),\n  /// The \"extends\" clause for a `defclass` was incorrectly formatted.\n  /// A `defclass` extends clause should be either a singleton list or\n  /// an empty list.\n  BadExtendsClause,\n  /// A clause of `defenum` was incorrectly formatted. A `defenum`\n  /// clause shall be a list of length either 1 or 2, indicating\n  /// either a name or a name together with a value.\n  BadEnumClause,\n  /// The type of a `sys/declare` was invalid. The type of a\n  /// `sys/declare` should be 'value', 'superglobal', 'function', or\n  /// 'superfunction'.\n  BadSysDeclare(String),\n  /// An attempt was made to import a name from another file, but that\n  /// name was not (publicly) declared in that file.\n  UnknownImportedName(Id),\n  /// The same name was declared twice in the same namespace and scope.\n  DuplicateNameConflictInMainClass(ClassNamespace, String),\n  /// An explicit `preload` call was made with a bad path.\n  BadPreloadArgument(String),\n  /// The directive of a `sys/bootstrap` was invalid.\n  BadBootstrappingDirective(String),\n  /// An error in the placement of a loop primitive, i.e. `break` or\n  /// `continue`.\n  LoopPrimitiveError(LoopPrimitiveError),\n  /// An expression was found at the top-level of a file.\n  ///\n  /// In principle, we would like to support expressions at the\n  /// top-level, but as of right now there does not seem to be a way\n  /// to run code when a file in loaded in Godot. So we forbid the\n  /// feature with a specific error message right now but leave all of\n  /// the infrastructure in place so that if such a mechanism is\n  /// found, or is added to Godot in a future version, we can allow it\n  /// easily.\n  ExprAtTopLevel(IRExpr),\n  /// The minimalist flag was set during REPL evaluation.\n  ///\n  /// The minimalist flag is only intended for use in stdlib. It is at\n  /// least sensible in other files, but in the REPL it's absolutely\n  /// meaningless.\n  MinimalistAtRepl,\n  /// A file load was attempted in a cyclic order.\n  CyclicImport(String),\n  /// A constructor `_init` function was labeled as `&sys/nullargs`.\n  NullargsConstructor,\n}\n\n/// Variant of [`GDErrorF`] with source offset information. See\n/// [`Sourced`].\n#[derive(PartialEq, Eq, Debug)]\npub struct GDError {\n  pub value: GDErrorF,\n  pub pos: SourceOffset,\n}\n\nconst INTERNAL_ERROR_NOTE: &str = \"Note: Unless you're doing something really strange, you should probably report this as a compiler bug\";\n\nimpl GDError {\n\n  /// Constructs a new error from an `GDErrorF` and a source offset.\n  pub fn new(value: GDErrorF, pos: SourceOffset) -> GDError {\n    GDError { value, pos }\n  }\n\n  /// Constructs a new error from a value compatible with `GDErrorF` and\n  /// a source offset.\n  pub fn from_value<T>(value: T, pos: SourceOffset) -> GDError\n  where GDErrorF: From<T> {\n    GDError::new(GDErrorF::from(value), pos)\n  }\n\n  /// Gets the unique identifier of the error shape. See\n  /// [`GDErrorF::error_number`].\n  pub fn error_number(&self) -> u32 {\n    self.value.error_number()\n  }\n\n  /// Returns whether the error is internal to GDLisp. See\n  /// [`GDErrorF::is_internal`].\n  pub fn is_internal(&self) -> bool {\n    self.value.is_internal()\n  }\n\n}\n\nimpl GDErrorF {\n\n  /// Produces a unique numerical value representing this type of\n  /// error, intended to make identifying the error easier for the\n  /// user.\n  pub fn error_number(&self) -> u32 {\n    match self {\n      // Note: Error code 0 is not used.\n      GDErrorF::DottedListError => 1,\n      GDErrorF::ArgListParseError(err) => {\n        match &err.value {\n          ArgListParseErrorF::InvalidArgument(_) => 2,\n          ArgListParseErrorF::UnknownDirective(_) => 3,\n          ArgListParseErrorF::DirectiveOutOfOrder(_) => 4,\n          ArgListParseErrorF::SimpleArgListExpected => 5,\n          ArgListParseErrorF::BadSelf(_) => 53,\n          ArgListParseErrorF::SimpleArgExpected => 54,\n          ArgListParseErrorF::ConstructorArgListExpected => 55,\n        }\n      }\n      GDErrorF::ImportDeclParseError(err) => {\n        match err {\n          ImportDeclParseError::NoFilename => 6,\n          ImportDeclParseError::BadFilename(_) => 7,\n          ImportDeclParseError::InvalidPath(_) => 8,\n          ImportDeclParseError::MalformedFunctionImport(_) => 9,\n          ImportDeclParseError::InvalidEnding(_) => 10,\n        }\n      }\n      GDErrorF::CannotCall(_) => 11,\n      GDErrorF::WrongNumberArgs(_, _, _) => 12,\n      GDErrorF::InvalidArg(_, _, _) => 13,\n      GDErrorF::NoSuchVar(_) => 14,\n      GDErrorF::NoSuchFn(_) => 15,\n      GDErrorF::NoSuchEnumValue(_, _) => 16,\n      GDErrorF::NoSuchMagic(_) => 17,\n      GDErrorF::UnknownDecl(_) => 18,\n      // NOTE: 19 belongs to the removed InvalidDecl, which has been\n      // split into several different error types: InvalidArg,\n      // BadExtendsClause, BadEnumClause, and BadSysDeclare.\n      GDErrorF::UnquoteOutsideQuasiquote => 20,\n      GDErrorF::UnquoteSplicedOutsideQuasiquote => 21,\n      GDErrorF::BadUnquoteSpliced(_) => 22,\n      GDErrorF::NoSuchFile(_) => 23,\n      GDErrorF::AmbiguousNamespace(_) => 24,\n      GDErrorF::NotConstantEnough(_) => 25,\n      GDErrorF::CannotAssignTo(_) => 26,\n      GDErrorF::CannotExtend(_) => 27,\n      GDErrorF::ExportOnInnerClassVar(_) => 28,\n      // NOTE: 29 belongs to the removed ResourceDoesNotExist, which\n      // has been replaced by NoSuchFile and InvalidImportOnResource.\n      GDErrorF::InvalidImportOnResource(_) => 30,\n      GDErrorF::GodotServerError(_) => 31,\n      GDErrorF::StaticConstructor => 32,\n      GDErrorF::StaticOnLambdaClass(_) => 33,\n      GDErrorF::ModifierParseError(err) => {\n        match &err.value {\n          ModifierParseErrorF::UniquenessError(_) => 34,\n          ModifierParseErrorF::Expecting(_, _) => 35,\n          ModifierParseErrorF::ExhaustedAlternatives => 36,\n        }\n      }\n      GDErrorF::MacroInMinimalistError(_) => 37,\n      GDErrorF::MacroBeforeDefinitionError(_) => 38,\n      GDErrorF::DuplicateMainClass => 39,\n      GDErrorF::ContextualFilenameUnresolved => 40,\n      GDErrorF::DuplicateConstructor => 41,\n      GDErrorF::DuplicateName(_, _) => 42,\n      GDErrorF::BadGetterArguments(_) => 43,\n      GDErrorF::BadSetterArguments(_) => 44,\n      GDErrorF::FieldAccessorConflict(_) => 45,\n      GDErrorF::BadSuperCall(_) => 46,\n      GDErrorF::BadExtendsClause => 47,\n      GDErrorF::BadEnumClause => 48,\n      GDErrorF::BadSysDeclare(_) => 49,\n      GDErrorF::UnknownImportedName(_) => 50,\n      GDErrorF::DuplicateNameConflictInMainClass(_, _) => 51,\n      GDErrorF::BadPreloadArgument(_) => 52,\n      // NOTE: 53 is ArgListParseErrorF::BadSelf above.\n      //\n      // NOTE: 54 is ArgListParseErrorF::SimpleArgExpected above.\n      //\n      // NOTE: 55 is ArgListParseErrorF::ConstructorArgListExpected above.\n      GDErrorF::BadBootstrappingDirective(_) => 56,\n      GDErrorF::LoopPrimitiveError(_) => 57,\n      GDErrorF::ExprAtTopLevel(_) => 58,\n      GDErrorF::MinimalistAtRepl => 59,\n      GDErrorF::CyclicImport(_) => 60,\n      GDErrorF::NullargsConstructor => 61,\n    }\n  }\n\n  /// Returns whether or not the error is an internal GDLisp error.\n  /// Internal GDLisp errors are those that the compiler emits which\n  /// should generally never be seen by users. If a user encounters\n  /// such an error, it is probably a bug in the GDLisp compiler, and\n  /// such errors are displayed with a disclaimer indicating as much.\n  pub fn is_internal(&self) -> bool {\n    #[allow(clippy::match_like_matches_macro)] // Suggested alternative is quite lengthy on one line\n    match self {\n      GDErrorF::NoSuchMagic(_) => true,\n      GDErrorF::MacroInMinimalistError(_) => true,\n      GDErrorF::ContextualFilenameUnresolved => true,\n      GDErrorF::BadSysDeclare(_) => true,\n      GDErrorF::BadBootstrappingDirective(_) => true,\n      GDErrorF::MinimalistAtRepl => true,\n      GDErrorF::NullargsConstructor => true,\n      _ => false,\n    }\n  }\n\n}\n\nimpl fmt::Display for GDErrorF {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"Error {:04}: \", self.error_number())?;\n    match self {\n      GDErrorF::DottedListError => {\n        write!(f, \"Unexpected dotted list\")?;\n      }\n      GDErrorF::ArgListParseError(err) => {\n        write!(f, \"Error parsing argument list: {}\", err)?;\n      }\n      GDErrorF::ImportDeclParseError(err) => {\n        write!(f, \"Error parsing import declaration: {}\", err)?;\n      }\n      GDErrorF::CannotCall(ast) => {\n        write!(f, \"Cannot make function call on expression {}\", ast)?;\n      }\n      GDErrorF::WrongNumberArgs(name, expected, actual) => {\n        write!(f, \"Wrong number of arguments to call {}: expected {}, got {}\", name, expected, actual)?;\n      }\n      GDErrorF::InvalidArg(name, provided, expected) => {\n        write!(f, \"Invalid argument to {}, given {}, expecting {}\", name, provided, expected)?;\n      }\n      GDErrorF::NoSuchVar(name) => {\n        write!(f, \"No such variable {}\", name)?;\n      }\n      GDErrorF::NoSuchFn(name) => {\n        write!(f, \"No such function {}\", name)?;\n      }\n      GDErrorF::NoSuchEnumValue(name, subname) => {\n        write!(f, \"No such enum value {}:{}\", name, subname)?;\n      }\n      GDErrorF::NoSuchMagic(name) => {\n        write!(f, \"No such call magic {}\", name)?;\n      }\n      GDErrorF::UnknownDecl(ast) => {\n        write!(f, \"Unknown declaration {}\", ast)?;\n      }\n      GDErrorF::UnquoteOutsideQuasiquote => {\n        write!(f, \"Unquote (,) can only be used inside quasiquote (`)\")?;\n      }\n      GDErrorF::UnquoteSplicedOutsideQuasiquote => {\n        write!(f, \"Spliced unquote (,.) can only be used inside quasiquote (`)\")?;\n      }\n      GDErrorF::BadUnquoteSpliced(ast) => {\n        write!(f, \"Spliced unquote (,.) does not make sense in this context: {}\", ast)?;\n      }\n      GDErrorF::NoSuchFile(p) => {\n        write!(f, \"Cannot locate file {}\", p)?;\n      }\n      GDErrorF::AmbiguousNamespace(s) => {\n        write!(f, \"Ambiguous namespace when importing {}\", s)?;\n      }\n      GDErrorF::NotConstantEnough(s) => {\n        write!(f, \"Expression for constant declaration {} is not constant enough\", s)?;\n      }\n      GDErrorF::CannotAssignTo(s) => {\n        write!(f, \"Cannot assign to immutable variable {}\", s)?;\n      }\n      GDErrorF::CannotExtend(err) => {\n        write!(f, \"{}\", err)?;\n      }\n      GDErrorF::ExportOnInnerClassVar(v) => {\n        write!(f, \"Export declarations can only be used on a file's main class, but one was found on {}\", v)?;\n      }\n      GDErrorF::InvalidImportOnResource(s) => {\n        write!(f, \"Cannot use restricted or open import lists on resource import at {}\", s)?;\n      }\n      GDErrorF::GodotServerError(err) => {\n        write!(f, \"Error during Godot server task execution (error code {}): {}\", err.error_code, err.error_string)?;\n      }\n      GDErrorF::StaticConstructor => {\n        write!(f, \"Class constructors cannot be static\")?;\n      }\n      GDErrorF::StaticOnLambdaClass(s) => {\n        write!(f, \"Static name {} is not allowed on anonymous class instance\", s)?;\n      }\n      GDErrorF::ModifierParseError(m) => {\n        write!(f, \"Modifier error: {}\", m)?;\n      }\n      GDErrorF::MacroInMinimalistError(m) => {\n        write!(f, \"Attempt to expand macro {} in minimalist file\", m)?;\n      }\n      GDErrorF::MacroBeforeDefinitionError(m) => {\n        write!(f, \"Attempt to use macro {} before definition was available\", m)?;\n      }\n      GDErrorF::DuplicateMainClass => {\n        write!(f, \"File has two main classes\")?; // TODO Would be nice to have the source offset of the *original* main class here as well.\n      }\n      GDErrorF::ContextualFilenameUnresolved => {\n        write!(f, \"Could not resolve contextual filename of current file\")?;\n      }\n      GDErrorF::DuplicateConstructor => {\n        write!(f, \"Class has two constructors\")?; // TODO Would be nice to have the source offset of the *original* constructor here as well.\n      }\n      GDErrorF::DuplicateName(ns, name) => {\n        write!(f, \"The {} '{}' was already declared in this scope\", ns.name(), name)?; // TODO Would be nice to have the source offset of the *original* name here as well.\n      }\n      GDErrorF::BadGetterArguments(field_name) => {\n        write!(f, \"The getter '{}' has a bad signature; getters must be 0-ary non-static functions\", field_name)?;\n      }\n      GDErrorF::BadSetterArguments(field_name) => {\n        write!(f, \"The setter '{}' has a bad signature; getters must be 1-ary non-static functions\", field_name)?;\n      }\n      GDErrorF::FieldAccessorConflict(field_name) => {\n        write!(f, \"The value '{}' was declared as both an instance variable and either a getter or a setter\", field_name)?;\n      }\n      GDErrorF::BadSuperCall(super_method) => {\n        write!(f, \"Cannot call superclass method '{}'; there is no superclass in the current scope\", super_method)?;\n      }\n      GDErrorF::BadExtendsClause => {\n        write!(f, \"Bad 'extends' clause; a class 'extends' clause should either be the empty list or a singleton list\")?;\n      }\n      GDErrorF::BadEnumClause => {\n        write!(f, \"Bad 'defenum' clause; expected a list of one or two elements\")?;\n      }\n      GDErrorF::BadSysDeclare(v) => {\n        write!(f, \"Bad 'sys/declare' type; expected 'value', 'superglobal', 'function', or 'superfunction', got {}\", v)?;\n      }\n      GDErrorF::UnknownImportedName(id) => {\n        write!(f, \"Unknown {} name '{}' in import\", id.namespace.name(), &id.name)?;\n      }\n      GDErrorF::DuplicateNameConflictInMainClass(ns, name) => {\n        write!(f, \"The {} '{}' was declared at the top-level and is also declared in the main class\", ns.name(), name)?; // TODO Would be nice to have the source offset of the *original* name here as well.\n      }\n      GDErrorF::BadPreloadArgument(name) => {\n        write!(f, \"Argument to 'preload' must be a valid resource path, got '{}'\", name)?;\n      }\n      GDErrorF::BadBootstrappingDirective(name) => {\n        write!(f, \"Bad directive to sys/bootstrap, got '{}'\", name)?;\n      }\n      GDErrorF::LoopPrimitiveError(err) => {\n        write!(f, \"{}\", err)?;\n      }\n      GDErrorF::ExprAtTopLevel(_) => {\n        write!(f, \"Expressions at the top-level of a file are not allowed\")?;\n      }\n      GDErrorF::MinimalistAtRepl => {\n        write!(f, \"Minimalist flag makes no sense at the REPL\")?;\n      }\n      GDErrorF::CyclicImport(filename) => {\n        write!(f, \"Attempted to load '{}' while it was being loaded (cyclic import)\", filename)?;\n      }\n      GDErrorF::NullargsConstructor => {\n        write!(f, \"Class constructors cannot be marked sys/nullargs\")?;\n      }\n    }\n    if self.is_internal() {\n      write!(f, \" ({})\", INTERNAL_ERROR_NOTE)?;\n    }\n    Ok(())\n  }\n}\n\nimpl fmt::Display for GDError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"{}\", &self.value)\n  }\n}\n\nimpl Error for GDError {\n\n  fn source(&self) -> Option<&(dyn Error + 'static)> {\n    match &self.value {\n      GDErrorF::ArgListParseError(err) => {\n        Some(err)\n      }\n      GDErrorF::ImportDeclParseError(err) => {\n        Some(err)\n      }\n      GDErrorF::ModifierParseError(err) => {\n        Some(err)\n      }\n      GDErrorF::LoopPrimitiveError(err) => {\n        Some(err)\n      }\n      _ => {\n        None\n      }\n    }\n  }\n\n}\n\nimpl Sourced for GDError {\n  type Item = GDErrorF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &GDErrorF {\n    &self.value\n  }\n\n}\n\nimpl From<sxp::dotted::TryFromDottedExprError> for GDErrorF {\n  fn from(_: sxp::dotted::TryFromDottedExprError) -> GDErrorF {\n    GDErrorF::DottedListError\n  }\n}\n\nimpl From<sxp::dotted::TryFromDottedExprError> for GDError {\n  fn from(err: sxp::dotted::TryFromDottedExprError) -> GDError {\n    GDError::new(GDErrorF::DottedListError, err.pos)\n  }\n}\n\nimpl From<ArgListParseError> for GDErrorF {\n  fn from(err: ArgListParseError) -> GDErrorF {\n    GDErrorF::ArgListParseError(err)\n  }\n}\n\nimpl From<ArgListParseError> for GDError {\n  fn from(err: ArgListParseError) -> GDError {\n    let pos = err.pos;\n    GDError::new(GDErrorF::from(err), pos)\n  }\n}\n\nimpl From<ImportDeclParseError> for GDErrorF {\n  fn from(err: ImportDeclParseError) -> GDErrorF {\n    GDErrorF::ImportDeclParseError(err)\n  }\n}\n\nimpl From<ImportNameResolutionError> for GDErrorF {\n  fn from(err: ImportNameResolutionError) -> GDErrorF {\n    match err {\n      ImportNameResolutionError::UnknownName(id) => {\n        GDErrorF::UnknownImportedName(id)\n      }\n      ImportNameResolutionError::AmbiguousNamespace(s) => {\n        GDErrorF::AmbiguousNamespace(s)\n      }\n    }\n  }\n}\n\nimpl From<VarNameIntoExtendsError> for GDErrorF {\n  fn from(err: VarNameIntoExtendsError) -> GDErrorF {\n    GDErrorF::CannotExtend(err)\n  }\n}\n\nimpl From<response::Failure> for GDErrorF {\n  fn from(err: response::Failure) -> GDErrorF {\n    GDErrorF::GodotServerError(err)\n  }\n}\n\nimpl From<ModifierParseError> for GDErrorF {\n  fn from(err: ModifierParseError) -> GDErrorF {\n    GDErrorF::ModifierParseError(err)\n  }\n}\n\nimpl From<ModifierParseError> for GDError {\n  fn from(err: ModifierParseError) -> GDError {\n    let pos = err.pos;\n    GDError::new(GDErrorF::from(err), pos)\n  }\n}\n\nimpl From<DuplicateMainClassError> for GDError {\n  fn from(err: DuplicateMainClassError) -> GDError {\n    GDError::new(GDErrorF::DuplicateMainClass, err.0)\n  }\n}\n\nimpl From<ScopeError<ClassNamespace>> for GDError {\n  fn from(err: ScopeError<ClassNamespace>) -> GDError {\n    match err {\n      ScopeError::DuplicateName(n, s, p) =>\n        GDError::new(GDErrorF::DuplicateName(n, s), p),\n      ScopeError::NameConflictWithMainClass(n, s, p) =>\n        GDError::new(GDErrorF::DuplicateNameConflictInMainClass(n, s), p),\n    }\n  }\n}\n\nimpl From<ScopeError<Namespace>> for GDError {\n  fn from(err: ScopeError<Namespace>) -> GDError {\n    GDError::from(\n      ScopeError::<ClassNamespace>::from(err),\n    )\n  }\n}\n\nimpl From<DependencyError> for GDError {\n  fn from(e: DependencyError) -> GDError {\n    match e {\n      DependencyError::UnknownName(id, pos) => {\n        let error_value = match id.namespace {\n          Namespace::Function => GDErrorF::NoSuchFn(id.name),\n          Namespace::Value => GDErrorF::NoSuchVar(id.name),\n        };\n        GDError::new(error_value, pos)\n      }\n    }\n  }\n}\n\nimpl From<LoopPrimitiveError> for GDError {\n  fn from(err: LoopPrimitiveError) -> GDError {\n    let pos = err.pos;\n    GDError::new(\n      GDErrorF::LoopPrimitiveError(err),\n      pos,\n    )\n  }\n}\n"
  },
  {
    "path": "src/compile/factory.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Factory functions for building and working with some common\n//! declaration types.\n\nuse super::import::ImportTable;\nuse super::frame::CompilerFrame;\nuse super::stateful::NeedsResult;\nuse super::names;\nuse super::names::generator::NameGenerator;\nuse super::names::registered::RegisteredNameGenerator;\nuse super::body::builder::{StmtBuilder, CodeBuilder, HasDecls};\nuse super::body::class_initializer::ClassBuilder;\nuse super::body::class_scope::DirectClassScope;\nuse super::stmt_wrapper::{self, StmtWrapper};\nuse super::symbol_table::{HasSymbolTable, ClassTablePair};\nuse super::symbol_table::local_var::LocalVar;\nuse super::symbol_table::function_call::OuterStaticRef;\nuse super::error::GDError;\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::decl::{self, Decl, DeclF};\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::gdscript::library;\nuse crate::gdscript::inner_class::{self, NeedsOuterClassRef};\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir;\nuse crate::ir::access_type::AccessType;\nuse crate::ir::arglist::simple::SimpleArgList;\n\ntype IRLiteral = ir::literal::Literal;\ntype IRExpr = ir::expr::Expr;\ntype IRArgList = ir::arglist::ordinary::ArgList;\n\n/// This is the structure returned by [`declare_function_with_init`].\nstruct DeclaredFnWithInit {\n  function: decl::FnDecl,\n  inits: Vec<Expr>,\n}\n\n/// Appends (to the builder) a variable declaration statement.\n///\n/// The variable will have a generated name produced by `gen`. The\n/// generated name is returned.\npub fn declare_var(gen: &mut impl NameGenerator,\n                   builder: &mut StmtBuilder,\n                   prefix: &str,\n                   value: Option<Expr>,\n                   pos: SourceOffset)\n                   -> String {\n  let var_name = gen.generate_with(prefix);\n  let value = value.unwrap_or_else(|| Expr::null(pos));\n  builder.append(Stmt::var_decl(var_name.clone(), value, pos));\n  var_name\n}\n\n/// As far as the IR is concerned, main classes and other classes are\n/// roughly equivalent in functionality, except that the former can\n/// have variables with custom export declarations. When we compile\n/// classes to GDScript, non-main classes compile to inner classes on\n/// the script (i.e. to a\n/// [`ClassDecl`](crate::gdscript::decl::ClassDecl)), while main\n/// classes flatten all of their declarations (and \"extends\" clauses)\n/// into the main content of the file itself. This function implements\n/// the latter behavior.\npub fn flatten_class_into_main(import_table: &ImportTable, builder: &mut CodeBuilder, class: decl::ClassDecl) {\n  let decl::ClassDecl { name: _, mut extends, body } = class;\n  transform_extends_path_for_main(import_table, &mut extends);\n  builder.extends(extends);\n  for decl in body {\n    builder.add_decl(decl);\n  }\n}\n\nfn transform_extends_path_for_main(import_table: &ImportTable, extends: &mut ClassExtends) {\n  match extends {\n    ClassExtends::Qualified(lhs, _) => {\n      transform_extends_path_for_main(import_table, lhs);\n    }\n    ClassExtends::SimpleIdentifier(name) => {\n      // If the name is an import name, convert it *back* to its path\n      // name, since we can't access import names at this point in the\n      // code.\n      if let Some(path) = import_table.get(name) {\n        *extends = ClassExtends::StringLit(path.to_owned());\n      }\n    }\n    ClassExtends::StringLit(_) => {\n      // Already a path, so do nothing.\n    }\n  }\n}\n\npub fn declare_function(frame: &mut CompilerFrame<impl HasDecls>,\n                        gd_name: String,\n                        args: IRArgList,\n                        body: &IRExpr,\n                        result_destination: &(impl StmtWrapper + ?Sized))\n                        -> Result<decl::FnDecl, GDError> {\n  let result = declare_function_with_init(frame, gd_name, args, &[], body, result_destination)?;\n  assert!(result.inits.is_empty(), \"declare_function got nonempty initializers: {:?}\", result.inits);\n  Ok(result.function)\n}\n\nfn declare_function_with_init(frame: &mut CompilerFrame<impl HasDecls>,\n                              gd_name: String,\n                              args: IRArgList,\n                              inits: &[IRExpr],\n                              body: &IRExpr,\n                              result_destination: &(impl StmtWrapper + ?Sized))\n                              -> Result<DeclaredFnWithInit, GDError> {\n  let local_vars = body.get_locals();\n  let (arglist, gd_args) = args.into_gd_arglist(&mut RegisteredNameGenerator::new_local_var(frame.table));\n  let mut stmt_builder = StmtBuilder::new();\n  for arg in &gd_args {\n    if local_vars.get(&arg.lisp_name).unwrap_or(&AccessType::None).requires_cell() {\n      // Special behavior to wrap the argument in a cell.\n      library::cell::wrap_var_in_cell(&mut stmt_builder, &arg.gd_name, body.pos)\n    }\n  }\n  frame.with_local_vars(&mut gd_args.into_iter().map(|x| (x.lisp_name.to_owned(), LocalVar::local(x.gd_name, *local_vars.get(&x.lisp_name).unwrap_or(&AccessType::None)))), |frame| {\n    frame.with_builder(&mut stmt_builder, |frame| {\n      frame.compile_stmt(result_destination, body)\n    })?;\n    let function = decl::FnDecl {\n      name: gd_name,\n      args: arglist,\n      body: stmt_builder.build_into(frame.builder),\n    };\n    let inits = inits.iter().map::<Result<Expr, GDError>, _>(|expr| {\n      let mut inner_builder = StmtBuilder::new();\n      let cexpr = frame.with_builder(&mut inner_builder, |frame| {\n        frame.compile_expr(expr, NeedsResult::Yes).map(|x| x.expr)\n      })?;\n      let (stmts, decls) = inner_builder.build();\n      if stmts.is_empty() {\n        // If we never used the stmts, then it's a simple enough\n        // expression to compile directly.\n        frame.builder.add_decls(decls);\n        Ok(cexpr)\n      } else {\n        // We used accessory statements, so we need an IIFE to get\n        // to a place where we can compile statements.\n        let mut new_inner_builder = StmtBuilder::new();\n        let cexpr = frame.with_builder(&mut new_inner_builder, |frame| {\n          frame.compile_expr(&expr.clone().self_evaluating_lambda(), NeedsResult::Yes).map(|x| x.expr)\n        })?;\n        let (stmts, decls) = new_inner_builder.build();\n        assert!(stmts.is_empty(), \"Non-empty statements in self-evaluating lambda at declare_function_with_init: got {:?}\", stmts);\n        frame.builder.add_decls(decls);\n        Ok(cexpr)\n      }\n    }).collect::<Result<Vec<_>, _>>()?;\n    Ok(DeclaredFnWithInit { function, inits })\n  })\n}\n\npub fn declare_class(frame: &mut CompilerFrame<impl HasDecls>,\n                     gd_name: String,\n                     extends: ClassExtends,\n                     main_class: bool,\n                     constructor: &ir::decl::ConstructorDecl,\n                     decls: &[ir::decl::ClassInnerDecl],\n                     pos: SourceOffset)\n                     -> Result<decl::ClassDecl, GDError> {\n\n  let mut class_scope = DirectClassScope::new();\n\n  let self_var = LocalVar::self_var();\n\n  let mut body = vec!();\n  let mut outer_ref_name = String::new();\n  let needs_outer_ref =\n    constructor.needs_outer_class_ref(frame.table) || decls.iter().any(|x| x.needs_outer_class_ref(frame.table));\n  if needs_outer_ref && !main_class {\n    outer_ref_name = frame.name_generator().generate_with(inner_class::OUTER_REFERENCE_NAME);\n  }\n\n  let mut class_init_builder = ClassBuilder::new();\n  let mut instance_table = frame.table.clone();\n  let mut static_table = frame.table.clone();\n  instance_table.with_local_var::<Result<(), GDError>, _>(String::from(\"self\"), self_var, |instance_table| {\n\n    // Modify all of the names in the instance / static table. We run\n    // this even if needs_outer_ref is false, because we might still\n    // need the static_table updates, and the instance_table updates\n    // will be harmlessly ignored in that case.\n    if !main_class {\n      for (_, call, _) in instance_table.fns_mut() {\n        call.object.update_for_inner_scope(&OuterStaticRef::InnerInstanceVar(&outer_ref_name), frame.preload_resolver(), frame.pipeline);\n      }\n      for (_, call, _) in static_table.fns_mut() {\n        call.object.update_for_inner_scope(&OuterStaticRef::InnerStatic, frame.preload_resolver(), frame.pipeline);\n      }\n    }\n\n    let (constructor, constructor_helpers) = declare_constructor(&mut CompilerFrame::new(frame.compiler, frame.pipeline, frame.builder, instance_table, &mut class_scope), constructor)?;\n    body.push(Decl::new(DeclF::InitFnDecl(constructor), pos));\n    for helper in constructor_helpers {\n      body.push(Decl::new(DeclF::FnDecl(decl::Static::NonStatic, helper), pos));\n    }\n\n    for d in decls {\n\n      // Do the actual declaration.\n      let tables = ClassTablePair { instance_table, static_table: &mut static_table };\n      body.push(frame.compiler.compile_class_inner_decl(frame.pipeline, &mut class_init_builder, tables, &mut class_scope, d)?);\n\n      // Sync up the instance and static tables' synthetic variables.\n      // (TODO (HACK) Can we make the tables sync up properly, rather\n      // than just blindly throwing information at each other at every\n      // turn?)\n      instance_table.dump_synthetics_to(&mut static_table);\n      static_table.dump_synthetics_to(instance_table);\n\n    }\n\n    // The instance and static tables should have the same synthetics,\n    // so dump one into the parent.\n    instance_table.dump_synthetics_to(frame.table);\n\n    Ok(())\n  })?;\n\n  let mut decl = decl::ClassDecl {\n    name: gd_name,\n    extends: extends,\n    body: body,\n  };\n\n  if needs_outer_ref && !main_class {\n    inner_class::add_outer_class_ref_named(&mut decl, frame.preload_resolver(), frame.pipeline, outer_ref_name, pos);\n  }\n\n  class_init_builder.declare_proxies_from_scope(class_scope);\n\n  class_init_builder.fill_out_synthetic_fields(&mut decl, pos);\n\n  let class_init = class_init_builder.build_into(frame.builder);\n  class_init.apply(&mut decl, pos)?;\n\n  Ok(decl)\n}\n\npub fn declare_constructor(frame: &mut CompilerFrame<impl HasDecls>,\n                           constructor: &ir::decl::ConstructorDecl)\n                           -> Result<(decl::InitFnDecl, Vec<decl::FnDecl>), GDError> {\n  let pos = constructor.body.pos;\n\n  // First, we save which original arguments which are instance\n  // fields.\n  let has_any_instance_field_args = constructor.args.has_any_instance_fields();\n  let instance_fields_to_init: Vec<(String, bool)> =\n    constructor.args.iter_vars()\n    .map(|(name, is_instance_field)| (name.to_owned(), is_instance_field))\n    .collect();\n\n  let simple_arglist = SimpleArgList { args: constructor.args.args.iter().map(|(name, is_instance_field)| {\n    if *is_instance_field {\n      frame.compiler.name_generator().generate_with(&names::lisp_to_gd(name))\n    } else {\n      name.to_owned()\n    }\n  }).collect() };\n\n  let constructor_with_init = declare_function_with_init(frame,\n                                                         String::from(library::CONSTRUCTOR_NAME),\n                                                         IRArgList::from(simple_arglist),\n                                                         &constructor.super_call.call,\n                                                         &constructor.body,\n                                                         &stmt_wrapper::Vacuous)?;\n\n  let DeclaredFnWithInit { function: constructor, inits } = constructor_with_init;\n  let decl::FnDecl { name: _, args, mut body } = constructor;\n\n  // Handle the instance field initialization\n  if has_any_instance_field_args {\n    let mut new_body: Vec<Stmt> = Vec::new();\n    let all_args: Vec<&str> = args.all_args_iter().collect();\n\n    // It should be the case that the argument list we put in at the\n    // beginning should have the same length as the one we got out at\n    // the end. I'm asserting that here, because if I change the\n    // calling convention somewhere down the line and this assumption\n    // breaks, I want to know.\n    assert!(all_args.len() == instance_fields_to_init.len(), \"Calling convention not compatible with factory.rs assumptions\");\n\n    for (arg_name, (field_name, is_instance_field)) in all_args.into_iter().zip(instance_fields_to_init.into_iter()) {\n      if is_instance_field {\n        let field_name = names::lisp_to_gd(&field_name);\n        new_body.push(make_instance_var_initializer(&field_name, arg_name, pos));\n      }\n    }\n\n    new_body.append(&mut body);\n    body = new_body;\n  }\n\n  let constructor = decl::InitFnDecl { args, super_call: inits, body };\n\n  Ok((constructor, vec!()))\n}\n\n/// Returns a statement which assigns the value of the given local\n/// variable to an instance variable with the given name.\nfn make_instance_var_initializer(instance_var_name: &str, local_var_name: &str, pos: SourceOffset) -> Stmt {\n  Stmt::simple_assign(\n    Expr::self_var(pos).attribute(instance_var_name, pos),\n    Expr::var(local_var_name, pos),\n    pos,\n  )\n}\n\n/// Compiles a GDLisp literal to a GDScript expression, using `pos` as\n/// the source offset of the resulting expression. Literals are simple\n/// expressions and, as such, this transformation cannot fail.\npub fn compile_literal(literal: &IRLiteral, pos: SourceOffset) -> Expr {\n  match literal {\n    IRLiteral::Nil => Expr::null(pos),\n    IRLiteral::Int(n) => Expr::from_value(*n, pos),\n    IRLiteral::Float(f) => Expr::from_value(*f, pos),\n    IRLiteral::Bool(b) => Expr::from_value(*b, pos),\n    IRLiteral::String(s) => Expr::from_value(s.to_owned(), pos),\n    IRLiteral::Symbol(s) => Expr::call(Some(library::gdlisp_root(pos)), \"intern\", vec!(Expr::from_value(s.to_owned(), pos)), pos),\n  }\n}\n"
  },
  {
    "path": "src/compile/frame.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! A compiler frame contains a [`Compiler`], as well as information\n//! about the current stack frame data.\n\nuse super::Compiler;\nuse super::factory;\nuse super::special_form;\nuse super::names;\nuse super::constant::validate_all_constant_scopes;\nuse super::special_form::lambda;\nuse super::special_form::flet;\nuse super::special_form::lambda_class;\nuse super::special_form::let_block;\nuse super::preload_resolver::{PreloadResolver, DefaultPreloadResolver};\nuse super::symbol_table::{SymbolTable, HasSymbolTable};\nuse super::symbol_table::local_var::{LocalVar, ValueHint, VarName};\nuse super::names::fresh::FreshNameGenerator;\nuse super::names::registered::RegisteredNameGenerator;\nuse super::error::{GDError, GDErrorF};\nuse super::stateful::{StExpr, NeedsResult, SideEffects};\nuse super::body::builder::{StmtBuilder, CodeBuilder, HasDecls};\nuse super::body::class_scope::ClassScope;\nuse super::stmt_wrapper::{self, StmtWrapper};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::error::PError;\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir;\nuse crate::ir::expr::{FuncRefTarget, AssignTarget, BareName, CallTarget, FunctionBindingType};\nuse crate::ir::special_ref::SpecialRef;\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::decl::{self, Decl, DeclF};\nuse crate::gdscript::inner_class;\nuse crate::gdscript::metadata;\nuse crate::sxp::reify::pretty::reify_pretty_expr;\nuse crate::runner::path::RPathBuf;\n\nuse std::cmp::max;\nuse std::convert::TryFrom;\n\ntype IRExpr = ir::expr::Expr;\ntype IRExprF = ir::expr::ExprF;\ntype IRDecl = ir::decl::Decl;\ntype IRDeclF = ir::decl::DeclF;\ntype IRArgList = ir::arglist::ordinary::ArgList;\n\n/// Quoted S-expressions which are nested deeper than this constant\n/// will be split into several local variables, for efficiency\n/// reasons. See [Godot\n/// #52113](https://github.com/godotengine/godot/issues/52113) for the\n/// reason this is necessary. This constant is passed to\n/// [`reify_pretty_expr`].\npub const MAX_QUOTE_REIFY_DEPTH: u32 = 4;\n\n/// A `CompilerFrame` contains references to all of the pertinent\n/// information about a particular frame (hence, scope) of a GDScript\n/// body during compilation.\n///\n/// `CompilerFrame` never takes ownership of any of its fields; it\n/// always mutably borrows every field. The type parameter `B` is the\n/// type of the builder. This structure can be used in declaration\n/// context\n/// ([`CodeBuilder`](crate::compile::body::builder::CodeBuilder)) or in\n/// statement context\n/// ([`StmtBuilder`](crate::compile::body::builder::StmtBuilder)), and\n/// the type of the builder determines which functionality is\n/// available.\n///\n/// Note that several of the methods on this structure are\n/// conditionally available, based on the type of `B`. There are three\n/// categories of methods.\n///\n/// * Some methods are always available, for all compiler frames.\n///\n/// * Some methods are available whenever `B` implements [`HasDecls`].\n///\n/// * Some methods are only available when `B` is the concrete type\n///   [`StmtBuilder`].\npub struct CompilerFrame<'a, 'b, 'c, 'd, 'e, B> {\n  /// The compiler for the file. The compiler is mutable, but it is\n  /// unlikely to be completely replaced in local scopes.\n  pub compiler: &'a mut Compiler,\n  /// The pipeline for the compilation. The pipeline is mutable, but\n  /// it is unlikely to be completely replaced in local scopes.\n  pub pipeline: &'b mut Pipeline,\n  /// The builder for the current block of code, whose type is\n  /// parameterized by the structure's `B` type parameter.\n  ///\n  /// Generally, this is one of [`StmtBuilder`] (if building\n  /// statements), [`CodeBuilder`](super::body::builder::CodeBuilder)\n  /// (if building declarations), or `()` (if not using the\n  /// builder argument), though there is no specific requirement that\n  /// the type `B` implement any particular trait in general.\n  ///\n  /// The builder is replaced using calls to\n  /// [`CompilerFrame::with_builder`] and company.\n  pub builder: &'c mut B,\n  /// The symbol table for the current scope. `CompilerFrame`\n  /// implements [`HasSymbolTable`], so methods such as\n  /// [`HasSymbolTable::with_local_var`] will work on `CompilerFrame`.\n  pub table: &'d mut SymbolTable,\n  /// The scope for the class we're currently conceptually inside of,\n  /// including information about whether we're in a closure. This is\n  /// important, because when we're in a closure, we're still\n  /// conceptually within the class as far as GDLisp is concerned, but\n  /// we're compiling to something outside of the class, so special\n  /// care needs to be taken when referencing information on the\n  /// current class.\n  pub class_scope: &'e mut dyn ClassScope,\n}\n\nimpl<'a, 'b, 'c, 'd, 'e, B> CompilerFrame<'a, 'b, 'c, 'd, 'e, B> {\n\n  /// Convenience function to construct a `CompilerFrame`.\n  pub fn new(compiler: &'a mut Compiler,\n             pipeline: &'b mut Pipeline,\n             builder: &'c mut B,\n             table: &'d mut SymbolTable,\n             class_scope: &'e mut dyn ClassScope)\n             -> Self {\n    CompilerFrame { compiler, pipeline, builder, table, class_scope }\n  }\n\n  /// Gets the [`FreshNameGenerator`] from the frame's [`Compiler`].\n  pub fn name_generator(&mut self) -> &mut FreshNameGenerator {\n    self.compiler.name_generator()\n  }\n\n  /// Gets the [`PreloadResolver`] from the frame's [`Compiler`].\n  pub fn preload_resolver(&self) -> &dyn PreloadResolver {\n    self.compiler.preload_resolver()\n  }\n\n  /// Constructs a new [`CompilerFrame`] identical to `self` except\n  /// with `new_builder` as its builder. The new frame is passed to\n  /// `block` as its sole argument, and the result of the block is\n  /// returned.\n  pub fn with_builder<B1, R, F>(&mut self, new_builder: &mut B1, block: F) -> R\n  where F : FnOnce(&mut CompilerFrame<B1>) -> R {\n    let mut new_frame = CompilerFrame::new(self.compiler, self.pipeline, new_builder, self.table, self.class_scope);\n    block(&mut new_frame)\n  }\n\n  /// Compiles the expression `expr`, as though through\n  /// [`CompilerFrame::compile_expr`].\n  ///\n  /// This method constructs a temporary [`StmtBuilder`] for use in\n  /// the compilation. If the temporary builder ends up being used\n  /// (i.e. if the compilation constructs any intermediate statements\n  /// or declarations that require its use), then this method will\n  /// return an [`GDErrorF::NotConstantEnough`] error. If the\n  /// temporary builder goes unused, then the resulting [`Expr`] is\n  /// returned.\n  pub fn compile_simple_expr(&mut self,\n                             src_name: &str,\n                             expr: &IRExpr,\n                             needs_result: NeedsResult)\n                             -> Result<Expr, GDError> {\n    let mut tmp_builder = StmtBuilder::new();\n    let value = self.with_builder(&mut tmp_builder, |frame| {\n      frame.compile_expr(expr, needs_result).map(|x| x.expr)\n    })?;\n    let (stmts, decls) = tmp_builder.build();\n    if stmts.is_empty() && decls.is_empty() {\n      Ok(value)\n    } else {\n      Err(GDError::new(GDErrorF::NotConstantEnough(String::from(src_name)), expr.pos))\n    }\n  }\n\n}\n\nimpl<'a, 'b, 'c, 'd, 'e, B: HasDecls> CompilerFrame<'a, 'b, 'c, 'd, 'e, B> {\n\n  /// This method allows a block of code to run, given access to a\n  /// compiler frame identical to `self` but with a new\n  /// [`StmtBuilder`].\n  ///\n  /// Specifically, this method constructs a new, empty `StmtBuilder`,\n  /// then runs the block using that builder via\n  /// [`CompilerFrame::with_builder`]. At the end of the block, the\n  /// new local builder will be built into (via\n  /// [`StmtBuilder::build_into`]) the outer builder `self.builder`.\n  /// Finally, the result of the block and all of the statements built\n  /// using the local builder are returned.\n  pub fn with_local_builder_value<R, F>(&mut self, block: F) -> (R, Vec<Stmt>)\n  where F : FnOnce(&mut CompilerFrame<StmtBuilder>) -> R {\n    let mut local_builder = StmtBuilder::new();\n    let result = self.with_builder(&mut local_builder, block);\n    let stmts = local_builder.build_into(self.builder);\n    (result, stmts)\n  }\n\n  /// As\n  /// [`with_local_builder_value`](CompilerFrame::with_local_builder_value),\n  /// but the block is expected to return a [`Result<R, GDError>`],\n  /// whose error values are propagated outward to the return value of\n  /// this method.\n  pub fn with_local_builder_result<R, F>(&mut self, block: F) -> Result<(R, Vec<Stmt>), GDError>\n  where F : FnOnce(&mut CompilerFrame<StmtBuilder>) -> Result<R, GDError> {\n    let (error_value, vec) = self.with_local_builder_value(block);\n    error_value.map(|r| (r, vec))\n  }\n\n  /// As\n  /// [`with_local_builder_result`](CompilerFrame::with_local_builder_result),\n  /// but with no return value `R`. Only the vector of statements from\n  /// the builder is returned. Equivalent to\n  /// `self.with_local_builder_result(block).map(|x| x.1)`.\n  pub fn with_local_builder<F>(&mut self, block: F) -> Result<Vec<Stmt>, GDError>\n  where F : FnOnce(&mut CompilerFrame<StmtBuilder>) -> Result<(), GDError> {\n    let (error_value, vec) = self.with_local_builder_value(block);\n    error_value.map(|_| vec)\n  }\n\n  /// As\n  /// [`with_local_builder_value`](CompilerFrame::with_local_builder_value),\n  /// but with no return value `R`. Only the vector of statements from\n  /// the builder is returned. Equivalent to\n  /// `self.with_local_builder_value(block).1`.\n  pub fn with_local_builder_ok<F>(&mut self, block: F) -> Vec<Stmt>\n  where F : FnOnce(&mut CompilerFrame<StmtBuilder>) {\n    let ((), vec) = self.with_local_builder_value(block);\n    vec\n  }\n\n}\n\nimpl<'a, 'b, 'c, 'd, 'e> CompilerFrame<'a, 'b, 'c, 'd, 'e, CodeBuilder> {\n\n  pub fn compile_toplevel(&mut self, toplevel: &ir::decl::TopLevel) -> Result<(), PError> {\n\n    // Special check to make sure there is only one main class.\n    let _ = toplevel.find_main_class()?;\n\n    for imp in &toplevel.imports {\n      self.compiler.resolve_import(self.pipeline, self.builder, self.table, imp)?;\n    }\n    self.compile_decls(&toplevel.decls)?;\n\n    Ok(())\n  }\n\n  pub fn compile_decls(&mut self, decls: &[IRDecl]) -> Result<(), GDError> {\n\n    // Bind all declarations into the symbol table.\n    for decl in decls {\n      Compiler::bind_decl(&self.compiler.magic_table, self.pipeline, self.table, decl)?;\n    }\n\n    // Validate the const-ness of any constants, enum values, or\n    // `export` clause bodies.\n    validate_all_constant_scopes(decls, self.table)?;\n\n    // Now compile.\n    for decl in decls {\n      self.table.clear_synthetic_locals();\n      self.compile_decl(decl)?;\n    }\n\n    Ok(())\n  }\n\n  pub fn compile_decl(&mut self, decl: &IRDecl) -> Result<(), GDError> {\n    match &decl.value {\n      IRDeclF::FnDecl(ir::decl::FnDecl { visibility: _, call_magic: _, name, args, body }) => {\n        let gd_name = names::lisp_to_gd(name);\n        let function = factory::declare_function(self, gd_name, args.clone(), body, &stmt_wrapper::Return)?;\n        self.builder.add_decl(Decl::new(DeclF::FnDecl(decl::Static::IsStatic, function), decl.pos));\n        Ok(())\n      }\n      IRDeclF::MacroDecl(ir::decl::MacroDecl { visibility: _, name, args, body }) => {\n        // Note: Macros compile identically to functions, as far as\n        // this stage of compilation is concerned. They'll be resolved\n        // and then purged during the IR phase.\n        let gd_name = names::lisp_to_gd(name);\n        let function = factory::declare_function(self, gd_name, args.clone(), body, &stmt_wrapper::Return)?;\n        self.builder.add_decl(Decl::new(DeclF::FnDecl(decl::Static::IsStatic, function), decl.pos));\n        Ok(())\n      }\n      IRDeclF::SymbolMacroDecl(ir::decl::SymbolMacroDecl { visibility: _, name, body }) => {\n        // Note: Macros compile identically to functions, as far as\n        // this stage of compilation is concerned. They'll be resolved\n        // and then purged during the IR phase.\n        let gd_name = metadata::symbol_macro(&names::lisp_to_gd(name));\n        let function = factory::declare_function(self, gd_name, IRArgList::empty(), body, &stmt_wrapper::Return)?;\n        self.builder.add_decl(Decl::new(DeclF::FnDecl(decl::Static::IsStatic, function), decl.pos));\n        Ok(())\n      }\n      IRDeclF::ConstDecl(ir::decl::ConstDecl { visibility: _, name, value }) => {\n        let gd_name = names::lisp_to_gd(name);\n        let value = self.compile_simple_expr(name, value, NeedsResult::Yes)?;\n        self.builder.add_decl(Decl::new(DeclF::ConstDecl(gd_name, value), decl.pos));\n        Ok(())\n      }\n      IRDeclF::ClassDecl(ir::decl::ClassDecl { visibility: _, name, extends, main_class, constructor, decls }) => {\n        let gd_name = names::lisp_to_gd(name);\n        let extends = Compiler::resolve_extends(self.table, extends, decl.pos)?;\n\n        // Synthesize default constructor if needed\n        let default_constructor: ir::decl::ConstructorDecl;\n        let constructor = match constructor {\n          None => {\n            default_constructor = ir::decl::ConstructorDecl::empty(decl.pos);\n            &default_constructor\n          }\n          Some(c) => {\n            c\n          }\n        };\n\n        let class = factory::declare_class(self, gd_name, extends, *main_class, constructor, decls, decl.pos)?;\n        if *main_class {\n          factory::flatten_class_into_main(self.compiler.import_path_table(), self.builder, class);\n          Ok(())\n        } else {\n          self.builder.add_decl(Decl::new(DeclF::ClassDecl(class), decl.pos));\n          Ok(())\n        }\n      }\n      IRDeclF::EnumDecl(ir::decl::EnumDecl { visibility: _, name, clauses }) => {\n        let gd_name = names::lisp_to_gd(name);\n        let gd_clauses = clauses.iter().map(|(const_name, const_value)| {\n          let gd_const_name = names::lisp_to_gd(const_name);\n          let gd_const_value = const_value.as_ref().map(|x| self.compile_simple_expr(const_name, x, NeedsResult::Yes)).transpose()?;\n          Ok((gd_const_name, gd_const_value))\n        }).collect::<Result<_, GDError>>()?;\n        self.builder.add_decl(Decl::new(DeclF::EnumDecl(decl::EnumDecl { name: Some(gd_name), clauses: gd_clauses }), decl.pos));\n        Ok(())\n      }\n      IRDeclF::DeclareDecl(_) => {\n        // (sys/declare ...) statements have no runtime presence and do\n        // nothing here.\n        Ok(())\n      }\n    }\n  }\n\n}\n\nimpl<'a, 'b, 'c, 'd, 'e> CompilerFrame<'a, 'b, 'c, 'd, 'e, StmtBuilder> {\n\n  /// Compiles the sequence of statements into the current frame's\n  /// builder.\n  ///\n  /// If `stmts` is empty, then the builder is unmodified and\n  /// [`Expr::null`] is returned. Otherwise, all except the *final*\n  /// statement in `stmts` are compiled using\n  /// [`compile_stmt`](CompilerFrame::compile_stmt) (with destination\n  /// of [`stmt_wrapper::Vacuous`]). The final statement is compiled\n  /// with [`compile_expr`](CompilerFrame::compile_expr), using\n  /// `needs_result` to determine if the result value will be used.\n  ///\n  /// The source offset `pos` is only used if `stmts` is empty, in\n  /// which case it is reported as the source location of the compiled\n  /// null value.\n  pub fn compile_stmts(&mut self,\n                       stmts: &[&IRExpr],\n                       needs_result: NeedsResult,\n                       pos: SourceOffset)\n                       -> Result<StExpr, GDError> {\n    if stmts.is_empty() {\n      Ok(Compiler::nil_expr(pos))\n    } else {\n      let prefix = &stmts[..stmts.len()-1];\n      let end = &stmts[stmts.len()-1];\n      for x in prefix {\n        self.compile_stmt(&stmt_wrapper::Vacuous, x)?;\n      }\n      self.compile_expr(end, needs_result)\n    }\n  }\n\n  /// Compiles a single statement into the current builder. The IR\n  /// expression `stmt` is compiled (via\n  /// [`compile_expr`](CompilerFrame::compile_expr)) into an [`Expr`].\n  /// Then that expression object is converted into a [`Stmt`] using\n  /// [`destination.wrap_to_builder`](StmtWrapper::wrap_to_builder).\n  ///\n  /// During the `compile_expr` call, `needs_result` is\n  /// [`NeedsResult::Yes`] if and only if `destination.is_vacuous()`\n  /// is false. That is, the expression result will be discarded if\n  /// and only if the statement destination is vacuous.\n  pub fn compile_stmt(&mut self,\n                      destination: &(impl StmtWrapper + ?Sized),\n                      stmt: &IRExpr)\n                      -> Result<(), GDError> {\n    let needs_result = NeedsResult::from(!destination.is_vacuous());\n    let expr = self.compile_expr(stmt, needs_result)?;\n    destination.wrap_to_builder(self.builder, expr);\n    Ok(())\n  }\n\n  // TODO Document me\n  pub fn compile_expr(&mut self,\n                      expr: &IRExpr,\n                      needs_result: NeedsResult)\n                      -> Result<StExpr, GDError> {\n    match &expr.value {\n      IRExprF::BareName(name) => {\n        self.compile_bare_name(name, expr.pos)\n      }\n      IRExprF::Literal(lit) => {\n        let lit = factory::compile_literal(lit, expr.pos);\n        Ok(StExpr { expr: lit, side_effects: SideEffects::None })\n      }\n      IRExprF::Progn(body) => {\n        let body: Vec<_> = body.iter().collect();\n        self.compile_stmts(&body[..], needs_result, expr.pos)\n      }\n      IRExprF::CondStmt(clauses) => {\n        special_form::compile_cond_stmt(self, clauses, needs_result, expr.pos)\n      }\n      IRExprF::WhileStmt(cond, body) => {\n        special_form::compile_while_stmt(self, cond, body, needs_result, expr.pos)\n      }\n      IRExprF::ForStmt(name, iter, body) => {\n        special_form::compile_for_stmt(self, name, iter, body, needs_result, expr.pos)\n      }\n      IRExprF::Call(object, f, args) => {\n        self.compile_function_call(object, f, args, expr.pos)\n      }\n      IRExprF::Let(clauses, body) => {\n        let_block::compile_let(self, clauses, body, needs_result, expr.pos)\n      }\n      IRExprF::FunctionLet(FunctionBindingType::OuterScoped, clauses, body) => {\n        flet::compile_flet(self, clauses, body, needs_result, self.compiler.is_minimalist(), expr.pos)\n      }\n      IRExprF::FunctionLet(FunctionBindingType::Recursive, clauses, body) => {\n        flet::compile_labels(self, clauses, body, needs_result, expr.pos)\n      }\n      IRExprF::Lambda(args, body) => {\n        lambda::compile_lambda_stmt(self, args, body, self.compiler.is_minimalist(), expr.pos)\n      }\n      IRExprF::FuncRef(name) => {\n        match name {\n          FuncRefTarget::SimpleName(name) => {\n            let func = self.table.get_fn(name).ok_or_else(|| GDError::new(GDErrorF::NoSuchFn(name.clone()), expr.pos))?.0.clone();\n            lambda::compile_function_ref(self.compiler, self.pipeline, self.builder, self.table, func, self.compiler.is_minimalist(), expr.pos)\n          }\n        }\n      }\n      IRExprF::Assign(target, expr) => {\n        self.compile_assignment(target, expr, needs_result)\n      }\n      IRExprF::Quote(ast) => {\n        let mut gen = RegisteredNameGenerator::new_local_var(self.table);\n        let (stmts, result) = reify_pretty_expr(ast, MAX_QUOTE_REIFY_DEPTH, &mut gen);\n        self.builder.append_all(&mut stmts.into_iter());\n        Ok(StExpr { expr: result, side_effects: SideEffects::None })\n      }\n      IRExprF::FieldAccess(lhs, sym) => {\n\n        // This is a special case to validate enum names, as an extra sanity check.\n        if let Some(lhs) = lhs.as_plain_name() {\n          if let Some(LocalVar { value_hint: Some(ValueHint::Enum(vs)), .. }) = self.table.get_var(lhs) {\n            // It's an enum and we know its values; validate\n            if !vs.contains(&names::lisp_to_gd(sym)) {\n              return Err(GDError::new(GDErrorF::NoSuchEnumValue(lhs.to_owned(), sym.clone()), expr.pos));\n            }\n          }\n        }\n\n        let StExpr { expr: lhs, side_effects: state } = self.compile_expr(lhs, NeedsResult::Yes)?;\n        let side_effects = max(SideEffects::ReadsState, state);\n        Ok(StExpr { expr: Expr::new(ExprF::Attribute(Box::new(lhs), names::lisp_to_gd(sym)), expr.pos), side_effects })\n\n      }\n      IRExprF::LambdaClass(cls) => {\n        lambda_class::compile_lambda_class(self, cls, expr.pos)\n      }\n      IRExprF::Yield(arg) => {\n        match arg {\n          None => Ok(StExpr { expr: Expr::yield_expr(None, expr.pos), side_effects: SideEffects::ModifiesState }),\n          Some((x, y)) => {\n            let x = self.compile_expr(x, NeedsResult::Yes)?.expr;\n            let y = self.compile_expr(y, NeedsResult::Yes)?.expr;\n            Ok(StExpr { expr: Expr::yield_expr(Some((x, y)), expr.pos), side_effects: SideEffects::ModifiesState })\n          }\n        }\n      }\n      IRExprF::Assert(cond, message) => {\n        let cond = self.compile_expr(cond, NeedsResult::Yes)?.expr;\n        let message = message.as_ref().map(|x| self.compile_expr(x, NeedsResult::Yes).map(|y| y.expr)).transpose()?;\n        Ok(StExpr { expr: Expr::assert_expr(cond, message, expr.pos), side_effects: SideEffects::ModifiesState })\n      }\n      IRExprF::Return(expr) => {\n        self.compile_stmt(&stmt_wrapper::Return, expr)?;\n        Ok(Compiler::nil_expr(expr.pos))\n      }\n      IRExprF::Break => {\n        self.builder.append(Stmt::break_stmt(expr.pos));\n        Ok(Compiler::nil_expr(expr.pos))\n      }\n      IRExprF::Continue => {\n        self.builder.append(Stmt::continue_stmt(expr.pos));\n        Ok(Compiler::nil_expr(expr.pos))\n      }\n      IRExprF::SpecialRef(special_ref) => {\n        Ok(self.compile_special_ref(*special_ref, expr.pos))\n      }\n      IRExprF::ContextualFilename(filename) => {\n        let new_filename = self.preload_resolver().resolve_preload(filename)\n          .ok_or_else(|| GDError::new(GDErrorF::ContextualFilenameUnresolved, expr.pos))?;\n        Ok(StExpr { expr: Expr::from_value(new_filename, expr.pos), side_effects: SideEffects::None })\n      }\n      IRExprF::Split(name, expr) => {\n        let pos = expr.pos;\n        let expr = self.compile_expr(expr, NeedsResult::Yes)?.expr;\n        let mut gen = RegisteredNameGenerator::new_local_var(self.table);\n        let tmp_var = factory::declare_var(&mut gen, self.builder, name, Some(expr), pos); // TODO Contextual name generator?\n        Ok(StExpr {\n          expr: Expr::new(ExprF::Var(tmp_var), pos),\n          side_effects: SideEffects::None,\n        })\n      }\n      IRExprF::Preload(arg) => {\n        let path = RPathBuf::try_from(arg.to_owned()).map_err(|_| {\n          GDError::new(GDErrorF::BadPreloadArgument(arg.to_owned()), expr.pos)\n        })?;\n        let preload_call = self.compiler.make_preload_expr(&path, expr.pos)?;\n        Ok(StExpr {\n          expr: preload_call,\n          side_effects: SideEffects::None,\n        })\n      }\n      /* // This will eventually be an optimization.\n      IRExprF::Funcall(f, args) => {\n        let func_expr = self.compile_expr(builder, table, f, NeedsResult::Yes)?.0;\n        let args_expr = args.iter().map(|arg| {\n        self.compile_expr(builder, table, arg, NeedsResult::Yes).map(|x| x.0)\n      }).collect::<Result<Vec<_>, _>>()?;\n        let fn_name = String::from(\"call_func\");\n        let expr = Expr::Call(Some(Box::new(func_expr)), fn_name, args_expr);\n        Ok(StExpr(expr, true))\n      }\n       */\n    }\n  }\n\n  fn compile_bare_name(&mut self,\n                       bare_name: &BareName,\n                       pos: SourceOffset)\n                       -> Result<StExpr, GDError> {\n    match bare_name {\n      BareName::Plain(s) => {\n        self.table.get_var(s).ok_or_else(|| GDError::new(GDErrorF::NoSuchVar(s.clone()), pos)).map(|var| {\n          StExpr { expr: var.expr(pos), side_effects: SideEffects::from(var.access_type) }\n        })\n      }\n      BareName::Atomic(s) => {\n        Ok(StExpr { expr: Expr::var(&names::lisp_to_gd_bare(s), pos), side_effects: SideEffects::ReadsState })\n      }\n    }\n  }\n\n  fn compile_function_call(&mut self,\n                           object: &CallTarget,\n                           name: &str,\n                           args: &[IRExpr],\n                           pos: SourceOffset)\n                           -> Result<StExpr, GDError> {\n    match object {\n      CallTarget::Scoped => {\n        self.compile_ordinary_function_call(name, args, pos)\n      }\n      CallTarget::Object(lhs) => {\n        // Note: No call magic, no optional/rest arguments. When\n        // calling a method, we assume all arguments are required, we\n        // perform no optimization, we do not check arity, and we\n        // simply blindly forward the call on the GDScript side.\n        let lhs = self.compile_expr(lhs, NeedsResult::Yes)?.expr;\n        let args = args.iter()\n          .map(|arg| self.compile_expr(arg, NeedsResult::Yes).map(|x| x.expr))\n          .collect::<Result<Vec<_>, _>>()?;\n        Ok(StExpr {\n          expr: Expr::call(Some(lhs), &names::lisp_to_gd(name), args, pos),\n          side_effects: SideEffects::ModifiesState\n        })\n      }\n      CallTarget::Super => {\n        let args = args.iter()\n          .map(|arg| self.compile_expr(arg, NeedsResult::Yes).map(|x| x.expr))\n          .collect::<Result<Vec<_>, _>>()?;\n        let self_binding = self.table.get_var(\"self\").ok_or_else(|| GDError::new(GDErrorF::BadSuperCall(String::from(name)), pos))?;\n        let expr = self.class_scope.super_call(self.compiler.name_generator(), self_binding, name.to_owned(), args, pos)?;\n        Ok(StExpr {\n          expr: expr,\n          side_effects: SideEffects::ModifiesState,\n        })\n      }\n      CallTarget::Atomic => {\n        let name = names::lisp_to_gd_bare(name);\n        let args = args.iter()\n          .map(|x| self.compile_expr(x, NeedsResult::Yes).map(|x| x.expr))\n          .collect::<Result<Vec<_>, _>>()?;\n        Ok(StExpr {\n          expr: Expr::call(None, &name, args, pos),\n          side_effects: SideEffects::ModifiesState,\n        })\n      }\n    }\n  }\n\n  /// Compiles a function call, given the name of the function and its\n  /// argument list.\n  pub fn compile_ordinary_function_call(&mut self,\n                                        name: &str,\n                                        args: &[IRExpr],\n                                        pos: SourceOffset)\n                                        -> Result<StExpr, GDError> {\n    let (fcall, call_magic) = match self.table.get_fn(name) {\n      None => return Err(GDError::new(GDErrorF::NoSuchFn(name.to_owned()), pos)),\n      Some((p, m)) => (p.clone(), dyn_clone::clone_box(m))\n    };\n    // Macro calls should not occur at this stage in compilation.\n    if fcall.is_macro {\n      return Err(GDError::new(GDErrorF::MacroBeforeDefinitionError(name.to_owned()), pos));\n    }\n    // Call magic is used to implement some commonly used wrappers\n    // for simple GDScript operations.\n    let args = args.iter()\n      .map(|x| self.compile_expr(x, NeedsResult::Yes))\n      .collect::<Result<Vec<_>, _>>()?;\n    Ok(StExpr {\n      expr: fcall.into_expr_with_magic(&call_magic, self.compiler, self.builder, self.table, args, pos)?,\n      side_effects: SideEffects::ModifiesState,\n    })\n  }\n\n  pub fn compile_special_ref(&mut self, special_ref: SpecialRef, pos: SourceOffset) -> StExpr {\n    match special_ref {\n      SpecialRef::ThisFile => {\n        let current_filename =\n          inner_class::get_current_filename(self.pipeline, self.compiler.preload_resolver())\n          .expect(\"Error identifying current file\");\n        let expr = VarName::load_expr(current_filename, pos);\n        StExpr { expr, side_effects: SideEffects::None }\n      }\n      SpecialRef::ThisFileName => {\n        let current_filename =\n          inner_class::get_current_filename(self.pipeline, self.compiler.preload_resolver())\n          .expect(\"Error identifying current file\");\n        let expr = Expr::from_value(current_filename, pos);\n        StExpr { expr, side_effects: SideEffects::None }\n      }\n      SpecialRef::ThisTrueFileName => {\n        let current_filename =\n          inner_class::get_current_filename(self.pipeline, &DefaultPreloadResolver)\n          .expect(\"Error identifying current file\");\n        let expr = Expr::from_value(current_filename, pos);\n        StExpr { expr, side_effects: SideEffects::None }\n      }\n      SpecialRef::GodotVersion => {\n        let godot_version = self.pipeline.config().godot_version.version;\n        let expr = Expr::from_value(godot_version.into_i32(), pos);\n        StExpr { expr, side_effects: SideEffects::None }\n      }\n    }\n  }\n\n  fn compile_assignment(&mut self,\n                        target: &AssignTarget,\n                        expr: &IRExpr,\n                        needs_result: NeedsResult)\n                        -> Result<StExpr, GDError> {\n    match target {\n      AssignTarget::Variable(pos, name) => {\n        let var = self.table.get_var(name).ok_or_else(|| GDError::new(GDErrorF::NoSuchVar(name.clone()), *pos))?.to_owned();\n        if !var.assignable {\n          return Err(GDError::new(GDErrorF::CannotAssignTo(var.name.to_gd(*pos)), expr.pos));\n        }\n        self.compile_stmt(&stmt_wrapper::AssignToExpr(var.expr(*pos)), expr)?;\n        Ok(StExpr { expr: var.expr(*pos), side_effects: SideEffects::from(var.access_type) })\n      }\n      AssignTarget::InstanceField(pos, lhs, name) => {\n        let StExpr { expr: lhs, side_effects: _ } = self.compile_expr(lhs, NeedsResult::Yes)?;\n        let lhs = Expr::new(ExprF::Attribute(Box::new(lhs), names::lisp_to_gd(name)), expr.pos);\n        let StExpr { expr: mut rhs, side_effects } = self.compile_expr(expr, NeedsResult::Yes)?;\n        // Assign to a temporary if the RHS is stateful and we need a result.\n        if needs_result == NeedsResult::Yes && side_effects.modifies_state() {\n          let mut gen = RegisteredNameGenerator::new_local_var(self.table);\n          let var = factory::declare_var(&mut gen, self.builder, \"_assign\", Some(rhs), expr.pos);\n          rhs = Expr::new(ExprF::Var(var), *pos);\n        }\n        self.builder.append(\n          Stmt::simple_assign(lhs, rhs.clone(), *pos),\n        );\n        if needs_result == NeedsResult::Yes {\n          Ok(StExpr { expr: rhs, side_effects: SideEffects::None })\n        } else {\n          Ok(Compiler::nil_expr(expr.pos))\n        }\n      }\n    }\n  }\n\n}\n\nimpl<'a, 'b, 'c, 'd, 'e, B> HasSymbolTable for CompilerFrame<'a, 'b, 'c, 'd, 'e, B> {\n\n  fn get_symbol_table(&self) -> &SymbolTable {\n    self.table\n  }\n\n  fn get_symbol_table_mut(&mut self) -> &mut SymbolTable {\n    self.table\n  }\n\n}\n"
  },
  {
    "path": "src/compile/import.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`ImportTable`] type, which maps constant names\n//! representing imports to the target pathname.\n\nuse std::collections::HashMap;\n\n/// An `ImportTable` is effectively a `HashMap<String, String>`\n/// mapping imported variable names to strings representing the target\n/// path.\n#[derive(Clone, Debug, Default)]\npub struct ImportTable {\n  values: HashMap<String, String>,\n}\n\nimpl ImportTable {\n\n  /// An empty `ImportTable`, equivalent to `ImportTable::default()`.\n  pub fn new() -> ImportTable {\n    ImportTable::default()\n  }\n\n  /// Gets the import path associated to the given variable name.\n  pub fn get(&self, name: &str) -> Option<&str> {\n    self.values.get(name).map(String::as_ref)\n  }\n\n  /// Assigns a path to the given name, replacing any previous path\n  /// associated to the name.\n  pub fn set(&mut self, name: String, value: String) {\n    self.values.insert(name, value);\n  }\n\n  /// Removes the path associated to the given name, if it exists.\n  pub fn del(&mut self, name: &str) {\n    self.values.remove(name);\n  }\n\n  /// Converts `self` into a hash map containing the same information.\n  pub fn into_hashmap(self) -> HashMap<String, String> {\n    self.values\n  }\n\n}\n"
  },
  {
    "path": "src/compile/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod names;\npub mod body;\npub mod error;\npub mod stmt_wrapper;\npub mod symbol_table;\npub mod special_form;\npub mod stateful;\npub mod preload_resolver;\npub mod resource_type;\npub mod constant;\npub mod args;\npub mod factory;\npub mod frame;\npub mod import;\n\nuse frame::CompilerFrame;\nuse body::builder::{CodeBuilder, StmtBuilder, HasDecls};\nuse body::class_initializer::{ClassBuilder, InitTime};\nuse body::class_scope::{ClassScope, OutsideOfClass, DirectClassScope};\nuse names::fresh::FreshNameGenerator;\nuse names::generator::NameGenerator;\nuse preload_resolver::PreloadResolver;\nuse constant::is_const_expr;\nuse crate::gdscript::literal::Literal;\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::decl::{self, Decl, DeclF, Onready, Setget};\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::gdscript::library;\nuse crate::gdscript::arglist::ArgList;\nuse error::{GDError, GDErrorF};\nuse symbol_table::{SymbolTable, ClassTablePair};\nuse symbol_table::local_var::{LocalVar, ValueHint, VarName, VarScope, VarNameIntoExtendsError};\nuse symbol_table::function_call;\nuse symbol_table::call_magic::CallMagic;\nuse symbol_table::call_magic::table::MagicTable;\nuse crate::ir;\nuse crate::ir::import::{ImportName, ImportDecl, ImportDetails};\nuse crate::ir::identifier::{Namespace, ClassNamespace};\nuse crate::ir::access_type::AccessType;\nuse crate::ir::decl::InstanceFunctionName;\nuse crate::runner::path::RPathBuf;\nuse crate::pipeline::error::PError;\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::can_load::CanLoad;\nuse crate::pipeline::source::SourceOffset;\nuse stateful::{StExpr, NeedsResult, SideEffects};\nuse resource_type::ResourceType;\nuse import::ImportTable;\n\nuse std::ffi::OsStr;\nuse std::convert::TryFrom;\nuse std::borrow::Borrow;\n\ntype IRDecl = ir::decl::Decl;\ntype IRDeclF = ir::decl::DeclF;\ntype IRExpr = ir::expr::Expr;\ntype IRExprF = ir::expr::ExprF;\ntype IRArgList = ir::arglist::ordinary::ArgList;\n\npub struct Compiler {\n  gen: FreshNameGenerator,\n  resolver: Box<dyn PreloadResolver>,\n  magic_table: MagicTable,\n  import_table: ImportTable,\n  minimalist: bool,\n}\n\nimpl Compiler {\n\n  /// Constructs a new compiler associated with the given name\n  /// generator and preload resolver.\n  pub fn new(gen: FreshNameGenerator, resolver: Box<dyn PreloadResolver>, minimalist: bool) -> Compiler {\n    let magic_table = library::magic::standard_magic_table();\n    let import_table = ImportTable::default();\n    Compiler { gen, resolver, magic_table, import_table, minimalist }\n  }\n\n  pub fn nil_expr(pos: SourceOffset) -> StExpr {\n    StExpr { expr: Expr::null(pos), side_effects: SideEffects::None }\n  }\n\n  pub fn name_generator(&mut self) -> &mut FreshNameGenerator {\n    &mut self.gen\n  }\n\n  pub fn preload_resolver(&self) -> &dyn PreloadResolver {\n    &*self.resolver\n  }\n\n  pub fn import_path_table(&self) -> &ImportTable {\n    &self.import_table\n  }\n\n  pub fn is_minimalist(&self) -> bool {\n    self.minimalist\n  }\n\n  pub fn frame<'a, 'b, 'c, 'd, 'e, B>(&'a mut self,\n                                      pipeline: &'b mut Pipeline,\n                                      builder: &'c mut B,\n                                      table: &'d mut SymbolTable,\n                                      class_scope: &'e mut dyn ClassScope)\n                                      -> CompilerFrame<'a, 'b, 'c, 'd, 'e, B> {\n    CompilerFrame::new(self, pipeline, builder, table, class_scope)\n  }\n\n  fn compile_export(&mut self,\n                    pipeline: &mut Pipeline,\n                    table: &mut SymbolTable,\n                    expr: &IRExpr) -> Result<Expr, GDError> {\n    // Any expression valid as a const is valid here, but then so are\n    // Expr::BareName since we need to allow type names.\n    //\n    // TODO Validate that the local vars appearing here make sense.\n    match &expr.value {\n      IRExprF::BareName(s) => Ok(Expr::new(ExprF::Var(s.to_gd_name_bare()), expr.pos)),\n      _ => {\n        let mut class_scope = OutsideOfClass;\n        let expr = self.frame(pipeline, &mut (), table, &mut class_scope).compile_simple_expr(\"export\", expr, NeedsResult::Yes)?;\n        Ok(expr)\n      }\n    }\n  }\n\n  // TODO To CompilerFrame (???)\n  pub fn compile_class_inner_decl(&mut self,\n                                  pipeline: &mut Pipeline,\n                                  builder: &mut ClassBuilder,\n                                  tables: ClassTablePair<'_, '_>,\n                                  class_scope: &mut DirectClassScope,\n                                  decl: &ir::decl::ClassInnerDecl)\n                                  -> Result<Decl, GDError> {\n    let table = tables.into_table(decl.is_static());\n    match &decl.value {\n      ir::decl::ClassInnerDeclF::ClassSignalDecl(s) => {\n        let name = names::lisp_to_gd(&s.name);\n        let args = s.args.args.iter().map(|x| names::lisp_to_gd(x)).collect();\n        Ok(Decl::new(DeclF::SignalDecl(name, ArgList::required(args)), decl.pos))\n      }\n      ir::decl::ClassInnerDeclF::ClassConstDecl(c) => {\n        // TODO Merge this with IRDecl::ConstDecl above\n        let gd_name = names::lisp_to_gd(&c.name);\n        let value = self.frame(pipeline, &mut (), table, class_scope).compile_simple_expr(&c.name, &c.value, NeedsResult::Yes)?;\n        Ok(Decl::new(DeclF::ConstDecl(gd_name, value), decl.pos))\n      }\n      ir::decl::ClassInnerDeclF::ClassVarDecl(v) => {\n        let export = v.export.as_ref().map(|export| {\n          export.args.iter().map(|expr| self.compile_export(pipeline, table, expr)).collect::<Result<Vec<_>, _>>()\n        }).transpose()?;\n        let export = export.map(|args| decl::Export { args });\n        let name = names::lisp_to_gd(&v.name);\n        // Note: immediate_value is *only* values which will be\n        // compiled directly into the declaration. Initializers added\n        // to _init or _ready are not included in this return value\n        // (they are added to the mutable builder frame instead).\n        let immediate_value = {\n          let mut local_frame = self.frame(pipeline, builder.builder_for(v.init_time), table, class_scope);\n          Compiler::compile_inner_var_value(&name, v.value.as_ref(), &mut local_frame, decl.pos)?\n        };\n        let onready = Onready::from(immediate_value.is_some() && v.init_time == InitTime::Ready);\n        Ok(Decl::new(DeclF::VarDecl(decl::VarDecl {\n          export,\n          onready,\n          name,\n          value: immediate_value,\n          setget: Setget::default(),\n        }), decl.pos))\n      }\n      ir::decl::ClassInnerDeclF::ClassFnDecl(f) => {\n\n        // If we're dealing with a setter or a getter, inform the\n        // builder that we will need a synthetic field and validate\n        // the argument list and modifiers.\n        match &f.name {\n          InstanceFunctionName::Ordinary(_) => {\n            // No action required; there is no proxy field for\n            // ordinary methods.\n          }\n          InstanceFunctionName::Setter(field_name) => {\n\n            if f.is_static == decl::Static::IsStatic || f.args.len() != 1 {\n              return Err(GDError::new(\n                GDErrorF::BadSetterArguments(field_name.to_owned()),\n                decl.pos,\n              ));\n            }\n\n            let field_name = names::lisp_to_gd(field_name);\n\n            let conflicting_name = builder.declare_setter_for(field_name.to_owned());\n            if conflicting_name.is_some() {\n              return Err(GDError::new(\n                GDErrorF::DuplicateName(ClassNamespace::Value, field_name),\n                decl.pos,\n              ));\n            }\n          }\n          InstanceFunctionName::Getter(field_name) => {\n\n            if f.is_static == decl::Static::IsStatic || !f.args.is_empty() {\n              return Err(GDError::new(\n                GDErrorF::BadGetterArguments(field_name.to_owned()),\n                decl.pos,\n              ));\n            }\n\n            let field_name = names::lisp_to_gd(field_name);\n\n            let conflicting_name = builder.declare_getter_for(field_name.to_owned());\n            if conflicting_name.is_some() {\n              return Err(GDError::new(\n                GDErrorF::DuplicateName(ClassNamespace::Value, field_name),\n                decl.pos,\n              ))\n            }\n          }\n        }\n\n        let gd_name = names::lisp_to_gd(f.name.method_name().borrow());\n        let destination: Box<dyn stmt_wrapper::StmtWrapper> =\n          if matches!(&f.name, InstanceFunctionName::Setter(_)) {\n            // Setters do not return anything\n            Box::new(stmt_wrapper::Vacuous)\n          } else {\n            Box::new(stmt_wrapper::Return)\n          };\n        let mut func = factory::declare_function(&mut self.frame(pipeline, builder, table, class_scope),\n                                                 gd_name,\n                                                 IRArgList::from(f.args.clone()),\n                                                 &f.body,\n                                                 &*destination)?;\n        if f.is_nullargs {\n          func.args.optionalize_all(&Expr::null(decl.pos));\n        }\n        Ok(Decl::new(DeclF::FnDecl(f.is_static, func), decl.pos))\n      }\n    }\n  }\n\n  fn compile_inner_var_value(name: &str,\n                             value: Option<&IRExpr>,\n                             frame: &mut CompilerFrame<StmtBuilder>,\n                             pos: SourceOffset)\n                             -> Result<Option<Expr>, GDError> {\n    match value {\n      None => Ok(None),\n      Some(value) => {\n\n        // If the value is a constant expression, then compile it\n        // inline.\n        if is_const_expr(value, frame.table) {\n          let simple_expr = frame.compile_simple_expr(name, value, NeedsResult::Yes)?;\n          return Ok(Some(simple_expr));\n        }\n\n        // Otherwise, compile as an ordinary expression into the\n        // builder.\n        let destination = stmt_wrapper::AssignToExpr(\n          Expr::self_var(pos).attribute(name.to_owned(), pos),\n        );\n        frame.compile_stmt(&destination, value)?;\n        Ok(None)\n\n      }\n    }\n  }\n\n  /// Given the (simple) name of an `extends` clause for a class or\n  /// singleton object, resolve that name (using the symbol table)\n  /// into a [`ClassExtends`].\n  ///\n  /// The name `extends` is looked up in the symbol table `table`.\n  /// Then the resulting variable's GDScript [`LocalVar`] is converted\n  /// into a [`ClassExtends`] via [`TryFrom::try_from`]. If either the\n  /// lookup or the conversion fails, then the appropriate error is\n  /// returned.\n  ///\n  /// Additionally, if the scope of the [`LocalVar`] is *not*\n  /// [`VarScope::GlobalVar`](crate::compile::symbol_table::local_var::VarScope::GlobalVar),\n  /// then [`GDErrorF::CannotExtend`] is returned. This situation should\n  /// never happen for top-level class or object declarations, but it\n  /// can occur in lambda classes.\n  pub fn resolve_extends(table: &SymbolTable, extends: &str, pos: SourceOffset) -> Result<ClassExtends, GDError> {\n    let var = table.get_var(extends).ok_or_else(|| GDError::new(GDErrorF::NoSuchVar(extends.to_owned()), pos))?;\n    if var.scope != VarScope::GlobalVar {\n      return Err(GDError::new(GDErrorF::CannotExtend(VarNameIntoExtendsError::CannotExtendLocal(extends.to_owned())), pos));\n    }\n    let var_name = var.name.clone();\n    ClassExtends::try_from(var_name).map_err(|x| GDError::from_value(x, pos))\n  }\n\n  fn bind_decl(magic_table: &MagicTable,\n               pipeline: &mut Pipeline,\n               table: &mut SymbolTable,\n               decl: &IRDecl)\n               -> Result<(), GDError> {\n    match &decl.value {\n      IRDeclF::FnDecl(ir::decl::FnDecl { visibility: _, call_magic, name, args, body: _ }) => {\n        let func = function_call::FnCall::file_constant(\n          function_call::FnSpecs::from(args.to_owned()),\n          function_call::FnScope::Global,\n          names::lisp_to_gd(name),\n        );\n        let call_magic: CallMagic = match call_magic {\n          None => CallMagic::DefaultCall,\n          Some(m) => {\n            // If a call magic declaration was specified, it MUST\n            // exist or it's a compile error.\n            match magic_table.get(m) {\n              None => return Err(GDError::new(GDErrorF::NoSuchMagic(m.to_owned()), decl.pos)),\n              Some(magic) => magic.clone(),\n            }\n          }\n        };\n        table.set_fn(name.clone(), func, call_magic);\n      }\n      IRDeclF::MacroDecl(ir::decl::MacroDecl { visibility: _, name, args, body: _ }) => {\n        // As above, macros compile basically the same as functions in\n        // terms of call semantics and should be resolved during the\n        // IR stage.\n        let func = function_call::FnCall::file_macro(\n          function_call::FnSpecs::from(args.to_owned()),\n          function_call::FnScope::Global,\n          names::lisp_to_gd(name),\n        );\n        table.set_fn(name.clone(), func, CallMagic::DefaultCall);\n      }\n      IRDeclF::SymbolMacroDecl(ir::decl::SymbolMacroDecl { name, .. }) => {\n        // No action; symbol macros have no runtime binding presence.\n        table.set_var(name.clone(), LocalVar {\n          name: VarName::Null,\n          access_type: AccessType::Read,\n          scope: VarScope::GlobalVar,\n          assignable: false,\n          value_hint: Some(ValueHint::SymbolMacro),\n        });\n      }\n      IRDeclF::ConstDecl(ir::decl::ConstDecl { visibility: _, name, value }) => {\n        let mut var = LocalVar::file_constant(names::lisp_to_gd(name)); // Can't assign to constants\n\n        // Do a literal value hint if possible, but fall back to\n        // GlobalConstant if we can't do better.\n        var = var.with_hint(ValueHint::GlobalConstant);\n        if let IRExprF::Literal(value) = &value.value {\n          if let Ok(value) = Literal::try_from(value.clone()) {\n            var = var.with_hint(ValueHint::Literal(value));\n          }\n        }\n\n        table.set_var(name.clone(), var);\n      }\n      IRDeclF::ClassDecl(ir::decl::ClassDecl { name, main_class, .. }) => {\n        if *main_class {\n          let var = LocalVar::current_file(pipeline.current_filename().to_string()).with_hint(ValueHint::ClassName);\n          table.set_var(name.clone(), var);\n        } else {\n          let var = LocalVar::file_constant(names::lisp_to_gd(name))\n            .no_assign() // Can't assign to class names\n            .with_hint(ValueHint::ClassName);\n          table.set_var(name.clone(), var);\n        }\n      }\n      IRDeclF::EnumDecl(edecl) => {\n        let name = edecl.name.clone();\n        let var = LocalVar::file_constant(names::lisp_to_gd(&name))\n          .no_assign() // Can't assign to constants\n          .with_hint(ValueHint::enumeration(edecl.value_names()));\n        table.set_var(name, var);\n      }\n      IRDeclF::DeclareDecl(ddecl) => {\n        let ir::decl::DeclareDecl { visibility: _, declare_type, name, target_name } = ddecl;\n        let target_name: String = match target_name {\n          // Default behavior: Target name is `lisp_to_gd` on the\n          // declared name. Overriding built-ins is forbidden in this\n          // case.\n          None => names::lisp_to_gd(name),\n          // Custom behavior: Target name is `lisp_to_gd_bare` on the\n          // specified name. Overriding built-ins is allowed in this\n          // case. This essentially acts as though the target name was\n          // placed inside a `(literally ...)` block.\n          Some(target_name) => names::lisp_to_gd_bare(target_name),\n        };\n        match declare_type {\n          ir::decl::DeclareType::Value => {\n            let var = LocalVar::file_constant(target_name);\n            table.set_var(name.clone(), var);\n          }\n          ir::decl::DeclareType::Constant => {\n            let var = LocalVar::file_constant(target_name)\n              .with_hint(ValueHint::GlobalConstant);\n            table.set_var(name.clone(), var);\n          }\n          ir::decl::DeclareType::Superglobal => {\n            let var = LocalVar::superglobal(target_name)\n              .with_hint(ValueHint::Superglobal);\n            table.set_var(name.clone(), var);\n          }\n          ir::decl::DeclareType::Function(args) => {\n            let func = function_call::FnCall::file_constant(\n              function_call::FnSpecs::from(args.to_owned()),\n              function_call::FnScope::Global,\n              target_name,\n            );\n            table.set_fn(name.clone(), func, CallMagic::DefaultCall);\n          }\n          ir::decl::DeclareType::SuperglobalFn(args) => {\n            let func = function_call::FnCall::superglobal(\n              function_call::FnSpecs::from(args.to_owned()),\n              function_call::FnScope::Superglobal,\n              target_name,\n            );\n            table.set_fn(name.clone(), func, CallMagic::DefaultCall);\n          }\n        }\n      }\n    };\n    Ok(())\n  }\n\n  fn make_preload_line(&self, var: String, path: &RPathBuf, pos: SourceOffset) -> Result<Decl, GDError> {\n    let expr = self.make_preload_expr(path, pos)?;\n    Ok(Decl::new(\n      DeclF::ConstDecl(var, expr),\n      pos,\n    ))\n  }\n\n  fn get_preload_resolved_path(&self, path: &RPathBuf, pos: SourceOffset) -> Result<String, GDError> {\n      let mut path = path.clone();\n      if path.path().extension() == Some(OsStr::new(\"lisp\")) {\n        path.path_mut().set_extension(\"gd\");\n      }\n      self.resolver.resolve_preload(&path)\n        .ok_or_else(|| GDError::new(GDErrorF::NoSuchFile(path.to_string()), pos))\n  }\n\n  /// Compile a `preload` call, using the current preload resolver on\n  /// `self` to resolve the path.\n  pub fn make_preload_expr(&self, path: &RPathBuf, pos: SourceOffset) -> Result<Expr, GDError> {\n    if self.resolver.include_resource(ResourceType::from(path.path())) {\n      let path = self.get_preload_resolved_path(path, pos)?;\n      Ok(Expr::simple_call(\"preload\", vec!(Expr::from_value(path, pos)), pos))\n    } else {\n      // We null out any resources we don't understand. This means\n      // that GDScript source files (those NOT written in GDLisp) and\n      // other resources like PackedScene instances cannot be used in\n      // macros, as they'll just be seen as \"null\" during macro\n      // resolution. I do not verify that you follow this rule; you\n      // are expected to be responsible with your macro resource\n      // usage.\n      Ok(Expr::null(pos))\n    }\n  }\n\n  fn import_name(&mut self, import: &ImportDecl) -> String {\n    let prefix = match &import.details {\n      ImportDetails::Named(s) => names::lisp_to_gd(s),\n      ImportDetails::Restricted(_) | ImportDetails::Open => String::from(\"_Import\"),\n    };\n    self.gen.generate_with(&prefix)\n  }\n\n  pub fn translate_call(import_name: String, mut call: function_call::FnCall) -> function_call::FnCall {\n    if call.scope != function_call::FnScope::Superglobal {\n      call.object = call.object.into_imported(import_name);\n    }\n    call\n  }\n\n  pub fn resolve_import(&mut self,\n                        pipeline: &mut Pipeline,\n                        builder: &mut CodeBuilder,\n                        table: &mut SymbolTable,\n                        import: &ImportDecl)\n                        -> Result<(), PError> {\n    let preload_name = self.import_name(import);\n    builder.add_decl(self.make_preload_line(preload_name.clone(), &import.filename, import.pos)?);\n    let res_type = ResourceType::from(import);\n\n    // If the path should be included and isn't being `null`ed out\n    // right now, then we add it to the import table.\n    if self.resolver.include_resource(ResourceType::from(import.filename.path())) {\n      let target_path = self.get_preload_resolved_path(&import.filename, import.pos)?;\n      self.import_table.set(preload_name.clone(), target_path);\n    }\n\n    ResourceType::check_import(pipeline, import)?;\n\n    if res_type == ResourceType::GDLispSource {\n      // Now add the pertinent symbols to the symbol table\n      let unit = pipeline.load_file(&import.filename.path(), import.pos)?;\n      let unit_table = &unit.table;\n      let exports = &unit.exports;\n      let names = import.names(&unit.exports);\n      for imp in names {\n        // TODO Can these situations happen? It seems like\n        // ImportNameResolutionError should've already caught any\n        // nonexistent names. Review this and see if we can turn these\n        // into panic! calls.\n        let ImportName { namespace: namespace, in_name: import_name, out_name: export_name } = imp;\n        match namespace {\n          Namespace::Function => {\n            let (call, _) = unit_table.get_fn(&export_name).ok_or_else(|| GDError::new(GDErrorF::NoSuchFn(export_name), import.pos))?;\n            let call = Compiler::translate_call(preload_name.clone(), call.clone());\n            table.set_fn(import_name.clone(), call, CallMagic::DefaultCall);\n          }\n          Namespace::Value => {\n            let mut var = unit_table.get_var(&export_name).ok_or_else(|| GDError::new(GDErrorF::NoSuchVar(export_name), import.pos))?.clone();\n            var.name = var.name.into_imported(preload_name.clone());\n            table.set_var(import_name.clone(), var);\n          }\n        }\n      }\n\n      // If it was a restricted import list, validate the import names\n      if let ImportDetails::Restricted(vec) = &import.details {\n        for imp in vec {\n          imp.refine(exports).map_err(|x| GDError::from_value(x, import.pos))?;\n        }\n      }\n    } else {\n      // Simple resource import\n      let name = match &import.details {\n        ImportDetails::Named(s) => s.to_owned(),\n        _ => return Err(PError::from(GDError::new(GDErrorF::InvalidImportOnResource(import.filename.to_string()), import.pos))),\n      };\n      // TODO Check that the file exists?\n      let mut var = LocalVar::file_constant(preload_name);\n      if let Some(value_hint) = import.filename.extension().and_then(ValueHint::from_file_ext) {\n        var = var.with_hint(value_hint);\n      }\n      table.set_var(name, var);\n    }\n\n    Ok(())\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::decl::Decl;\n  use crate::gdscript::stmt::{Stmt, StmtF};\n  use crate::sxp::ast::{AST, ASTF};\n  use crate::compile::symbol_table::function_call::{FnCall, FnScope, FnSpecs};\n  use crate::compile::preload_resolver::DefaultPreloadResolver;\n  use crate::compile::body::builder::StmtBuilder;\n  use crate::pipeline::config::ProjectConfig;\n  use crate::pipeline::source::SourceOffset;\n  use crate::ir::incremental::IncCompiler;\n  use crate::runner::version::VersionInfo;\n\n  use std::path::PathBuf;\n\n  // TODO A lot more of this\n\n  fn int(n: i32) -> AST {\n    AST::new(ASTF::int(n), SourceOffset::default())\n  }\n\n  fn nil() -> AST {\n    AST::nil(SourceOffset::default())\n  }\n\n  #[allow(dead_code)]\n  fn cons(a: AST, b: AST) -> AST {\n    AST::new(ASTF::cons(a, b), SourceOffset::default())\n  }\n\n  fn list(data: Vec<AST>) -> AST {\n    AST::dotted_list(data, nil())\n  }\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  fn bind_helper_symbols(table: &mut SymbolTable) {\n    // Binds a few helper names to the symbol table for the sake of\n    // debugging.\n    table.set_fn(String::from(\"foo1\"), FnCall::file_constant(FnSpecs::new(1, 0, None), FnScope::Global, String::from(\"foo1\")), CallMagic::DefaultCall);\n    table.set_fn(String::from(\"foo\"), FnCall::file_constant(FnSpecs::new(0, 0, None), FnScope::Global, String::from(\"foo\")), CallMagic::DefaultCall);\n    table.set_fn(String::from(\"bar\"), FnCall::file_constant(FnSpecs::new(0, 0, None), FnScope::Global, String::from(\"bar\")), CallMagic::DefaultCall);\n    table.set_var(String::from(\"foobar\"), LocalVar::read(String::from(\"foobar\")));\n  }\n\n  fn compile_stmt(ast: &AST) -> Result<(Vec<Stmt>, Vec<Decl>), PError> {\n\n    let mut pipeline = Pipeline::new(ProjectConfig { root_directory: PathBuf::from(\".\"), optimizations: false, godot_version: VersionInfo::default() });\n\n    let used_names = ast.all_symbols();\n    let mut compiler = Compiler::new(FreshNameGenerator::new(used_names), Box::new(DefaultPreloadResolver), false);\n    let mut table = SymbolTable::new();\n    bind_helper_symbols(&mut table);\n    library::bind_builtins(&mut table, true);\n    let mut builder = StmtBuilder::new();\n    let expr = {\n      let mut icompiler = IncCompiler::new(ast.all_symbols());\n      icompiler.bind_builtin_macros(&mut pipeline);\n      icompiler.compile_expr(&mut pipeline, ast)\n    }?;\n    {\n      let mut class_scope = OutsideOfClass;\n      let mut frame = compiler.frame(&mut pipeline, &mut builder, &mut table, &mut class_scope);\n      let () = frame.compile_stmt(&mut stmt_wrapper::Return, &expr)?;\n    }\n    Ok(builder.build())\n  }\n\n  #[test]\n  fn compile_var() {\n    let ast = AST::symbol(\"foobar\", SourceOffset::default());\n    let expected = s(StmtF::ReturnStmt(e(ExprF::Var(String::from(\"foobar\")))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, vec!(expected));\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_call() {\n    let ast = list(vec!(AST::symbol(\"foo1\", SourceOffset::default()), int(10)));\n    let expected = s(StmtF::ReturnStmt(e(ExprF::Call(None, String::from(\"foo1\"), vec!(e(ExprF::from(10)))))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, vec!(expected));\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_int() {\n    let ast = int(99);\n    let expected = s(StmtF::ReturnStmt(e(ExprF::from(99))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, vec!(expected));\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_bool_t() {\n    let ast = AST::new(ASTF::from(true), SourceOffset::default());\n    let expected = s(StmtF::ReturnStmt(e(ExprF::from(true))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, vec!(expected));\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_bool_f() {\n    let ast = AST::new(ASTF::from(false), SourceOffset::default());\n    let expected = s(StmtF::ReturnStmt(e(ExprF::from(false))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, vec!(expected));\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_string() {\n    let ast = AST::string(\"foobar\", SourceOffset::default());\n    let expected = s(StmtF::ReturnStmt(e(ExprF::from(\"foobar\".to_owned()))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, vec!(expected));\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_progn_vacuous() {\n    let ast = list(vec!(AST::symbol(\"progn\", SourceOffset::default()), int(1), int(2)));\n    let expected = vec!(s(StmtF::ReturnStmt(e(ExprF::from(2)))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, expected);\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_progn_stateful() {\n    let ast = list(vec!(AST::symbol(\"progn\", SourceOffset::default()),\n                        list(vec!(AST::symbol(\"foo\", SourceOffset::default()))),\n                        list(vec!(AST::symbol(\"bar\", SourceOffset::default())))));\n    let expected = vec!(s(StmtF::Expr(e(ExprF::Call(None, String::from(\"foo\"), vec!())))),\n                        s(StmtF::ReturnStmt(e(ExprF::Call(None, String::from(\"bar\"), vec!())))));\n    let actual = compile_stmt(&ast).unwrap();\n    assert_eq!(actual.0, expected);\n    assert_eq!(actual.1, vec!());\n  }\n\n  #[test]\n  fn compile_nil() {\n    let result1 = compile_stmt(&nil()).unwrap();\n    assert_eq!(result1, (vec!(s(StmtF::ReturnStmt(Compiler::nil_expr(SourceOffset::default()).expr))), vec!()));\n\n    let result2 = compile_stmt(&list(vec!(AST::symbol(\"progn\", SourceOffset::default())))).unwrap();\n    assert_eq!(result2, (vec!(s(StmtF::ReturnStmt(Compiler::nil_expr(SourceOffset::default()).expr))), vec!()));\n  }\n\n}\n"
  },
  {
    "path": "src/compile/names/contextual.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the struct [`ContextualNameGenerator`] for safely\n//! generating unused names based on a symbol table.\n\nuse super::generator::NameGenerator;\nuse crate::ir::identifier::Namespace;\nuse crate::compile::symbol_table::SymbolTable;\n\nuse std::borrow::ToOwned;\n\n/// A `ContextualNameGenerator` is based on a [`SymbolTable`] and\n/// generates names which do not appear in the symbol table.\n///\n/// A contextual name generator is designed to generate names in a\n/// particular namespace and cannot be used in other namespaces, since\n/// conflicts are only detected in one namespace.\n///\n/// **Note:** `ContextualNameGenerator` does not modify the symbol\n/// table. For a variant of this type which keeps a mutable reference\n/// to a symbol table and modifies it to be aware of the new names,\n/// see [`super::registered::RegisteredNameGenerator`].\n#[derive(Debug, Clone)]\npub struct ContextualNameGenerator<'a> {\n  context: &'a SymbolTable,\n  namespace: Namespace,\n}\n\nimpl<'a> ContextualNameGenerator<'a> {\n\n  /// Construct a new `ContextualNameGenerator` from a symbol table.\n  pub fn new(context: &SymbolTable, namespace: Namespace) -> ContextualNameGenerator<'_> {\n    ContextualNameGenerator { context, namespace }\n  }\n\n  fn is_name_in_use(&self, name: &str) -> bool {\n    match self.namespace {\n      Namespace::Value => self.context.has_var_with_gd_name(name),\n      Namespace::Function => self.context.has_fn_with_gd_name(name),\n    }\n  }\n\n}\n\nimpl<'a> NameGenerator for ContextualNameGenerator<'a> {\n\n  fn generate_with(&mut self, prefix: &str) -> String {\n    let mut index = -1;\n    let mut name: String = prefix.to_owned();\n    while self.is_name_in_use(&name) {\n      index += 1;\n      name = format!(\"{}_{}\", prefix, index);\n    }\n    name\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn empty_generate() {\n    let table = SymbolTable::new();\n    let mut gen = ContextualNameGenerator::new(&table, Namespace::Value);\n    assert_eq!(gen.generate(), \"_G\");\n    assert_eq!(gen.generate(), \"_G\");\n    assert_eq!(gen.generate(), \"_G\");\n  }\n\n  #[test]\n  fn contextual_generate() {\n    let mut table = SymbolTable::new();\n    table.add_synthetic_var(String::from(\"_G\"), true);\n    let mut gen = ContextualNameGenerator::new(&table, Namespace::Value);\n    assert_eq!(gen.generate(), \"_G_0\");\n    assert_eq!(gen.generate(), \"_G_0\");\n    assert_eq!(gen.generate(), \"_G_0\");\n  }\n\n}\n"
  },
  {
    "path": "src/compile/names/fresh.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the struct [`FreshNameGenerator`] for safely generating\n//! unused names.\n\nuse super::generator::NameGenerator;\n\nuse json::{JsonValue, object};\n\nuse std::borrow::ToOwned;\n\n/// A `FreshNameGenerator` retains state for generating names which\n/// are guaranteed to be unused.\n///\n/// The name generator retains a list of strings which it considers\n/// \"reserved words\". Those words will never be generated by the\n/// generator.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct FreshNameGenerator {\n  reserved: Vec<String>,\n  index: u32,\n}\n\n// Currently a placeholder enum that has a single dummy error value\n// right now. We can fill in more details later if we feel the need to\n// be any more specific than \"JSON parse failed\".\n\n/// Parse errors that can occur during parsing of JSON as a\n/// [`FreshNameGenerator`].\n#[derive(Clone, Debug)]\npub enum ParseError {\n  MalformedInput,\n}\n\nimpl FreshNameGenerator {\n\n  /// Construct a new `FreshNameGenerator` given a collection\n  /// `reserved` of reserved words. The words in `reserved` are\n  /// guaranteed to never be generated by this name generator object.\n  pub fn new(reserved: Vec<&str>) -> FreshNameGenerator {\n    let reserved: Vec<_> = reserved.into_iter().map(|x| x.to_owned()).collect();\n    FreshNameGenerator {\n      reserved: reserved,\n      index: 0,\n    }\n  }\n\n  /// Convert the `FreshNameGenerator` to a JSON value, suitable for\n  /// restoring the generator later with\n  /// [`FreshNameGenerator::from_json`].\n  pub fn to_json(&self) -> JsonValue {\n    let reserved: Vec<_> = self.reserved.iter().map(|x| (**x).to_owned()).collect();\n    object!{\n      \"reserved\" => reserved,\n      \"index\" => self.index,\n    }\n  }\n\n  /// Produce a `FreshNameGenerator` from a JSON value.\n  ///\n  /// For any `gen: FreshNameGenerator`, it should be the case that\n  /// `from_json(&gen.to_json())` should be an object\n  /// indistinguishable from `gen` for all practical purposes.\n  pub fn from_json(value: &JsonValue) -> Result<Self, ParseError> {\n    let value = match value {\n      JsonValue::Object(value) => value,\n      _ => return Err(ParseError::MalformedInput),\n    };\n\n    // reserved\n    let reserved = match value.get(\"reserved\") {\n      Some(JsonValue::Array(reserved)) => reserved,\n      _ => return Err(ParseError::MalformedInput),\n    };\n    let reserved = reserved.iter()\n      .map(|value| value.as_str().ok_or(ParseError::MalformedInput).map(|string| {\n        string.to_owned()\n      }))\n      .collect::<Result<_, _>>()?;\n\n    // index\n    let index = match value.get(\"index\") {\n      Some(JsonValue::Number(index)) => u32::from(*index),\n      _ => return Err(ParseError::MalformedInput),\n    };\n\n    Ok(FreshNameGenerator { reserved, index })\n  }\n\n}\n\nimpl NameGenerator for FreshNameGenerator {\n\n  fn generate_with(&mut self, prefix: &str) -> String {\n    let mut name: String;\n    loop {\n      name = format!(\"{}_{}\", prefix, self.index);\n      self.index += 1;\n      if !self.reserved.contains(&name) {\n        break;\n      }\n    }\n    name\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn empty_generate() {\n    let mut gen = FreshNameGenerator::new(vec!());\n    assert_eq!(gen.generate(), \"_G_0\");\n    assert_eq!(gen.generate(), \"_G_1\");\n    assert_eq!(gen.generate(), \"_G_2\");\n  }\n\n  #[test]\n  fn unrelated_generate() {\n    let mut gen = FreshNameGenerator::new(vec!(\"foo\", \"bar\", \"these_names_change_nothing\", \"a99\"));\n    assert_eq!(gen.generate(), \"_G_0\");\n    assert_eq!(gen.generate(), \"_G_1\");\n    assert_eq!(gen.generate(), \"_G_2\");\n  }\n\n  #[test]\n  fn conflicting_generate() {\n    let mut gen = FreshNameGenerator::new(vec!(\"_G_1\", \"_G_3\"));\n    assert_eq!(gen.generate(), \"_G_0\");\n    assert_eq!(gen.generate(), \"_G_2\");\n    assert_eq!(gen.generate(), \"_G_4\");\n  }\n\n  #[test]\n  fn custom_prefix() {\n    let mut gen = FreshNameGenerator::new(vec!(\"foo_1\", \"foo_3\", \"_G_0\"));\n    assert_eq!(gen.generate_with(\"foo\"), \"foo_0\");\n    assert_eq!(gen.generate_with(\"foo\"), \"foo_2\");\n    assert_eq!(gen.generate_with(\"foo\"), \"foo_4\");\n  }\n\n  #[test]\n  fn through_json() {\n    let mut gen = FreshNameGenerator::new(vec!(\"abc\", \"def\", \"foo_1\", \"_example_names\"));\n    assert_eq!(gen.generate_with(\"foo\"), \"foo_0\");\n    assert_eq!(gen.generate_with(\"foo\"), \"foo_2\");\n    assert_eq!(gen.index, 3);\n    let json = gen.to_json();\n    let gen1 = FreshNameGenerator::from_json(&json).unwrap();\n    assert_eq!(gen, gen1);\n  }\n\n}\n"
  },
  {
    "path": "src/compile/names/generator.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`NameGenerator`] trait, whose implementations are\n//! capable of generating names.\n\n/// `NameGenerator` represents structures intended to generate new,\n/// unique symbolic names.\npub trait NameGenerator {\n\n  /// Generate a new name, beginning with `prefix`.\n  fn generate_with(&mut self, prefix: &str) -> String;\n\n  /// Generate a new name, using\n  /// [`generate_with`](NameGenerator::generate_with) and\n  /// [`DEFAULT_PREFIX`] as the prefix.\n  fn generate(&mut self) -> String {\n    self.generate_with(DEFAULT_PREFIX)\n  }\n\n}\n\n/// The default `prefix` argument to\n/// [`NameGenerator::generate_with`].\npub const DEFAULT_PREFIX: &str = \"_G\";\n"
  },
  {
    "path": "src/compile/names/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helpers for managing GDLisp and GDScript names.\n\nuse phf::phf_map;\n\nuse std::fmt::Write;\n\npub mod contextual;\npub mod fresh;\npub mod generator;\npub mod registered;\npub mod reserved;\n\n// Note: Many of these translations are based on the similar\n// translations performed by the Scala compiler.\n//\n// https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/util/NameTransformer.scala\nconst TRANSLATIONS: phf::Map<char, &'static str> = phf_map! {\n  '-' => \"_\",\n  '<' => \"_LT_\",\n  '>' => \"_GT_\",\n  '=' => \"_EQ_\",\n  '~' => \"_TILDE_\",\n  '!' => \"_BANG_\",\n  '#' => \"_HASH_\",\n  '%' => \"_PERCENT_\",\n  '^' => \"_UP_\",\n  '&' => \"_AMP_\",\n  '|' => \"_BAR_\",\n  '*' => \"_TIMES_\",\n  '/' => \"_DIV_\",\n  '+' => \"_PLUS_\",\n  ':' => \"_COLON_\",\n  '\\\\' => \"_BSLASH_\",\n  '?' => \"_QMARK_\",\n  '@' => \"_AT_\",\n  '$' => \"_DSIGN_\",\n};\n\n/// A `NameTrans` is stored information about how a given GDLisp name\n/// translates into a GDScript name.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct NameTrans {\n  /// The GDLisp name.\n  pub lisp_name: String,\n  /// The GDScript name which corresponds to the GDLisp name.\n  pub gd_name: String,\n}\n\n/// Whether `ch` is a valid GDScript identifier character.\n///\n/// Valid GDScript identifier characters are: uppercase and lowercase\n/// ASCII letters, the digits 0-9, and underscore. Equivalently, this\n/// function returns true if the character satisfies the regex\n/// `[A-Za-z0-9_]`.\npub fn is_valid_gd_char(ch: char) -> bool {\n  ch.is_digit(36) || ch == '_'\n}\n\n/// Converts `name` to a GDScript-friendly name.\n///\n/// First, if the name begins with a digit, then an underscore is\n/// prefixed before continuing to the next steps.\n///\n/// Second, for every character which is *not* a valid GDScript\n/// character (under [`is_valid_gd_char`]), a transformation takes\n/// place to translate that character. The following transformations\n/// are tried, in order.\n///\n/// 1. The sequence `->` is replaced by `_to_`.\n///\n/// 2. A question mark `?` at the end of an identifier is replaced by\n/// `is_` at the beginning.\n///\n/// 3. `-`, `<`, `>`, and `=` are converted to, respectively, `_`,\n/// `_LT_`, `_GT_`, `_EQ_`.\n///\n/// 4. All other characters are converted to `_uXXXX` where `XXXX` is\n/// the hex value (minimum four digits) of the Unicode code point for\n/// the character. If the character lies outside the basic\n/// multilingual plane, then all digits will be printed.\n///\n/// Finally, if the resulting string is a GDScript reserved word (See\n/// the [`reserved`] module documentation), then an underscore is\n/// added before the beginning of the string.\n///\n/// # Examples\n///\n/// ```\n/// # use gdlisp::compile::names::lisp_to_gd;\n/// // No escaping necessary\n/// assert_eq!(lisp_to_gd(\"foobar\"), \"foobar\");\n/// assert_eq!(lisp_to_gd(\"_private0\"), \"_private0\");\n/// assert_eq!(lisp_to_gd(\"xposition3\"), \"xposition3\");\n/// assert_eq!(lisp_to_gd(\"a0_0_EEe\"), \"a0_0_EEe\");\n///\n/// // Leading digit must be escaped\n/// assert_eq!(lisp_to_gd(\"3e\"), \"_3e\");\n/// assert_eq!(lisp_to_gd(\"2_\"), \"_2_\");\n///\n/// // Special transformation rules\n/// assert_eq!(lisp_to_gd(\"foo->bar\"), \"foo_to_bar\");\n/// assert_eq!(lisp_to_gd(\"failure?\"), \"is_failure\");\n///\n/// // Known character translations\n/// assert_eq!(lisp_to_gd(\"my-identifier\"), \"my_identifier\");\n/// assert_eq!(lisp_to_gd(\"<=>\"), \"_LT__EQ__GT_\");\n///\n/// // General codepoints\n/// assert_eq!(lisp_to_gd(\"w~~w\"), \"w_TILDE__TILDE_w\");\n/// assert_eq!(lisp_to_gd(\"α\"), \"_u03B1\");\n/// assert_eq!(lisp_to_gd(\"😃\"), \"_u1F603\");\n///\n/// // Known GDScript keywords\n/// assert_eq!(lisp_to_gd(\"preload\"), \"_preload\");\n/// assert_eq!(lisp_to_gd(\"assert\"), \"_assert\");\n/// assert_eq!(lisp_to_gd(\"class_name\"), \"_class_name\");\n/// assert_eq!(lisp_to_gd(\"class-name\"), \"_class_name\");\n/// ```\npub fn lisp_to_gd(name: &str) -> String {\n  // Escape known GDScript keywords\n  let transformed = lisp_to_gd_bare(name);\n  if reserved::RESERVED_WORDS.contains(&*transformed) {\n    format!(\"_{}\", transformed)\n  } else {\n    transformed\n  }\n}\n\n/// This implements the same transformations as [`lisp_to_gd`], except\n/// that known GDScript keywords are not underscore-escaped. See the\n/// documentation for that function for details on the translations\n/// which take place.\n///\n/// This function should be used sparingly, as it can result in\n/// generating GDScript code that attempts to use names like `if` as\n/// an identifier, which is obviously an error. However, it can also\n/// be used to bypass the usual keyword-escaping rules, to allow\n/// direct calls to the GDScript `Vector2` or `Dictionary` types,\n/// which are normally barred in GDLisp.\npub fn lisp_to_gd_bare(name: &str) -> String {\n  let length = name.chars().count();\n  let mut result = String::with_capacity(2 * length);\n  let mut iter = name.chars().peekable();\n  let mut first = true;\n  while let Some(ch) = iter.next() {\n    if is_valid_gd_char(ch) {\n      // Special exception if it's the first character and a digit.\n      // Otherwise, leave it as is.\n      if first && ch.is_ascii_digit() {\n        result.push('_');\n      }\n      result.push(ch);\n    } else {\n      let next = iter.peek();\n      match (ch, next) {\n        ('-', Some('>')) => {\n          iter.next();\n          result.push_str(\"_to_\");\n        }\n        ('?', None) => {\n          result = format!(\"is_{}\", result);\n        }\n        (_, _) => {\n          if let Some(s) = TRANSLATIONS.get(&ch) {\n            result.push_str(s);\n          } else {\n            write!(result, \"_u{:04X}\", ch as u32).expect(\"Failed to write to local string\");\n          }\n        }\n      }\n    }\n    first = false;\n  }\n  result\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn special_cases() {\n    assert_eq!(lisp_to_gd(\"bar->baz\"), \"bar_to_baz\");\n    assert_eq!(lisp_to_gd(\"success?\"), \"is_success\");\n    assert_eq!(lisp_to_gd(\"->->?\"), \"is__to__to_\");\n  }\n\n  #[test]\n  fn translations() {\n    assert_eq!(lisp_to_gd(\"foo-bar\"), \"foo_bar\");\n    assert_eq!(lisp_to_gd(\"foo-bar_baz\"), \"foo_bar_baz\");\n    assert_eq!(lisp_to_gd(\">>=\"), \"_GT__GT__EQ_\");\n    assert_eq!(lisp_to_gd(\"~a~\"), \"_TILDE_a_TILDE_\");\n    assert_eq!(lisp_to_gd(\"!\"), \"_BANG_\");\n    assert_eq!(lisp_to_gd(\"+1+\"), \"_PLUS_1_PLUS_\");\n    assert_eq!(lisp_to_gd(\"1^2\"), \"_1_UP_2\");\n    assert_eq!(lisp_to_gd(\"a&&b\"), \"a_AMP__AMP_b\");\n    assert_eq!(lisp_to_gd(\"a||b\"), \"a_BAR__BAR_b\");\n    assert_eq!(lisp_to_gd(\"a*b\"), \"a_TIMES_b\");\n    assert_eq!(lisp_to_gd(\"a/b\"), \"a_DIV_b\");\n    assert_eq!(lisp_to_gd(\"a+b\"), \"a_PLUS_b\");\n    assert_eq!(lisp_to_gd(\"keyword:\"), \"keyword_COLON_\");\n    assert_eq!(lisp_to_gd(\"\\\\\"), \"_BSLASH_\");\n    assert_eq!(lisp_to_gd(\"?-not-at-end\"), \"_QMARK__not_at_end\");\n    assert_eq!(lisp_to_gd(\"@annotation\"), \"_AT_annotation\");\n    assert_eq!(lisp_to_gd(\"$jquery\"), \"_DSIGN_jquery\");\n  }\n\n  #[test]\n  fn starts_with_number() {\n    assert_eq!(lisp_to_gd(\"99\"), \"_99\");\n    assert_eq!(lisp_to_gd(\"d99\"), \"d99\");\n  }\n\n  #[test]\n  fn keywords() {\n    assert_eq!(lisp_to_gd(\"if\"), \"_if\");\n    assert_eq!(lisp_to_gd(\"while\"), \"_while\");\n    assert_eq!(lisp_to_gd(\"While\"), \"While\"); // No translation necessary\n    assert_eq!(lisp_to_gd(\"PI\"), \"_PI\");\n    assert_eq!(lisp_to_gd(\"BUTTON_LEFT\"), \"_BUTTON_LEFT\");\n  }\n\n  #[test]\n  fn special_cases_bare() {\n    assert_eq!(lisp_to_gd_bare(\"bar->baz\"), \"bar_to_baz\");\n    assert_eq!(lisp_to_gd_bare(\"success?\"), \"is_success\");\n    assert_eq!(lisp_to_gd_bare(\"->->?\"), \"is__to__to_\");\n  }\n\n  #[test]\n  fn translations_bare() {\n    assert_eq!(lisp_to_gd_bare(\"foo-bar\"), \"foo_bar\");\n    assert_eq!(lisp_to_gd_bare(\"foo-bar_baz\"), \"foo_bar_baz\");\n    assert_eq!(lisp_to_gd_bare(\">>=\"), \"_GT__GT__EQ_\");\n  }\n\n  #[test]\n  fn keywords_bare() {\n    assert_eq!(lisp_to_gd_bare(\"if\"), \"if\");\n    assert_eq!(lisp_to_gd_bare(\"while\"), \"while\");\n    assert_eq!(lisp_to_gd_bare(\"While\"), \"While\"); // No translation necessary\n    assert_eq!(lisp_to_gd_bare(\"PI\"), \"PI\");\n    assert_eq!(lisp_to_gd_bare(\"BUTTON_LEFT\"), \"BUTTON_LEFT\");\n  }\n\n  #[test]\n  fn translations_unicode() {\n    assert_eq!(lisp_to_gd(\"α\"), \"_u03B1\");\n    assert_eq!(lisp_to_gd(\"aaβaa\"), \"aa_u03B2aa\");\n    assert_eq!(lisp_to_gd(\"⊕\"), \"_u2295\");\n    assert_eq!(lisp_to_gd(\"😎\"), \"_u1F60E\");\n  }\n\n}\n"
  },
  {
    "path": "src/compile/names/registered.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the struct [`RegisteredNameGenerator`] for safely\n//! generating (and registering) unused names based on a symbol table.\n\nuse super::generator::NameGenerator;\nuse super::contextual::ContextualNameGenerator;\nuse crate::ir::identifier::Namespace;\nuse crate::compile::symbol_table::SymbolTable;\n\n/// A `RegisteredNameGenerator` is based on a [`SymbolTable`] and\n/// generates names which do not appear in the symbol table.\n///\n/// A contextual name generator is designed to generate names in a\n/// particular namespace and cannot be used in other namespaces, since\n/// conflicts are only detected in one namespace. Whenever a name is\n/// generated, it is also stored as a synthetic name in the symbol\n/// table. For a variant of this type which does *not* modify the\n/// symbol table, see [`super::contextual::ContextualNameGenerator`].\n#[derive(Debug)]\npub struct RegisteredNameGenerator<'a> {\n  context: &'a mut SymbolTable,\n  namespace: Namespace,\n  is_local: bool, // Note: Only used if namespace is Namespace::Value\n}\n\nimpl<'a> RegisteredNameGenerator<'a> {\n\n  /// Construct a new `RegisteredNameGenerator` for function names.\n  pub fn new_fn(context: &mut SymbolTable) -> RegisteredNameGenerator<'_> {\n    RegisteredNameGenerator {\n      context: context,\n      namespace: Namespace::Function,\n      is_local: false,\n    }\n  }\n\n  /// Construct a new `RegisteredNameGenerator` for *local* variable names.\n  pub fn new_local_var(context: &mut SymbolTable) -> RegisteredNameGenerator<'_> {\n    RegisteredNameGenerator {\n      context: context,\n      namespace: Namespace::Value,\n      is_local: true,\n    }\n  }\n\n  /// Construct a new `RegisteredNameGenerator` for *global* variable names.\n  pub fn new_global_var(context: &mut SymbolTable) -> RegisteredNameGenerator<'_> {\n    RegisteredNameGenerator {\n      context: context,\n      namespace: Namespace::Value,\n      is_local: false,\n    }\n  }\n\n  fn register_name(&mut self, name: String) {\n    match self.namespace {\n      Namespace::Value => self.context.add_synthetic_var(name, self.is_local),\n      Namespace::Function => self.context.add_synthetic_fn(name),\n    }\n  }\n\n}\n\nimpl<'a> NameGenerator for RegisteredNameGenerator<'a> {\n\n  fn generate_with(&mut self, prefix: &str) -> String {\n    // Get the name, using the same implementation as\n    // ContextualNameGenerator.\n    let mut contextual_generator = ContextualNameGenerator::new(self.context, self.namespace);\n    let name = contextual_generator.generate_with(prefix);\n    self.register_name(name.clone());\n    name\n  }\n\n}\n"
  },
  {
    "path": "src/compile/names/reserved.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helper constants for the collection of reserved words in GDScript.\n\nuse crate::gdscript::library::gdnative::NativeClasses;\n\nuse std::collections::HashSet;\nuse std::borrow::Cow;\n\n/// All of the words which have special syntactic meaning in GDScript.\n///\n/// Pulled from `godot/modules/gdscript_editor.cpp`.\nconst GDSCRIPT_KEYWORDS: [&str; 42] = [\n  \"if\", \"elif\", \"else\", \"for\", \"while\", \"match\", \"break\",\n  \"continue\", \"pass\", \"return\", \"class\", \"class_name\", \"extends\",\n  \"is\", \"as\", \"self\", \"tool\", \"signal\", \"func\", \"static\", \"const\",\n  \"enum\", \"var\", \"onready\", \"export\", \"setget\", \"breakpoint\", \"preload\",\n  \"yield\", \"assert\", \"remote\", \"master\", \"slave\", \"puppet\", \"remotesync\",\n  \"mastersync\", \"puppetsync\", \"sync\", \"not\", \"and\", \"or\", \"in\",\n];\n\n/// Built-in GDScript function names, which cannot be overridden by\n/// the programmer.\nconst GDSCRIPT_FUNCTIONS: [&str; 93] = [\n  \"Color8\", \"ColorN\", \"abs\", \"acos\", \"asin\", \"assert\", \"atan\", \"atan2\", \"bytes2var\", \"cartesian2polar\",\n  \"ceil\", \"char\", \"clamp\", \"convert\", \"cos\", \"cosh\", \"db2linear\", \"decimals\", \"dectime\", \"deg2rad\",\n  \"dict2inst\", \"ease\", \"exp\", \"floor\", \"fmod\", \"fposmod\", \"funcref\", \"get_stack\", \"hash\", \"inst2dict\",\n  \"instance_from_id\", \"inverse_lerp\", \"is_equal_approx\", \"is_inf\", \"is_instance_valid\", \"is_nan\",\n  \"is_zero_approx\", \"len\", \"lerp\", \"lerp_angle\", \"linear2db\", \"load\", \"log\", \"max\", \"min\",\n  \"move_toward\", \"nearest_po2\", \"ord\", \"parse_json\", \"polar2cartesian\", \"posmod\", \"pow\",\n  \"preload\", \"print\", \"print_debug\", \"print_stack\", \"printerr\", \"printraw\", \"prints\",\n  \"printt\", \"push_error\", \"push_warning\", \"rad2deg\", \"rand_range\", \"rand_seed\", \"randf\",\n  \"randi\", \"randomize\", \"range\", \"range_lerp\", \"round\", \"seed\", \"sign\", \"sin\", \"sinh\",\n  \"smoothstep\", \"sqrt\", \"step_decimals\", \"stepify\", \"str\", \"str2var\", \"tan\", \"tanh\",\n  \"to_json\", \"type_exists\", \"typeof\", \"validate_json\", \"var2bytes\", \"var2str\", \"weakref\",\n  \"wrapf\", \"wrapi\", \"yield\",\n];\n\n/// The GDScript top-level global constant names which are not\n/// included in `api.json`.\n///\n/// Pulled from `godot/modules/gdscript_editor.cpp`.\nconst GLOBAL_CONSTANTS: [&str; 6] = [\n  \"TAU\", \"INF\", \"NAN\", \"PI\", \"true\", \"false\",\n];\n\n/// The types in GDScript whose names are considered reserved.\n///\n/// Pulled from `godot/modules/gdscript_editor.cpp`.\nconst NAMED_PRIMITIVE_TYPES: [&str; 27] = [\n  \"null\", \"bool\", \"int\", \"float\", \"String\", \"Vector2\", \"Rect2\", \"Vector3\", \"Transform2D\",\n  \"Plane\", \"Quat\", \"AABB\", \"Basis\", \"Transform\", \"Color\", \"NodePath\", \"RID\", \"Object\",\n  \"Array\", \"Dictionary\", \"PoolByteArray\", \"PoolIntArray\", \"PoolRealArray\", \"PoolStringArray\",\n  \"PoolVector2Array\", \"PoolVector3Array\", \"PoolColorArray\",\n];\n\nfn get_all_reserved_words() -> HashSet<Cow<'static, str>> {\n  let api = NativeClasses::get_api_from_godot().expect(\"Could not read GDNative API from Godot binary\");\n  let mut set: HashSet<Cow<'static, str>> = HashSet::with_capacity(100);\n\n  // GDScript keywords (hard-coded into GDLisp above)\n  set.extend(GDSCRIPT_KEYWORDS.iter().map(|x| Cow::Borrowed(*x)));\n\n  // GDScript built-in function names (hard-coded into GDLisp above)\n  set.extend(GDSCRIPT_FUNCTIONS.iter().map(|x| Cow::Borrowed(*x)));\n\n  // Global constants defined in the GlobalConstants class\n  let global_constants = api.get_global_constants();\n  set.extend(global_constants.constants.keys().map(|x| Cow::Owned(x.clone())));\n\n  // Extra global constants\n  set.extend(GLOBAL_CONSTANTS.iter().map(|x| Cow::Borrowed(*x)));\n\n  // Named primitive types\n  set.extend(NAMED_PRIMITIVE_TYPES.iter().map(|x| Cow::Borrowed(*x)));\n\n  set\n}\n\nlazy_static! {\n\n  /// All of the reserved words, as a hash set which can be checked\n  /// for membership efficiently.\n  pub static ref RESERVED_WORDS: HashSet<Cow<'static, str>> =\n    get_all_reserved_words();\n\n}\n"
  },
  {
    "path": "src/compile/preload_resolver.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Mechanism for controlling how GDScript `preload` calls are\n//! resolved.\n//!\n//! Normally, when we need to transform a GDLisp `use` directive into\n//! a GDScript `preload` call, we translate the name directly,\n//! preserving the path and potentially changing the file extension\n//! (`.lisp` to `.gd`). However, for macro loading, we may end up\n//! needing to load using an alternative translation scheme, probably\n//! to some temporary directory. The trait [`PreloadResolver`]\n//! encompasses these behaviors. The first behavior is provided by the\n//! singleton struct [`DefaultPreloadResolver`] and the second by\n//! [`LookupPreloadResolver`].\n\nuse crate::runner::path::RPathBuf;\nuse crate::compile::resource_type::ResourceType;\n\nuse std::collections::HashMap;\nuse std::path::PathBuf;\n\n/// A `DefaultPreloadResolver` is a [`PreloadResolver`] which passes\n/// through all information. This is the default resolution rule, used\n/// for generating runtime `.gd` files.\n#[derive(Debug, Clone)]\npub struct DefaultPreloadResolver;\n\n/// A [`LookupPreloadResolver`] acts as a sort of proxy, translating\n/// names which reference real files into names which reference\n/// temporary files in some virtual file system constructed by the\n/// compiler.\n///\n/// A `LookupPreloadResolver` retains a hashmap mapping known\n/// pathnames to virtual pathnames. This hashmap is used in\n/// [`LookupPreloadResolver::resolve_preload`].\n#[derive(Debug, Clone)]\npub struct LookupPreloadResolver(pub HashMap<PathBuf, PathBuf>);\n\n/// A `PreloadResolver` controls how Godot `preload` calls will be\n/// constructed and provides a way to inject custom pathname\n/// resolution behavior into the compilation process.\npub trait PreloadResolver {\n\n  /// Given a [`RPathBuf`], this method resolves the path into a\n  /// Godot-friendly string, suitable for use as the argument to\n  /// `preload`.\n  ///\n  /// If the `PreloadResolver` cannot resolve the path according to\n  /// its rules, then `None` should be returned.\n  fn resolve_preload(&self, path: &RPathBuf) -> Option<String>;\n\n  /// Given a [`ResourceType`], indicates whether or not we should\n  /// include the resource under `self`'s rules. If this returns\n  /// false, then callers should replace the `preload` call with\n  /// `null` or, if the resource is completely unavoidable, issue an\n  /// error.\n  fn include_resource(&self, res: ResourceType) -> bool;\n}\n\nimpl LookupPreloadResolver {\n\n  /// Add a new binding to the known paths for `self`, overwriting any\n  /// existing bindings.\n  ///\n  /// If the key already existed in the `LookupPreloadResolver`,\n  /// returns its previous value. Otherwise, returns `None`.\n  pub fn insert(&mut self, key: PathBuf, value: PathBuf) -> Option<PathBuf> {\n    self.0.insert(key, value)\n  }\n\n}\n\nimpl PreloadResolver for DefaultPreloadResolver {\n\n  /// Calls `path.to_string` and succeeds unconditionally.\n  fn resolve_preload(&self, path: &RPathBuf) -> Option<String> {\n    Some(path.to_string())\n  }\n\n  /// Constantly returns true. All resource types are included in the\n  /// default resolver rule.\n  fn include_resource(&self, _res: ResourceType) -> bool {\n    true\n  }\n\n}\n\nimpl PreloadResolver for LookupPreloadResolver {\n\n  /// Translates the name according to the hashmap within `self`. If\n  /// the name is not found in the hashmap, then this method returns\n  /// `None`.\n  fn resolve_preload(&self, path: &RPathBuf) -> Option<String> {\n    self.0.get(path.path()).map(RPathBuf::path_to_string)\n  }\n\n  /// `LookupPreloadResolver` only suggests including GDLisp source\n  /// files, i.e. [`ResourceType::GDLispSource`]. All other resource\n  /// types result in false.\n  fn include_resource(&self, res: ResourceType) -> bool {\n    res == ResourceType::GDLispSource\n  }\n\n}\n"
  },
  {
    "path": "src/compile/resource_type.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! The various types of resources GDLisp might encounter.\n//!\n//! [`ResourceType`] enumerates the possible resource types GDLisp\n//! might attempt to load.\n\nuse crate::ir::import::{ImportDecl, ImportDetails};\nuse crate::pipeline::Pipeline;\nuse super::error::{GDError, GDErrorF};\n\nuse std::convert::AsRef;\nuse std::path::Path;\nuse std::ffi::OsStr;\n\n/// The various resource types a `use` statement might encounter.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum ResourceType {\n  /// A GDLisp source file. Must be compiled to GDScript. GDLisp\n  /// source files support the full breadth of import declaration\n  /// types.\n  GDLispSource,\n  /// A GDScript source file. The file must exist, and the import must\n  /// be a simple import or an aliased import. Open or restricted\n  /// imports are not allowed.\n  GDScriptSource,\n  /// A packed scene file. The file must exist, and the import must be\n  /// a simple import or an aliased import. Open or restricted imports\n  /// are not allowed.\n  PackedScene,\n  /// Any other resource file. The file must exist, and the import\n  /// must be a simple import or an aliased import. Open or restricted\n  /// imports are not allowed.\n  Miscellaneous,\n}\n\nimpl ResourceType {\n\n  /// Constructs a [`ResourceType`] for the type of resource at the\n  /// given path. The file at `path` will not be read; all resource\n  /// type inference is done by looking at the file name, specifically\n  /// its extension.\n  pub fn from_path<P : AsRef<Path> + ?Sized>(path: &P) -> ResourceType {\n    ResourceType::from_file_extension(&path.as_ref().extension().unwrap_or_else(|| OsStr::new(\"\")))\n  }\n\n  /// Returns the appropriate resource type given a file extension.\n  /// `ext` should be an all-lowercase file extension excluding the\n  /// initial dot. If the extension is not recognized, then\n  /// [`ResourceType::Miscellaneous`] is returned.\n  pub fn from_file_extension<S : AsRef<OsStr> + ?Sized>(ext: &S) -> ResourceType {\n    let ext = ext.as_ref();\n    if ext == \"lisp\" {\n      ResourceType::GDLispSource\n    } else if ext == \"gd\" {\n      ResourceType::GDScriptSource\n    } else if ext == \"tscn\" {\n      ResourceType::PackedScene\n    } else {\n      ResourceType::Miscellaneous\n    }\n  }\n\n  /// Whether or not the resource type can have macros defined in it.\n  /// Only [`ResourceType::GDLispSource`] can have macros.\n  pub fn can_have_macros(&self) -> bool {\n    *self == ResourceType::GDLispSource\n  }\n\n  /// Whether the given import declaration is allowed for this\n  /// particular resource type.\n  ///\n  /// GDLisp source files allow all import types, whereas other\n  /// resource types are restricted to simple or aliased imports.\n  pub fn is_import_allowed(&self, import: &ImportDecl) -> bool {\n    if *self == ResourceType::GDLispSource {\n      true // GDLispSource allows all imports\n    } else {\n      // For other resources, it must be ImportDetails::Named\n      matches!(import.details, ImportDetails::Named(_))\n    }\n  }\n\n  /// Checks [`ResourceType::is_import_allowed`]. If it is false, this\n  /// method issues an appropriate error via `Err`. Otherwise, returns\n  /// `Ok(())`.\n  pub fn check_import(_pipeline: &Pipeline, import: &ImportDecl) -> Result<(), GDError> {\n\n    // if !pipeline.file_exists(import.filename.path()) {\n    //   return Err(GDError::ResourceDoesNotExist(import.filename.to_string()));\n    // }\n\n    let res_type = ResourceType::from_path(import.filename.path());\n    if !res_type.is_import_allowed(import) {\n      return Err(GDError::new(GDErrorF::InvalidImportOnResource(import.filename.to_string()), import.pos));\n    }\n\n    Ok(())\n  }\n\n}\n\n/// Construct a `ResourceType` from a reference to a path. Delegates\n/// to [`ResourceType::from_path`].\nimpl From<&Path> for ResourceType {\n  fn from(path: &Path) -> ResourceType {\n    ResourceType::from_path(path)\n  }\n}\n\n/// Construct a `ResourceType` from the path referenced by the import\n/// declaration.\nimpl From<&ImportDecl> for ResourceType {\n  fn from(imp: &ImportDecl) -> ResourceType {\n    ResourceType::from_path(imp.filename.path())\n  }\n}\n"
  },
  {
    "path": "src/compile/special_form/closure.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helpers for generating closures, for use in constructs such as\n//! lambdas and lambda classes.\n\nuse crate::ir::expr::{Locals, Functions, LambdaClass, LocalFnClause};\nuse crate::compile::symbol_table::function_call::FnCall;\nuse crate::compile::symbol_table::local_var::VarScope;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::pipeline::source::SourceOffset;\n\ntype IRArgList = crate::ir::arglist::ordinary::ArgList;\ntype IRExpr = crate::ir::expr::Expr;\n\n/// A `ClosureData` contains information about collections of\n/// variables and functions to close over.\n#[derive(PartialEq, Eq, Debug, Clone)]\npub struct ClosureData {\n  /// The collection of all local variables in-scope which need to be\n  /// closed over.\n  pub closure_vars: Locals,\n  /// The collection of all local variables introduced in the\n  /// closure's scope or a strictly larger one. This is similar to\n  /// `closed_vars` and will always be a superset of that collection,\n  /// but this collection also includes variables which are introduced\n  /// as part of the closure's scope, most commonly arguments to the\n  /// lambda which created the closure. Such variables do not need to\n  /// be closed over (since they do not exist outside the closure),\n  /// but it is still meaningful to ask what their `access_type` is\n  /// and whether they need a GDLisp cell.\n  pub all_vars: Locals,\n  /// The collection of all local functions in-scope which need to be\n  /// closed over.\n  pub closure_fns: Functions,\n}\n\n/// A function consists of an argument list and a body expression.\n/// This simple wrapper couples the two, so that we can pass them as a\n/// pair to [`ClosureData`] methods.\n#[derive(Clone, Debug)]\npub struct Function<'a, 'b> {\n  pub args: &'a IRArgList,\n  pub body: &'b IRExpr,\n}\n\n/// A simple wrapper around a collection of [`LocalFnClause`]. A\n/// `labels` SCC is a collection of interconnected local function\n/// clauses, which reference each other in a circular fashion. More\n/// generally, this structure can be used for any collection of local\n/// function clauses, regardless of referencing requirements.\n#[repr(transparent)]\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct LabelsComponent<'a, 'b>(pub &'a [&'b LocalFnClause]);\n\nimpl ClosureData {\n\n  /// Purges globals from `self.closure_vars`, as though via the\n  /// module-level function [`purge_globals`].\n  ///\n  /// Equivalent to `purge_globals(&mut self.closure_vars, table)`.\n  pub fn purge_globals(&mut self, table: &SymbolTable) {\n    purge_globals(&mut self.closure_vars, table)\n  }\n\n  /// Assuming `self` contains all of the relevant closure information\n  /// on the GDLisp side, this function constructs a list of the\n  /// necessary variables which need to be looked up on the GDScript\n  /// side to construct or utilize the closure.\n  ///\n  /// Every variable in `self.closure_vars` and every function in\n  /// `self.closure_fns` will be considered for inclusion in the\n  /// result. Specifically, each variable will be looked up in `table`\n  /// and, if the result has a\n  /// [`LocalVar::simple_name`](crate::compile::symbol_table::local_var::LocalVar::simple_name),\n  /// then it will be included in the returned translation vector.\n  /// Likewise, each function is looked up in `table` and, if\n  /// [`closure_fn_to_gd_var`] returns a name, then that name is added\n  /// to the translation vector.\n  ///\n  /// `self.all_vars` is not considered during this calculation.\n  ///\n  /// This function can be used in two similar ways. By passing the\n  /// lambda symbol table as `table` to this function, the resulting\n  /// vector will contain the GDScript names used to refer to the\n  /// closure variables from *within* the closure. This is useful for\n  /// building the lambda's constructor function. By passing the\n  /// enclosing outer table as `table`, on the other hand, the\n  /// resulting vector will contain the names used to refer to the\n  /// closure variables from the scope *surrounding* the closure. This\n  /// is useful for building the expression that will *call* the\n  /// lambda's constructor function.\n  ///\n  /// # Panics\n  ///\n  /// It is a precondition of this function that every name in\n  /// `self.closure_vars` and in `self.closure_fns` appears in `table`\n  /// under the appropriate namespace. If any names are missing, then\n  /// this function will panic.\n  pub fn to_gd_closure_vars(&self, table: &SymbolTable) -> Vec<String> {\n    let mut gd_closure_vars = Vec::new();\n\n    // Get closure variables\n    for lisp_name in self.closure_vars.names() {\n      let var = table.get_var(lisp_name).unwrap_or_else(|| {\n        panic!(\"Internal error compiling lambda variable {}\", lisp_name);\n      });\n      if let Some(name) = var.simple_name() {\n        gd_closure_vars.push(name.to_owned());\n      }\n    }\n\n    // Get closure functions (functions also get moved to the variable\n    // namespace when closed around, since GDScript doesn't treat\n    // function names as first-class objects)\n    for lisp_name in self.closure_fns.names() {\n      let (call, _) = table.get_fn(lisp_name).unwrap_or_else(|| {\n        panic!(\"Internal error compiling lambda variable {}\", lisp_name);\n      });\n      if let Some(var) = closure_fn_to_gd_var(call) {\n        gd_closure_vars.push(var);\n      }\n    }\n\n    gd_closure_vars\n  }\n\n}\n\nimpl<'a, 'b> Function<'a, 'b> {\n\n  /// Convenience function to construct a new `Function`.\n  pub fn new(args: &'a IRArgList, body: &'b IRExpr) -> Self {\n    Function { args, body }\n  }\n\n}\n\nimpl<'a, 'b> From<Function<'a, 'b>> for ClosureData {\n\n  /// If we're constructing a simple lambda function, we can convert\n  /// its [`Function`] value into a [`ClosureData`] in a well-defined\n  /// way.\n  fn from(function: Function<'a, 'b>) -> ClosureData {\n    let (all_vars, closure_fns) = function.body.get_names();\n    let mut closure_vars = all_vars.clone();\n    for arg in function.args.iter_vars() {\n      closure_vars.remove(arg);\n    }\n    ClosureData { closure_vars, all_vars, closure_fns }\n  }\n\n}\n\nimpl<'a> From<&'a LambdaClass> for ClosureData {\n\n  /// A lambda class involves closing around any variables inside of\n  /// it, similar to a lambda function. The lambda class case is\n  /// somewhat more complex, as it consists of multiple functions and,\n  /// in general, a `self` variable that gets implicitly overwritten\n  /// by the class' `self`.\n  fn from(class: &'a LambdaClass) -> ClosureData {\n    let (mut closure_vars, mut closure_fns) = class.constructor_or_default(SourceOffset::from(0)).get_names();\n    for d in &class.decls {\n      let (decl_vars, decl_fns) = d.get_names();\n      closure_vars.merge_with(decl_vars);\n      closure_fns.merge_with(decl_fns);\n    }\n    closure_vars.remove(\"self\"); // Don't close around self; we get a new self\n    ClosureData { closure_vars: closure_vars.clone(), all_vars: closure_vars, closure_fns }\n  }\n\n}\n\nimpl<'a, 'b> From<LabelsComponent<'a, 'b>> for ClosureData {\n\n  /// A strongly-connected component (SCC) of a `labels` clause is a\n  /// collection of interconnected local function clauses. Those\n  /// function clauses each have their own closure, and together they\n  /// have a common closure for the object which encapsulates them.\n  ///\n  /// Note that all of the function names in the SCC are in scope for\n  /// the duration of all of the function bodies, so the function\n  /// names themselves will never appear in the resulting\n  /// `closure_fns`.\n  fn from(comp: LabelsComponent<'a, 'b>) -> ClosureData {\n    let LabelsComponent(clauses) = comp;\n    let mut closure_vars = Locals::new();\n    let mut closure_fns = Functions::new();\n    let mut all_vars = Locals::new();\n\n    for clause in clauses {\n      let (mut inner_vars, inner_fns) = clause.body.get_names();\n      all_vars.merge_with(inner_vars.clone());\n      for arg in clause.args.iter_vars() {\n        inner_vars.remove(arg);\n      }\n      closure_vars.merge_with(inner_vars);\n      closure_fns.merge_with(inner_fns);\n    }\n\n    // Function names are in scope for the duration of their own bodies\n    for clause in clauses {\n      closure_fns.remove(&clause.name);\n    }\n\n    ClosureData { closure_vars, all_vars, closure_fns }\n  }\n\n}\n\n/// Removes all of the variables from `vars` whose scope (according to\n/// the corresponding entry in `table`) is\n/// [`VarScope::GlobalVar`](crate::compile::symbol_table::local_var::VarScope::GlobalVar).\n///\n/// Lambdas are lifted to the file-level scope. A variable with scope\n/// `VarScope::GlobalVar` is defined either at the file-level scope or\n/// as a superglobal. In either case, the lambda will still have a\n/// reference to the variable without any help, so we don't need to\n/// close around those variables. This function removes from `vars`\n/// the variables which it is unnecessary to explicitly create\n/// closures around.\npub fn purge_globals(vars: &mut Locals, table: &SymbolTable) {\n  vars.retain(|var, _| {\n    table.get_var(var).map_or(true, |v| v.scope != VarScope::GlobalVar)\n  });\n}\n\n/// If the function call comes from a local variable (most commonly,\n/// if it was built out of `flet` or `labels`), then this function\n/// returns the name of that variable, as a `String`. If the function\n/// does *not* come from a local variable, then this function returns\n/// `None`.\npub fn closure_fn_to_gd_var(call: &FnCall) -> Option<String> {\n  call.scope.local_name().map(str::to_owned)\n}\n"
  },
  {
    "path": "src/compile/special_form/flet.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::ir;\nuse crate::ir::expr::LocalFnClause;\nuse crate::compile::body::builder::StmtBuilder;\nuse crate::compile::symbol_table::{HasSymbolTable, SymbolTable};\nuse crate::compile::symbol_table::function_call::{FnCall, FnSpecs, FnScope, FnName};\nuse crate::compile::symbol_table::local_var::VarName;\nuse crate::compile::error::GDError;\nuse crate::compile::stateful::{StExpr, NeedsResult};\nuse crate::compile::stmt_wrapper;\nuse crate::compile::factory;\nuse crate::compile::frame::CompilerFrame;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::compile::names::registered::RegisteredNameGenerator;\nuse crate::gdscript::decl::{self, Decl, DeclF};\nuse crate::graph::Graph;\nuse crate::graph::top_sort::top_sort;\nuse crate::graph::tarjan;\nuse crate::pipeline::source::SourceOffset;\nuse super::lambda;\n\nuse std::convert::AsRef;\n\ntype IRExpr = ir::expr::Expr;\ntype IRArgList = ir::arglist::ordinary::ArgList;\n\npub fn compile_flet(frame: &mut CompilerFrame<StmtBuilder>,\n                    clauses: &[LocalFnClause],\n                    body: &IRExpr,\n                    needs_result: NeedsResult,\n                    minimalist: bool,\n                    pos: SourceOffset)\n                    -> Result<StExpr, GDError> {\n  let local_fns = clauses.iter().map(|clause| {\n    let call = compile_flet_call(frame, clause.args.to_owned(), &clause.body, minimalist, pos)?;\n    Ok((clause.name.to_owned(), call))\n  }).collect::<Result<Vec<_>, GDError>>()?;\n  frame.with_local_fns(&mut local_fns.into_iter(), |frame| {\n    frame.compile_expr(body, needs_result)\n  })\n}\n\nfn compile_flet_call(frame: &mut CompilerFrame<StmtBuilder>,\n                     args: IRArgList,\n                     body: &IRExpr,\n                     minimalist: bool,\n                     pos: SourceOffset)\n                     -> Result<FnCall, GDError> {\n  if is_declaration_semiglobal(&args, body, frame.table) {\n    // No closure vars and any closure fns (if there are any) are\n    // free of closures, so we can compile to SemiGlobal.\n    let gd_name = RegisteredNameGenerator::new_fn(frame.table).generate_with(\"_flet\");\n    let func = factory::declare_function(frame, gd_name.clone(), args.clone(), body, &stmt_wrapper::Return)?;\n    frame.builder.add_helper(Decl::new(DeclF::FnDecl(decl::Static::IsStatic, func), pos));\n    let specs = FnSpecs::from(args);\n    Ok(FnCall {\n      scope: FnScope::SemiGlobal,\n      object: FnName::FileConstant,\n      function: gd_name,\n      specs,\n      is_macro: false,\n    })\n  } else {\n    // Have to make a full closure object.\n    let stmt = lambda::compile_lambda_stmt(frame, &args, body, minimalist, pos)?.expr;\n    let local_name = factory::declare_var(&mut RegisteredNameGenerator::new_local_var(frame.table), frame.builder, \"_flet\", Some(stmt), pos);\n    let specs = FnSpecs::from(args);\n    Ok(FnCall {\n      scope: FnScope::Local(local_name.clone()),\n      object: FnName::on_local_var(VarName::Local(local_name)),\n      function: \"call_func\".to_owned(),\n      specs,\n      is_macro: false,\n    })\n  }\n}\n\npub fn compile_labels(frame: &mut CompilerFrame<StmtBuilder>,\n                      clauses: &[LocalFnClause],\n                      body: &IRExpr,\n                      needs_result: NeedsResult,\n                      pos: SourceOffset)\n                      -> Result<StExpr, GDError> {\n  // TODO This is rife with string cloning, because of the sloppy way\n  // Graph is implemented. Once we fix Graph, we can eliminate some\n  // clones here.\n  let mut dependencies = Graph::from_nodes(clauses.iter().map(|clause| clause.name.clone()));\n  for clause in clauses {\n    for ref_name in clause.body.get_functions().into_names() {\n      if dependencies.has_node(&ref_name) {\n        dependencies.add_edge_no_dup(clause.name.clone(), ref_name);\n      }\n    }\n  }\n  let sccs = tarjan::find_scc(&dependencies);\n  let collated_graph = tarjan::build_scc_graph(&dependencies, &sccs);\n  let collated_graph = collated_graph.transpose(); // We need the arrows pointing in load order, not dependency order\n  let ordering = top_sort(&collated_graph)\n    .expect(\"SCC detection failed (cycle in resulting graph)\")\n    .into_iter().copied();\n  let mut alg = CompileLabelsRecAlgorithm { frame, body, needs_result, pos, clauses, full_graph: &dependencies, sccs: &sccs, ordering: ordering };\n  alg.compile_labels_rec()\n}\n\nstruct CompileLabelsRecAlgorithm<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, I> {\n  frame: &'a mut CompilerFrame<'b, 'c, 'd, 'e, 'f, StmtBuilder>,\n  body: &'g IRExpr,\n  needs_result: NeedsResult,\n  pos: SourceOffset,\n  clauses: &'h [LocalFnClause],\n  full_graph: &'i Graph<String>,\n  sccs: &'j tarjan::SCCSummary<'k, String>,\n  ordering: I,\n}\n\nimpl<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, I : Iterator<Item=usize>> CompileLabelsRecAlgorithm<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, I> {\n\n  fn compile_labels_rec(&mut self) -> Result<StExpr, GDError> {\n    if let Some(current_scc_idx) = self.ordering.next() {\n      let tarjan::SCC(current_scc) = self.sccs.get_scc_by_id(current_scc_idx).expect(\"SCC detection failed (invalid ID)\");\n      if current_scc.is_empty() {\n        // That's weird. But whatever. No action needed.\n        self.compile_labels_rec()\n      } else {\n        let name = current_scc.iter().next().expect(\"Internal error in SCC detection (no first element?)\");\n        if current_scc.len() == 1 && !self.full_graph.has_edge(name, name) {\n          // Simple FLet-like case.\n          let name = current_scc.iter().next().expect(\"Internal error in SCC detection (no first element?)\");\n          let clause = self.clauses.iter().find(|clause| clause.name == **name).expect(\"Internal error in SCC detection (no function found?)\");\n          let call = compile_flet_call(self.frame, clause.args.to_owned(), &clause.body, self.frame.compiler.is_minimalist(), self.pos)?;\n          self.with_local_fn((*name).to_owned(), call, |alg| {\n            alg.compile_labels_rec()\n          })\n        } else {\n          // Complicated mutual recursion case.\n          let mut relevant_clauses = Vec::new();\n          for name in current_scc {\n            let clause = self.clauses.iter().find(|clause| clause.name == **name).expect(\"Internal error in SCC detection (no function found?)\");\n            relevant_clauses.push(clause);\n          }\n          // Go ahead and sort them just so we guarantee a consistent order for testing purposes.\n          relevant_clauses.sort_by_key(|clause| &clause.name);\n          let calls = lambda::compile_labels_scc(self.frame, &relevant_clauses[..], self.pos)?;\n          self.with_local_fns(&mut calls.into_iter(), |alg| {\n            alg.compile_labels_rec()\n          })\n        }\n      }\n    } else {\n      self.frame.compile_expr(self.body, self.needs_result)\n    }\n  }\n\n}\n\nimpl<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, I> HasSymbolTable for CompileLabelsRecAlgorithm<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, I> {\n\n  fn get_symbol_table(&self) -> &SymbolTable {\n    self.frame.get_symbol_table()\n  }\n\n  fn get_symbol_table_mut(&mut self) -> &mut SymbolTable {\n    self.frame.get_symbol_table_mut()\n  }\n\n}\n\n/// A function declaration is eligible to be semiglobal if all of the\n/// following are true.\n///\n/// * All functions referenced in the body of the function are\n///   non-local (i.e. [`FnScope::is_local`] returns false on their\n///   scope).\n///\n/// * All variables referenced in the body of the function are\n///   arguments to the function.\n///\n/// Semiglobal functions do not need to have explicit closure objects\n/// constructed for them and can instead be hoisted on the GDScript\n/// side into top-level global functions.\npub fn is_declaration_semiglobal(args: &IRArgList, body: &IRExpr, table: &SymbolTable) -> bool {\n  let (closure_vars, closure_fns) = body.get_names();\n  let arg_var_names: Vec<_> = args.iter_vars().collect();\n  // All referenced functions should be Global or SemiGlobal and all\n  // referenced local variables should be found in the argument list.\n  let mut closure_names = closure_vars.names();\n  closure_names.all(|x| arg_var_names.contains(&x)) &&\n    all_names_are_nonlocal(closure_fns.names(), table)\n}\n\nfn all_names_are_nonlocal<I, T>(mut names: I, table: &SymbolTable)\n                                -> bool\n  where I : Iterator<Item=T>,\n        T : AsRef<str> {\n  names.all(|name| {\n    table.get_fn(name.as_ref()).map_or(false, |(call, _)| !call.scope.is_local())\n  })\n}\n"
  },
  {
    "path": "src/compile/special_form/lambda.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::util::unzip_err;\nuse crate::ir;\nuse crate::ir::expr::{Locals, Functions, LocalFnClause};\nuse crate::ir::access_type::AccessType;\nuse crate::compile::{Compiler, StExpr};\nuse crate::compile::frame::CompilerFrame;\nuse crate::compile::body::builder::StmtBuilder;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::compile::symbol_table::inner::InnerSymbolTable;\nuse crate::compile::symbol_table::local_var::{LocalVar, VarScope, VarName};\nuse crate::compile::symbol_table::function_call::{FnCall, FnSpecs, FnScope, FnName, OuterStaticRef};\nuse crate::compile::symbol_table::call_magic::CallMagic;\nuse crate::compile::stmt_wrapper;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::compile::stateful::SideEffects;\nuse crate::compile::names::{self, NameTrans};\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::compile::names::registered::RegisteredNameGenerator;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::decl::{self, Decl, DeclF, VarDecl};\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::gdscript::arglist::ArgList;\nuse crate::gdscript::library;\nuse crate::gdscript::inner_class::{self, NeedsOuterClassRef};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::can_load::CanLoad;\nuse crate::pipeline::source::SourceOffset;\nuse super::lambda_vararg::generate_lambda_class;\nuse super::closure::{ClosureData, Function, LabelsComponent};\n\nuse std::borrow::Borrow;\nuse std::cmp::max;\n\ntype IRExpr = ir::expr::Expr;\ntype IRArgList = ir::arglist::ordinary::ArgList;\n\npub fn compile_labels_scc(frame: &mut CompilerFrame<StmtBuilder>,\n                          clauses: &[&LocalFnClause],\n                          pos: SourceOffset)\n                          -> Result<Vec<(String, FnCall)>, GDError> {\n  // In the perfect world, we would do all of our operations *on*\n  // frame rather than destructuring that variable here. But, the way\n  // this function is currently written, we need access to both\n  // frame.table and lambda_table throughout most of the computation.\n  // Perhaps there's a way to refactor it, but it won't be easy.\n  let CompilerFrame { compiler, pipeline, table, builder, class_scope } = frame;\n\n  let mut class_scope = class_scope.closure_mut();\n\n  let closure = {\n    let mut closure = ClosureData::from(LabelsComponent(clauses));\n    // No need to close around global variables, as they're available everywhere\n    closure.purge_globals(table);\n    closure\n  };\n\n  // Generate an outer class ref if we need access to the scope from\n  // within the lambda class.\n  let mut outer_ref_name = String::new();\n  let needs_outer_ref = closure.closure_fns.needs_outer_class_ref(table);\n  if needs_outer_ref {\n    outer_ref_name = compiler.name_generator().generate_with(inner_class::OUTER_REFERENCE_NAME);\n  }\n\n  // Determine a name for the global class to represent the labels.\n  let class_name = RegisteredNameGenerator::new_global_var(table).generate_with(\"_Labels\");\n\n  // Bind all of the closure variables, closure functions, and global\n  // variables inside.\n  let mut lambda_table = SymbolTable::with_synthetics_from(table);\n  locally_bind_vars(compiler, table, &mut lambda_table, &closure.closure_vars, &[], pos)?;\n  locally_bind_fns(compiler, *pipeline, table, &mut lambda_table, &closure.closure_fns, pos, &OuterStaticRef::InnerInstanceVar(&outer_ref_name))?;\n  copy_global_vars(table, &mut lambda_table);\n\n  // Convert the closures to GDScript names.\n  let gd_closure_vars = closure.to_gd_closure_vars(&lambda_table);\n  let gd_src_closure_vars = closure.to_gd_closure_vars(table);\n\n  let local_var_name = RegisteredNameGenerator::new_local_var(table).generate_with(\"_locals\");\n\n  let mut lambda_table = InnerSymbolTable::new(lambda_table, table);\n\n  // Bind the functions themselves\n  let named_clauses = generate_names_for_scc_clauses(clauses.iter().copied(), compiler.name_generator());\n  for (func_name, clause) in &named_clauses {\n    let specs = FnSpecs::from(clause.args.to_owned());\n    let fn_call = special_local_fn_call(local_var_name.clone(), func_name.clone(), specs);\n    lambda_table.set_fn(clause.name.to_owned(), fn_call, CallMagic::DefaultCall);\n  }\n\n  let (bound_calls, functions) = unzip_err::<GDError, Vec<_>, Vec<_>, _, _, _>(named_clauses.iter().map(|(func_name, clause)| {\n    let mut lambda_table = InnerSymbolTable::cloned_from(&mut lambda_table); // New table for this particular function\n    let mut lambda_builder = StmtBuilder::new();\n    let (arglist, gd_args) = clause.args.clone().into_gd_arglist(&mut RegisteredNameGenerator::new_local_var(&mut lambda_table));\n    // Bind the function arguments\n    for NameTrans { lisp_name: arg, gd_name: gd_arg } in &gd_args {\n      let access_type = *closure.all_vars.get(arg).unwrap_or(&AccessType::None);\n      lambda_table.set_var(arg.to_owned(), LocalVar::local(gd_arg.to_owned(), access_type));\n      wrap_in_cell_if_needed(arg, gd_arg, &closure.all_vars, &mut lambda_builder, pos);\n    }\n    compiler.frame(pipeline, &mut lambda_builder, &mut lambda_table, &mut *class_scope).compile_stmt(&stmt_wrapper::Return, &clause.body)?;\n    let lambda_body = lambda_builder.build_into(*builder);\n    let func_name = func_name.to_owned();\n    let func = decl::FnDecl {\n      name: func_name.clone(),\n      args: arglist,\n      body: lambda_body,\n    };\n    let call = FnCall {\n      scope: FnScope::SpecialLocal(local_var_name.clone()),\n      object: FnName::on_local_var(VarName::local(&local_var_name)),\n      function: func_name,\n      specs: FnSpecs::from(clause.args.to_owned()),\n      is_macro: false,\n    };\n    Ok(((clause.name.to_owned(), call), func))\n  }))?;\n\n  let mut constructor_body = Vec::new();\n  for var in &gd_closure_vars {\n    constructor_body.push(super::assign_to_compiler(var.to_string(), var.to_string(), pos));\n  }\n  let constructor = decl::FnDecl {\n    name: String::from(library::CONSTRUCTOR_NAME),\n    args: ArgList::required(gd_closure_vars.iter().map(|x| x.to_owned()).collect()),\n    body: constructor_body,\n  };\n  let mut class_body = vec!();\n  for var in &gd_closure_vars {\n    class_body.push(Decl::new(DeclF::VarDecl(VarDecl::simple(var.clone())), pos));\n  }\n  class_body.push(Decl::new(DeclF::FnDecl(decl::Static::NonStatic, constructor), pos));\n  for func in functions {\n    class_body.push(Decl::new(DeclF::FnDecl(decl::Static::NonStatic, func), pos));\n  }\n  let mut class = decl::ClassDecl {\n    name: class_name.clone(),\n    extends: ClassExtends::SimpleIdentifier(String::from(\"Reference\")),\n    body: class_body,\n  };\n\n  if needs_outer_ref {\n    inner_class::add_outer_class_ref_named(&mut class, compiler.preload_resolver(), *pipeline, outer_ref_name, pos);\n  }\n\n  builder.add_helper(Decl::new(DeclF::ClassDecl(class), pos));\n  let constructor_args: Vec<_> = gd_src_closure_vars.into_iter().map(|x| Expr::new(ExprF::Var(x), pos)).collect();\n  let expr = Expr::call(Some(Expr::new(ExprF::Var(class_name), pos)), \"new\", constructor_args, pos);\n  builder.append(Stmt::new(StmtF::VarDecl(local_var_name, expr), pos));\n\n  Ok(bound_calls)\n}\n\n/// Generate an appropriate name for an SCC function generated from a\n/// GDLisp function with the given name.\nfn generate_scc_name(original_name: &str, gen: &mut FreshNameGenerator) -> String {\n  let name_prefix = format!(\"_fn_{}\", names::lisp_to_gd(original_name));\n  gen.generate_with(&name_prefix)\n}\n\nfn generate_names_for_scc_clauses<'a>(clauses: impl Iterator<Item=&'a LocalFnClause>, gen: &mut FreshNameGenerator)\n                                      -> Vec<(String, &'a LocalFnClause)> {\n  clauses.map(|clause| {\n    let func_name = generate_scc_name(&clause.name, gen);\n    (func_name, clause)\n  }).collect()\n}\n\n/// Compiles an [`FnCall`] for a `labels`-style function with the\n/// given name and specs, on the given labels object.\npub fn special_local_fn_call(labels_var: String, function: String, specs: FnSpecs) -> FnCall {\n  FnCall {\n    scope: FnScope::SpecialLocal(labels_var),\n    object: FnName::OnLocalScope,\n    function,\n    specs,\n    is_macro: false,\n  }\n}\n\npub fn locally_bind_vars(compiler: &mut Compiler,\n                         table: &SymbolTable,\n                         lambda_table: &mut SymbolTable,\n                         closure_vars: &Locals,\n                         forbidden_names: &[&str],\n                         _pos: SourceOffset) // _pos unused right now, might need it later :)\n                         -> Result<(), GDError> {\n  for (var, _access_type, var_pos) in closure_vars.iter_with_offset() {\n    // Ensure the variable actually exists\n    match table.get_var(var.borrow()) {\n      None => return Err(GDError::new(GDErrorF::NoSuchVar(var.borrow().to_owned()), var_pos)),\n      Some(gdvar) => {\n        let mut new_var = gdvar.to_owned();\n        // Ad-hoc rule for closing around self (TODO Generalize?)\n        if new_var == LocalVar::self_var() { // TODO Special case over in VarName?\n          new_var = LocalVar::local(compiler.name_generator().generate_with(\"_self\"), AccessType::ClosedRead);\n        }\n        let mut name_generator = RegisteredNameGenerator::new_local_var(lambda_table);\n        protect_closure_var_name(&mut name_generator, &mut new_var, forbidden_names);\n        lambda_table.set_var(var.borrow().to_owned(), new_var);\n      }\n    };\n  }\n  Ok(())\n}\n\nfn protect_closure_var_name(gen: &mut impl NameGenerator, var: &mut LocalVar, forbidden_names: &[&str]) {\n  if let Some(original_name) = var.simple_name() {\n    let mut final_name = original_name.to_owned();\n    while forbidden_names.contains(&&*final_name) {\n      final_name = gen.generate_with(original_name);\n    }\n    var.set_simple_name(final_name);\n  }\n}\n\npub fn locally_bind_fns<L>(compiler: &mut Compiler,\n                           pipeline: &L,\n                           table: &SymbolTable,\n                           lambda_table: &mut SymbolTable,\n                           closure_fns: &Functions,\n                           _pos: SourceOffset, // Unused right now, might need it later :)\n                           outer_static_ref: &OuterStaticRef<'_>)\n                           -> Result<(), GDError>\nwhere L : CanLoad {\n  for (func, (), func_pos) in closure_fns.iter_with_offset() {\n    // Ensure the function actually exists\n    match table.get_fn(func.borrow()) {\n      None => { return Err(GDError::new(GDErrorF::NoSuchFn(func.borrow().to_owned()), func_pos)) }\n      Some((call, magic)) => {\n        let mut call = call.clone();\n        call.object.update_for_inner_scope(outer_static_ref, compiler.preload_resolver(), pipeline);\n        lambda_table.set_fn(func.borrow().to_owned(), call, magic.clone());\n      }\n    };\n  }\n  Ok(())\n}\n\n/// Copies all of the variables from `src_table` to `dest_table` whose\n/// [`VarScope`] is [`VarScope::GlobalVar`]. This function does not\n/// modify the function namespace in either table.\npub fn copy_global_vars(src_table: &SymbolTable, dest_table: &mut SymbolTable) {\n  for (name, var) in src_table.vars() {\n    if var.scope == VarScope::GlobalVar {\n      dest_table.set_var(name.to_owned(), var.clone());\n    }\n  }\n}\n\n/// Compiles a call to a lambda class constructor, where the lambda\n/// class has name `class_name`. The closure variables are given by\n/// `gd_src_closure_vars`, and the resulting expression will be given\n/// source offset `pos`.\n///\n/// Any expression in `suffix_args` will be suffixed onto the end of\n/// the closure variable constructor arguments verbatim and can be\n/// used to pass custom arguments to the constructor.\n///\n/// Despite technically being a method call, lambda constructors are\n/// never stateful, so `side_effects` on the result will always be\n/// [`SideEffects::None`].\npub fn make_constructor_call(class_name: String,\n                             gd_src_closure_vars: impl IntoIterator<Item=String>,\n                             suffix_args: Vec<StExpr>,\n                             pos: SourceOffset)\n                             -> StExpr {\n  let side_effects = suffix_args.iter().map(|x| x.side_effects).fold(SideEffects::None, max);\n\n  let mut constructor_args: Vec<_> = gd_src_closure_vars.into_iter().map(|x| Expr::new(ExprF::Var(x), pos)).collect();\n  constructor_args.extend(suffix_args.into_iter().map(|x| x.expr));\n\n  let expr = Expr::call(Some(Expr::new(ExprF::Var(class_name), pos)), \"new\", constructor_args, pos);\n  StExpr { expr, side_effects }\n}\n\nfn wrap_in_cell_if_needed(name: &str, gd_name: &str, all_vars: &Locals, lambda_builder: &mut StmtBuilder, pos: SourceOffset) {\n  if all_vars.get(name).unwrap_or(&AccessType::None).requires_cell() {\n    lambda_builder.append(Stmt::simple_assign(Expr::var(gd_name, pos),\n                                              library::cell::construct_cell(Expr::var(gd_name, pos)),\n                                              pos));\n  }\n}\n\npub fn compile_lambda_stmt(frame: &mut CompilerFrame<StmtBuilder>,\n                           args: &IRArgList,\n                           body: &IRExpr,\n                           minimalist: bool,\n                           pos: SourceOffset)\n                           -> Result<StExpr, GDError> {\n  // In the perfect world, we would do all of our operations *on*\n  // frame rather than destructuring that variable here. But, the way\n  // this function is currently written, we need access to both\n  // frame.table and lambda_table throughout most of the computation.\n  // Perhaps there's a way to refactor it, but it won't be easy.\n  let CompilerFrame { compiler, pipeline, table, builder, class_scope } = frame;\n\n  let mut class_scope = class_scope.closure_mut();\n\n  let closure = {\n    let mut closure = ClosureData::from(Function::new(args, body));\n    // No need to close around global variables, as they're available\n    // everywhere.\n    closure.purge_globals(table);\n    closure\n  };\n\n  // Determine the eventual class name.\n  let class_name = RegisteredNameGenerator::new_global_var(table).generate_with(\"_LambdaBlock\");\n\n  let mut lambda_table = SymbolTable::with_synthetics_from(table);\n\n  let (arglist, gd_args) = args.clone().into_gd_arglist(&mut RegisteredNameGenerator::new_local_var(&mut lambda_table));\n\n  // Bind the arguments to the lambda in the new lambda table.\n  for arg in &gd_args {\n    let access_type = *closure.all_vars.get(&arg.lisp_name).unwrap_or(&AccessType::None);\n    lambda_table.set_var(arg.lisp_name.to_owned(), LocalVar::local(arg.gd_name.to_owned(), access_type));\n  }\n\n  // Generate an outer class ref if we need access to the scope from\n  // within the lambda.\n  let mut outer_ref_name = String::new();\n  let needs_outer_ref = closure.closure_fns.needs_outer_class_ref(table);\n  if needs_outer_ref {\n    outer_ref_name = compiler.name_generator().generate_with(inner_class::OUTER_REFERENCE_NAME);\n  }\n\n  // Bind all of the closure variables, closure functions, and global\n  // variables inside.\n  locally_bind_vars(compiler, table, &mut lambda_table, &closure.closure_vars, &[], pos)?;\n  locally_bind_fns(compiler, *pipeline, table, &mut lambda_table, &closure.closure_fns, pos, &OuterStaticRef::InnerInstanceVar(&outer_ref_name))?;\n  copy_global_vars(table, &mut lambda_table);\n\n  // Convert the closures to GDScript names.\n  let gd_closure_vars = closure.to_gd_closure_vars(&lambda_table);\n  let gd_src_closure_vars = closure.to_gd_closure_vars(table);\n\n  let mut lambda_table = InnerSymbolTable::new(lambda_table, table);\n\n  let lambda_body = {\n    let mut lambda_builder = StmtBuilder::new();\n\n    // Wrap arguments in cells, as needed.\n    for NameTrans { lisp_name: arg, gd_name: gd_arg } in &gd_args {\n      wrap_in_cell_if_needed(arg, gd_arg, &closure.all_vars, &mut lambda_builder, pos);\n    }\n\n    // Compile the lambda body.\n    compiler.frame(pipeline, &mut lambda_builder, &mut lambda_table, &mut *class_scope).compile_stmt(&stmt_wrapper::Return, body)?;\n    lambda_builder.build_into(*builder)\n  };\n\n  // Generate the enclosing class.\n  let mut class = generate_lambda_class(class_name.clone(), args.clone().into(), arglist, &gd_closure_vars, lambda_body, minimalist, pos);\n\n  // Add outer class reference.\n  if needs_outer_ref {\n    inner_class::add_outer_class_ref_named(&mut class, compiler.preload_resolver(), *pipeline, outer_ref_name, pos);\n  }\n\n  // Place the resulting values in the builder.\n  builder.add_helper(Decl::new(DeclF::ClassDecl(class), pos));\n  Ok(make_constructor_call(class_name, gd_src_closure_vars, vec!(), pos))\n}\n\n/// This function compiles a GDLisp function reference, as constructed\n/// using the `(function ...)` special form. GDLisp function\n/// references compile to instances of private helper classes, similar\n/// to lambda expressions.\n///\n/// If the function has scope [`FnScope::Local`], then it is already a\n/// local variable, and the name of that local variable will be\n/// returned without constructing an unnecessary second helper class.\n/// If the function has scope [`FnScope::SpecialLocal`], then the\n/// resulting helper class will have a single constructor argument:\n/// the special local function object. Otherwise, the constructor\n/// function will have no constructor arguments.\npub fn compile_function_ref(compiler: &mut Compiler,\n                            pipeline: &mut Pipeline,\n                            builder: &mut StmtBuilder,\n                            table: &mut SymbolTable,\n                            func: FnCall,\n                            minimalist: bool,\n                            pos: SourceOffset)\n                            -> Result<StExpr, GDError> {\n  if let FnScope::Local(name) = func.scope {\n    // If the function is already bound to a local variable, we can\n    // happily reuse that variable. This is most likely to come up if\n    // we take a function ref of an flet function with a nontrivial\n    // closure.\n    Ok(StExpr { expr: Expr::new(ExprF::Var(name), pos), side_effects: SideEffects::None })\n  } else {\n    let arglist = simple_arg_names(func.specs.runtime_arity());\n\n    // Normally, function references are either to local variables (in\n    // which case, we have a reference already) or functions (in which\n    // case, there is no closure so a simple top-level class will do).\n    // However, if the referent is from a nontrivial SCC of a labels\n    // block, then we have to paradoxically close around it, since it\n    // *is* local but doesn't satisfy the funcref interface.\n    let gd_src_closure_vars =\n      if let FnScope::SpecialLocal(name) = func.scope {\n        vec!(name)\n      } else {\n        vec!()\n      };\n\n    let object = func.object.clone().into_inner_scope(&OuterStaticRef::InnerStatic, compiler.preload_resolver(), pipeline).into_expr(pos);\n    let body = Stmt::new(\n      StmtF::ReturnStmt(\n        Expr::call(object, &func.function, arglist.all_args_iter().map(|x| Expr::var(x, pos)).collect(), pos)\n      ),\n      pos,\n    );\n\n    // Generate the class and the constructor call.\n    let class_name = RegisteredNameGenerator::new_global_var(table).generate_with(\"_FunctionRefBlock\");\n    let class = generate_lambda_class(class_name.clone(), func.specs, arglist, &gd_src_closure_vars, vec!(body), minimalist, pos);\n    builder.add_helper(Decl::new(DeclF::ClassDecl(class), pos));\n    Ok(make_constructor_call(class_name, gd_src_closure_vars, vec!(), pos))\n  }\n}\n\n/// Simple helper function to generate basic function arguments with a\n/// regular naming scheme. This function makes no effort to avoid\n/// conflicts with other variables and should only be used in scopes\n/// where such conflicts are impossible.\n///\n/// The names generated by this function begin with \"arg\" and are\n/// followed by a numerical index from 0 up to (exclusive) `count`.\n///\n/// # Examples\n///\n/// ```\n/// # use gdlisp::compile::special_form::lambda::simple_arg_names;\n/// # use gdlisp::gdscript::arglist::ArgList;\n/// assert_eq!(simple_arg_names(0), ArgList::required(vec!()));\n/// assert_eq!(simple_arg_names(4), ArgList::required(vec!(\"arg0\".to_string(), \"arg1\".to_string(), \"arg2\".to_string(), \"arg3\".to_string())));\n/// ```\npub fn simple_arg_names(count: usize) -> ArgList {\n  let arg_names = (0..count).map(|i| format!(\"arg{}\", i)).collect();\n  ArgList::required(arg_names)\n}\n"
  },
  {
    "path": "src/compile/special_form/lambda_class.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::ir;\nuse crate::ir::expr::LambdaClass;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::compile::factory;\nuse crate::compile::CompilerFrame;\nuse crate::compile::Compiler;\nuse crate::compile::stateful::{StExpr, NeedsResult};\nuse crate::compile::body::builder::{StmtBuilder, HasDecls};\nuse crate::compile::body::class_initializer::ClassBuilder;\nuse crate::compile::body::class_scope::DirectClassScope;\nuse crate::compile::symbol_table::{SymbolTable, ClassTablePair};\nuse crate::compile::symbol_table::inner::InnerSymbolTable;\nuse crate::compile::symbol_table::local_var::LocalVar;\nuse crate::compile::symbol_table::function_call::OuterStaticRef;\nuse crate::compile::names::registered::RegisteredNameGenerator;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::gdscript::decl::{self, Decl, DeclF, VarDecl};\nuse crate::gdscript::inner_class::{self, NeedsOuterClassRef};\nuse crate::pipeline::source::SourceOffset;\nuse super::lambda;\nuse super::closure::ClosureData;\n\npub fn compile_lambda_class(frame: &mut CompilerFrame<StmtBuilder>,\n                            class: &LambdaClass,\n                            pos: SourceOffset)\n                            -> Result<StExpr, GDError> {\n  // In the perfect world, we would do all of our operations *on*\n  // frame rather than destructuring that variable here. But, the way\n  // this function is currently written, we need access to both\n  // frame.table and lambda_table throughout most of the computation.\n  // Perhaps there's a way to refactor it, but it won't be easy.\n  let CompilerFrame { compiler, pipeline, table, builder, class_scope: original_class_scope } = frame;\n  let LambdaClass { extends, args: constructor_args, constructor, decls } = class;\n\n  // Note: We construct a new class scope here, since we're now inside\n  // of a newly-declared class. We use the original_class_scope to\n  // compile the constructor arguments, since super calls to the scope\n  // enclosing the class declaration are still valid in that syntactic\n  // position.\n  let mut class_scope = DirectClassScope::new();\n\n  // Validate the extends declaration (must be a global variable)\n  let extends = Compiler::resolve_extends(table, extends, pos)?;\n\n  // New GD name\n  let gd_class_name = RegisteredNameGenerator::new_global_var(table).generate_with(\"_AnonymousClass\");\n\n  let closure = {\n    let mut closure = ClosureData::from(class);\n    // No need to close around global variables, as they're available\n    // everywhere.\n    closure.purge_globals(table);\n    closure\n  };\n\n  // Generate an outer class ref if we need access to the scope from\n  // within the lambda class.\n  let mut outer_ref_name = String::new();\n  let needs_outer_ref = closure.closure_fns.needs_outer_class_ref(table);\n  if needs_outer_ref {\n    outer_ref_name = compiler.name_generator().generate_with(inner_class::OUTER_REFERENCE_NAME);\n  }\n\n  let mut lambda_table = SymbolTable::with_synthetics_from(table);\n\n  // Bind self into the lambda table.\n  lambda_table.set_var(String::from(\"self\"), LocalVar::self_var());\n\n  // Bind all of the closure variables, closure functions, and global\n  // variables inside.\n  let forbidden_names = get_all_instance_scoped_vars(decls);\n  lambda::locally_bind_vars(compiler, table, &mut lambda_table, &closure.closure_vars, &forbidden_names, pos)?;\n  lambda::locally_bind_fns(compiler, *pipeline, table, &mut lambda_table, &closure.closure_fns, pos, &OuterStaticRef::InnerInstanceVar(&outer_ref_name))?;\n  lambda::copy_global_vars(table, &mut lambda_table);\n\n  // Convert the closures to GDScript names.\n  let gd_closure_vars = closure.to_gd_closure_vars(&lambda_table);\n  let gd_src_closure_vars = closure.to_gd_closure_vars(table);\n\n  let mut lambda_table = InnerSymbolTable::new(lambda_table, table);\n\n  // Build the constructor for the lambda class.\n  let default_constructor: ir::decl::ConstructorDecl;\n  let constructor = match constructor {\n    None => {\n      default_constructor = ir::decl::ConstructorDecl::empty(pos);\n      &default_constructor\n    }\n    Some(c) => {\n      c\n    }\n  };\n  let (constructor, constructor_helpers) = compile_lambda_class_constructor(&mut compiler.frame(pipeline, *builder, &mut lambda_table, &mut class_scope), constructor, &gd_closure_vars, pos)?;\n\n  // Build the class body for the lambda class.\n  let mut class_init_builder = ClassBuilder::new();\n  #[allow(clippy::vec_init_then_push)] // For style consistency\n  let class_body = {\n    let mut class_body = vec!();\n    class_body.push(Decl::new(DeclF::InitFnDecl(constructor), pos));\n    for helper in constructor_helpers {\n      class_body.push(Decl::new(DeclF::FnDecl(decl::Static::NonStatic, helper), pos));\n    }\n    for name in gd_closure_vars.iter() {\n      class_body.push(Decl::new(DeclF::VarDecl(VarDecl::simple(name.clone())), pos));\n    }\n    for d in decls {\n\n      if d.is_static() {\n        // Static methods / constants are not allowed on lambda classes\n        return Err(GDError::new(GDErrorF::StaticOnLambdaClass(d.name().into_owned()), d.pos));\n      }\n\n      // Nothing static is allowed in lambda classes (static methods\n      // or constants). The ClassTablePair simply gets a dummy static\n      // symbol table that will never be used, since we just checked\n      // in the above code that the declaration is non-static.\n      let mut dummy_table = SymbolTable::new();\n      let tables = ClassTablePair { instance_table: &mut lambda_table, static_table: &mut dummy_table };\n      class_body.push(compiler.compile_class_inner_decl(pipeline, &mut class_init_builder, tables, &mut class_scope, d)?);\n\n    }\n    class_body\n  };\n\n  drop(lambda_table);\n\n  let mut class = decl::ClassDecl {\n    name: gd_class_name.clone(),\n    extends: extends,\n    body: class_body,\n  };\n\n  if needs_outer_ref {\n    inner_class::add_outer_class_ref_named(&mut class, compiler.preload_resolver(), *pipeline, outer_ref_name, pos);\n  }\n\n  class_init_builder.declare_proxies_from_scope(class_scope);\n\n  let class_init = class_init_builder.build_into(*builder);\n  class_init.apply(&mut class, pos)?;\n\n  builder.add_helper(Decl::new(DeclF::ClassDecl(class), pos));\n\n  let constructor_args = constructor_args.iter().map(|expr| compiler.frame(pipeline, *builder, table, *original_class_scope).compile_expr(expr, NeedsResult::Yes)).collect::<Result<Vec<_>, _>>()?;\n  let expr = lambda::make_constructor_call(gd_class_name, gd_src_closure_vars, constructor_args, pos);\n  Ok(expr)\n}\n\nfn get_all_instance_scoped_vars(decls: &[ir::decl::ClassInnerDecl]) -> Vec<&str> {\n  // TODO This is NOT a complete solution. See Issue #82\n  // (https://github.com/Mercerenies/gdlisp/issues/82) for the\n  // problems with this implementation.\n  let mut result: Vec<&str> = vec![];\n  for decl in decls {\n    match &decl.value {\n      ir::decl::ClassInnerDeclF::ClassVarDecl(var) => {\n        result.push(&var.name);\n      }\n      ir::decl::ClassInnerDeclF::ClassConstDecl(cdecl) => {\n        result.push(&cdecl.name);\n      }\n      ir::decl::ClassInnerDeclF::ClassSignalDecl(_) => {}\n      ir::decl::ClassInnerDeclF::ClassFnDecl(_) => {}\n    }\n  }\n  result\n}\n\nfn compile_lambda_class_constructor(frame: &mut CompilerFrame<impl HasDecls>,\n                                    constructor: &ir::decl::ConstructorDecl,\n                                    gd_closure_vars: &[String],\n                                    pos: SourceOffset)\n                                    -> Result<(decl::InitFnDecl, Vec<decl::FnDecl>), GDError> {\n  let (mut constructor, constructor_helpers) = factory::declare_constructor(frame, constructor)?;\n  constructor.args.prepend_required(gd_closure_vars.iter().cloned());\n  for name in gd_closure_vars.iter().rev() {\n    constructor.body.insert(0, super::assign_to_compiler(name.to_string(), name.to_string(), pos));\n  }\n  Ok((constructor, constructor_helpers))\n}\n"
  },
  {
    "path": "src/compile/special_form/lambda_vararg/builder.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::stmt::{self, Stmt};\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::op;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::iter;\n\n/// A `LambdaVarargBuilder` builds up what will eventually be a\n/// `Vec<Stmt>`. In the course of doing so, we assume that a variable\n/// with the name given by `args_variable` (in the constructor) is in\n/// scope, and we accumulate values from that variable into several\n/// others, eventually culminating in a final function call which uses\n/// the accumulated arguments.\n#[derive(Debug, Clone)]\npub struct LambdaVarargBuilder {\n  args_variable: String,\n  stmts: Vec<Stmt>,\n  args: Vec<Expr>,\n  pos: SourceOffset,\n}\n\nimpl LambdaVarargBuilder {\n\n  /// Construct a new builder with the given arguments variable, an\n  /// empty list of accumulated arguments, and the given source\n  /// position. The source position is used for GDLisp compiler error\n  /// messages.\n  pub fn new(args_variable: String, pos: SourceOffset) -> LambdaVarargBuilder {\n    LambdaVarargBuilder::with_existing_args(args_variable, iter::empty(), pos)\n  }\n\n  /// Construct a new builder with the given arguments variable, an\n  /// inherited list of accumulated arguments, and the given source\n  /// position. The source position is used for GDLisp compiler error\n  /// messages.\n  pub fn with_existing_args(args_variable: String, args: impl Iterator<Item=Expr>, pos: SourceOffset) -> LambdaVarargBuilder {\n    LambdaVarargBuilder {\n      args_variable: args_variable,\n      stmts: Vec::new(),\n      args: args.collect(),\n      pos: pos,\n    }\n  }\n\n  /// Runs an internal block with a builder inherited from `self`,\n  /// returning the statements produced by the inner builder.\n  ///\n  /// The new builder will be created with the same arguments variable\n  /// and `SourceOffset`, and it will be created with the current\n  /// accumulated argument list of the outer builder. Changes to the\n  /// inner builder's argument list will not propagate to the outer.\n  /// The given `block` shall be called with the inner builder, and\n  /// then `inner_builder.build()` will be returned.\n  pub fn with_inner_builder(&self, block: impl FnOnce(&mut LambdaVarargBuilder)) -> Vec<Stmt> {\n    let mut inner_builder = LambdaVarargBuilder::with_existing_args(\n      self.args_variable.to_owned(),\n      self.args.iter().cloned(),\n      self.pos,\n    );\n    block(&mut inner_builder);\n    inner_builder.build()\n  }\n\n  /// Declares a variable (whose initial value is [`Expr::null`]), and\n  /// adds it to the accumulated arguments list.\n  pub fn declare_argument_var(&mut self, name: String) {\n    self.stmts.push(Stmt::var_decl(name.clone(), Expr::null(self.pos), self.pos));\n    self.args.push(Expr::new(ExprF::Var(name), self.pos));\n  }\n\n  /// Pushes a call to the GDScript built-in function `push_error`,\n  /// with the given error message.\n  pub fn push_error(&mut self, message: &str) {\n    self.stmts.push(Stmt::expr(\n      Expr::simple_call(\"push_error\", vec!(Expr::str_lit(message, self.pos)), self.pos),\n    ));\n  }\n\n  /// This method generates code to take the first element (i.e. the\n  /// `car`) of the arguments variable and assign it to the variable\n  /// indicated by `variable_name`. Then the arguments variable is\n  /// reassigned to its own `cdr`. Note that this will fail if the\n  /// arguments variable contains `nil`, so it is often better to run\n  /// this inside of a\n  /// [`if_args_is_empty`](LambdaVarargBuilder::if_args_is_empty)\n  /// block, to be sure.\n  pub fn pop_argument(&mut self, variable_name: &str) {\n    self.stmts.push(Stmt::simple_assign(\n      Expr::var(variable_name, self.pos),\n      Expr::var(&self.args_variable, self.pos).attribute(\"car\", self.pos),\n      self.pos,\n    ));\n    self.stmts.push(Stmt::simple_assign(\n      Expr::var(&self.args_variable, self.pos),\n      Expr::var(&self.args_variable, self.pos).attribute(\"cdr\", self.pos),\n      self.pos,\n    ));\n  }\n\n  /// Assigns an arbitrary value to the given variable.\n  pub fn assign_to_var(&mut self, variable_name: &str, value: Expr) {\n    self.stmts.push(Stmt::simple_assign(Expr::var(variable_name, self.pos), value, self.pos));\n  }\n\n  /// Takes the arguments variable, indicating all of the arguments\n  /// that have not been processed yet, and adds it, as a single\n  /// scalar unit, to the accumulated arguments list.\n  ///\n  /// The `function` argument indicates a transformative function to\n  /// be applied to the arguments variable before using it as an\n  /// accumulated argument. If the transformative function is not\n  /// necessary, then [`LambdaVarargBuilder::pop_rest_of_arguments`]\n  /// can be used instead.\n  pub fn pop_rest_of_arguments_with<F>(&mut self, function: F)\n  where F : FnOnce(Expr) -> Expr {\n    let args_var = Expr::new(ExprF::Var(self.args_variable.to_owned()), self.pos);\n    self.args.push(function(args_var));\n  }\n\n  /// Takes the arguments variable, indicating all of the arguments\n  /// that have not been processed yet, and adds it, as a single\n  /// scalar unit, to the accumulated arguments list.\n  ///\n  /// Equivalent to `self.pop_rest_of_arguments_with(|x| x)`.\n  pub fn pop_rest_of_arguments(&mut self) {\n    self.pop_rest_of_arguments_with(|x| x);\n  }\n\n  /// Generates code to call the function given by `function_name`,\n  /// passing all of the accumulated arguments in order. The code is\n  /// generated as part of a `return` statement, so generally speaking\n  /// no more code should be executed after this point.\n  pub fn call_function_with_arguments(&mut self, function_name: &str) {\n    let all_args = self.args.clone();\n    self.stmts.push(Stmt::return_stmt(Expr::simple_call(function_name, all_args, self.pos), self.pos));\n  }\n\n  /// Generates code for an `if` statement, where the first branch is\n  /// followed if the arguments variable has the value `nil` and the\n  /// second is followed otherwise.\n  ///\n  /// The two branches are created by passing `empty_block` and\n  /// `nonempty_block`, respectively, to\n  /// [`LambdaVarargBuilder::with_inner_builder`].\n  pub fn if_args_is_empty<F1, F2>(&mut self, empty_block: F1, nonempty_block: F2)\n  where F1 : FnOnce(&mut LambdaVarargBuilder),\n        F2 : FnOnce(&mut LambdaVarargBuilder) {\n    let empty_case: Vec<Stmt> = self.with_inner_builder(empty_block);\n    let nonempty_case: Vec<Stmt> = self.with_inner_builder(nonempty_block);\n    self.stmts.push(stmt::if_else(\n      Expr::binary(Expr::var(&self.args_variable, self.pos), op::BinaryOp::Eq, Expr::null(self.pos), self.pos),\n      empty_case,\n      nonempty_case,\n      self.pos,\n    ));\n  }\n\n  /// Consumes the builder and returns the sequence of statements\n  /// generated.\n  pub fn build(self) -> Vec<Stmt> {\n    self.stmts\n  }\n\n}\n"
  },
  {
    "path": "src/compile/special_form/lambda_vararg/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod builder;\n\nuse builder::LambdaVarargBuilder;\nuse crate::ir::arglist::vararg::VarArg;\nuse crate::compile::symbol_table::function_call::FnSpecs;\nuse crate::gdscript::decl::{self, Decl, DeclF, VarDecl};\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::library;\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::gdscript::arglist::ArgList;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::convert::TryInto;\n\nconst ARGS_VAR: &str = \"args\";\n\npub fn generate_lambda_vararg(specs: FnSpecs, pos: SourceOffset) -> decl::FnDecl {\n  let mut builder = LambdaVarargBuilder::new(ARGS_VAR.to_owned(), pos);\n\n  let required: Vec<_> = (0..specs.required).map(|i| format!(\"required_{}\", i)).collect();\n  let optional: Vec<_> = (0..specs.optional).map(|i| format!(\"optional_{}\", i)).collect();\n\n  for req in &required {\n    builder.declare_argument_var(req.to_owned());\n    builder.if_args_is_empty(|builder| {\n      builder.push_error(\"Not enough arguments\");\n    }, |builder| {\n      builder.pop_argument(req);\n    });\n  }\n\n  for opt in &optional {\n    builder.declare_argument_var(opt.to_owned());\n    builder.if_args_is_empty(|builder| {\n      builder.assign_to_var(opt, Expr::null(pos));\n    }, |builder| {\n      builder.pop_argument(opt);\n    });\n  }\n\n  match specs.rest {\n    Some(VarArg::RestArg) => {\n      builder.pop_rest_of_arguments();\n      builder.call_function_with_arguments(\"call_func\");\n    }\n    Some(VarArg::ArrArg) => {\n      builder.pop_rest_of_arguments_with(|args| {\n        Expr::call(Some(Expr::var(library::GDLISP_NAME, pos)), \"list_to_array\", vec!(args), pos)\n      });\n      builder.call_function_with_arguments(\"call_func\");\n    }\n    None => {\n      builder.if_args_is_empty(|builder| {\n        builder.call_function_with_arguments(\"call_func\");\n      }, |builder| {\n        builder.push_error(\"Too many arguments\");\n      });\n    }\n  }\n\n  decl::FnDecl {\n    name: String::from(\"call_funcv\"),\n    args: ArgList::required(vec!(String::from(ARGS_VAR))),\n    body: builder.build(),\n  }\n}\n\npub fn generate_lambda_class(class_name: String,\n                             specs: FnSpecs,\n                             args: ArgList,\n                             closed_vars: &[String],\n                             lambda_body: Vec<Stmt>,\n                             minimalist_run: bool,\n                             pos: SourceOffset)\n                             -> decl::ClassDecl {\n  let func_name = String::from(\"call_func\");\n  let func = decl::FnDecl {\n    name: func_name,\n    args: args,\n    body: lambda_body,\n  };\n  let funcv = generate_lambda_vararg(specs, pos);\n  let mut constructor_body = Vec::new();\n  for name in closed_vars.iter() {\n    constructor_body.push(super::assign_to_compiler(name.to_string(), name.to_string(), pos));\n  }\n  let r: i32  = specs.required.try_into().unwrap();\n  let o: i32  = specs.optional.try_into().unwrap();\n  let x: i32  = VarArg::arg_to_const(specs.rest);\n  constructor_body.push(super::assign_expr_to_compiler(String::from(\"__gdlisp_required\"), Expr::from_value(r, pos)));\n  constructor_body.push(super::assign_expr_to_compiler(String::from(\"__gdlisp_optional\"), Expr::from_value(o, pos)));\n  constructor_body.push(super::assign_expr_to_compiler(String::from(\"__gdlisp_rest\"), Expr::from_value(x, pos)));\n  let constructor =\n    decl::FnDecl {\n      name: String::from(library::CONSTRUCTOR_NAME),\n      args: ArgList::required(closed_vars.iter().map(|x| x.to_owned()).collect()),\n      body: constructor_body,\n    };\n  let mut class_body = vec!();\n  for var in closed_vars {\n    class_body.push(Decl::new(DeclF::VarDecl(VarDecl::simple(var.to_owned())), pos));\n  }\n  class_body.append(&mut vec!(\n    Decl::new(DeclF::FnDecl(decl::Static::NonStatic, constructor), pos),\n    Decl::new(DeclF::FnDecl(decl::Static::NonStatic, func), pos),\n    Decl::new(DeclF::FnDecl(decl::Static::NonStatic, funcv), pos),\n  ));\n  decl::ClassDecl {\n    name: class_name,\n    extends: gdlisp_function_class(minimalist_run),\n    body: class_body,\n  }\n}\n\nfn gdlisp_function_class(minimalist_run: bool) -> ClassExtends {\n  if minimalist_run {\n    ClassExtends::SimpleIdentifier(String::from(\"Function\"))\n  } else {\n    ClassExtends::SimpleIdentifier(String::from(\"GDLisp\")).attribute(String::from(\"Function\"))\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::ir::arglist::vararg::VarArg;\n\n  fn compile_vararg(specs: FnSpecs) -> String {\n    let result = generate_lambda_vararg(specs, SourceOffset::default());\n    Decl::new(DeclF::FnDecl(decl::Static::NonStatic, result), SourceOffset::default()).to_gd(0)\n  }\n\n  #[test]\n  fn test_lambda_vararg() {\n    assert_eq!(compile_vararg(FnSpecs::new(0, 0, None)), \"func call_funcv(args):\\n    if args == null:\\n        return call_func()\\n    else:\\n        push_error(\\\"Too many arguments\\\")\\n\");\n    assert_eq!(compile_vararg(FnSpecs::new(0, 0, Some(VarArg::RestArg))), \"func call_funcv(args):\\n    return call_func(args)\\n\");\n    assert_eq!(compile_vararg(FnSpecs::new(0, 0, Some(VarArg::ArrArg))), \"func call_funcv(args):\\n    return call_func(GDLisp.list_to_array(args))\\n\");\n    assert_eq!(compile_vararg(FnSpecs::new(1, 0, Some(VarArg::RestArg))), \"func call_funcv(args):\\n    var required_0 = null\\n    if args == null:\\n        push_error(\\\"Not enough arguments\\\")\\n    else:\\n        required_0 = args.car\\n        args = args.cdr\\n    return call_func(required_0, args)\\n\");\n    assert_eq!(compile_vararg(FnSpecs::new(0, 1, Some(VarArg::RestArg))), \"func call_funcv(args):\\n    var optional_0 = null\\n    if args == null:\\n        optional_0 = null\\n    else:\\n        optional_0 = args.car\\n        args = args.cdr\\n    return call_func(optional_0, args)\\n\");\n  }\n\n}\n"
  },
  {
    "path": "src/compile/special_form/let_block.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::compile::frame::CompilerFrame;\nuse crate::compile::stateful::{StExpr, NeedsResult};\nuse crate::compile::factory;\nuse crate::compile::names;\nuse crate::compile::names::registered::RegisteredNameGenerator;\nuse crate::compile::body::builder::StmtBuilder;\nuse crate::compile::error::GDError;\nuse crate::compile::symbol_table::HasSymbolTable;\nuse crate::compile::symbol_table::local_var::LocalVar;\nuse crate::gdscript::library;\nuse crate::ir;\nuse crate::ir::access_type::AccessType;\nuse crate::ir::expr::LocalVarClause;\nuse crate::pipeline::source::SourceOffset;\n\ntype IRExpr = ir::expr::Expr;\n\npub fn compile_let(frame: &mut CompilerFrame<StmtBuilder>,\n                   clauses: &[LocalVarClause],\n                   body: &IRExpr,\n                   needs_result: NeedsResult,\n                   _pos: SourceOffset)\n                   -> Result<StExpr, GDError> {\n  let closure_vars = body.get_locals();\n  let var_names = clauses.iter().map::<Result<(String, String), GDError>, _>(|clause| {\n    let LocalVarClause { name: ast_name, value: expr } = clause;\n    let ast_name = ast_name.to_owned();\n    let result_value = frame.compile_expr(expr, NeedsResult::Yes)?.expr;\n    let result_value =\n      if closure_vars.get(&ast_name).unwrap_or(&AccessType::None).requires_cell() {\n        library::cell::construct_cell(result_value)\n      } else {\n        result_value\n      };\n    let gd_name = factory::declare_var(&mut RegisteredNameGenerator::new_local_var(frame.table), frame.builder, &names::lisp_to_gd(&ast_name), Some(result_value), clause.value.pos);\n    Ok((ast_name, gd_name))\n  }).collect::<Result<Vec<_>, _>>()?;\n  frame.with_local_vars(&mut var_names.into_iter().map(|x| (x.0.clone(), LocalVar::local(x.1, *closure_vars.get(&x.0).unwrap_or(&AccessType::None)))), |frame| {\n    frame.compile_expr(body, needs_result)\n  })\n}\n"
  },
  {
    "path": "src/compile/special_form/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod lambda;\npub mod flet;\npub mod lambda_class;\npub mod lambda_vararg;\npub mod closure;\npub mod let_block;\n\nuse crate::ir;\nuse crate::ir::access_type::AccessType;\nuse crate::compile::{Compiler, StExpr, NeedsResult};\nuse crate::compile::body::builder::StmtBuilder;\nuse crate::compile::symbol_table::HasSymbolTable;\nuse crate::compile::symbol_table::local_var::LocalVar;\nuse crate::compile::stmt_wrapper;\nuse crate::compile::error::GDError;\nuse crate::compile::stateful::SideEffects;\nuse crate::compile::names;\nuse crate::compile::names::registered::RegisteredNameGenerator;\nuse crate::compile::factory;\nuse crate::compile::frame::CompilerFrame;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::gdscript::stmt::{self, Stmt, StmtF};\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::op;\nuse crate::pipeline::source::SourceOffset;\nuse crate::util;\n\ntype IRExpr = ir::expr::Expr;\n\npub fn compile_cond_stmt(frame: &mut CompilerFrame<StmtBuilder>,\n                         clauses: &[(IRExpr, Option<IRExpr>)],\n                         needs_result: NeedsResult,\n                         pos: SourceOffset)\n                         -> Result<StExpr, GDError> {\n  let (destination, result) = needs_result.into_destination(&mut RegisteredNameGenerator::new_local_var(frame.table), frame.builder, \"_cond\", pos);\n  let init: Vec<Stmt> = util::option_to_vec(destination.wrap_to_stmt(Compiler::nil_expr(pos)));\n  let body = clauses.iter().rev().fold(Ok(init), |acc: Result<_, GDError>, curr| {\n    let acc = acc?;\n    let (cond, body) = curr;\n    match body {\n      None => {\n        // Outer local builder (for constructing any values needed in\n        // the conditional).\n        frame.with_local_builder(|frame| {\n          let cond = frame.compile_expr(cond, NeedsResult::Yes)?.expr;\n          let var_name = factory::declare_var(&mut RegisteredNameGenerator::new_local_var(frame.table), frame.builder, \"_cond\", Some(cond), pos);\n          let var_expr = StExpr { expr: Expr::new(ExprF::Var(var_name.clone()), pos), side_effects: SideEffects::None };\n          // Inner local builder (for the contents of the \"true\"\n          // block).\n          let if_branch = frame.with_local_builder_ok(|frame| {\n            destination.wrap_to_builder(frame.builder, var_expr);\n          });\n          frame.builder.append(stmt::if_else(Expr::new(ExprF::Var(var_name), pos), if_branch, acc, pos));\n          Ok(())\n        })\n      }\n      Some(body) => {\n        // Outer local builder (for constructing any values needed in\n        // the conditional).\n        frame.with_local_builder(|frame| {\n          let cond = frame.compile_expr(cond, NeedsResult::Yes)?.expr;\n          // Inner local builder (for the contents of the \"true\"\n          // block).\n          let if_branch = frame.with_local_builder(|frame| {\n            frame.compile_stmt(destination.as_ref(), body)\n          })?;\n          frame.builder.append(stmt::if_else(cond, if_branch, acc, pos));\n          Ok(())\n        })\n      }\n    }\n  })?;\n  frame.builder.append_all(&mut body.into_iter());\n  Ok(StExpr { expr: result, side_effects: SideEffects::None })\n}\n\npub fn compile_while_stmt(frame: &mut CompilerFrame<StmtBuilder>,\n                          cond: &IRExpr,\n                          body: &IRExpr,\n                          _needs_result: NeedsResult,\n                          pos: SourceOffset)\n                          -> Result<StExpr, GDError> {\n  // If the condition fits in a single GDScript expression, then we'll\n  // just compile straight to a GDScript while loop. If not, then we\n  // need to compile to \"while True:\" and have a break statement when\n  // we check our conditional. So, to figure out whether the condition\n  // fits in a single expression, we'll compile it with a temporary\n  // builder and then ask that builder whether or not it received any\n  // statements.\n  let (mut cond_expr, cond_body) = frame.with_local_builder_result(|frame| {\n    frame.compile_expr(cond, NeedsResult::Yes).map(|x| x.expr)\n  })?;\n  let body = frame.with_local_builder(|frame| {\n    if !cond_body.is_empty() {\n      // Compound while form\n      frame.builder.append_all(&mut cond_body.into_iter());\n      let inner_cond_expr = cond_expr.clone().unary(op::UnaryOp::Not, pos);\n      frame.builder.append(stmt::if_then(inner_cond_expr, vec!(Stmt::new(StmtF::BreakStmt, cond.pos)), cond.pos));\n      cond_expr = Expr::from_value(true, pos);\n    }\n    frame.compile_stmt(&stmt_wrapper::Vacuous, body)?;\n    Ok(())\n  })?;\n  frame.builder.append(Stmt::new(StmtF::WhileLoop(stmt::WhileLoop { condition: cond_expr, body: body }), pos));\n  Ok(Compiler::nil_expr(pos))\n}\n\npub fn compile_for_stmt(frame: &mut CompilerFrame<StmtBuilder>,\n                        name: &str,\n                        iter: &IRExpr,\n                        body: &IRExpr,\n                        _needs_result: NeedsResult,\n                        pos: SourceOffset)\n                        -> Result<StExpr, GDError> {\n  let closure_vars = body.get_locals();\n  let citer = frame.compile_expr(iter, NeedsResult::Yes)?.expr;\n  let var_name = RegisteredNameGenerator::new_fn(frame.table).generate_with(&names::lisp_to_gd(name));\n  let body = frame.with_local_builder(|frame| {\n    let local_var = LocalVar::local(var_name.to_owned(), *closure_vars.get(name).unwrap_or(&AccessType::None));\n    frame.with_local_var(name.to_owned(), local_var, |frame| {\n      frame.compile_stmt(&stmt_wrapper::Vacuous, body).map(|_| ())\n    })\n  })?;\n  frame.builder.append(Stmt::new(StmtF::ForLoop(stmt::ForLoop { iter_var: var_name, collection: citer, body: body }), pos));\n  Ok(Compiler::nil_expr(pos))\n}\n\n/// A [`Stmt`] which assigns the value of the local variable\n/// `local_var` to the variable `inst_var` on the Godot `self` object.\npub fn assign_to_compiler(inst_var: String, local_var: String, pos: SourceOffset) -> Stmt {\n  assign_expr_to_compiler(inst_var, Expr::new(ExprF::Var(local_var), pos))\n}\n\n/// A [`Stmt`] which assigns `expr` to the variable `inst_var` on the\n/// Godot `self` object.\npub fn assign_expr_to_compiler(inst_var: String, expr: Expr) -> Stmt {\n  let pos = expr.pos;\n  let self_target = Expr::self_var(pos).attribute(inst_var, pos);\n  Stmt::simple_assign(self_target, expr, pos)\n}\n"
  },
  {
    "path": "src/compile/stateful.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Types for expressing whether or not an [`Expr`] has side effects.\n\nuse super::factory;\nuse super::stmt_wrapper::{self, StmtWrapper};\nuse super::names::generator::NameGenerator;\nuse super::body::builder::StmtBuilder;\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::ir::access_type::AccessType;\nuse crate::pipeline::source::SourceOffset;\n\n/// An `StExpr` is an expression coupled with a declaration of that\n/// expression's side effects.\n///\n/// Note that such a declaration is context-sensitive, so there is no\n/// general-purpose function for converting an `Expr` into an\n/// `StExpr`. For example, simple variable access `foobar` could be\n/// read-only, assuming the variable is a local variable, but it could\n/// also be read-write, if the variable is an instance variable which\n/// has a `setget` modifier. Likewise, a function call is generally\n/// read-write, but if we can prove that the call is free of side\n/// effects, we can reduce this.\n#[derive(Debug, Clone)]\npub struct StExpr {\n  /// The expression being wrapped.\n  pub expr: Expr,\n  /// A declaration of the side effects of the wrapped expression, as\n  /// a [`SideEffects`] value.\n  pub side_effects: SideEffects,\n}\n\n/// A declaration of side effects.\n///\n/// `SideEffects` has an [`Ord`] instance, where more invasive effects\n/// are considered greater than less invasive ones. This makes it\n/// possible to use [`std::cmp::max`] to take the more invasive of two\n/// side effects.\n#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]\npub enum SideEffects {\n  /// The expression has no side effects. An expression with this\n  /// declaration modifies nothing and reads no mutable state. It\n  /// should be possible to move the expression, for inlining\n  /// purposes, to anywhere else within the same scope, or anywhere\n  /// else which contains the appropriate constant values necessary\n  /// for evaluation. An expression with `SideEffects::None` can\n  /// safely be evaluated several times, or have several identical\n  /// evaluations reduced to one. The optimizer has a great deal of\n  /// control over these expressions.\n  None,\n  /// The expression does not modify any state, but it may read from\n  /// mutable state. An expression with `SideEffects::ReadsState` may\n  /// be evaluated multiple times, or may have multiple evaluations\n  /// reduced to one, provided that no [`SideEffects::ModifiesState`]\n  /// calls happen in between the evaluations. Such expressions may\n  /// also be reordered relative to other `SideEffects::ReadsState` or\n  /// `SideEffects::None` expressions.\n  ReadsState,\n  /// The expression may read or write to ambient state. It is not\n  /// safe to reorder multiple `SideEffects::ModifiesState`\n  /// expressions relative to one another, and evaluating such a\n  /// statement multiple times is different than evaluating it only\n  /// once. The optimizer is very limited in this case.\n  ModifiesState,\n}\n\n/// A Boolean-isomorphic type indicating whether a result is required.\n///\n/// An argument of type `NeedsResult` is used in functions such as\n/// [`CompilerFrame::compile_expr`](super::frame::CompilerFrame::compile_expr)\n/// to indicate whether or not the result is going to be used for\n/// anything. There are some expressions that can compile to a more\n/// efficient form if we know the result is unneeded. For instance,\n/// `if` expressions in general need to create a local variable to\n/// store the result in, but if we know the result is going to be\n/// discarded, then we can safely skip that step.\n#[derive(Debug, PartialEq, Eq, Clone, Copy)]\npub enum NeedsResult { No, Yes }\n\nimpl From<NeedsResult> for bool {\n  fn from(s: NeedsResult) -> bool {\n    s == NeedsResult::Yes\n  }\n}\n\nimpl From<bool> for NeedsResult {\n  fn from(b: bool) -> NeedsResult {\n    if b { NeedsResult::Yes } else { NeedsResult::No }\n  }\n}\n\nimpl NeedsResult {\n\n  /// Returns `Yes` iff either `self` or `other` is `Yes`.\n  pub fn or(self, other: NeedsResult) -> NeedsResult {\n    NeedsResult::from(self == NeedsResult::Yes || other == NeedsResult::Yes)\n  }\n\n  /// Returns `Yes` iff both `self` and `other` are `Yes`.\n  pub fn and(self, other: NeedsResult) -> NeedsResult {\n    NeedsResult::from(self == NeedsResult::Yes && other == NeedsResult::Yes)\n  }\n\n  /// Construct an appropriate [`StmtWrapper`] that can store the\n  /// result of an expression, if needed.\n  ///\n  /// If `self` is `Yes`, then this function declares a variable (into\n  /// `builder`) with a local, generated name (using `prefix` as the\n  /// prefix) and then returns a `StmtWrapper` which assigns a value\n  /// to that variable. The `Expr`, in this case, is the variable\n  /// name.\n  ///\n  /// If `self` is `No`, then this function returns a vacuous\n  /// `StmtWrapper` and [`Expr::null()`] as the expression.\n  ///\n  /// In general, the returned `StmtWrapper` should be supplied with\n  /// the Godot expression which is, semantically, the result of the\n  /// IR expression, and the returned `Expr` can be used to reference\n  /// it later. If `self` is `No`, then the returned `Expr` is nil, as\n  /// we chose not to store the result anywhere.\n  pub fn into_destination(self,\n                          gen: &mut impl NameGenerator,\n                          builder: &mut StmtBuilder,\n                          prefix: &str,\n                          pos: SourceOffset)\n                          -> (Box<dyn StmtWrapper>, Expr) {\n    if self.into() {\n      let var_name = factory::declare_var(gen, builder, prefix, None, pos);\n      let destination = Box::new(stmt_wrapper::assign_to_var(var_name.clone(), pos)) as Box<dyn StmtWrapper>;\n      (destination, Expr::new(ExprF::Var(var_name), pos))\n    } else {\n      let destination = Box::new(stmt_wrapper::Vacuous) as Box<dyn StmtWrapper>;\n      (destination, Expr::null(pos))\n    }\n  }\n\n}\n\nimpl SideEffects {\n\n  /// Whether or not `self` is *at least* `SideEffects::ReadsState`.\n  ///\n  /// # Examples\n  ///\n  /// ```\n  /// # use gdlisp::compile::stateful::SideEffects;\n  /// assert!(!SideEffects::None.reads_state());\n  /// assert!(SideEffects::ReadsState.reads_state());\n  /// assert!(SideEffects::ModifiesState.reads_state());\n  /// ```\n  pub fn reads_state(&self) -> bool {\n    *self >= SideEffects::ReadsState\n  }\n\n  /// Whether or not `self` is *at least*\n  /// `SideEffects::ModifiesState`.\n  ///\n  /// # Examples\n  ///\n  /// ```\n  /// # use gdlisp::compile::stateful::SideEffects;\n  /// assert!(!SideEffects::None.modifies_state());\n  /// assert!(!SideEffects::ReadsState.modifies_state());\n  /// assert!(SideEffects::ModifiesState.modifies_state());\n  /// ```\n  pub fn modifies_state(&self) -> bool {\n    *self >= SideEffects::ModifiesState\n  }\n}\n\n/// If we're writing to a variable, then the side effect is obviously\n/// ModifiesState and this From instance should not be used. If we're\n/// reading from a variable, then we can convert the AccessType of\n/// that variable into a SideEffects by determining whether or not the\n/// variable is ever modified (x.is_written_to) to figure out if we're\n/// querying mutable state or simply accessing a constant value.\nimpl From<AccessType> for SideEffects {\n  fn from(x: AccessType) -> SideEffects {\n    if x.is_written_to() {\n      SideEffects::ReadsState\n    } else {\n      SideEffects::None\n    }\n  }\n}\n"
  },
  {
    "path": "src/compile/stmt_wrapper.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Trait and implementations for conveniently wrapping [`Expr`] into\n//! [`Stmt`].\n//!\n//! This module defines the [`StmtWrapper`] trait, as well as several\n//! implementations of it by structs.\n\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::op;\nuse crate::compile::body::builder::StmtBuilder;\nuse crate::pipeline::source::SourceOffset;\nuse super::StExpr;\n\n/// A `StmtWrapper` provides a mechanism for taking an arbitrary\n/// [`Expr`] and wrapping it into a [`Stmt`]. It can be thought of as\n/// a `Stmt` with a hole in it, which we'll later fill with an\n/// expression.\npub trait StmtWrapper {\n\n  /// Wraps the expression in a statement.\n  fn wrap_expr(&self, expr: Expr) -> Stmt;\n\n  /// Normally, a wrapper, as the name implies, wraps an expression in\n  /// a statement. Sometimes, however, the expression has no side\n  /// effects, and the work itself has already been done elsewhere. In\n  /// this case, `is_vacuous` specifies whether we actually *need* the\n  /// result or not. If `is_vacuous` is true and the expression has no\n  /// side effects, it will be elided completely.\n  ///\n  /// The default `is_vacuous` always returns false. It can be\n  /// overridden in implementors.\n  fn is_vacuous(&self) -> bool {\n    false\n  }\n\n  /// Wraps `expr` and places it in `builder`.\n  ///\n  /// If `self` is vacuous and `expr` does not modify state, this\n  /// method does nothing. The reasoning for this is as follows. If\n  /// `expr.side_effects` does not modify state, then the expression,\n  /// as a value, is only being used for its result, not for its side\n  /// effects. If `self.is_vacuous()` is true, then we've declared\n  /// that this statement wrapper does not care about the result. So\n  /// if we have an expression for which the only thing that matters\n  /// is the result and a statement wrapper that's ready to ignore\n  /// that result, then we effectively have no need to ever evaluate\n  /// the expression in the first place.\n  fn wrap_to_builder(&self, builder: &mut StmtBuilder, expr: StExpr) {\n    if let Some(stmt) = self.wrap_to_stmt(expr) {\n      builder.append(stmt);\n    }\n  }\n\n  /// Wraps `expr` and returns either zero or one statements, using\n  /// the same reasoning at [`StmtWrapper.wrap_to_builder`]. If `self`\n  /// is vacuous and the expression is stateless, then the vector will\n  /// be empty. Otherwise, a single statement will be produced via\n  /// [`StmtWrapper::wrap_expr`].\n  fn wrap_to_stmt(&self, expr: StExpr) -> Option<Stmt> {\n    let StExpr { expr, side_effects } = expr;\n    if side_effects.modifies_state() || !self.is_vacuous() {\n      Some(self.wrap_expr(expr))\n    } else {\n      None\n    }\n  }\n\n}\n\n/// A [`StmtWrapper`] which wraps the expression in a\n/// [`StmtF::ReturnStmt`].\npub struct Return;\n\n/// A [`StmtWrapper`] which wraps the expression in a [`StmtF::Expr`].\n/// This is a vacuous statement wrapper, as per\n/// [`StmtWrapper::is_vacuous`].\npub struct Vacuous;\n\n/// A [`StmtWrapper`] which wraps the expression in a\n/// [`StmtF::Assign`], where the left-hand side is given by the\n/// `AssignToExpr` value.\npub struct AssignToExpr(pub Expr);\n\nimpl StmtWrapper for Return {\n  fn wrap_expr(&self, expr: Expr) -> Stmt {\n    let pos = expr.pos;\n    Stmt::new(StmtF::ReturnStmt(expr), pos)\n  }\n}\n\nimpl StmtWrapper for Vacuous {\n\n  fn wrap_expr(&self, expr: Expr) -> Stmt {\n    let pos = expr.pos;\n    Stmt::new(StmtF::Expr(expr), pos)\n  }\n\n  fn is_vacuous(&self) -> bool {\n    true\n  }\n\n}\n\n/// An [`AssignToExpr`] which assigns to an [`ExprF::Var`] with the\n/// given name.\npub fn assign_to_var(s: String, pos: SourceOffset) -> AssignToExpr {\n  AssignToExpr(Expr::new(ExprF::Var(s), pos))\n}\n\nimpl StmtWrapper for AssignToExpr {\n  fn wrap_expr(&self, expr: Expr) -> Stmt {\n    let pos = expr.pos;\n    let lhs = Box::new(self.0.clone());\n    let rhs = Box::new(expr);\n    Stmt::new(StmtF::Assign(lhs, op::AssignOp::Eq, rhs), pos)\n  }\n}\n"
  },
  {
    "path": "src/compile/symbol_table/call_magic/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Function call magic, for deterministic inlining behavior.\n//!\n//! Function calls, by default, compile to function calls in GDScript,\n//! naturally. However, there are many situations where the function\n//! on the GDScript side is simply a trivial wrapper around some\n//! operation that should be inlined. This is such a trivial inline\n//! step that's so ubiquitously useful that we do it here as a rule.\n//! If a built-in function is called directly (i.e. not through a\n//! funcref) then it can trigger a special [`CallMagic`] which\n//! effectively inlines it.\n//!\n//! For a good example, look at the `+` GDLisp function. In general,\n//! it compiles to `GDLisp.plus`, which iterates over its arguments,\n//! adds them, and returns the result. But, of course, `(+ a b)`\n//! shouldn't require a for loop, so any time we call `+` with an\n//! arity known at compile time (i.e. without invoking funcrefs or the\n//! like), we can compile directly to the `+` operator in GDScript,\n//! which is much more efficient.\n//!\n//! Note that this is *not* general-purpose inlining, which will be\n//! implemented later as a general pass over the IR. This is for the\n//! very specific case of certain GDScript functions written in\n//! `GDLisp.lisp` which I know how to inline effectively by hand.\n\npub mod table;\n\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::literal::Literal;\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::op;\nuse crate::gdscript::library;\nuse crate::gdscript::expr_wrapper;\nuse crate::compile::Compiler;\nuse crate::compile::factory;\nuse crate::compile::error::GDError;\nuse crate::compile::body::builder::StmtBuilder;\nuse crate::compile::stateful::StExpr;\nuse crate::compile::stmt_wrapper::{self, StmtWrapper};\nuse crate::compile::args::{self, Expecting};\nuse crate::compile::names::registered::RegisteredNameGenerator;\nuse crate::compile::constant::CONSTANT_GDSCRIPT_FUNCTIONS;\nuse crate::ir::arglist::vararg::VarArg;\nuse crate::util;\nuse crate::pipeline::source::SourceOffset;\nuse super::function_call::FnCall;\nuse super::SymbolTable;\n\nuse serde::{Serialize, Deserialize};\n\n/// A `CallMagic` can meaningfully compile a given function call\n/// expression `call` into some GDScript [`Expr`].\n///\n/// Note that, although we talk about call magic applying to certain\n/// designated builtin calls, strictly speaking every function call\n/// written in GDLisp invokes call magic. This trait is *always* used\n/// to compile function calls. In most cases, for user-defined\n/// functions or for builtins without special behavior, the\n/// [`CallMagic::DefaultCall`] option is used, which performs the\n/// basic function call compilation routine via\n/// [`compile_default_call`].\n#[derive(Clone, Debug, Serialize, Deserialize)]\npub enum CallMagic {\n  /// A default [`CallMagic`] for any functions without special\n  /// behavior. The `CallMagic` implementation for `DefaultCall`\n  /// simply delegates to [`compile_default_call`].\n  DefaultCall,\n  /// [Call magic](CallMagic) for the `-` builtin.\n  MinusOperation,\n  /// [Call magic](CallMagic) for the (fractional) division operator.\n  DivOperation,\n  /// [Call magic](CallMagic) for the integer division operator.\n  ///\n  /// **Note:** This call magic is currently not used in\n  /// `GDLisp.lisp`.\n  IntDivOperation,\n  /// [Call magic](CallMagic) for the mathematical modulo operator.\n  ModOperation,\n  /// [Call magic](CallMagic) for the `min` function.\n  MinFunction,\n  /// [Call magic](CallMagic) for the `max` function.\n  MaxFunction,\n  /// [`CallMagic`] for the inequality `/=` operator. `NEqOperation`\n  /// only provides special behavior if two or fewer arguments are\n  /// given. If more than two arguments are given, `NEqOperation` will\n  /// fall back to `fallback`.\n  NEqOperation(Box<CallMagic>),\n  /// The [call magic](CallMagic) for the Boolean negation operation.\n  BooleanNotOperation,\n  /// [`CallMagic`] for the builtin `list` function. This call magic\n  /// compiles calls to literal `cons` cell constructors.\n  ListOperation,\n  /// [`CallMagic`] for the builtin `array` function. This call magic\n  /// compiles calls to a literal GDScript array.\n  ArrayOperation,\n  /// [`CallMagic`] for the builtin `dict` function. This call magic\n  /// compiles calls to a literal GDScript dictionary. If given an odd\n  /// number of arguments, the last argument will be silently dropped.\n  DictOperation,\n  /// [`CallMagic`] for the builtin `vector` function, which compiles\n  /// `vector` calls to literal `Vector2` or `Vector3` constructions\n  /// in GDScript.\n  VectorOperation,\n  /// [`CallMagic`] for array subscript (via `elt`). This magic\n  /// compiles directly to the `foo[bar]` subscript notation in\n  /// GDScript.\n  ArraySubscript,\n  /// [`CallMagic`] for array subscript assignment (via `set-elt`). This\n  /// magic compiles directly to the `foo[bar] = baz` subscript notation\n  /// in GDScript.\n  ArraySubscriptAssign,\n  /// [Call magic] for the `member?` builtin function. This magic\n  /// compiles to the GDScript `in` operator.\n  ElementOf,\n  /// [Call magic] for instance type checks in Godot.\n  ///\n  /// Note that the GDLisp built-in `instance?` does *not* compile to\n  /// this magic, as that function is overloaded internally to work on\n  /// primitive type constants as well as actual instances. This magic\n  /// is used on the internal system function `sys/instance-direct?`.\n  InstanceOf,\n  /// [Call magic] for the `$` syntax used to invoke `get_node` in\n  /// GDScript.\n  ///\n  /// This call magic compiles expressions of the form `(sys/get-node\n  /// a b)` into GDScript `$x` syntax wherever it makes sense to do\n  /// so, or `(a:get-node b)` in any other context.\n  GetNodeSyntax,\n  /// `CompileToBinOp` is a [`CallMagic`] which compiles function\n  /// calls to sequences of binary operator application.\n  ///\n  /// If no arguments are provided, then this magic compiles to `zero`\n  /// unconditionally. If one argument is provided, it is passed\n  /// through untouched. If two or more arguments are provided, they\n  /// are combined using `bin` in order, with associativity given by\n  /// `assoc`.\n  ///\n  /// This call magic is used to implement the GDLisp builtins `+` and\n  /// `*`.\n  CompileToBinOp(Literal, op::BinaryOp, Assoc),\n  /// `CompileToTransCmp` is a [`CallMagic`] which compiles function\n  /// calls to transitive sequences of binary comparison applications.\n  ///\n  /// Conceptually, `CompileToTransCmp` can be thought of as\n  /// translating a call like `(< a b c d)` into `a < b and b < c and\n  /// c < d`. However, the translation is more subtle than that, as\n  /// any of the names which are evaluated twice (i.e. all except the\n  /// first and last) have to be checked for side effects. If the\n  /// arguments might exhibit side effects, then they will need to be\n  /// assigned to local variables to avoid evaluating the calls twice\n  /// in the expression.\n  ///\n  /// If zero arguments are provided, then an error is returned. If\n  /// one argument is provided, the result is vacuously true, as there\n  /// are no comparisons to be performed.\n  ///\n  /// This call magic is used for most builtin comparison operators,\n  /// such as `=` and `<`. It is notably *not* used for `/=`, which is\n  /// handled by [`CallMagic::NEqOperation`] due to its unique (non-transitive)\n  /// behavior.\n  CompileToTransCmp(op::BinaryOp),\n  /// `CompileToVarargCall` is a [`CallMagic`] which compiles a\n  /// function call to a literal function call (to a GDScript global\n  /// function) with the same set of arguments.\n  ///\n  /// This is used for variable-argument built-in functions like\n  /// `print`. Since we can't (meaningfully) make a GDLisp function\n  /// reference object for the built-in `print` function, GDLisp\n  /// provides its own `print` function that wraps the built-in one,\n  /// and we provide call magic to compile that directly to the\n  /// original `print` in all situations except indirect ones.\n  CompileToVarargCall(String),\n  /// [`CallMagic`] for the inequality `NodePath` constructor.\n  /// `NodePathConstructor` will compile to the literal `@\"...\"`\n  /// GDScript syntax if given a literal string as argument. If given\n  /// a variable or anything else, it will fall back to `fallback`.\n  NodePathConstructor(Box<CallMagic>),\n}\n\n/// Associativity of an operator.\n#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]\npub enum Assoc {\n  /// A left associative operator, as `(a + b) + c`.\n  Left,\n  /// A right associative operator, as `a + (b + c)`.\n  Right,\n}\n\n// For most of these (but *not* all of them), we need Vec<Expr>, not\n// Vec<StExpr>. The latter gives more information than we usually\n// need. So this helper just strips off the excess.\nfn strip_st(x: Vec<StExpr>) -> Vec<Expr> {\n  x.into_iter().map(|x| x.expr).collect()\n}\n\n/// The \"default\" mechanism for compiling a function call, in the\n/// absence of any nontrivial magic. This function compiles `call` and\n/// its arguments `args` into a standard GDScript function call,\n/// taking into consideration any optional-argument or rest-argument\n/// padding which needs to be done to make the call correct on the\n/// GDScript call. This is called by [`CallMagic::compile`] in the\n/// [`CallMagic::DefaultCall`] case to perform its work.\npub fn compile_default_call(call: FnCall, mut args: Vec<Expr>, pos: SourceOffset) -> Result<Expr, GDError> {\n  let FnCall { scope: _, object, function, specs, is_macro: _ } = call;\n  // First, check arity\n  Expecting::from(specs).validate(&function, pos, &args)?;\n  // Get the \"rest\" arguments\n  let rest = if args.len() < (specs.required + specs.optional) as usize {\n    vec!()\n  } else {\n    args.split_off((specs.required + specs.optional) as usize)\n  };\n  // Extend with nil\n  while args.len() < (specs.required + specs.optional) as usize {\n    args.push(Expr::null(pos));\n  }\n  // Bind the \"rest\" arguments\n  match specs.rest {\n    None => {\n      // We already checked arity, so if this assertion fails it's a\n      // bug in the compiler.\n      assert!(rest.is_empty());\n    }\n    Some(VarArg::RestArg) => {\n      args.push(library::construct_list(rest, pos));\n    }\n    Some(VarArg::ArrArg) => {\n      args.push(Expr::new(ExprF::ArrayLit(rest), pos));\n    }\n  }\n  // Then compile the call.\n  let object: Option<Expr> = object.into_expr(pos);\n  Ok(Expr::new(ExprF::Call(object.map(Box::new), function, args), pos))\n}\n\nimpl CallMagic {\n  // TODO Currently, this uses the GD name in error messages, which is\n  // super wonky, especially for stdlib calls. Store the Lisp name and\n  // use it for this.\n\n  pub fn is_default(&self) -> bool {\n    matches!(self, CallMagic::DefaultCall)\n  }\n\n  /// Given a [`FnCall`] instance `call` and argument list `args`,\n  /// compile the call into a GDScript [`Expr`]. `compiler` provides a\n  /// fresh name generator and compilation state, `builder` provides\n  /// the enclosing block body as a [`StmtBuilder`], and `table`\n  /// provides the enclosing scope information.\n  pub fn compile(&self,\n                 call: FnCall,\n                 _compiler: &mut Compiler,\n                 builder: &mut StmtBuilder,\n                 table: &mut SymbolTable,\n                 mut args: Vec<StExpr>, // TODO Get this declared immutable here and mutable on inner scopes only\n                 pos: SourceOffset) -> Result<Expr, GDError> {\n    match self {\n      CallMagic::DefaultCall => {\n        let args = strip_st(args);\n        compile_default_call(call, args, pos)\n      }\n      CallMagic::CompileToBinOp(zero, op, assoc) => {\n        let args = strip_st(args);\n        if args.is_empty() {\n          let expr = Expr::from_value(zero.clone(), pos);\n          Ok(expr)\n        } else {\n          Ok(match assoc {\n            Assoc::Left => {\n              util::fold1(args.into_iter(), |x, y| Expr::new(ExprF::Binary(Box::new(x), *op, Box::new(y)), pos))\n            }\n            Assoc::Right => {\n              util::fold1(args.into_iter().rev(), |x, y| Expr::new(ExprF::Binary(Box::new(y), *op, Box::new(x)), pos))\n            }\n          }.unwrap())\n        }\n      }\n      CallMagic::CompileToTransCmp(op) => {\n        Expecting::at_least(1).validate(&call.function, pos, &args)?;\n        match args.len() {\n          0 => {\n            unreachable!()\n          }\n          1 => {\n            // Dump to the builder as a simple statement if it's stateful.\n            stmt_wrapper::Vacuous.wrap_to_builder(builder, args[0].clone());\n            Ok(Expr::from_value(true, pos))\n          }\n          2 => {\n            let a = args.remove(0).expr;\n            let b = args.remove(0).expr;\n            Ok(Expr::new(ExprF::Binary(Box::new(a), *op, Box::new(b)), pos))\n          }\n          _ => {\n            // We need to use several of the arguments twice, so any\n            // arguments (such as function calls) which are\n            // potentially stateful need to be stored in temporaries.\n            // Note that simply accessing variables, even variables\n            // which may change, is fine, since we're doing it twice\n            // in a row, and nothing happens in between.\n            let args = args.into_iter().map(|x| {\n              let StExpr { expr, side_effects } = x;\n              if side_effects.modifies_state() {\n                let var_name = factory::declare_var(&mut RegisteredNameGenerator::new_local_var(table), builder, \"_cmp\", Some(expr), pos);\n                Expr::new(ExprF::Var(var_name), pos)\n              } else {\n                expr\n              }\n            });\n            let comparisons = util::each_pair(args).map(|(x, y)| {\n              Expr::new(ExprF::Binary(Box::new(x), *op, Box::new(y)), pos)\n            });\n            Ok(\n              util::fold1(comparisons, |x, y| Expr::new(ExprF::Binary(Box::new(x), op::BinaryOp::And, Box::new(y)), pos)).unwrap()\n            )\n          }\n        }\n      }\n      CallMagic::MinusOperation => {\n        let args = strip_st(args);\n        Expecting::at_least(1).validate(&call.function, pos, &args)?;\n        match args.len() {\n          0 => {\n            unreachable!()\n          }\n          1 => {\n            let arg = args::one(args);\n            Ok(arg.unary(op::UnaryOp::Negate, pos))\n          }\n          _ => {\n            Ok(\n              util::fold1(args.into_iter(), |x, y| x.binary(op::BinaryOp::Sub, y, pos)).unwrap()\n            )\n          }\n        }\n      }\n      CallMagic::DivOperation => {\n        let mut args = strip_st(args);\n        Expecting::at_least(1).validate(&call.function, pos, &args)?;\n        match args.len() {\n          0 => {\n            unreachable!()\n          }\n          1 => {\n            let one = Expr::from_value(1, pos);\n            let arg = args::one(args);\n            Ok(one.binary(op::BinaryOp::Div, arg, pos))\n          }\n          _ => {\n            let first = args.remove(0);\n            let result = args.into_iter().fold(first, |x, y| {\n              x.binary(op::BinaryOp::Div, y, pos)\n            });\n            Ok(result)\n          }\n        }\n      }\n      CallMagic::IntDivOperation => {\n        let mut args = strip_st(args);\n        Expecting::at_least(1).validate(&call.function, pos, &args)?;\n        match args.len() {\n          0 => {\n            unreachable!()\n          }\n          1 => {\n            let one = Expr::from_value(1, pos);\n            let arg = args::one(args);\n            Ok(one.binary(op::BinaryOp::Div, expr_wrapper::int(arg), pos))\n          }\n          _ => {\n            let first = args.remove(0);\n            let result = args.into_iter().fold(first, |x, y| {\n              x.binary(op::BinaryOp::Div, expr_wrapper::int(y), pos)\n            });\n            Ok(result)\n          }\n        }\n      }\n      CallMagic::ModOperation => {\n        let args = strip_st(args);\n        Expecting::exactly(2).validate(&call.function, pos, &args)?;\n        let (x, y) = args::two(args);\n        Ok(Expr::new(ExprF::Binary(Box::new(x), op::BinaryOp::Mod, Box::new(y)), pos))\n      }\n      CallMagic::MinFunction => {\n        let args = strip_st(args);\n        if args.is_empty() {\n          let expr = Expr::var(\"INF\", pos);\n          Ok(expr)\n        } else {\n          Ok(\n            util::fold1(args.into_iter(), |x, y| Expr::simple_call(\"min\", vec!(x, y), pos)).unwrap(),\n          )\n        }\n      }\n      CallMagic::MaxFunction => {\n        let args = strip_st(args);\n        if args.is_empty() {\n          let expr = Expr::var(\"INF\", pos).unary(op::UnaryOp::Negate, pos);\n          Ok(expr)\n        } else {\n          Ok(\n            util::fold1(args.into_iter(), |x, y| Expr::simple_call(\"max\", vec!(x, y), pos)).unwrap(),\n          )\n        }\n      }\n      CallMagic::NEqOperation(fallback) => {\n        // We only optimize for the 0, 1, and 2 argument cases. Any more\n        // arguments than that and the resulting expression would just be\n        // long and annoying, and it's simply easier to call the built-in\n        // anyway.\n        Expecting::at_least(1).validate(&call.function, pos, &args)?;\n        match args.len() {\n          0 => {\n            unreachable!()\n          }\n          1 => {\n            // Dump to the builder as a simple statement if it's stateful.\n            stmt_wrapper::Vacuous.wrap_to_builder(builder, args[0].clone());\n            Ok(Expr::from_value(true, pos))\n          }\n          2 => {\n            let (lhs, rhs) = args::two(strip_st(args));\n            Ok(lhs.binary(op::BinaryOp::NE, rhs, pos))\n          }\n          _ => {\n            fallback.compile(call, _compiler, builder, table, args, pos)\n          }\n        }\n      }\n      CallMagic::BooleanNotOperation => {\n        let args = strip_st(args);\n        Expecting::exactly(1).validate(&call.function, pos, &args)?;\n        let arg = args::one(args);\n        Ok(arg.unary(op::UnaryOp::Not, pos))\n      }\n      CallMagic::ListOperation => {\n        let args = strip_st(args);\n        Ok(library::construct_list(args, pos))\n      }\n      CallMagic::ArrayOperation => {\n        let args = strip_st(args);\n        Ok(Expr::new(ExprF::ArrayLit(args), pos))\n      }\n      CallMagic::DictOperation => {\n        let args = strip_st(args);\n        let contents: Vec<_> = util::each_non_overlapping_pair(args.into_iter()).collect();\n        Ok(Expr::new(ExprF::DictionaryLit(contents), pos))\n      }\n      CallMagic::VectorOperation => {\n        let args = strip_st(args);\n        Expecting::between(2, 3).validate(&call.function, pos, &args)?;\n        match args.len() {\n          2 => {\n            let (x, y) = args::two(args);\n            Ok(Expr::call(None, \"Vector2\", vec!(x, y), pos))\n          }\n          3 => {\n            let (x, y, z) = args::three(args);\n            Ok(Expr::call(None, \"Vector3\", vec!(x, y, z), pos))\n          }\n          _ => {\n            unreachable!()\n          }\n        }\n      }\n      CallMagic::ArraySubscript => {\n        let args = strip_st(args);\n        Expecting::exactly(2).validate(&call.function, pos, &args)?;\n        let (arr, n) = args::two(args);\n        Ok(arr.subscript(n, pos))\n      }\n      CallMagic::ArraySubscriptAssign => {\n        let args = strip_st(args);\n        Expecting::exactly(3).validate(&call.function, pos, &args)?;\n        let (x, arr, n) = args::three(args);\n\n        let assign_target = arr.subscript(n, pos);\n        builder.append(Stmt::simple_assign(assign_target.clone(), x, pos));\n        Ok(assign_target)\n      }\n      CallMagic::ElementOf => {\n        let args = strip_st(args);\n        Expecting::exactly(2).validate(&call.function, pos, &args)?;\n        let (value, arr) = args::two(args);\n\n        Ok(value.binary(op::BinaryOp::In, arr, pos))\n      }\n      CallMagic::InstanceOf => {\n        let args = strip_st(args);\n        Expecting::exactly(2).validate(&call.function, pos, &args)?;\n        let (value, type_) = args::two(args);\n\n        Ok(value.binary(op::BinaryOp::Is, type_, pos))\n      }\n      CallMagic::GetNodeSyntax => {\n        let args = strip_st(args);\n        Expecting::exactly(2).validate(&call.function, pos, &args)?;\n        let (value, path) = args::two(args);\n\n        if let ExprF::Literal(Literal::String(s)) = &path.value {\n          if value.value == ExprF::Var(String::from(\"self\")) {\n            // We can use the $x syntax on the GDScript side\n            return Ok(Expr::from_value(Literal::NodeLiteral(s.to_owned()), pos));\n          }\n        }\n\n        // Otherwise, just compile to self.get_node.\n        Ok(\n          Expr::call(\n            Some(value),\n            \"get_node\",\n            vec!(path),\n            pos,\n          ),\n        )\n\n      }\n      CallMagic::CompileToVarargCall(name) => {\n        let args = strip_st(args);\n        Expecting::from(call.specs).validate(&call.function, pos, &args)?;\n        Ok(Expr::simple_call(name, args, pos))\n      }\n      CallMagic::NodePathConstructor(fallback) => {\n        if args.len() == 1 {\n          if let ExprF::Literal(Literal::String(s)) = &args[0].expr.value {\n            return Ok(\n              Expr::new(ExprF::Literal(Literal::NodePathLiteral(s.clone())), pos),\n            );\n          }\n        }\n        fallback.compile(call, _compiler, builder, table, args, pos)\n      }\n    }\n  }\n\n  /// Returns whether the function indicated by this call magic object\n  /// can be called in a `const` context, given sufficiently constant\n  /// arguments. This function also includes the argument count, for\n  /// magics that depend on the number of arguments.\n  pub fn can_be_called_as_const(&self, arg_count: usize) -> bool {\n    match self {\n      CallMagic::DefaultCall => false, // In the abstract, we cannot perform a function call in const context\n      CallMagic::MinusOperation => true,\n      CallMagic::DivOperation => true,\n      CallMagic::IntDivOperation => true,\n      CallMagic::ModOperation => true,\n      CallMagic::MinFunction => true,\n      CallMagic::MaxFunction => true,\n      CallMagic::NEqOperation(inner_magic) => (arg_count <= 2) || inner_magic.can_be_called_as_const(arg_count),\n      CallMagic::BooleanNotOperation => true,\n      CallMagic::ListOperation => false,\n      CallMagic::ArrayOperation => true,\n      CallMagic::DictOperation => true,\n      CallMagic::VectorOperation => true,\n      CallMagic::ArraySubscript => true,\n      CallMagic::ArraySubscriptAssign => false,\n      CallMagic::ElementOf => true,\n      CallMagic::InstanceOf => false, // Weirdly enough, GDScript seems to not consider `is` a const operator\n      CallMagic::GetNodeSyntax => false, // Either requires `self` or a `get_node` call; either way, non-const\n      CallMagic::CompileToBinOp(_, _, _) => true, // TODO Some operators might cause trouble\n      CallMagic::CompileToTransCmp(_) => true, // TODO Some operators might cause trouble\n      CallMagic::CompileToVarargCall(name) => CONSTANT_GDSCRIPT_FUNCTIONS.contains(&**name),\n      CallMagic::NodePathConstructor(_) => true, // Either a literal string or `NodePath`, both of which are const\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/compile/symbol_table/call_magic/table.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! The [`MagicTable`] structure for tables of [call magic](super).\n//!\n//! The compiler maintains a table of known call magic that the user's\n//! source code is allowed to link up to during compilation.\n//! Currently, this table is read-only and there are no facilities in\n//! GDLisp to change it.\n//!\n//! Note that this functionality is used for bootstrapping and there\n//! is *never* a legitimate reason for an end user to ever need to\n//! interface with this directly. As such, everything here should be\n//! regarded as an implementation detail (like anything else in the\n//! `sys/*` namespace).\n\nuse super::CallMagic;\n\nuse std::collections::HashMap;\n\n/// A `MagicTable` is a table where the keys are strings and the\n/// values are [`CallMagic`](super::CallMagic) instances.\n///\n/// This type can be thought of, in spirit, as `HashMap<String,\n/// CallMagic>`.\n#[derive(Clone, Debug, Default)]\npub struct MagicTable {\n  values: HashMap<String, CallMagic>,\n}\n\nimpl MagicTable {\n\n  /// An empty `MagicTable`. Equivalent to `MagicTable::default()`.\n  pub fn new() -> MagicTable {\n    MagicTable::default()\n  }\n\n  /// Gets the call magic associated to the given name.\n  pub fn get(&self, name: &str) -> Option<&CallMagic> {\n    self.values.get(name)\n  }\n\n  /// Assigns call magic to the given name, replacing any previous\n  /// call magic affiliated with that name.\n  pub fn set(&mut self, name: String, value: CallMagic) {\n    self.values.insert(name, value);\n  }\n\n  /// Removes the call magic associated to the given name, if it\n  /// exists.\n  pub fn del(&mut self, name: &str) {\n    self.values.remove(name);\n  }\n\n  /// Converts `self` into a hash map containing the same information.\n  pub fn into_hashmap(self) -> HashMap<String, CallMagic> {\n    self.values\n  }\n\n}\n\nimpl From<HashMap<String, CallMagic>> for MagicTable {\n  fn from(values: HashMap<String, CallMagic>) -> MagicTable {\n    MagicTable { values }\n  }\n}\n"
  },
  {
    "path": "src/compile/symbol_table/function_call.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! [`FnCall`] and various helper structures for storing information\n//! about functions.\n\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::inner_class;\nuse crate::ir::arglist::vararg::VarArg;\nuse crate::compile::Compiler;\nuse crate::compile::error::GDError;\nuse crate::compile::body::builder::StmtBuilder;\nuse crate::compile::stateful::StExpr;\nuse crate::compile::preload_resolver::PreloadResolver;\nuse crate::compile::args::Expecting;\nuse crate::compile::constant::CONSTANT_GDSCRIPT_FUNCTIONS;\nuse crate::pipeline::can_load::CanLoad;\nuse crate::pipeline::source::SourceOffset;\nuse super::call_magic::{CallMagic};\nuse super::local_var::VarName;\nuse super::SymbolTable;\n\nuse serde::{Serialize, Deserialize};\n\n/// All of the relevant information needed to make a call to a\n/// function is stored in `FnCall`.\n/// [`CallMagic`](super::call_magic::CallMagic) requires an `FnCall` to\n/// identify the information about a function.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct FnCall {\n  /// The function's scope.\n  pub scope: FnScope,\n  /// The expression for the object on which the function is being\n  /// called.\n  pub object: FnName,\n  /// The name of the function, as a GDScript identifier.\n  pub function: String,\n  /// The shape of the function.\n  pub specs: FnSpecs,\n  /// Whether or not the function being called is in fact a macro.\n  /// This can affect the way in which a function is imported in\n  /// scope.\n  pub is_macro: bool,\n}\n\n/// The type of scope in which a function declaration appears. This\n/// affects certain compiled behaviors of the function, such as\n/// whether it needs to be included in closures and how it gets\n/// imported into new scopes.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum FnScope {\n  /// A superglobal function is available in all scopes, such as\n  /// built-in GDScript functions like `abs()` or `min()`.\n  /// Superglobals never need to be closed around or qualified, even\n  /// if imported.\n  Superglobal,\n  /// A global function is defined at the global scope and will be\n  /// compiled to a globally-scoped function. When imported, globals\n  /// will have their name qualified. Global functions do not require\n  /// closures, as they do not close around any data.\n  Global,\n  /// A semiglobal function is still local in scope from the GDLisp\n  /// perspective, but it doesn't require any variable closures, so it\n  /// will be promoted to a global function on the GDScript side, for\n  /// efficiency reasons.\n  SemiGlobal,\n  /// A local function is a closure which exists as a local variable\n  /// on the GDScript side, very similar to a lambda but with\n  /// different name resolution rules. The string parameter is the\n  /// name of the (GDScript) local variable which contains the\n  /// function. The variable should refer to an object with\n  /// `call_func` and `call_funcv` methods. If the variable has\n  /// nonstandard method names, then [`FnScope::SpecialLocal`] should\n  /// be used instead, as the object is not suitable as a funcref in\n  /// this case.\n  Local(String),\n  /// A special local function is like a local function in that it\n  /// needs a closure. But a special local function is potentially\n  /// constructed with several other functions like it, so it will\n  /// still need an explicit closure if referred to via a funcref.\n  /// This is the worst case scenario, as we can make no assumptions\n  /// about the scoping of this function. As with [`FnScope::Local`],\n  /// the argument to `FnScope::SpecialLocal` should be the name of a\n  /// local variable on which the function is defined. No assumptions\n  /// are made about the name of the function on this object, only\n  /// that it exists.\n  SpecialLocal(String),\n}\n\n/// Like [`VarName`](super::local_var::VarName), this will eventually\n/// translate into an [`Expr`] (or possibly a lack thereof) and\n/// consists of all of the expressions which denote valid function\n/// \"name\" translations.\n///\n/// `FnName` should be thought of as a more restricted form of `Expr`.\n/// The ultimate goal of this type is to eventually be converted to an\n/// `Expr` via [`From::from`].\n#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]\npub enum FnName {\n  /// A static function local to the file and defined at the\n  /// top-level.\n  FileConstant,\n  /// A superglobal name, such as built-in GDScript functions.\n  Superglobal,\n  /// A file-level function defined in another file and imported.\n  ImportedConstant(Box<VarName>),\n  /// A local function referenced using a local variable.\n  OnLocalVar(Box<VarName>),\n  /// A local function referenced by the current local scope.\n  OnLocalScope,\n}\n\n/// A specification of the parameters a function takes. `FnSpecs` is\n/// similar to [`ArgList`](crate::ir::arglist::ordinary::ArgList)\n/// except that the latter specifies names for its arguments, whereas\n/// this structure simply designates the shape of the function.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\npub struct FnSpecs {\n  /// The number of required parameters to the function. If the\n  /// function is provided with fewer than this many arguments, an\n  /// error is issued.\n  pub required: usize,\n  /// The number of optional parameters to the function. After\n  /// required arguments are bound, the next arguments are bound to\n  /// optional parameters until the argument list is exhausted or\n  /// optional parameters have all been bound.\n  pub optional: usize,\n  /// The \"rest\" argument. After required and optional arguments have\n  /// been handled, if there are excess arguments, they are bound to\n  /// the \"rest\" parameter. If there is no \"rest\" parameter and there\n  /// are excess arguments, then an error is issued.\n  pub rest: Option<VarArg>,\n}\n\n/// When referencing enclosing static data from an inner class scope,\n/// there are two possible types of inner class scopes we could be\n/// within.\n///\n/// If we're in a static inner scope and need to reference a static\n/// outer scope, then the inner scope will need to load the current\n/// file, using [`FnName::inner_static_load`]. On the other hand, if\n/// we're in a non-static inner scope and need to reference a static\n/// outer scope, then we're expected to do a single `load` call and\n/// store the result in an instance variable.\n///\n/// This enum encapsulates these two behaviors. An outer static\n/// reference is either referred to by a live static load or by an\n/// instance variable with a given name.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum OuterStaticRef<'a> {\n  /// An outer static reference from an inner static scope uses a live\n  /// `load` call on-site.\n  InnerStatic,\n  /// An outer static reference from an inner non-static scope\n  /// references an instance variable on the current (inner) class\n  /// with the given name.\n  InnerInstanceVar(&'a str),\n}\n\nimpl FnCall {\n\n  /// A top-level macro. A macro always has `object` of\n  /// [`FnName::FileConstant`] and `is_macro` of true.\n  pub fn file_macro(specs: FnSpecs, scope: FnScope, function: String) -> FnCall {\n    FnCall { specs, scope, object: FnName::FileConstant, function, is_macro: true }\n  }\n\n  /// A top-level function, with [`FnName::FileConstant`].\n  pub fn file_constant(specs: FnSpecs, scope: FnScope, function: String) -> FnCall {\n    FnCall { specs, scope, object: FnName::FileConstant, function, is_macro: false }\n  }\n\n  /// A superglobal function, with [`FnName::Superglobal`].\n  pub fn superglobal(specs: FnSpecs, scope: FnScope, function: String) -> FnCall {\n    FnCall { specs, scope, object: FnName::Superglobal, function, is_macro: false }\n  }\n\n  /// Returns whether the function indicated by this function call\n  /// object can be called in a `const` context, given sufficiently\n  /// constant arguments.\n  pub fn can_be_called_as_const(&self) -> bool {\n    match self.object {\n      FnName::FileConstant => false,\n      FnName::Superglobal => {\n        // Very specific superglobals can be called.\n        CONSTANT_GDSCRIPT_FUNCTIONS.contains(&*self.function)\n      }\n      FnName::ImportedConstant(_) => false,\n      FnName::OnLocalVar(_) => false,\n      FnName::OnLocalScope => false,\n    }\n  }\n\n  /// As [`FnCall::into_expr_with_magic`] with [`CallMagic::DefaultCall`] as the\n  /// call magic type.\n  pub fn into_expr(self,\n                   compiler: &mut Compiler,\n                   builder: &mut StmtBuilder,\n                   table: &mut SymbolTable,\n                   args: Vec<StExpr>,\n                   pos: SourceOffset)\n                   -> Result<Expr, GDError> {\n    self.into_expr_with_magic(&CallMagic::DefaultCall, compiler, builder, table, args, pos)\n  }\n\n  /// Compile, via [`CallMagic::compile`], the function call `self`\n  /// into an [`Expr`].\n  pub fn into_expr_with_magic(self,\n                              magic: &CallMagic,\n                              compiler: &mut Compiler,\n                              builder: &mut StmtBuilder,\n                              table: &mut SymbolTable,\n                              args: Vec<StExpr>,\n                              pos: SourceOffset)\n                              -> Result<Expr, GDError> {\n    magic.compile(self, compiler, builder, table, args, pos)\n  }\n\n}\n\nimpl FnSpecs {\n\n  /// An `FnSpecs` object representing a function of no arguments.\n  pub const EMPTY: FnSpecs = FnSpecs { required: 0, optional: 0, rest: None };\n\n  /// Convenience constructor for a `FnSpecs`.\n  pub fn new(required: usize, optional: usize, rest: Option<VarArg>) -> FnSpecs {\n    FnSpecs { required, optional, rest }\n  }\n\n  /// Returns whether `self` has a \"rest\" argument of any kind (list\n  /// or array). Equivalent to `self.rest.is_some()`.\n  pub fn has_rest(&self) -> bool {\n    self.rest.is_some()\n  }\n\n  /// In GDLisp, a function can have a \"rich\" argument list (as per\n  /// [`ArgList`](crate::ir::arglist::ordinary::ArgList)), consisting\n  /// of required arguments, optional arguments, and a rest argument.\n  /// However, on the GDScript side, we always compile down to a set\n  /// number of required arguments. Given an `FnSpecs`, this method\n  /// returns the number of arguments that will be present in that\n  /// resulting compiled function.\n  ///\n  /// Required arguments in GDLisp compile one-to-one to required\n  /// arguments in GDScript. Optional arguments in GDLisp are\n  /// converted into required arguments and padded (at compile-time)\n  /// with `null` as needed. Finally, if a rest argument (of either\n  /// type) is present, it is converted into a single additional\n  /// argument.\n  ///\n  /// # Examples\n  ///\n  /// ```\n  /// # use gdlisp::compile::symbol_table::function_call::FnSpecs;\n  /// # use gdlisp::ir::arglist::vararg::VarArg;\n  /// assert_eq!(FnSpecs::new(0, 0, None).runtime_arity(), 0);\n  /// assert_eq!(FnSpecs::new(5, 0, None).runtime_arity(), 5);\n  /// assert_eq!(FnSpecs::new(0, 5, None).runtime_arity(), 5);\n  /// assert_eq!(FnSpecs::new(2, 5, None).runtime_arity(), 7);\n  /// assert_eq!(FnSpecs::new(0, 0, Some(VarArg::RestArg)).runtime_arity(), 1);\n  /// assert_eq!(FnSpecs::new(0, 0, Some(VarArg::ArrArg)).runtime_arity(), 1);\n  /// assert_eq!(FnSpecs::new(2, 1, Some(VarArg::RestArg)).runtime_arity(), 4);\n  /// assert_eq!(FnSpecs::new(1, 2, Some(VarArg::ArrArg)).runtime_arity(), 4);\n  /// ```\n  pub fn runtime_arity(&self) -> usize {\n    self.required + self.optional + usize::from(self.has_rest())\n  }\n\n  /// The minimum number of arguments necessary to correctly call a\n  /// function with this shape. Equivalent to `self.required`.\n  ///\n  /// # Examples\n  ///\n  /// ```\n  /// # use gdlisp::compile::symbol_table::function_call::FnSpecs;\n  /// # use gdlisp::ir::arglist::vararg::VarArg;\n  /// assert_eq!(FnSpecs::new(0, 0, None).min_arity(), 0);\n  /// assert_eq!(FnSpecs::new(5, 0, None).min_arity(), 5);\n  /// assert_eq!(FnSpecs::new(0, 5, None).min_arity(), 0);\n  /// assert_eq!(FnSpecs::new(2, 5, None).min_arity(), 2);\n  /// assert_eq!(FnSpecs::new(0, 0, Some(VarArg::RestArg)).min_arity(), 0);\n  /// assert_eq!(FnSpecs::new(0, 0, Some(VarArg::ArrArg)).min_arity(), 0);\n  /// assert_eq!(FnSpecs::new(2, 1, Some(VarArg::RestArg)).min_arity(), 2);\n  /// assert_eq!(FnSpecs::new(1, 2, Some(VarArg::ArrArg)).min_arity(), 1);\n  /// ```\n  pub fn min_arity(&self) -> usize {\n    self.required\n  }\n\n  /// The maximum number of arguments that can be correctly supplied\n  /// to a function with this shape.\n  ///\n  /// If the function takes a rest argument, then this returns\n  /// [`usize::MAX`].\n  ///\n  /// # Examples\n  ///\n  /// ```\n  /// # use gdlisp::compile::symbol_table::function_call::FnSpecs;\n  /// # use gdlisp::ir::arglist::vararg::VarArg;\n  /// assert_eq!(FnSpecs::new(0, 0, None).max_arity(), 0);\n  /// assert_eq!(FnSpecs::new(5, 0, None).max_arity(), 5);\n  /// assert_eq!(FnSpecs::new(0, 5, None).max_arity(), 5);\n  /// assert_eq!(FnSpecs::new(2, 5, None).max_arity(), 7);\n  /// assert_eq!(FnSpecs::new(0, 0, Some(VarArg::RestArg)).max_arity(), usize::MAX);\n  /// assert_eq!(FnSpecs::new(0, 0, Some(VarArg::ArrArg)).max_arity(), usize::MAX);\n  /// assert_eq!(FnSpecs::new(2, 1, Some(VarArg::RestArg)).max_arity(), usize::MAX);\n  /// assert_eq!(FnSpecs::new(1, 2, Some(VarArg::ArrArg)).max_arity(), usize::MAX);\n  /// ```\n  pub fn max_arity(&self) -> usize {\n    // TODO Is usize.MAX correct here? If we put an upper limit on\n    // function arity, use that instead.\n    if self.has_rest() { usize::MAX } else { self.required + self.optional }\n  }\n\n}\n\nimpl FnScope {\n\n  /// Whether or not the scope is local.\n  ///\n  /// [`FnScope::Local`] and [`FnScope::SpecialLocal`] are local\n  /// scopes. All other scopes are considered global.\n  pub fn is_local(&self) -> bool {\n    self.local_name().is_some()\n  }\n\n  /// If `self` refers to a local scope (i.e.\n  /// [`is_local`](FnScope::is_local) returns true), then this method\n  /// returns the name of the GDScript local variable which contains\n  /// the enclosing object for the function. If `self` refers to a\n  /// global scope, this method returns [`None`].\n  pub fn local_name(&self) -> Option<&str> {\n    match self {\n      FnScope::Local(name) | FnScope::SpecialLocal(name) => Some(name),\n      FnScope::Superglobal | FnScope::Global | FnScope::SemiGlobal => None,\n    }\n  }\n\n}\n\nimpl FnName {\n\n  /// A value imported from another scope.\n  pub fn imported_constant(orig_name: VarName) -> FnName {\n    FnName::ImportedConstant(Box::new(orig_name))\n  }\n\n  /// A call made on a local variable with the given name.\n  pub fn on_local_var(local_name: VarName) -> FnName {\n    FnName::OnLocalVar(Box::new(local_name))\n  }\n\n  /// Converts the `FnName` into an appropriate value to be called\n  /// from another module.\n  ///\n  /// If a name `foo` is available at top-level scope `A.gd` and some\n  /// file `B.gd` imports `A.gd` and calls the top-level preload\n  /// constant `AConst`, then calling `foo.as_imported(\"AConst\")` will\n  /// convert the name to how it should be referenced from `B.gd`.\n  pub fn into_imported(self, import_name: String) -> FnName {\n    self.into_imported_var(VarName::FileConstant(import_name))\n  }\n\n  pub fn into_imported_var(self, import: VarName) -> FnName {\n    match self {\n      FnName::FileConstant => {\n        FnName::imported_constant(import)\n      }\n      FnName::Superglobal => {\n        FnName::Superglobal\n      }\n      FnName::ImportedConstant(v) => {\n        FnName::imported_constant(v.into_imported_var(import))\n      }\n      FnName::OnLocalVar(v) => {\n        // This case probably shouldn't happen, but oh well. Delegate to VarName.\n        FnName::on_local_var(v.into_imported_var(import))\n      }\n      FnName::OnLocalScope => {\n        // This case definitely shouldn't happen. Leave it alone I guess.\n        FnName::OnLocalScope\n      }\n    }\n  }\n\n  /// Generate a `load` expression for the current file (as per\n  /// `loader`) using `resolver` as the current preload resolver. See\n  /// [`FnName::update_for_inner_scope`] for details on why this is\n  /// necessary. Note that this function is usually called *through*\n  /// that one and should seldom be called directly.\n  ///\n  /// # Panics\n  ///\n  /// If the current filename cannot be detected from `loader`, or if\n  /// `resolver` fails to resolve the name given by `loader`, then\n  /// this function will panic.\n  pub fn inner_static_load(resolver: &(impl PreloadResolver + ?Sized), loader: &impl CanLoad) -> FnName {\n    let fname = inner_class::get_current_filename(loader, resolver)\n      .expect(\"Cannot identify currently-loading filename\");\n    FnName::on_local_var(VarName::CurrentFile(fname))\n  }\n\n  /// In Godot, inner classes do not retain a reference to the\n  /// enclosing scope, not even to be able to call static functions in\n  /// an enclosing scope. GDLisp makes extensive use of static\n  /// file-level functions, so this poses an issue for any nontrivial\n  /// inner class. `inner_static_load` provides a viable workaround\n  /// for this.\n  ///\n  /// If we are in a static context in an inner class and we need to\n  /// refer to an enclosing static function, we need to `load` the\n  /// current file again. In most cases, this `load` will be a cache\n  /// hit and will simply see an existing resource (though it's not\n  /// impossible for a cache miss to occur, and in this case the\n  /// `load` will still work as intended, albeit a bit slower).\n  /// `inner_static_load` constructs an `FnName` representing a `load`\n  /// expression for the appropriate file. `resolver` shall be the\n  /// current preload resolver, and `loader`, naturally, provides the\n  /// current filename.\n  ///\n  /// If we're in a non-static inner scope and need to refer to an\n  /// enclosing static scope, then for efficiency reasons we don't\n  /// want to load the outer script every time. Instead, we simply\n  /// require that an instance variable (whose name is given by\n  /// `outer_ref_name`) on the class be loaded (via `load`) to point\n  /// to the current file at construction time.\n  ///\n  /// See [Issue #30](https://github.com/Mercerenies/gdlisp/issues/30)\n  /// for a further discussion.\n  pub fn update_for_inner_scope(&mut self,\n                                binding_type: &OuterStaticRef<'_>,\n                                resolver: &(impl PreloadResolver + ?Sized),\n                                loader: &impl CanLoad) {\n    if self.needs_inner_scope_reference() {\n      match binding_type {\n        OuterStaticRef::InnerStatic => {\n          *self = FnName::inner_static_load(resolver, loader);\n        }\n        OuterStaticRef::InnerInstanceVar(outer_ref_name) => {\n          *self = FnName::OnLocalVar(Box::new(VarName::outer_class_ref(outer_ref_name)));\n        }\n      }\n    }\n  }\n\n  /**\n   * Returns true if this function name type requires an outer class\n   * reference in order to be called. Functions require an outer class\n   * reference if they are either (1) a file-level constant, or (2)\n   * already an outer class reference. The latter case occurs if we're\n   * in a nested lambda inside of a lambda scope that already has an\n   * outer class reference.\n   */\n  pub fn needs_inner_scope_reference(&self) -> bool {\n    match self {\n      FnName::FileConstant => {\n        true\n      }\n      FnName::OnLocalVar(var_name) => {\n        matches!(&**var_name, VarName::OuterClassRef(_))\n      }\n      _ => {\n        false\n      }\n    }\n  }\n\n  /// As [`update_for_inner_scope`](FnName::update_for_inner_scope),\n  /// but takes ownership of `self` and returns the modified `FnName`.\n  pub fn into_inner_scope(mut self,\n                          binding_type: &OuterStaticRef<'_>,\n                          resolver: &(impl PreloadResolver + ?Sized),\n                          loader: &impl CanLoad)\n                          -> Self {\n    self.update_for_inner_scope(binding_type, resolver, loader);\n    self\n  }\n\n  /// **Note:** An `Option` here does NOT denote failure to convert.\n  /// `FnName` can be converted to an `Option<Expr>`, in the sense\n  /// that \"there is no expression here\" is a completely valid result\n  /// of conversion and indicates a function call which is not\n  /// subscripted on a name.\n  pub fn into_expr(self, pos: SourceOffset) -> Option<Expr> {\n    match self {\n      FnName::FileConstant => None,\n      FnName::Superglobal => None,\n      FnName::ImportedConstant(var_name) => Some(var_name.into_expr(pos)),\n      FnName::OnLocalVar(var_name) => Some(var_name.into_expr(pos)),\n      FnName::OnLocalScope => None,\n    }\n  }\n\n}\n\nimpl<'a> OuterStaticRef<'a> {\n\n  /// If `static_binding` is true, then this method returns\n  /// [`OuterStaticRef::InnerStatic`]. Otherwise, this method returns\n  /// [`OuterStaticRef::InnerInstanceVar`] with `outer_ref_name` as\n  /// the reference name. This is useful in situations where we\n  /// already have an outer reference name and simply need to know\n  /// whether we can use it from the given scope.\n  pub fn from_ref_type(static_binding: bool, outer_ref_name: &'a str) -> Self {\n    if static_binding {\n      OuterStaticRef::InnerStatic\n    } else {\n      OuterStaticRef::InnerInstanceVar(outer_ref_name)\n    }\n  }\n\n  /// Converts `None` to [`OuterStaticRef::InnerStatic`] and `Some` to\n  /// [`OuterStaticRef::InnerInstanceVar`].\n  pub fn from_option_ref(outer_ref_name: Option<&'a str>) -> Self {\n    match outer_ref_name {\n      None => OuterStaticRef::InnerStatic,\n      Some(ref_name) => OuterStaticRef::InnerInstanceVar(ref_name),\n    }\n  }\n\n}\n\nimpl From<VarName> for FnName {\n  fn from(var_name: VarName) -> FnName {\n    FnName::imported_constant(var_name)\n  }\n}\n\nimpl From<FnSpecs> for Expecting {\n  fn from(specs: FnSpecs) -> Expecting {\n    Expecting::new(specs.min_arity(), specs.max_arity())\n  }\n}\n"
  },
  {
    "path": "src/compile/symbol_table/inner.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`InnerSymbolTable`] structure, which behaves mostly\n//! like a [`SymbolTable`] but returns global synthetic variables when\n//! it goes out of scope, as if by\n//! [`SymbolTable::dump_synthetics_to`].\n\nuse super::SymbolTable;\n\nuse std::ops::{Deref, DerefMut};\n\n/// An `InnerSymbolTable` is a thin wrapper around a [`SymbolTable`].\n/// However, `InnerSymbolTable` keeps a (mutable) reference to an\n/// enclosing symbol table, and when the `InnerSymbolTable` is\n/// dropped, it dumps all global synthetic variables from the current\n/// symbol table into the enclosing table, as if by\n/// [`SymbolTable::dump_synthetics_to`].\n#[derive(Debug)]\npub struct InnerSymbolTable<'a> {\n  inner_table: SymbolTable,\n  outer_table: &'a mut SymbolTable,\n}\n\nimpl<'a> InnerSymbolTable<'a> {\n\n  /// Constructs a new `InnerSymbolTable` object wrapping\n  /// `inner_table` and pointing to the enclosing table `outer_table`.\n  pub fn new(inner_table: SymbolTable, outer_table: &'a mut SymbolTable) -> Self {\n    InnerSymbolTable { inner_table, outer_table }\n  }\n\n  /// Constructs a new `InnerSymbolTable` with no concrete symbols and\n  /// with synthetic symbols inherited from the enclosing table.\n  pub fn with_synthetics_from(outer_table: &'a mut SymbolTable) -> Self {\n    let inner_table = SymbolTable::with_synthetics_from(outer_table);\n    Self::new(inner_table, outer_table)\n  }\n\n  /// Constructs a new `InnerSymbolTable` which is a clone of\n  /// `outer_table` and points to it.\n  pub fn cloned_from(outer_table: &'a mut SymbolTable) -> Self {\n    let inner_table = outer_table.clone();\n    Self::new(inner_table, outer_table)\n  }\n\n}\n\nimpl<'a> Deref for InnerSymbolTable<'a> {\n  type Target = SymbolTable;\n\n  fn deref(&self) -> &SymbolTable {\n    &self.inner_table\n  }\n\n}\n\nimpl<'a> DerefMut for InnerSymbolTable<'a> {\n  fn deref_mut(&mut self) -> &mut SymbolTable {\n    &mut self.inner_table\n  }\n}\n\nimpl<'a> Drop for InnerSymbolTable<'a> {\n\n  fn drop(&mut self) {\n    self.inner_table.dump_synthetics_to(self.outer_table);\n  }\n\n}\n"
  },
  {
    "path": "src/compile/symbol_table/local_var.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! [`LocalVar`] and various helper structures for storing information\n//! about variables.\n\nuse crate::ir::access_type::AccessType;\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::literal::Literal;\nuse crate::gdscript::library::cell::CELL_CONTENTS;\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::pipeline::source::SourceOffset;\nuse crate::compile::names;\n\nuse serde::{Serialize, Deserialize};\n\nuse std::borrow::ToOwned;\nuse std::convert::TryFrom;\nuse std::fmt;\nuse std::error::Error;\nuse std::ffi::OsStr;\n\n/// All of the relevant information needed to understand a variable is\n/// stored in `LocalVar`. Despite its name, this structure is used to\n/// describe all names in the variable namespace in Godot, including\n/// local variables, global constants, and class names.\n#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]\npub struct LocalVar {\n  /// The name of the variable.\n  pub name: VarName,\n  /// The broadest type of access that will ever be required of the\n  /// variable.\n  pub access_type: AccessType,\n  /// The scope of the variable.\n  pub scope: VarScope,\n  /// Whether or not the variable can be assigned to.\n  pub assignable: bool,\n  /// In the case of global constants, the compiler often knows the\n  /// constant's value at compile-time. We can use this information to\n  /// provide optimizations, as well as possible warnings about\n  /// incorrect usage of a value. If no value is known, then\n  /// `value_hint` is simply `None`.\n  pub value_hint: Option<ValueHint>,\n}\n\n/// `VarName` will eventually translate into an [`Expr`] (via\n/// [`From::from`]). This type should be thought of as a spiritual\n/// subtype of `Expr`, only supporting a strict subset of expressions\n/// which are necessary for name translations.\n#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]\npub enum VarName {\n  /// A variable which is local to the current scope and can be seen\n  /// unqualified.\n  Local(String),\n  /// Similar to `Local`, with the caveat that the variable\n  /// `OuterClassRef` refers to is an outer class reference. These are\n  /// treated like local variables for most purposes but will be\n  /// handled specially when used in nested closures.\n  OuterClassRef(String),\n  /// A file-level constant defined in the current file.\n  FileConstant(String),\n  /// A superglobal name, such as built-in GDScript constants. These\n  /// names will never be modified if imported.\n  Superglobal(String),\n  /// A file-level constant defined in another file and imported.\n  ImportedConstant(Box<VarName>, String),\n  /// A file-level constant subscripted by a given constant numerical value.\n  SubscriptedConstant(Box<VarName>, i32),\n  /// The current file, as a constant value. This is semantically\n  /// similar to a [`VarName::FileConstant`], but it is a special\n  /// case, as `VarName::CurrentFile` is required to compile to a\n  /// `load(...)` call rather than a simple name.\n  CurrentFile(String),\n  /// A `load` call on a file other than the current file.\n  DirectLoad(String),\n  /// A null value. This is used as a placeholder for things in the\n  /// value namespace that don't have runtime presence, such as symbol\n  /// macros.\n  Null,\n}\n\n/// [`VarName`] can always be converted (without loss of information)\n/// into an [`Expr`]. It can also sometimes be converted (via\n/// [`TryFrom`]) into a [`ClassExtends`](ClassExtends). This is\n/// the error type that can result when the latter conversion fails.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum VarNameIntoExtendsError {\n  /// It is not permitted to have a class extend a local variable.\n  /// Even if the class is an anonymous class defined at local scope,\n  /// there is no good semantic way to permit this behavior.\n  CannotExtendLocal(String),\n  /// It is not permitted to have a class which extends a subscripted\n  /// (i.e. `foo[bar]`) expression.\n  CannotExtendSubscript(Box<VarName>, i32),\n  /// It is not permitted to have a class extend the currently loading\n  /// file. This would cause a cyclic load error in Godot.\n  CannotExtendCurrentFile(String),\n  /// It is not permitted to have a class extend a `load` function\n  /// call.\n  CannotExtendLoadDirective(String),\n  /// The null [`VarName`] is used in some places as a placeholder for\n  /// values that don't exist at runtime (i.e. those fully handled\n  /// during the IR macro expansion phase). Needless to say, we can't\n  /// extend from a name that doesn't exist at runtime.\n  CannotExtendNull,\n}\n\n/// The scope of a variable.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]\npub enum VarScope {\n  /// Global variables are available at least in the current file, if\n  /// not everywhere. A global variable never needs to be closed over,\n  /// since any closures will be lifted no further than the top of the\n  /// current file.\n  GlobalVar,\n  /// Local variables are available in some scope strictly smaller\n  /// than file-level. A local variable *does* need a closure if\n  /// referenced in an inner lambda or anonymous class, as it may not\n  /// be available outside the original scope of the variable.\n  LocalVar,\n}\n\n/// A hint as to the value of a [`VarName`], if known. This\n/// information can be used in optimizations or possible compiler\n/// warnings.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum ValueHint {\n  /// The variable refers to a class. Class names cannot be reassigned\n  /// and are always valid as types.\n  ClassName,\n  /// The variable refers to a singleton object. Singleton objects\n  /// cannot be reassigned.\n  ObjectName,\n  /// The variable refers to a GDScript literal value, whose exact\n  /// value is already known.\n  Literal(Literal),\n  /// The variable refers to an enumeration declaration with the given\n  /// names as enum cases. If this value hint is given, then `:` slot\n  /// access can be checked at compile time to ensure that the name in\n  /// question actually exists on the enum.\n  Enum(Vec<String>),\n  /// The variable is a constant whose exact value may not be known.\n  GlobalConstant,\n  /// The variable is the result of a superglobal declaration.\n  /// Currently, this is only available if explicitly requested via\n  /// `sys/declare`.\n  Superglobal,\n  /// A symbol macro name. If we end up using one of these at runtime\n  /// somehow, then it should be a hard error wherever possible.\n  SymbolMacro,\n}\n\n/// A `ValueHintsTable` provides a means to look up a variable and get\n/// a potential [`ValueHint`] for the variable. The most relevant\n/// implementor of this trait is [`SymbolTable`](super::SymbolTable),\n/// where the value hint is simply looked up in the current scope.\npub trait ValueHintsTable {\n  /// Get a hint for the value of the variable with name `name`.\n  /// Returns `None` if no hint is available.\n  fn get_value_hint(&self, name: &str) -> Option<&ValueHint>;\n}\n\n/// `VacuousValueHintsTable` is a trivial implementation of\n/// [`ValueHintsTable`] that never returns a hint.\npub struct VacuousValueHintsTable;\n\nimpl ValueHintsTable for VacuousValueHintsTable {\n  fn get_value_hint(&self, _name: &str) -> Option<&ValueHint> {\n    None\n  }\n}\n\nimpl LocalVar {\n\n  /// A read-only (non-closure) local variable, as per\n  /// [`LocalVar::local`].\n  pub fn read(name: String) -> LocalVar {\n    LocalVar::local(name, AccessType::Read)\n  }\n\n  /// A read-write (non-closure) local variable, as per\n  /// [`LocalVar::local`].\n  pub fn rw(name: String) -> LocalVar {\n    LocalVar::local(name, AccessType::RW)\n  }\n\n  /// A closed read-write local variable, as per [`LocalVar::local`].\n  pub fn closed_rw(name: String) -> LocalVar {\n    LocalVar::local(name, AccessType::ClosedRW)\n  }\n\n  /// A local variable with the given access type.\n  pub fn local(name: String, access_type: AccessType) -> LocalVar {\n    LocalVar {\n      name: VarName::Local(name),\n      access_type: access_type,\n      scope: VarScope::LocalVar,\n      assignable: true,\n      value_hint: None,\n    }\n  }\n\n  /// A local outer class reference variable with the given access\n  /// type.\n  pub fn outer_class_ref(name: String, access_type: AccessType) -> LocalVar {\n    LocalVar {\n      name: VarName::OuterClassRef(name),\n      access_type: access_type,\n      scope: VarScope::LocalVar,\n      assignable: true,\n      value_hint: None,\n    }\n  }\n\n  /// A superglobal variable, available in all GDScript scopes, not\n  /// just the current file. Superglobals are always\n  /// [`AccessType::Read`] and are never `assignable`.\n  pub fn superglobal(name: String) -> LocalVar {\n    LocalVar {\n      name: VarName::Superglobal(name),\n      access_type: AccessType::Read,\n      scope: VarScope::GlobalVar,\n      assignable: false,\n      value_hint: None,\n    }\n  }\n\n  /// A file-level constant GDScript variable. File-level constants\n  /// are always [`AccessType::Read`] and are never `assignable`.\n  pub fn file_constant(name: String) -> LocalVar {\n    LocalVar {\n      name: VarName::FileConstant(name),\n      access_type: AccessType::Read,\n      scope: VarScope::GlobalVar,\n      assignable: false,\n      value_hint: None,\n    }\n  }\n\n  /// A `LocalVar` representing the current file (whose name is\n  /// `name`) via [`VarName::CurrentFile`]. The current file is always\n  /// a non-`assignable` value with access type [`AccessType::Read`].\n  pub fn current_file(name: String) -> LocalVar {\n    LocalVar {\n      name: VarName::CurrentFile(name),\n      access_type: AccessType::Read,\n      scope: VarScope::GlobalVar,\n      assignable: false,\n      value_hint: None,\n    }\n  }\n\n  /// A `LocalVar` referencing the special GDScript `self` variable.\n  /// `self` is a local, non-assignable variable. We give it access\n  /// type [`AccessType::ClosedRead`], as the broadest possible access\n  /// type `self` can use, since it cannot be written to but it could\n  /// potentially be closed over.\n  pub fn self_var() -> LocalVar { // TODO Should this be a special case?\n    // Note: Cannot assign to self\n    LocalVar::local(String::from(\"self\"), AccessType::ClosedRead).no_assign()\n  }\n\n  /// Sets `self.assignable` to false and returns `self`. This method\n  /// is intended to be used in a builder style.\n  pub fn no_assign(mut self) -> Self {\n    self.assignable = false;\n    self\n  }\n\n  /// Sets the `self.value_hint` value and returns `self`. This method\n  /// is intended to be used in a builder style.\n  pub fn with_hint(mut self, value_hint: ValueHint) -> Self {\n    self.value_hint = Some(value_hint);\n    self\n  }\n\n  /// The simple, unqualified name of the variable, if it exists.\n  /// Equivalent to `self.name.simple_name()`.\n  pub fn simple_name(&self) -> Option<&str> {\n    self.name.simple_name()\n  }\n\n  /// Replaces the simple, unqualified name of the variable. If the\n  /// variable has no unqualified name, then this function does\n  /// nothing.\n  pub fn set_simple_name(&mut self, name: String) {\n    match &mut self.name {\n      VarName::Local(s) => {\n        *s = name;\n      }\n      VarName::OuterClassRef(s) => {\n        *s = name;\n      }\n      VarName::FileConstant(s) => {\n        *s = name;\n      }\n      VarName::Superglobal(s) => {\n        *s = name;\n      }\n      VarName::ImportedConstant(_, _) => {}\n      VarName::SubscriptedConstant(_, _) => {}\n      VarName::CurrentFile(_) => {}\n      VarName::DirectLoad(_) => {}\n      VarName::Null => {}\n    }\n  }\n\n  /// Returns whether or not the variable name in question compiles to\n  /// an expression which is valid as the right-hand side of a `const`\n  /// in GDScript.\n  ///\n  /// This function is permitted to be overly conservative. That is,\n  /// in situations where the variable name may or may not be valid,\n  /// this function will return `false`. However, if this function\n  /// returns `true`, then it *must* be valid as a `const` expression.\n  pub fn is_valid_const_expr(&self) -> bool {\n    match &self.name {\n      VarName::Local(_) => false,\n      VarName::OuterClassRef(_) => false,\n      VarName::FileConstant(_) | VarName::ImportedConstant(_, _) | VarName::SubscriptedConstant(_, _) => {\n        // If it's a top-level constant, use the value hint to figure\n        // out which constant.\n        match self.value_hint {\n          None => false,\n          Some(ValueHint::ClassName) => true,\n          Some(ValueHint::ObjectName) => false, // Implemented as a 0-ary function call by GDLisp\n          Some(ValueHint::Literal(_)) => true,\n          Some(ValueHint::Enum(_)) => true,\n          Some(ValueHint::GlobalConstant) => true,\n          Some(ValueHint::Superglobal) => true,\n          Some(ValueHint::SymbolMacro) => false, // How did we even get into this situation?\n        }\n      }\n      VarName::Superglobal(_) => true,\n      VarName::CurrentFile(_) => false,\n      VarName::DirectLoad(_) => false,\n      VarName::Null => true,\n    }\n  }\n\n  /// An `Expr` which references the value of this variable. If this\n  /// variable requires a cell (`self.access_type.requires_cell()`),\n  /// then this access expression contains the necessary subscripting\n  /// to access the *contents* of the cell, not the cell itself.\n  pub fn expr(&self, pos: SourceOffset) -> Expr {\n    let inner: Expr = self.name.clone().into_expr(pos);\n    if self.access_type.requires_cell() {\n      Expr::new(ExprF::Attribute(Box::new(inner), CELL_CONTENTS.to_owned()), pos)\n    } else {\n      inner\n    }\n  }\n\n  // TODO Put all of the declaration-site stuff here as well, like\n  // .expr() for access, so we have it all in one place (i.e. the\n  // difference between \"var x = ...\" and \"var x = Cell.new(...)\")\n\n}\n\nimpl VarName {\n\n  /// Helper function to produce a `load(...)` GDScript expression for\n  /// the file named `filename`.\n  pub fn load_expr(filename: String, pos: SourceOffset) -> Expr {\n    Expr::call(None, \"load\", vec!(Expr::from_value(filename, pos)), pos)\n  }\n\n  /// `VarName` for a local variable.\n  pub fn local(name: &str) -> VarName {\n    VarName::Local(String::from(name))\n  }\n\n  /// `VarName` for an outer class reference (local) variable.\n  pub fn outer_class_ref(name: &str) -> VarName {\n    VarName::OuterClassRef(String::from(name))\n  }\n\n  /// `VarName` for a file-level constant.\n  pub fn file_constant(name: &str) -> VarName {\n    VarName::FileConstant(String::from(name))\n  }\n\n  /// `VarName` for a superglobal variable.\n  pub fn superglobal(name: &str) -> VarName {\n    VarName::Superglobal(String::from(name))\n  }\n\n  /// `VarName` for an imported constant name.\n  pub fn imported_constant(orig_name: VarName, name: &str) -> VarName {\n    VarName::ImportedConstant(Box::new(orig_name), String::from(name))\n  }\n\n  /// `VarName` for the current file.\n  pub fn current_file(filename: &str) -> VarName {\n    VarName::CurrentFile(String::from(filename))\n  }\n\n  /// Converts `self` to valid GDScript syntax. Equivalent to\n  /// `self.clone().into_expr(pos).to_gd()`.\n  pub fn to_gd(&self, pos: SourceOffset) -> String {\n    self.clone().into_expr(pos).to_gd()\n  }\n\n  /// If `self` refers to a simple (unqualified) name, such as a local\n  /// variable or an unimported constant defined in the current file,\n  /// then this method returns the name. If `self` refers to a\n  /// qualified or otherwise special name (such as a `load` on the\n  /// current file), then this method returns `None`.\n  pub fn simple_name(&self) -> Option<&str> {\n    match self {\n      VarName::Local(s) => Some(s),\n      VarName::OuterClassRef(s) => Some(s),\n      VarName::FileConstant(s) => Some(s),\n      VarName::Superglobal(s) => Some(s),\n      VarName::ImportedConstant(_, _) => None,\n      VarName::SubscriptedConstant(_, _) => None,\n      VarName::CurrentFile(_) => None,\n      VarName::DirectLoad(_) => None,\n      VarName::Null => None,\n    }\n  }\n\n  /// Converts the `VarName` into an appropriate value to be called\n  /// from another module.\n  ///\n  /// If a name `foo` is available at top-level scope `A.gd` and some\n  /// file `B.gd` imports `A.gd` and calls the top-level preload\n  /// constant `AConst`, then calling `foo.as_imported(\"AConst\")` will\n  /// convert the name to how it should be referenced from `B.gd`.\n  pub fn into_imported(self, import_name: String) -> VarName {\n    self.into_imported_var(VarName::FileConstant(import_name))\n  }\n\n  pub fn into_imported_var(self, import: VarName) -> VarName {\n    match self {\n      VarName::Local(s) => {\n        // To be honest, this case probably should never occur. So\n        // we'll just pretend it's FileConstant.\n        VarName::ImportedConstant(Box::new(import), s)\n      }\n      VarName::OuterClassRef(s) => {\n        // To be honest, this case probably should never occur. So\n        // we'll just pretend it's FileConstant.\n        VarName::ImportedConstant(Box::new(import), s)\n      }\n      VarName::FileConstant(s) => {\n        // Import file constants by qualifying the name.\n        VarName::ImportedConstant(Box::new(import), s)\n      }\n      VarName::Superglobal(s) => {\n        // Superglobals are always in scope and don't change on import.\n        VarName::Superglobal(s)\n      }\n      VarName::ImportedConstant(lhs, s) => {\n        // Import the constant transitively.\n        let lhs = Box::new(lhs.into_imported_var(import));\n        VarName::ImportedConstant(lhs, s)\n      }\n      VarName::SubscriptedConstant(lhs, n) => {\n        // Import the constant transitively.\n        let lhs = Box::new(lhs.into_imported_var(import));\n        VarName::SubscriptedConstant(lhs, n)\n      }\n      VarName::DirectLoad(filename) => {\n        // A direct load does not change when imported. It still directly loads the same file.\n        VarName::DirectLoad(filename)\n      }\n      VarName::CurrentFile(_) => {\n        // The current file imports as the name of the import itself.\n        import\n      }\n      VarName::Null => {\n        // Null is already available everywhere.\n        VarName::Null\n      }\n    }\n  }\n\n  /// [`VarName`] can always be converted into an [`Expr`]. `VarName`\n  /// is, by definition, the subset of GDScript expressions suitable\n  /// for variable name expansion.\n  pub fn into_expr(self, pos: SourceOffset) -> Expr {\n    match self {\n      VarName::Local(s) => Expr::new(ExprF::Var(s), pos),\n      VarName::OuterClassRef(s) => Expr::new(ExprF::Var(s), pos),\n      VarName::FileConstant(s) => Expr::new(ExprF::Var(s), pos),\n      VarName::Superglobal(s) => Expr::new(ExprF::Var(s), pos),\n      VarName::ImportedConstant(lhs, s) => Expr::new(ExprF::Attribute(Box::new(lhs.into_expr(pos)), s), pos),\n      VarName::SubscriptedConstant(lhs, n) => Expr::new(ExprF::Subscript(Box::new(lhs.into_expr(pos)), Box::new(Expr::from_value(n, pos))), pos),\n      VarName::DirectLoad(filename) => VarName::load_expr(filename, pos),\n      VarName::CurrentFile(filename) => VarName::load_expr(filename, pos),\n      VarName::Null => Expr::null(pos),\n    }\n  }\n\n}\n\n/// Classes extend from variables which have `VarName`. We can\n/// (attempt to) convert from `VarName` to [`ClassExtends`]. We\n/// cannot, however, extend from local variables using this technique.\nimpl TryFrom<VarName> for ClassExtends {\n  type Error = VarNameIntoExtendsError;\n\n  // Note: This will only succeed into ClassExtends::Qualified. It\n  // will never produce any other alternative value for ClassExtends.\n  // This behavior may change in the future, at which point we'll need\n  // to change VarName::ImportedConstant to transitively handle that\n  // (or at least err in a better way than panicking).\n  fn try_from(var_name: VarName) -> Result<ClassExtends, VarNameIntoExtendsError> {\n    match var_name {\n      VarName::Local(s) => {\n        Err(VarNameIntoExtendsError::CannotExtendLocal(s))\n      }\n      VarName::OuterClassRef(s) => {\n        Err(VarNameIntoExtendsError::CannotExtendLocal(s))\n      }\n      VarName::FileConstant(s) => {\n        Ok(ClassExtends::SimpleIdentifier(s))\n      }\n      VarName::Superglobal(s) => {\n        Ok(ClassExtends::SimpleIdentifier(s))\n      }\n      VarName::ImportedConstant(lhs, s) => {\n        ClassExtends::try_from(*lhs).map(|x| x.attribute(s))\n      }\n      VarName::SubscriptedConstant(lhs, n) => {\n        Err(VarNameIntoExtendsError::CannotExtendSubscript(lhs, n))\n      }\n      VarName::DirectLoad(s) => {\n        Err(VarNameIntoExtendsError::CannotExtendLoadDirective(s))\n      }\n      VarName::CurrentFile(s) => {\n        Err(VarNameIntoExtendsError::CannotExtendCurrentFile(s))\n      }\n      VarName::Null => {\n        Err(VarNameIntoExtendsError::CannotExtendNull)\n      }\n    }\n  }\n\n}\n\nimpl ValueHint {\n\n  /// Helper method for constructing a [`ValueHint::Enum`] from an\n  /// iterator of unowned strings. Implicitly clones the strings and\n  /// stores them in a `ValueHint::Enum`.\n  pub fn enumeration<'a>(values: impl Iterator<Item=&'a str>) -> ValueHint {\n    ValueHint::Enum(values.map(names::lisp_to_gd).collect())\n  }\n\n  /// The value hint for a resource with the given file extension. The\n  /// file extension should *not* include the dot. If the file type\n  /// cannot be inferred or if there isn't a value hint that\n  /// represents the given file type, then `None` is returned.\n  pub fn from_file_ext(file_extension: &OsStr) -> Option<ValueHint> {\n    let file_extension = file_extension.to_ascii_lowercase();\n    if file_extension == \"gd\" {\n      Some(ValueHint::ClassName)\n    } else {\n      None\n    }\n  }\n\n}\n\nimpl fmt::Display for VarNameIntoExtendsError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      VarNameIntoExtendsError::CannotExtendLocal(s) => {\n        write!(f, \"Cannot extend local variable {}\", s)\n      }\n      VarNameIntoExtendsError::CannotExtendSubscript(expr, n) => {\n        let expr = expr.clone().into_expr(SourceOffset(0)).subscript(Expr::from_value(*n, SourceOffset(0)), SourceOffset(0));\n        write!(f, \"Cannot extend reference to current file {}\", expr.to_gd())\n      }\n      VarNameIntoExtendsError::CannotExtendCurrentFile(s) => {\n        write!(f, \"Cannot extend reference to current file {}\", s)\n      }\n      VarNameIntoExtendsError::CannotExtendLoadDirective(s) => {\n        write!(f, \"Cannot extend direct load of file {}\", s)\n      }\n      VarNameIntoExtendsError::CannotExtendNull => {\n        write!(f, \"Cannot extend null value\")\n      }\n    }\n  }\n}\n\nimpl Error for VarNameIntoExtendsError {}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  // TODO More\n\n  fn qualified(mut name: Vec<&'static str>) -> ClassExtends {\n    assert!(!name.is_empty(), \"Empty identifier not allowed\");\n    let first = ClassExtends::SimpleIdentifier(name.remove(0).to_owned());\n    name.into_iter().fold(first, |acc, name| acc.attribute(name))\n  }\n\n  #[test]\n  fn extends_constant() {\n    assert_eq!(\n      ClassExtends::try_from(VarName::file_constant(\"Abc\")),\n      Ok(qualified(vec!(\"Abc\"))),\n    );\n  }\n\n  #[test]\n  fn extends_superglobal() {\n    assert_eq!(\n      ClassExtends::try_from(VarName::superglobal(\"Node\")),\n      Ok(qualified(vec!(\"Node\"))),\n    );\n  }\n\n  #[test]\n  fn extends_imported() {\n    assert_eq!(\n      ClassExtends::try_from(VarName::imported_constant(VarName::file_constant(\"Foo\"), \"MyClass\")),\n      Ok(qualified(vec!(\"Foo\", \"MyClass\"))),\n    );\n  }\n\n  #[test]\n  fn cannot_extend_local() {\n    assert_eq!(\n      ClassExtends::try_from(VarName::local(\"abc\")),\n      Err(VarNameIntoExtendsError::CannotExtendLocal(String::from(\"abc\"))),\n    );\n  }\n\n  #[test]\n  fn cannot_extend_subscripted() {\n    assert_eq!(\n      ClassExtends::try_from(VarName::SubscriptedConstant(Box::new(VarName::file_constant(\"abc\")), 10)),\n      Err(VarNameIntoExtendsError::CannotExtendSubscript(Box::new(VarName::file_constant(\"abc\")), 10)),\n    );\n  }\n\n  #[test]\n  fn cannot_extend_current_file() {\n    assert_eq!(\n      ClassExtends::try_from(VarName::CurrentFile(String::from(\"Filename.gd\"))),\n      Err(VarNameIntoExtendsError::CannotExtendCurrentFile(String::from(\"Filename.gd\"))),\n    );\n  }\n\n  #[test]\n  fn cannot_extend_null_value() {\n    assert_eq!(\n      ClassExtends::try_from(VarName::Null),\n      Err(VarNameIntoExtendsError::CannotExtendNull),\n    );\n  }\n\n  #[test]\n  fn from_file_ext_test() {\n    assert_eq!(ValueHint::from_file_ext(OsStr::new(\"GD\")), Some(ValueHint::ClassName));\n    assert_eq!(ValueHint::from_file_ext(OsStr::new(\"gd\")), Some(ValueHint::ClassName));\n    assert_eq!(ValueHint::from_file_ext(OsStr::new(\"tscn\")), None);\n    assert_eq!(ValueHint::from_file_ext(OsStr::new(\"png\")), None);\n    assert_eq!(ValueHint::from_file_ext(OsStr::new(\"abc\")), None);\n  }\n\n}\n"
  },
  {
    "path": "src/compile/symbol_table/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Symbol tables store names and keep track of what variable or\n//! function they reference.\n//!\n//! See the [`SymbolTable`] structure for more. For anything in the\n//! variable namespace, we use [`local_var::LocalVar`] to keep track\n//! of the value. For anything in the function namespace, we use\n//! [`function_call::FnCall`] to keep track of its value, and we use\n//! [`call_magic::CallMagic`] for special call semantics on functions.\n\npub mod call_magic;\npub mod function_call;\npub mod inner;\npub mod local_var;\n\nuse crate::ir::identifier::{Id, Namespace};\nuse function_call::FnCall;\nuse call_magic::{CallMagic};\nuse local_var::{LocalVar, ValueHint, ValueHintsTable};\n\nuse serde::{Serialize, Deserialize};\n\nuse std::collections::HashMap;\nuse std::collections::HashSet;\nuse std::borrow::Borrow;\n\n/// GDLisp has two separate namespaces when it comes to name\n/// resolution: the variable namespace and the function namespace.\n/// Types, such as classes and primitive types, fall into the variable\n/// namespace. `SymbolTable` encompasses a table of names available in\n/// the current scope, in either namespace.\n#[derive(Clone, Debug, Default, Serialize, Deserialize)]\npub struct SymbolTable {\n\n  /// A mapping from GDLisp names to [`LocalVar`] instances, which can\n  /// be local variables, file-level constants, GDScript constants, or\n  /// several other types of values.\n  locals: HashMap<String, LocalVar>,\n\n  /// A mapping from GDScript names to GDLisp names. The values in\n  /// this map shall always be valid keys in `locals`.\n  reverse_locals: HashMap<String, String>,\n\n  /// A mapping of GDScript variables that were\n  /// synthetically-generated and do not have a GDLisp name\n  /// corresponding to them. The Boolean indicates whether the\n  /// variable is local to the current function or not. Locals will be\n  /// cleared by [`SymbolTable::clear_synthetic_locals`].\n  synthetic_locals: HashMap<String, bool>,\n\n  /// A mapping from GDLisp names to [`FnCall`] and corresponding\n  /// [`CallMagic`] instances.\n  functions: HashMap<String, (FnCall, CallMagic)>,\n\n  /// A mapping from GDScript names to GDLisp names. The values in\n  /// this map shall always be valid keys in `functions`.\n  reverse_functions: HashMap<String, String>,\n\n  /// A set of GDScript functions that were synthetically-generated\n  /// and do not have a GDLisp name corresponding to them.\n  synthetic_functions: HashSet<String>,\n}\n\n/// When we move into a class scope, we need to keep two symbol\n/// tables: one for use in static contexts and one for use in instance\n/// (non-static) contexts. This struct encapsulates that concept.\npub struct ClassTablePair<'a, 'b> {\n  /// The table for use in static context.\n  pub static_table: &'a mut SymbolTable,\n  /// The table for use in instance (non-static) context.\n  pub instance_table: &'b mut SymbolTable,\n}\n\nimpl SymbolTable {\n\n  /// A new, empty symbol table. Equivalent to\n  /// `SymbolTable::default()`.\n  pub fn new() -> SymbolTable {\n    SymbolTable::default()\n  }\n\n  /// A new, empty symbol table which inherits its synthetic variables\n  /// from the given symbol table. Only global synthetic variables and\n  /// functions are inherited; local synthetic variables from the\n  /// prior table are discarded.\n  pub fn with_synthetics_from(table: &SymbolTable) -> SymbolTable {\n    let mut new_table = SymbolTable::new();\n    table.dump_synthetics_to(&mut new_table);\n    new_table\n  }\n\n  /// Copies the global synthetic variables and functions from `self`\n  /// back into the given table. Local synthetic names are ignored.\n  pub fn dump_synthetics_to(&self, destination_table: &mut SymbolTable) {\n    // Variables\n    for (var, is_local) in &self.synthetic_locals {\n      if !is_local {\n        destination_table.add_synthetic_var(var.to_owned(), false);\n      }\n    }\n\n    // Functions (cannot be local, so assume all are global)\n    for fun in &self.synthetic_functions {\n      destination_table.add_synthetic_fn(fun.to_owned());\n    }\n  }\n\n  /// Gets the variable with the given GDLisp name, or `None` if no such\n  /// variable exists in the table.\n  pub fn get_var(&self, name: &str) -> Option<&LocalVar> {\n    self.locals.get(name)\n  }\n\n  /// Gets the variable with the given local GDScript name. Variables\n  /// are indexed by both names, so this access is as efficient as\n  /// [`SymbolTable::get_var`].\n  pub fn get_var_by_gd_name(&self, gd_name: &str) -> Option<&LocalVar> {\n    self.reverse_locals.get(gd_name).and_then(|name| self.get_var(name))\n  }\n\n  /// Gets the function with the given local GDScript name. Functions\n  /// are indexed by both names, so this access is as efficient as\n  /// [`SymbolTable::get_fn`].\n  pub fn get_fn_by_gd_name(&self, gd_name: &str) -> Option<(&FnCall, &CallMagic)> {\n    self.reverse_functions.get(gd_name).and_then(|name| self.get_fn(name))\n  }\n\n  /// Sets the variable with the given GDLisp name, returning the old\n  /// value if one existed.\n  pub fn set_var(&mut self, name: String, value: LocalVar) -> Option<LocalVar> {\n    let new_gd_name = value.simple_name().map(|x| x.to_owned());\n    let old_value = self.locals.insert(name.clone(), value);\n    if let Some(old_value) = &old_value {\n      if let Some(old_gd_name) = old_value.simple_name() {\n        self.reverse_locals.remove(old_gd_name);\n      }\n    }\n    if let Some(new_gd_name) = new_gd_name {\n      self.reverse_locals.insert(new_gd_name, name);\n    }\n    old_value\n  }\n\n  /// Removes the variable with the given GDLisp name. If no such\n  /// variable exists, then nothing is changed.\n  pub fn del_var(&mut self, name: &str) {\n    let value = self.locals.remove(name);\n    if let Some(value) = value {\n      if let Some(gd_name) = value.simple_name() {\n        self.reverse_locals.remove(gd_name);\n      }\n    }\n  }\n\n  /// Adds a synthetic GDScript variable to the symbol table.\n  /// Synthetic GDScript variables do not appear in the symbol table's\n  /// maps but will respond to [`SymbolTable::has_var_with_gd_name`].\n  pub fn add_synthetic_var(&mut self, name: String, is_local: bool) {\n    self.synthetic_locals.insert(name, is_local);\n  }\n\n  /// Removes a synthetic GDScript variable. See\n  /// [`SymbolTable::add_synthetic_var`].\n  pub fn remove_synthetic_var(&mut self, name: &str) {\n    self.synthetic_locals.remove(name);\n  }\n\n  /// Removes all synthetic variables marked with `is_local`.\n  pub fn clear_synthetic_locals(&mut self) {\n    self.synthetic_locals.retain(|_, is_local| !*is_local);\n  }\n\n  /// Gets the function call and call magic associated with the given\n  /// GDLisp function name.\n  pub fn get_fn(&self, name: &str) -> Option<(&FnCall, &CallMagic)> {\n    self.functions.get(name).map(|(call, magic)| {\n      (call, magic)\n    })\n  }\n\n  /// Sets the function call and call magic associated with the given\n  /// function name.\n  pub fn set_fn(&mut self, name: String, value: FnCall, magic: CallMagic) {\n    let new_gd_name = value.function.clone();\n    let old_function = self.functions.insert(name.clone(), (value, magic));\n    if let Some((old_function, _)) = old_function {\n      self.reverse_functions.remove(&old_function.function);\n    }\n    self.reverse_functions.insert(new_gd_name, name);\n  }\n\n  /// Deletes any function call info and call magic associated with\n  /// the given function name.\n  pub fn del_fn(&mut self, name: &str) {\n    let old_function = self.functions.remove(name);\n    if let Some((old_function, _)) = old_function {\n      self.reverse_locals.remove(&old_function.function);\n    }\n  }\n\n  /// Adds a synthetic GDScript function to the symbol table.\n  /// Synthetic GDScript functions do not appear in the symbol table's\n  /// maps but will respond to [`SymbolTable::has_fn_with_gd_name`].\n  pub fn add_synthetic_fn(&mut self, name: String) {\n    self.synthetic_functions.insert(name);\n  }\n\n  /// Removes a synthetic GDScript function. See\n  /// [`SymbolTable::add_synthetic_fn`].\n  pub fn remove_synthetic_fn(&mut self, name: &str) {\n    self.synthetic_functions.remove(name);\n  }\n\n  /// An iterator over the variable namespace of this table.\n  pub fn vars(&self) -> impl Iterator<Item=(&str, &LocalVar)> {\n    return self.locals.iter().map(|x| (x.0.borrow(), x.1));\n  }\n\n  /// An iterator over the function namespace of this table.\n  pub fn fns(&self) -> impl Iterator<Item=(&str, &FnCall, &CallMagic)> {\n    self.functions.iter().map(|(name, value)| {\n      (name.borrow(), &value.0, &value.1)\n    })\n  }\n\n  /// An iterator over the function namespace of this table, providing\n  /// mutable access to the function call information and the call\n  /// magic.\n  pub fn fns_mut(&mut self) -> impl Iterator<Item=(&str, &mut FnCall, &mut CallMagic)> {\n    self.functions.iter_mut().map(|(name, value)| {\n      (name.borrow(), &mut value.0, &mut value.1)\n    })\n  }\n\n  /// An iterator over all of the names in the symbol table, in both\n  /// namespaces. The order in which this iterator outputs names is\n  /// not specified.\n  pub fn keys(&self) -> impl Iterator<Item=(Namespace, &str)> {\n    let vars = self.vars().map(|(name, _)| (Namespace::Value, name));\n    let fns = self.fns().map(|(name, _, _)| (Namespace::Function, name));\n    vars.chain(fns)\n  }\n\n  /// An iterator over all of the names in the symbol table, taking\n  /// ownership of the respective identifiers, in both namespaces. The\n  /// order in which this iterator outputs names is not specified.\n  pub fn into_keys(self) -> impl Iterator<Item=Id> {\n    let vars = self.locals.into_iter().map(|(name, _)| Id::new(Namespace::Value, name));\n    let fns = self.functions.into_iter().map(|(name, _)| Id::new(Namespace::Function, name));\n    vars.chain(fns)\n  }\n\n  /// Iterates over both namespaces of `other` and sets the variable\n  /// and function names in `self` to the values from `other`. If\n  /// values already existed in `self` for some name, then they are\n  /// overwritten.\n  pub fn assign_from(&mut self, other: &SymbolTable) {\n    for (name, value) in other.vars() {\n      self.set_var(name.to_owned(), value.to_owned());\n    }\n    for (name, call, magic) in other.fns() {\n      self.set_fn(name.to_owned(), call.to_owned(), magic.to_owned());\n    }\n  }\n\n  /// Returns true if the symbol table has a variable with the given\n  /// GDScript name.\n  pub fn has_var_with_gd_name(&self, name: &str) -> bool {\n    self.reverse_locals.contains_key(name) ||\n      self.synthetic_locals.contains_key(name)\n  }\n\n  /// Returns true if the symbol table has a function with the given\n  /// GDScript name.\n  pub fn has_fn_with_gd_name(&self, name: &str) -> bool {\n    self.reverse_functions.contains_key(name) ||\n      self.synthetic_functions.contains(name)\n  }\n\n}\n\n/// Trait for objects which have a symbol table. Currently, there is\n/// only one implementor of this trait: [`SymbolTable`] itself.\npub trait HasSymbolTable {\n\n  /// Borrows the symbol table from `self`.\n  fn get_symbol_table(&self) -> &SymbolTable;\n\n  /// Borrows the symbol table mutably from `self`.\n  fn get_symbol_table_mut(&mut self) -> &mut SymbolTable;\n\n  /// Binds the local variable, calls `block` with `self` as argument,\n  /// then binds the variable back to its previous value. This is the\n  /// correct semantic behavior for introducing a local variable into\n  /// some inner scope in GDScript.\n  fn with_local_var<B, F>(&mut self,\n                          name: String,\n                          value: LocalVar,\n                          block: F) -> B\n  where F : FnOnce(&mut Self) -> B {\n    let previous = self.get_symbol_table_mut().set_var(name.clone(), value);\n    let result = block(self);\n    if let Some(previous) = previous {\n      self.get_symbol_table_mut().set_var(name, previous);\n    } else {\n      self.get_symbol_table_mut().del_var(&name);\n    };\n    result\n  }\n\n  /// Recursive convenience helper for calling\n  /// [`HasSymbolTable::with_local_var`] on several variable names in\n  /// sequence.\n  fn with_local_vars<B>(&mut self,\n                        vars: &mut dyn Iterator<Item=(String, LocalVar)>,\n                        block: impl FnOnce(&mut Self) -> B) -> B {\n    if let Some((name, value)) = vars.next() {\n      self.with_local_var(name, value, |curr| {\n        curr.with_local_vars(vars, block)\n      })\n    } else {\n      block(self)\n    }\n  }\n\n  /// Binds the local function, calls `block` with `self` as argument,\n  /// then binds the function back to its previous value. This is the\n  /// correct semantic behavior for introducing a local function into\n  /// some inner scope in GDScript.\n  fn with_local_fn<B>(&mut self,\n                      name: String,\n                      value: FnCall,\n                      block: impl FnOnce(&mut Self) -> B) -> B {\n    let previous = self.get_symbol_table_mut().get_fn(&name).map(|(p, m)| (p.clone(), m.clone()));\n    self.get_symbol_table_mut().set_fn(name.clone(), value, CallMagic::DefaultCall);\n    let result = block(self);\n    if let Some((p, m)) = previous {\n      self.get_symbol_table_mut().set_fn(name, p, m);\n    } else {\n      self.get_symbol_table_mut().del_fn(&name);\n    };\n    result\n  }\n\n  /// Recursive convenience helper for calling\n  /// [`HasSymbolTable::with_local_fn`] on several function names in\n  /// sequence.\n  fn with_local_fns<B>(&mut self,\n                       vars: &mut dyn Iterator<Item=(String, FnCall)>,\n                       block: impl FnOnce(&mut Self) -> B) -> B {\n    if let Some((name, value)) = vars.next() {\n      self.with_local_fn(name, value, |curr| {\n        curr.with_local_fns(vars, block)\n      })\n    } else {\n      block(self)\n    }\n  }\n\n}\n\nimpl HasSymbolTable for SymbolTable {\n\n  fn get_symbol_table(&self) -> &SymbolTable {\n    self\n  }\n\n  fn get_symbol_table_mut(&mut self) -> &mut SymbolTable {\n    self\n  }\n\n}\n\n// TODO This is a mess. Can we please store the value hints some way\n// that doesn't require a reverse lookup on GDScript names, because\n// that reverse lookup is just going to be awkward no matter what.\nimpl ValueHintsTable for SymbolTable {\n  fn get_value_hint(&self, name: &str) -> Option<&ValueHint> {\n    self.get_var_by_gd_name(name).and_then(|var| var.value_hint.as_ref())\n  }\n}\n\nimpl<'a> ClassTablePair<'a, 'a> {\n\n  /// Returns either `self.static_table` or `self.instance_table`,\n  /// depending on the type of scope we're in.\n  pub fn into_table(self, is_static: bool) -> &'a mut SymbolTable {\n    if is_static {\n      self.static_table\n    } else {\n      self.instance_table\n    }\n  }\n\n}\n\n// TODO Test magic here as well.\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use local_var::VarName;\n  use function_call::{FnSpecs, FnScope};\n\n  fn from_var_name(e: &VarName) -> &str {\n    e.simple_name().expect(\"Unexpected nontrivial variable name\")\n  }\n\n  #[test]\n  fn test_vars() {\n    let mut table = SymbolTable::new();\n    assert_eq!(table.get_var(\"foo\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"foo\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"bar\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"baz\"), None);\n\n    assert_eq!(table.set_var(\"foo\".to_owned(), LocalVar::read(\"bar\".to_owned())), None);\n    assert_eq!(table.get_var(\"foo\"), Some(&LocalVar::read(\"bar\".to_owned())));\n    assert_eq!(table.get_var_by_gd_name(\"foo\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"bar\"), Some(&LocalVar::read(\"bar\".to_owned())));\n    assert_eq!(table.get_var_by_gd_name(\"baz\"), None);\n\n    assert_eq!(table.set_var(\"foo\".to_owned(), LocalVar::read(\"baz\".to_owned())),\n               Some(LocalVar::read(\"bar\".to_owned())));\n    assert_eq!(table.get_var_by_gd_name(\"foo\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"bar\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"baz\"), Some(&LocalVar::read(\"baz\".to_owned())));\n\n    table.del_var(\"foo\");\n    assert_eq!(table.get_var(\"foo\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"foo\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"bar\"), None);\n    assert_eq!(table.get_var_by_gd_name(\"baz\"), None);\n\n  }\n\n  #[test]\n  fn test_iter_vars() {\n    let mut table = SymbolTable::new();\n    table.set_var(\"foo\".to_owned(), LocalVar::read(\"bar\".to_owned()));\n    table.set_var(\"foo1\".to_owned(), LocalVar::rw(\"baz\".to_owned()));\n    table.set_var(\"foo2\".to_owned(), LocalVar::read(\"abcdef\".to_owned()));\n    let mut vec: Vec<_> = table.vars().map(|x| (x.0, from_var_name(&x.1.name))).collect();\n    vec.sort_unstable();\n    assert_eq!(vec, vec!((\"foo\", \"bar\"), (\"foo1\", \"baz\"), (\"foo2\", \"abcdef\")));\n  }\n\n  fn sample_fn() -> FnCall {\n    FnCall::file_constant(FnSpecs::new(1, 0, None), FnScope::Global, \"foobar\".to_owned())\n  }\n\n  #[test]\n  fn test_fns() {\n    let mut table = SymbolTable::new();\n\n    assert_eq!(table.get_fn(\"foo\").map(|x| x.0), None);\n\n    table.set_fn(\"foo\".to_owned(), sample_fn(), CallMagic::DefaultCall);\n    assert_eq!(table.get_fn(\"foo\").map(|x| x.0), Some(&sample_fn()));\n    assert_eq!(table.get_fn_by_gd_name(\"foo\").map(|x| x.0), None);\n    assert_eq!(table.get_fn_by_gd_name(\"foobar\").map(|x| x.0), Some(&sample_fn()));\n\n    table.set_fn(\"foo\".to_owned(), sample_fn(), CallMagic::DefaultCall);\n    table.del_fn(\"foo\");\n    assert_eq!(table.get_fn(\"foo\").map(|x| x.0), None);\n    assert_eq!(table.get_fn_by_gd_name(\"foo\").map(|x| x.0), None);\n    assert_eq!(table.get_fn_by_gd_name(\"foobar\").map(|x| x.0), None);\n\n  }\n\n  #[test]\n  fn test_iter_fns() {\n    let mut table = SymbolTable::new();\n    table.set_fn(\"foo\".to_owned(), sample_fn(), CallMagic::DefaultCall);\n    table.set_fn(\"foo1\".to_owned(), sample_fn(), CallMagic::DefaultCall);\n    table.set_fn(\"foo2\".to_owned(), sample_fn(), CallMagic::DefaultCall);\n    let mut vec: Vec<_> = table.fns().map(|(x, y, _)| (x, y)).collect();\n    vec.sort_unstable_by(|a, b| a.0.cmp(b.0));\n    let tmp = sample_fn();\n    assert_eq!(vec, vec!((\"foo\", &tmp), (\"foo1\", &tmp), (\"foo2\", &tmp)));\n  }\n\n  #[test]\n  fn test_synthetic_vars() {\n    let mut table = SymbolTable::new();\n    assert_eq!(table.has_var_with_gd_name(\"foo\"), false);\n    table.add_synthetic_var(String::from(\"foo\"), false);\n    assert_eq!(table.has_var_with_gd_name(\"foo\"), true);\n    table.remove_synthetic_var(\"foo\");\n    assert_eq!(table.has_var_with_gd_name(\"foo\"), false);\n  }\n\n  #[test]\n  fn test_synthetic_fns() {\n    let mut table = SymbolTable::new();\n    assert_eq!(table.has_fn_with_gd_name(\"foo\"), false);\n    table.add_synthetic_fn(String::from(\"foo\"));\n    assert_eq!(table.has_fn_with_gd_name(\"foo\"), true);\n    table.remove_synthetic_fn(\"foo\");\n    assert_eq!(table.has_fn_with_gd_name(\"foo\"), false);\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/arglist.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDScript argument lists.\n//!\n//! The type [`ArgList`] defines a list of arguments as GDScript sees\n//! them.\n//!\n//! See [`crate::ir::arglist`] for the companion module used during IR\n//! analysis.\n\nuse super::expr::Expr;\n\nuse std::convert::AsRef;\nuse std::mem::swap;\n\n/// A list of arguments in a function declaration.\n///\n/// Currently, GDLisp only compiles to required GDScript arguments and\n/// does not provide support for optional arguments on the GDScript\n/// side.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ArgList {\n  /// The required arguments.\n  required_args: Vec<String>,\n  /// The optional arguments.\n  optional_args: Vec<(String, Expr)>,\n}\n\nimpl ArgList {\n\n  /// An empty argument list.\n  pub fn empty() -> ArgList {\n    ArgList { required_args: Vec::new(), optional_args: Vec::new() }\n  }\n\n  /// A list of required arguments.\n  pub fn required(args: Vec<String>) -> ArgList {\n    ArgList { required_args: args, optional_args: Vec::new() }\n  }\n\n  /// Appends the given values to the current argument list as\n  /// optional arguments. Takes ownership but returns `self`.\n  ///\n  /// This method is intended to be used in a builder style, like so.\n  ///\n  /// ```\n  /// # use gdlisp::gdscript::arglist::ArgList;\n  /// # use gdlisp::gdscript::expr::Expr;\n  /// # use gdlisp::pipeline::source::SourceOffset;\n  /// # let required_vec = vec!(String::from(\"a\"), String::from(\"b\"));\n  /// # let optional_vec = vec!((String::from(\"c\"), Expr::null(SourceOffset(0))));\n  /// let args = ArgList::required(required_vec).optional(optional_vec);\n  /// # let all_arg_names: Vec<&str> = args.all_args_iter().collect();\n  /// # assert_eq!(all_arg_names, vec!(\"a\", \"b\", \"c\"));\n  /// ```\n  pub fn optional(mut self, args: impl IntoIterator<Item=(String, Expr)>) -> Self {\n    self.optional_args.extend(args);\n    self\n  }\n\n  /// Whether the argument list is empty.\n  pub fn is_empty(&self) -> bool {\n    self.required_args.is_empty() && self.optional_args.is_empty()\n  }\n\n  /// Insert required arguments at the beginning of this argument\n  /// list.\n  pub fn prepend_required(&mut self, required: impl Iterator<Item=String>) {\n    let mut new_vec: Vec<_> = required.collect();\n    swap(&mut new_vec, &mut self.required_args);\n    self.required_args.extend(new_vec);\n  }\n\n  /// Converts all required arguments into optional arguments, where\n  /// the default value is the given value.\n  ///\n  /// The default value will be cloned once for each argument.\n  pub fn optionalize_all(&mut self, default_value: &Expr) {\n    let required_args = self.required_args.drain(..).map(|arg| (arg, default_value.to_owned()));\n    self.optional_args.splice(0..0, required_args);\n  }\n\n  /// All of the arguments, required and optional alike, as an\n  /// iterator.\n  pub fn all_args_iter(&self) -> impl Iterator<Item=&str> {\n    self.required_args.iter().map(String::as_ref)\n      .chain(self.optional_args.iter().map(|x| x.0.as_ref()))\n  }\n\n  /// Returns the list of arguments in GDScript form, i.e. as a\n  /// comma-separated list of arguments.\n  pub fn to_gd(&self) -> String {\n    let required = self.required_args.iter().map(String::from);\n    let optional = self.optional_args.iter().map(|(name, default)| {\n      format!(\"{} = {}\", name, default.to_gd())\n    });\n\n    let mut all_args = Vec::new();\n    all_args.extend(required);\n    all_args.extend(optional);\n    all_args.join(\", \")\n\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::source::SourceOffset;\n\n  fn null() -> Expr {\n    Expr::null(SourceOffset(0))\n  }\n\n  #[test]\n  fn empty_arglist_to_gd_test() {\n    assert_eq!(ArgList::empty().to_gd(), \"\");\n  }\n\n  #[test]\n  fn required_arglist_to_gd_test() {\n    assert_eq!(ArgList::required(vec!()).to_gd(), \"\");\n    assert_eq!(ArgList::required(vec!(String::from(\"foo\"))).to_gd(), \"foo\");\n    assert_eq!(ArgList::required(vec!(String::from(\"foo\"), String::from(\"bar\"))).to_gd(), \"foo, bar\");\n  }\n\n  #[test]\n  fn optional_arglist_to_gd_test() {\n    assert_eq!(ArgList::empty().optional(vec!((String::from(\"foo\"), null()))).to_gd(), \"foo = null\");\n    assert_eq!(ArgList::empty().optional(vec!((String::from(\"foo\"), null()), (String::from(\"bar\"), null()))).to_gd(), \"foo = null, bar = null\");\n  }\n\n  #[test]\n  fn full_arglist_to_gd_test() {\n    assert_eq!(ArgList::required(vec!(String::from(\"foo\"), String::from(\"bar\")))\n               .optional(vec!((String::from(\"baz\"), null())))\n               .to_gd(),\n               \"foo, bar, baz = null\");\n  }\n\n  #[test]\n  fn optional_arglist_custom_expr_to_gd_test() {\n    let expr = Expr::from_value(10, SourceOffset(0));\n    assert_eq!(ArgList::empty().optional(vec!((String::from(\"foo\"), expr))).to_gd(), \"foo = 10\");\n  }\n\n  #[test]\n  fn prepend_required_test() {\n    let mut args = ArgList::required(vec!(String::from(\"c\"), String::from(\"d\")));\n    args.prepend_required(vec!(String::from(\"a\"), String::from(\"b\")).into_iter());\n    assert_eq!(args.to_gd(), \"a, b, c, d\");\n  }\n\n  #[test]\n  fn all_args_iter_test() {\n    let args =\n      ArgList::required(vec!(String::from(\"foo\"), String::from(\"bar\")))\n      .optional(vec!((String::from(\"baz\"), null())));\n    let names: Vec<_> = args.all_args_iter().collect();\n    assert_eq!(names, vec!(\"foo\", \"bar\", \"baz\"));\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/class_extends.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`ClassExtends`] type, for qualified names valid in\n//! an `extends` clause.\n\nuse super::literal::Literal;\n\n/// A descriptor of what class is being extended.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum ClassExtends {\n  /// A qualified name, separating the final identifier from the\n  /// left-hand side by a dot.\n  Qualified(Box<ClassExtends>, String),\n  /// A simple, unquoted identifier.\n  SimpleIdentifier(String),\n  /// A string literal, referencing a known file name.\n  StringLit(String),\n}\n\nimpl ClassExtends {\n\n  /// Qualifies the given `extends` name with an additional\n  /// identifier.\n  pub fn attribute(self, attr: impl Into<String>) -> ClassExtends {\n    ClassExtends::Qualified(Box::new(self), attr.into())\n  }\n\n  /// Convert `self` to a string suitable as the tail end of a\n  /// `extends` clause in GDScript.\n  pub fn to_gd(&self) -> String {\n    match self {\n      ClassExtends::Qualified(lhs, rhs) => format!(\"{}.{}\", lhs.to_gd(), rhs),\n      ClassExtends::SimpleIdentifier(string) => string.to_owned(),\n      ClassExtends::StringLit(string) => Literal::String(string.to_owned()).to_gd(),\n    }\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn test_simple_identifier() {\n    let cls = ClassExtends::SimpleIdentifier(String::from(\"foobar\"));\n    assert_eq!(cls.to_gd(), \"foobar\");\n  }\n\n  #[test]\n  fn test_string_literal() {\n    let cls = ClassExtends::StringLit(String::from(\"abc\"));\n    assert_eq!(cls.to_gd(), \"\\\"abc\\\"\");\n  }\n\n  #[test]\n  fn test_string_literal_escaped() {\n    let cls = ClassExtends::StringLit(String::from(r#\"abc\"\\def\"#));\n    assert_eq!(cls.to_gd(), r#\"\"abc\\\"\\\\def\"\"#);\n  }\n\n  #[test]\n  fn test_qualified_identifier() {\n    let cls = ClassExtends::SimpleIdentifier(String::from(\"foobar\")).attribute(\"baz\").attribute(\"xyz\");\n    assert_eq!(cls.to_gd(), \"foobar.baz.xyz\");\n  }\n\n  #[test]\n  fn test_qualified_path() {\n    let cls = ClassExtends::StringLit(String::from(\"foobar\")).attribute(\"baz\").attribute(\"xyz\");\n    assert_eq!(cls.to_gd(), \"\\\"foobar\\\".baz.xyz\");\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/decl.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDScript declarations.\n//!\n//! This module defines a [datatype](Decl) for representing\n//! declarations in the GDScript language, as well as\n//! [`Decl::write_gd`] for writing declarations as GDScript syntax to\n//! a [`fmt::Write`] instance.\n\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::indent;\nuse crate::gdscript::library;\nuse crate::gdscript::arglist::ArgList;\nuse crate::pipeline::source::{SourceOffset, Sourced};\nuse super::spacing::SpacedDeclPrinter;\nuse super::class_extends::ClassExtends;\n\nuse std::fmt::{self, Write};\n\n/// The type of GDScript declarations.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum DeclF {\n  VarDecl(VarDecl),\n  ConstDecl(String, Expr),\n  ClassDecl(ClassDecl),\n  InitFnDecl(InitFnDecl),\n  FnDecl(Static, FnDecl),\n  EnumDecl(EnumDecl),\n  SignalDecl(String, ArgList),\n  PassDecl,\n}\n\n/// GDScript declaration with its source offset. See [`Sourced`].\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Decl {\n  pub value: DeclF,\n  pub pos: SourceOffset,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ClassDecl {\n  pub name: String,\n  pub extends: ClassExtends,\n  pub body: Vec<Decl>,\n}\n\n/// The top-level class is a special kind of class, similar to a\n/// [`ClassDecl`].\n///\n/// The top-level class, however, is not required to have a name and\n/// will print using subtly different syntax.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct TopLevelClass {\n  pub name: Option<String>, // The top-level class is not required to have a name.\n  pub extends: ClassExtends,\n  pub body: Vec<Decl>,\n}\n\n/// A variable declaration in a GDScript class. This also includes\n/// variable declarations at the top-level (i.e. in the implied \"main\"\n/// class of the file) but does *not* include constant declarations.\n/// For the latter, [`ConstDecl`](DeclF::ConstDecl) should be used.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct VarDecl {\n  pub export: Option<Export>,\n  pub onready: Onready,\n  pub name: String,\n  pub value: Option<Expr>,\n  pub setget: Setget,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct FnDecl {\n  pub name: String,\n  pub args: ArgList,\n  pub body: Vec<Stmt>,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct InitFnDecl {\n  pub args: ArgList,\n  pub super_call: Vec<Expr>,\n  pub body: Vec<Stmt>,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct EnumDecl {\n  pub name: Option<String>,\n  pub clauses: Vec<(String, Option<Expr>)>,\n}\n\n/// A GDScript export declaration attached to a variable.\n///\n/// Not all expressions are valid in an export declaration, so the\n/// user should take care to use this type only with valid\n/// expressions.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct Export {\n  pub args: Vec<Expr>,\n}\n\n/// A Boolean-isomorphic type which indicates whether or not a\n/// function is static.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum Static {\n  NonStatic, IsStatic,\n}\n\n/// A Boolean-isomorphic type which indicates whether or not a\n/// variable has the `onready` modifier.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub enum Onready {\n  No, Yes,\n}\n\n/// A setter declaration and a getter declaration. Either part may be\n/// omitted.\n#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]\npub struct Setget {\n  pub setter: Option<String>,\n  pub getter: Option<String>,\n}\n\nfn empty_class_body(pos: SourceOffset) -> Decl {\n  Decl::new(\n    DeclF::InitFnDecl(InitFnDecl {\n      args: ArgList::empty(),\n      super_call: vec!(),\n      body: vec!(),\n    }),\n    pos\n  )\n}\n\nimpl Decl {\n\n  /// A new `Decl` with its source offset.\n  pub fn new(value: DeclF, pos: SourceOffset) -> Decl {\n    Decl { value, pos }\n  }\n\n  /// The name of the identifier referenced by this declaration, if\n  /// one exists. Some declarations, such as enums, may not always\n  /// have names.\n  pub fn name(&self) -> Option<&str> {\n    match &self.value {\n      DeclF::VarDecl(vdecl) => Some(&vdecl.name),\n      DeclF::ConstDecl(n, _) => Some(n),\n      DeclF::ClassDecl(cdecl) => Some(&cdecl.name),\n      DeclF::InitFnDecl(_) => Some(library::CONSTRUCTOR_NAME),\n      DeclF::FnDecl(_, fdecl) => Some(&fdecl.name),\n      DeclF::EnumDecl(edecl) => edecl.name.as_ref().map(|x| x.as_ref()),\n      DeclF::SignalDecl(n, _) => Some(n),\n      DeclF::PassDecl => None,\n    }\n  }\n\n  /// Write the declaration, as GDScript code, to the [`fmt::Write`]\n  /// instance `w`.\n  ///\n  /// We are assumed to be at the indentation level `ind`, so that all\n  /// lines in the result will be indented to that level.\n  ///\n  /// The writer `w` should currently be either empty or immediately\n  /// after a newline. The declaration will always end by printing a\n  /// newline, making it suitable for writing a subsequent declaration\n  /// immediately after.\n  pub fn write_gd<W : fmt::Write>(&self, w: &mut W, ind: u32) -> Result<(), fmt::Error> {\n    indent(w, ind)?;\n    match &self.value {\n      DeclF::VarDecl(VarDecl { export, onready, name, value, setget }) => {\n        if let Some(export) = export {\n          if export.args.is_empty() {\n            write!(w, \"export \")?;\n          } else {\n            write!(w, \"export({}) \", export.args.iter().map(|x| x.to_gd()).collect::<Vec<_>>().join(\", \"))?;\n          }\n        }\n        if bool::from(*onready) {\n          write!(w, \"onready \")?;\n        }\n        write!(w, \"var {}\", name)?;\n        if let Some(value) = value {\n          write!(w, \" = {}\", value.to_gd())?;\n        }\n        let setget_str = setget.to_gd();\n        if !setget_str.is_empty() {\n          write!(w, \" {}\", setget_str)?;\n        }\n        writeln!(w)\n      }\n      DeclF::ConstDecl(name, value) => {\n        writeln!(w, \"const {} = {}\", name, value.to_gd())\n      }\n      DeclF::ClassDecl(ClassDecl { name, extends, body }) => {\n        write!(w, \"class {} extends {}:\\n\\n\", name, extends.to_gd())?;\n        if body.is_empty() {\n          empty_class_body(self.pos).write_gd(w, ind + 4)\n        } else {\n          let printer = SpacedDeclPrinter::new().with_spacing(1).with_indentation(ind + 4);\n          printer.write_gd(w, body.iter())\n        }\n      }\n      DeclF::FnDecl(stat, FnDecl { name, args, body }) => {\n        if *stat == Static::IsStatic {\n          write!(w, \"static \")?;\n        }\n        writeln!(w, \"func {}({}):\", name, args.to_gd())?;\n        Stmt::write_gd_stmts(body, w, ind + 4)\n      }\n      DeclF::InitFnDecl(InitFnDecl { args, super_call, body }) => {\n        let super_args: Vec<_> = super_call.iter().map(Expr::to_gd).collect();\n        if super_args.is_empty() {\n          writeln!(w, \"func _init({}):\", args.to_gd())?;\n        } else {\n          writeln!(w, \"func _init({}).({}):\", args.to_gd(), super_args.join(\", \"))?;\n        }\n        Stmt::write_gd_stmts(body, w, ind + 4)\n      }\n      DeclF::SignalDecl(name, args) => {\n        if args.is_empty() {\n          writeln!(w, \"signal {}\", name)\n        } else {\n          writeln!(w, \"signal {}({})\", name, args.to_gd())\n        }\n      }\n      DeclF::EnumDecl(EnumDecl { name, clauses }) => {\n        write!(w, \"enum \")?;\n        if let Some(name) = name {\n          write!(w, \"{} \", name)?;\n        }\n        writeln!(w, \"{{\")?;\n        for (const_name, const_value) in clauses {\n          indent(w, ind + 4)?;\n          write!(w, \"{}\", const_name)?;\n          if let Some(const_value) = const_value {\n            write!(w, \" = {}\", const_value.to_gd())?;\n          }\n          writeln!(w, \",\")?;\n        }\n        indent(w, ind)?;\n        writeln!(w, \"}}\")\n      }\n      DeclF::PassDecl => {\n        writeln!(w, \"pass\")\n      }\n    }\n  }\n\n  /// Write the declaration to a string, using [`Decl::write_gd`].\n  ///\n  /// # Panics\n  ///\n  /// This function panics if there is a write error to the string. If\n  /// you wish to handle that case yourself, use [`Stmt::write_gd`]\n  /// explicitly.\n  pub fn to_gd(&self, ind: u32) -> String {\n    let mut string = String::new();\n    self.write_gd(&mut string, ind).expect(\"Could not write to string in Decl::to_gd\");\n    string\n  }\n\n}\n\nimpl Sourced for Decl {\n  type Item = DeclF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &DeclF {\n    &self.value\n  }\n\n}\n\nimpl TopLevelClass {\n\n  /// Convert `self` to a string suitable for writing to a `.gd` file.\n  pub fn to_gd(&self) -> String {\n    let mut string = String::new();\n    if let Some(name) = &self.name {\n      writeln!(string, \"class_name {}\", name).expect(\"Could not write to string in TopLevelClass::to_gd\");\n    }\n    writeln!(string, \"extends {}\", self.extends.to_gd()).expect(\"Could not write to string in TopLevelClass::to_gd\");\n    write!(string, \"\\n\\n\").expect(\"Could not write to string in TopLevelClass::to_gd\");\n    if self.body.is_empty() {\n      empty_class_body(SourceOffset(0)).write_gd(&mut string, 0)\n        .expect(\"Could not write to string in TopLevelClass::to_gd\");\n    } else {\n      let printer = SpacedDeclPrinter::new();\n      printer.write_gd(&mut string, self.body.iter())\n        .expect(\"Could not write to string in TopLevelClass::to_gd\");\n    }\n    string\n  }\n\n}\n\nimpl VarDecl {\n\n  /// A new variable declaration with the given name and optional\n  /// initializer. The resulting variable will have no `export`\n  /// declaration, will not be declared `onready`, and will have no\n  /// `setget` modifiers.\n  pub fn new(name: String, value: Option<Expr>) -> VarDecl {\n    VarDecl {\n      export: None,\n      onready: Onready::No,\n      name: name,\n      value: value,\n      setget: Setget::default(),\n    }\n  }\n\n  /// A variable declaration without an initializer. Equivalent to\n  /// `VarDecl::new(name, None)`.\n  pub fn simple(name: String) -> VarDecl {\n    VarDecl::new(name, None)\n  }\n\n  /// Adds or removes an export clause from the variable declaration.\n  pub fn with_export(mut self, export: Option<Export>) -> VarDecl {\n    self.export = export;\n    self\n  }\n\n  /// Adds the `onready` flag to the variable declaration.\n  pub fn onready(mut self) -> VarDecl {\n    self.onready = Onready::Yes;\n    self\n  }\n\n  /// Changes the `setget` modifier to the given value.\n  pub fn setget(mut self, setget: Setget) -> VarDecl {\n    self.setget = setget;\n    self\n  }\n\n}\n\nimpl Setget {\n\n  /// Convert `self` to a string suitable for suffixing onto a\n  /// variable declaration. If all fields are `None`, this returns the\n  /// empty string.\n  pub fn to_gd(&self) -> String {\n    match (&self.setter, &self.getter) {\n      (None, None) => String::from(\"\"),\n      (Some(s), None) => format!(\"setget {}\", s),\n      (None, Some(g)) => format!(\"setget ,{}\", g), // The leading comma is an intentional part of the GDScript syntax\n      (Some(s), Some(g)) => format!(\"setget {}, {}\", s, g),\n    }\n  }\n\n}\n\nimpl From<Static> for bool {\n  fn from(s: Static) -> bool {\n    s == Static::IsStatic\n  }\n}\n\nimpl From<Onready> for bool {\n  fn from(r: Onready) -> bool {\n    r == Onready::Yes\n  }\n}\n\nimpl From<bool> for Static {\n  fn from(b: bool) -> Static {\n    if b { Static::IsStatic } else { Static::NonStatic }\n  }\n}\n\nimpl From<bool> for Onready {\n  fn from(b: bool) -> Onready {\n    if b { Onready::Yes } else { Onready::No }\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::expr::{Expr, ExprF};\n  use crate::pipeline::source::SourceOffset;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn d(decl: DeclF) -> Decl {\n    Decl::new(decl, SourceOffset::default())\n  }\n\n  // Helper function to match the \"old\" VarDecl interface, just for\n  // convenience purposes in testing.\n  fn var(export: Option<Export>, onready: Onready, name: String, value: Option<Expr>, setget: Setget) -> VarDecl {\n    VarDecl { export, onready, name, value, setget }\n  }\n\n  #[test]\n  fn var_and_const() {\n    let expr = e(ExprF::from(10));\n    assert_eq!(d(DeclF::VarDecl(var(None, Onready::No, String::from(\"foo\"), None, Setget::default()))).to_gd(0), \"var foo\\n\");\n    assert_eq!(d(DeclF::VarDecl(var(None, Onready::No, String::from(\"foo\"), Some(expr.clone()), Setget::default()))).to_gd(0), \"var foo = 10\\n\");\n    assert_eq!(d(DeclF::VarDecl(var(None, Onready::Yes, String::from(\"foo\"), None, Setget::default()))).to_gd(0), \"onready var foo\\n\");\n    assert_eq!(d(DeclF::VarDecl(var(None, Onready::Yes, String::from(\"foo\"), Some(expr.clone()), Setget::default()))).to_gd(0), \"onready var foo = 10\\n\");\n    assert_eq!(d(DeclF::ConstDecl(String::from(\"FOO\"), expr.clone())).to_gd(0), \"const FOO = 10\\n\");\n  }\n\n  #[test]\n  fn exported_var() {\n    let expr = e(ExprF::from(10));\n\n    let export1 = Export { args: vec!(Expr::var(\"int\", SourceOffset::default())) };\n    assert_eq!(d(DeclF::VarDecl(var(Some(export1), Onready::No, String::from(\"foo\"), None, Setget::default()))).to_gd(0), \"export(int) var foo\\n\");\n\n    let export2 = Export { args: vec!() };\n    assert_eq!(d(DeclF::VarDecl(var(Some(export2), Onready::No, String::from(\"foo\"), None, Setget::default()))).to_gd(0), \"export var foo\\n\");\n\n    let export3 = Export { args: vec!() };\n    assert_eq!(d(DeclF::VarDecl(var(Some(export3), Onready::Yes, String::from(\"foo\"), None, Setget::default()))).to_gd(0), \"export onready var foo\\n\");\n\n    let export4 = Export { args: vec!(Expr::var(\"int\", SourceOffset::default()), Expr::from_value(1, SourceOffset::default()), Expr::from_value(10, SourceOffset::default())) };\n    assert_eq!(d(DeclF::VarDecl(var(Some(export4), Onready::No, String::from(\"foo\"), Some(expr.clone()), Setget::default()))).to_gd(0), \"export(int, 1, 10) var foo = 10\\n\");\n  }\n\n  #[test]\n  fn setget_var() {\n\n    let setget1 = Setget {\n      setter: Some(String::from(\"setter_name\")),\n      getter: None,\n    };\n    assert_eq!(d(DeclF::VarDecl(var(None, Onready::No, String::from(\"foo\"), None, setget1))).to_gd(0), \"var foo setget setter_name\\n\");\n\n    let setget2 = Setget {\n      setter: None,\n      getter: Some(String::from(\"getter_name\")),\n    };\n    assert_eq!(d(DeclF::VarDecl(var(None, Onready::No, String::from(\"foo\"), None, setget2))).to_gd(0), \"var foo setget ,getter_name\\n\");\n\n    let setget3 = Setget {\n      setter: Some(String::from(\"setter_name\")),\n      getter: Some(String::from(\"getter_name\")),\n    };\n    assert_eq!(d(DeclF::VarDecl(var(None, Onready::No, String::from(\"foo\"), None, setget3))).to_gd(0), \"var foo setget setter_name, getter_name\\n\");\n\n  }\n\n  #[test]\n  fn signal() {\n    assert_eq!(d(DeclF::SignalDecl(String::from(\"signal_name\"), ArgList::empty())).to_gd(0), \"signal signal_name\\n\");\n    assert_eq!(d(DeclF::SignalDecl(String::from(\"signal_name\"), ArgList::required(vec!(String::from(\"a\"), String::from(\"b\"))))).to_gd(0), \"signal signal_name(a, b)\\n\");\n  }\n\n  #[test]\n  fn functions() {\n\n    let decl1 = d(DeclF::FnDecl(Static::NonStatic, FnDecl {\n      name: String::from(\"foobar\"),\n      args: ArgList::required(vec!()),\n      body: vec!()\n    }));\n    assert_eq!(decl1.to_gd(0), \"func foobar():\\n    pass\\n\");\n\n    let decl2 = d(DeclF::FnDecl(Static::IsStatic, FnDecl {\n      name: String::from(\"foobar\"),\n      args: ArgList::required(vec!()),\n      body: vec!()\n    }));\n    assert_eq!(decl2.to_gd(0), \"static func foobar():\\n    pass\\n\");\n\n    let decl3 = d(DeclF::FnDecl(Static::NonStatic, FnDecl {\n      name: String::from(\"foobar\"),\n      args: ArgList::required(vec!(String::from(\"arg1\"))),\n      body: vec!()\n    }));\n    assert_eq!(decl3.to_gd(0), \"func foobar(arg1):\\n    pass\\n\");\n\n    let decl4 = d(DeclF::FnDecl(Static::NonStatic, FnDecl {\n      name: String::from(\"foobar\"),\n      args: ArgList::required(vec!(String::from(\"arg1\"), String::from(\"arg2\"))),\n      body: vec!()\n    }));\n    assert_eq!(decl4.to_gd(0), \"func foobar(arg1, arg2):\\n    pass\\n\");\n\n    let decl5 = d(DeclF::FnDecl(Static::NonStatic, FnDecl {\n      name: String::from(\"foobar\"),\n      args: ArgList::required(vec!(String::from(\"arg1\"), String::from(\"arg2\"))),\n      body: vec!(Stmt::expr(e(ExprF::Var(String::from(\"function_body\")))))\n    }));\n    assert_eq!(decl5.to_gd(0), \"func foobar(arg1, arg2):\\n    function_body\\n\");\n\n  }\n\n  #[test]\n  fn init_functions() {\n\n    let decl1 = d(DeclF::InitFnDecl(InitFnDecl {\n      args: ArgList::required(vec!()),\n      super_call: vec!(),\n      body: vec!()\n    }));\n    assert_eq!(decl1.to_gd(0), \"func _init():\\n    pass\\n\");\n\n    let decl2 = d(DeclF::InitFnDecl(InitFnDecl {\n      args: ArgList::required(vec!(String::from(\"arg1\"))),\n      super_call: vec!(e(ExprF::Var(String::from(\"arg1\"))), e(ExprF::from(8))),\n      body: vec!()\n    }));\n    assert_eq!(decl2.to_gd(0), \"func _init(arg1).(arg1, 8):\\n    pass\\n\");\n\n    let decl3 = d(DeclF::InitFnDecl(InitFnDecl {\n      args: ArgList::required(vec!(String::from(\"arg1\"), String::from(\"arg2\"))),\n      super_call: vec!(e(ExprF::Var(String::from(\"arg1\"))), e(ExprF::from(8))),\n      body: vec!()\n    }));\n    assert_eq!(decl3.to_gd(0), \"func _init(arg1, arg2).(arg1, 8):\\n    pass\\n\");\n\n    let decl4 = d(DeclF::InitFnDecl(InitFnDecl {\n      args: ArgList::required(vec!(String::from(\"arg1\"), String::from(\"arg2\"))),\n      super_call: vec!(e(ExprF::Var(String::from(\"arg1\"))), e(ExprF::from(8))),\n      body: vec!(Stmt::expr(e(ExprF::Var(String::from(\"function_body\")))))\n    }));\n    assert_eq!(decl4.to_gd(0), \"func _init(arg1, arg2).(arg1, 8):\\n    function_body\\n\");\n\n  }\n\n  #[test]\n  fn classes() {\n\n    let sample_function = d(DeclF::FnDecl(Static::NonStatic, FnDecl {\n      name: String::from(\"sample\"),\n      args: ArgList::required(vec!()),\n      body: vec!()\n    }));\n\n    let decl1 = d(DeclF::ClassDecl(ClassDecl {\n      name: String::from(\"MyClass\"),\n      extends: ClassExtends::SimpleIdentifier(String::from(\"ParentClass\")),\n      body: vec!(),\n    }));\n    assert_eq!(decl1.to_gd(0), \"class MyClass extends ParentClass:\\n\\n    func _init():\\n        pass\\n\");\n\n    let decl2 = d(DeclF::ClassDecl(ClassDecl {\n      name: String::from(\"MyClass\"),\n      extends: ClassExtends::SimpleIdentifier(String::from(\"ParentClass\")),\n      body: vec!(\n        d(DeclF::VarDecl(var(None, Onready::No, String::from(\"variable\"), None, Setget::default()))),\n      ),\n    }));\n    assert_eq!(decl2.to_gd(0), r#\"class MyClass extends ParentClass:\n\n    var variable\n\"#);\n\n    let decl3 = d(DeclF::ClassDecl(ClassDecl {\n      name: String::from(\"MyClass\"),\n      extends: ClassExtends::SimpleIdentifier(String::from(\"ParentClass\")),\n      body: vec!(\n        d(DeclF::VarDecl(var(None, Onready::No, String::from(\"variable\"), None, Setget::default()))),\n        sample_function.clone(),\n      ),\n    }));\n    assert_eq!(decl3.to_gd(0), r#\"class MyClass extends ParentClass:\n\n    var variable\n\n    func sample():\n        pass\n\"#);\n\n    let decl4 = d(DeclF::ClassDecl(ClassDecl {\n      name: String::from(\"MyClass\"),\n      extends: ClassExtends::SimpleIdentifier(String::from(\"ParentClass\")),\n      body: vec!(\n        d(DeclF::VarDecl(var(None, Onready::Yes, String::from(\"variable\"), None, Setget::default()))),\n        sample_function.clone(),\n      ),\n    }));\n    assert_eq!(decl4.to_gd(0), r#\"class MyClass extends ParentClass:\n\n    onready var variable\n\n    func sample():\n        pass\n\"#);\n\n  }\n\n  #[test]\n  fn enums() {\n    let decl1 = d(DeclF::EnumDecl(EnumDecl {\n      name: None,\n      clauses: vec!(),\n    }));\n    assert_eq!(decl1.to_gd(0), \"enum {\\n}\\n\");\n\n    let decl2 = d(DeclF::EnumDecl(EnumDecl {\n      name: None,\n      clauses: vec!((String::from(\"Value1\"), None), (String::from(\"Value2\"), None)),\n    }));\n    assert_eq!(decl2.to_gd(0), \"enum {\\n    Value1,\\n    Value2,\\n}\\n\");\n\n    let decl3 = d(DeclF::EnumDecl(EnumDecl {\n      name: None,\n      clauses: vec!((String::from(\"Value1\"), Some(e(ExprF::from(99)))), (String::from(\"Value2\"), None)),\n    }));\n    assert_eq!(decl3.to_gd(0), \"enum {\\n    Value1 = 99,\\n    Value2,\\n}\\n\");\n\n    let decl4 = d(DeclF::EnumDecl(EnumDecl {\n      name: Some(String::from(\"EnumName\")),\n      clauses: vec!((String::from(\"Value1\"), Some(e(ExprF::from(99)))), (String::from(\"Value2\"), None)),\n    }));\n    assert_eq!(decl4.to_gd(0), \"enum EnumName {\\n    Value1 = 99,\\n    Value2,\\n}\\n\");\n  }\n\n  #[test]\n  fn decl_names() {\n    let decl1 = d(DeclF::VarDecl(var(None, Onready::No, String::from(\"abc\"), None, Setget::default())));\n    assert_eq!(decl1.name(), Some(\"abc\"));\n\n    let decl2 = d(DeclF::ConstDecl(String::from(\"MY_CONST\"), e(ExprF::from(10))));\n    assert_eq!(decl2.name(), Some(\"MY_CONST\"));\n\n    let decl3 = d(DeclF::ClassDecl(ClassDecl { name: String::from(\"MyClass\"),\n                                               extends: ClassExtends::SimpleIdentifier(String::from(\"Node\")),\n                                               body: vec!() }));\n    assert_eq!(decl3.name(), Some(\"MyClass\"));\n\n    let decl4 = d(DeclF::InitFnDecl(InitFnDecl { args: ArgList::empty(), super_call: vec!(), body: vec!() }));\n    assert_eq!(decl4.name(), Some(\"_init\"));\n\n    let decl5 = d(DeclF::FnDecl(Static::NonStatic, FnDecl { name: String::from(\"foobar\"), args: ArgList::empty(), body: vec!() }));\n    assert_eq!(decl5.name(), Some(\"foobar\"));\n\n    let decl6 = d(DeclF::EnumDecl(EnumDecl { name: Some(String::from(\"MyEnum\")), clauses: vec!() }));\n    assert_eq!(decl6.name(), Some(\"MyEnum\"));\n\n    let decl7 = d(DeclF::EnumDecl(EnumDecl { name: None, clauses: vec!() }));\n    assert_eq!(decl7.name(), None);\n\n    let decl8 = d(DeclF::SignalDecl(String::from(\"signal_emitted\"), ArgList::empty()));\n    assert_eq!(decl8.name(), Some(\"signal_emitted\"));\n\n    let decl9 = d(DeclF::PassDecl);\n    assert_eq!(decl9.name(), None);\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/expr.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDScript expressions.\n//!\n//! This module defines a [datatype](Expr) for representing\n//! expressions in the GDScript language, as well as [`Expr::to_gd`]\n//! for converting to GDScript syntax.\n//!\n//! Note: For names (such as strings), we expect that they've already\n//! been sanitized for GDScript output. That should've happened\n//! earlier in the compilation process. This precondition is not\n//! checked anywhere in this module.\n\nuse crate::gdscript::op::{self, UnaryOp, BinaryOp, OperatorHasInfo};\nuse crate::gdscript::literal::Literal;\nuse crate::pipeline::source::{SourceOffset, Sourced};\n\nuse std::fmt::Write;\n\npub const PRECEDENCE_LOWEST: i32 = -99;\npub const PRECEDENCE_SUBSCRIPT: i32 = 21;\npub const PRECEDENCE_ATTRIBUTE: i32 = 20;\npub const PRECEDENCE_CALL: i32 = 19;\n\n/// The type of GDScript expressions.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum ExprF {\n  Var(String),\n  Literal(Literal),\n  /// Subscript access, i.e. `foo[bar]`.\n  Subscript(Box<Expr>, Box<Expr>),\n  /// Attribute access, i.e. `foo.bar`.\n  Attribute(Box<Expr>, String),\n  /// A function call, possibly qualified by a value name. If the\n  /// first argument is `None`, then this is akin to a call of the\n  /// form `bar(...)`. If the first argument is `Some(foo)`, then this\n  /// is akin to `foo.bar(...)`.\n  Call(Option<Box<Expr>>, String, Vec<Expr>),\n  /// A super call, i.e. `.bar(...)`.\n  SuperCall(String, Vec<Expr>),\n  Unary(UnaryOp, Box<Expr>),\n  Binary(Box<Expr>, BinaryOp, Box<Expr>),\n  /// A use of the ternary-if operator `foo if bar else baz`.\n  TernaryIf(TernaryIf),\n  ArrayLit(Vec<Expr>),\n  DictionaryLit(Vec<(Expr, Expr)>),\n}\n\n/// GDScript expression with its source offset. See [`Sourced`].\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Expr {\n  pub value: ExprF,\n  pub pos: SourceOffset,\n}\n\n/// The type used by [`ExprF::TernaryIf`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct TernaryIf {\n  pub true_case: Box<Expr>,\n  pub cond: Box<Expr>,\n  pub false_case: Box<Expr>,\n}\n\nfn maybe_parens(cond: bool, inner: String) -> String {\n  if cond {\n    format!(\"({})\", inner)\n  } else {\n    inner\n  }\n}\n\nimpl Expr {\n\n  /// A new `Expr` with the given [`SourceOffset`].\n  pub fn new(value: ExprF, pos: SourceOffset) -> Expr {\n    Expr { value, pos }\n  }\n\n  /// The literal expression `null`.\n  pub fn null(pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Literal(Literal::Null), pos)\n  }\n\n  /// The expression referring to the special \"self\" variable.\n  pub fn self_var(pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Var(String::from(\"self\")), pos)\n  }\n\n  /// A literal string.\n  pub fn str_lit(a: &str, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::from(a.to_owned()), pos)\n  }\n\n  /// An [`ExprF::Var`], referenced by name. The name will be cloned\n  /// into the resulting value.\n  pub fn var(a: &str, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Var(a.to_owned()), pos)\n  }\n\n  /// An [`ExprF::Attribute`] on `self`, referencing the name given by\n  /// `attr`.\n  pub fn attribute(self, attr: impl Into<String>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Attribute(Box::new(self), attr.into()), pos)\n  }\n\n  /// An [`ExprF::Subscript`] on `self`, subscripted by `rhs`.\n  pub fn subscript(self, rhs: Expr, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Subscript(Box::new(self), Box::new(rhs)), pos)\n  }\n\n  /// A unary operator application.\n  pub fn unary(self, op: UnaryOp, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Unary(op, Box::new(self)), pos)\n  }\n\n  /// Binary operator application.\n  pub fn binary(self, op: BinaryOp, rhs: Expr, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Binary(Box::new(self), op, Box::new(rhs)), pos)\n  }\n\n  /// A function call expression.\n  pub fn call(lhs: Option<Expr>, name: &str, args: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Call(lhs.map(Box::new), name.to_owned(), args), pos)\n  }\n\n  /// A function call expression, with no receiver object. Equivalent\n  /// to `Expr::call(None, name, args)`.\n  pub fn simple_call(name: &str, args: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::call(None, name, args, pos)\n  }\n\n  /// A super-method call expression.\n  pub fn super_call(name: &str, args: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::SuperCall(name.to_owned(), args), pos)\n  }\n\n  /// A GDScript `yield` call.\n  ///\n  /// `yield` takes either zero or two arguments, so this function can\n  /// produce either form of `yield` (by passing either `None` or\n  /// `Some(a, b)`).\n  pub fn yield_expr(args: Option<(Expr, Expr)>, pos: SourceOffset) -> Expr {\n    let args = match args {\n      None => vec!(),\n      Some((x, y)) => vec!(x, y),\n    };\n    Expr::simple_call(\"yield\", args, pos)\n  }\n\n  /// A GDScript `assert` call.\n  ///\n  /// `assert` takes either one or two arguments.\n  pub fn assert_expr(cond: Expr, message: Option<Expr>, pos: SourceOffset) -> Expr {\n    let args = match message {\n      None => vec!(cond),\n      Some(message) => vec!(cond, message),\n    };\n    Expr::simple_call(\"assert\", args, pos)\n  }\n\n  /// A GDScript `preload` call.\n  pub fn preload_expr(arg: String, pos: SourceOffset) -> Expr {\n    Expr::simple_call(\"preload\", vec!(Expr::from_value(arg, pos)), pos)\n  }\n\n  /// Uses a [`From`] instance of [`ExprF`] to construct an `Expr`.\n  pub fn from_value<T>(value: T, pos: SourceOffset) -> Expr\n  where ExprF : From<T> {\n    Expr::new(ExprF::from(value), pos)\n  }\n\n  /// Convert to a GDScript string, assuming the ambient precedence is\n  /// a specific value.\n  ///\n  /// Generally, callers will want to invoke [`Expr::to_gd`] and let the\n  /// expression manage its own precedence.\n  pub fn to_gd_prec(&self, prec: i32) -> String {\n    match &self.value {\n      ExprF::Var(s) => s.clone(),\n      ExprF::Literal(lit) => lit.to_gd(),\n      ExprF::Subscript(lhs, index) =>\n        format!(\"{}[{}]\", lhs.to_gd_prec(PRECEDENCE_SUBSCRIPT), index.to_gd_prec(PRECEDENCE_LOWEST)),\n      ExprF::Attribute(lhs, name) =>\n        format!(\"{}.{}\", lhs.to_gd_prec(PRECEDENCE_ATTRIBUTE), name),\n      ExprF::Call(class, name, args) => {\n        let prefix = if let Some(class) = class {\n          format!(\"{}.\", class.to_gd_prec(PRECEDENCE_CALL))\n        } else {\n          String::from(\"\")\n        };\n        let arglist = args.iter().map(|arg| arg.to_gd_prec(PRECEDENCE_LOWEST)).collect::<Vec<_>>().join(\", \");\n        format!(\"{}{}({})\", prefix, name, arglist)\n      },\n      ExprF::SuperCall(name, args) => {\n        let arglist = args.iter().map(|arg| arg.to_gd_prec(PRECEDENCE_LOWEST)).collect::<Vec<_>>().join(\", \");\n        format!(\".{}({})\", name, arglist)\n      },\n      ExprF::Unary(op, arg) => {\n        let info = op.op_info();\n        let arg = arg.to_gd_prec(info.precedence);\n        let inner = if info.padding == op::Padding::NotRequired {\n          format!(\"{}{}\", info.name, arg)\n        } else {\n          format!(\"{} {}\", info.name, arg)\n        };\n        maybe_parens(prec > info.precedence, inner)\n      },\n      ExprF::Binary(lhs, op, rhs) => {\n        // Implicit assumption that all operators are left-associative.\n        let info = op.op_info();\n        let lhs = lhs.to_gd_prec(info.precedence);\n        let rhs = rhs.to_gd_prec(info.precedence + 1);\n        let inner = if info.padding == op::Padding::NotRequired {\n          format!(\"{}{}{}\", lhs, info.name, rhs)\n        } else {\n          format!(\"{} {} {}\", lhs, info.name, rhs)\n        };\n        maybe_parens(prec > info.precedence, inner)\n      },\n      ExprF::TernaryIf(TernaryIf { true_case, cond, false_case }) => {\n        // Ternary is right-associative.\n        let info = op::TernaryOp.op_info();\n        let lhs = true_case.to_gd_prec(info.precedence + 1);\n        let cond = cond.to_gd_prec(PRECEDENCE_LOWEST);\n        let rhs = false_case.to_gd_prec(info.precedence);\n        let inner = format!(\"{} if {} else {}\", lhs, cond, rhs);\n        maybe_parens(prec > info.precedence, inner)\n      },\n      ExprF::ArrayLit(vec) => {\n        let mut first = true;\n        let mut result = String::from(\"[\");\n        for x in vec {\n          if !first {\n            result.push_str(\", \");\n          }\n          result.push_str(&x.to_gd_prec(PRECEDENCE_LOWEST));\n          first = false;\n        }\n        result.push(']');\n        result\n      },\n      ExprF::DictionaryLit(vec) => {\n        let mut first = true;\n        let mut result = String::from(\"{\");\n        for (k, v) in vec {\n          if !first {\n            result.push_str(\", \");\n          }\n          write!(result, \"{}: {}\", k.to_gd_prec(PRECEDENCE_LOWEST), v.to_gd_prec(PRECEDENCE_LOWEST))\n            .expect(\"Failed to write to local string\");\n          first = false;\n        }\n        result.push('}');\n        result\n      },\n    }\n  }\n\n  /// Convert a GDScript expression to a string. The result will\n  /// contain valid GDScript syntax.\n  pub fn to_gd(&self) -> String {\n    self.to_gd_prec(PRECEDENCE_LOWEST)\n  }\n\n}\n\nimpl Sourced for Expr {\n  type Item = ExprF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &ExprF {\n    &self.value\n  }\n\n}\n\nimpl<T> From<T> for ExprF\n  where Literal : From<T> {\n  fn from(x: T) -> ExprF {\n    ExprF::Literal(Literal::from(x))\n  }\n}\n\nimpl From<TernaryIf> for ExprF {\n  fn from(x: TernaryIf) -> ExprF {\n    ExprF::TernaryIf(x)\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  #[test]\n  fn basic_expr_types() {\n    let var = e(ExprF::Var(String::from(\"foobar\")));\n    let n = e(ExprF::from(99));\n    let name = String::from(\"attr\");\n\n    let arg1 = e(ExprF::from(1));\n    let arg2 = e(ExprF::from(2));\n    let lhs = e(ExprF::Binary(Box::new(arg1), BinaryOp::Add, Box::new(arg2)));\n\n    let attr = e(ExprF::Attribute(Box::new(var.clone()), name.clone()));\n    let subs = e(ExprF::Subscript(Box::new(var.clone()), Box::new(n.clone())));\n\n    assert_eq!(var.to_gd(), \"foobar\");\n    assert_eq!(n.to_gd(), \"99\");\n    assert_eq!(e(ExprF::Subscript(Box::new(var.clone()), Box::new(n.clone()))).to_gd(), \"foobar[99]\");\n    assert_eq!(attr.to_gd(), \"foobar.attr\");\n    assert_eq!(subs.to_gd(), \"foobar[99]\");\n    assert_eq!(e(ExprF::Attribute(Box::new(attr.clone()), name.clone())).to_gd(), \"foobar.attr.attr\");\n    assert_eq!(e(ExprF::Attribute(Box::new(subs.clone()), name.clone())).to_gd(), \"foobar[99].attr\");\n    assert_eq!(e(ExprF::Subscript(Box::new(attr.clone()), Box::new(n.clone()))).to_gd(), \"foobar.attr[99]\");\n    assert_eq!(e(ExprF::Subscript(Box::new(subs.clone()), Box::new(n.clone()))).to_gd(), \"foobar[99][99]\");\n\n    assert_eq!(e(ExprF::Subscript(Box::new(lhs.clone()), Box::new(n.clone()))).to_gd(), \"(1 + 2)[99]\");\n    assert_eq!(e(ExprF::Attribute(Box::new(lhs.clone()), name.clone())).to_gd(), \"(1 + 2).attr\");\n\n  }\n\n  #[test]\n  fn call_exprs() {\n    let lhs = e(ExprF::Var(String::from(\"lhs\")));\n    let name = String::from(\"func\");\n    let arg1 = e(ExprF::from(1));\n    let arg2 = e(ExprF::from(2));\n    let arg3 = e(ExprF::from(3));\n\n    let lhs_p = e(ExprF::Binary(Box::new(arg1.clone()), BinaryOp::Add, Box::new(arg2.clone())));\n\n    assert_eq!(e(ExprF::Call(None, name.clone(), vec!())).to_gd(), \"func()\");\n    assert_eq!(e(ExprF::Call(None, name.clone(), vec!(arg1.clone()))).to_gd(), \"func(1)\");\n    assert_eq!(e(ExprF::Call(None, name.clone(), vec!(arg1.clone(), arg2.clone()))).to_gd(), \"func(1, 2)\");\n    assert_eq!(e(ExprF::Call(None, name.clone(), vec!(arg1.clone(), arg2.clone(), arg3.clone()))).to_gd(), \"func(1, 2, 3)\");\n\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs.clone())), name.clone(), vec!())).to_gd(), \"lhs.func()\");\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs.clone())), name.clone(), vec!(arg1.clone()))).to_gd(), \"lhs.func(1)\");\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs.clone())), name.clone(), vec!(arg1.clone(), arg2.clone()))).to_gd(), \"lhs.func(1, 2)\");\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs.clone())), name.clone(), vec!(arg1.clone(), arg2.clone(), arg3.clone()))).to_gd(), \"lhs.func(1, 2, 3)\");\n\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs_p.clone())), name.clone(), vec!())).to_gd(), \"(1 + 2).func()\");\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs_p.clone())), name.clone(), vec!(arg1.clone()))).to_gd(), \"(1 + 2).func(1)\");\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs_p.clone())), name.clone(), vec!(arg1.clone(), arg2.clone()))).to_gd(), \"(1 + 2).func(1, 2)\");\n    assert_eq!(e(ExprF::Call(Some(Box::new(lhs_p.clone())), name.clone(), vec!(arg1.clone(), arg2.clone(), arg3.clone()))).to_gd(), \"(1 + 2).func(1, 2, 3)\");\n\n    assert_eq!(e(ExprF::SuperCall(name.clone(), vec!())).to_gd(), \".func()\");\n\n  }\n\n  fn unary(op: UnaryOp, expr: &Expr) -> Expr {\n    e(ExprF::Unary(op, Box::new(expr.clone())))\n  }\n\n  fn binary(a: &Expr, op: BinaryOp, b: &Expr) -> Expr {\n    e(ExprF::Binary(Box::new(a.clone()), op, Box::new(b.clone())))\n  }\n\n  #[test]\n  fn unary_ops() {\n    let operand = e(ExprF::from(3));\n    assert_eq!(unary(UnaryOp::BitNot, &operand).to_gd(), \"~3\");\n    assert_eq!(unary(UnaryOp::Negate, &operand).to_gd(), \"-3\");\n    assert_eq!(unary(UnaryOp::Not, &operand).to_gd(), \"!3\");\n  }\n\n  #[test]\n  fn binary_ops() {\n    let a = e(ExprF::Var(String::from(\"a\")));\n    let b = e(ExprF::Var(String::from(\"b\")));\n    assert_eq!(binary(&a, BinaryOp::Times, &b).to_gd(), \"a * b\");\n    assert_eq!(binary(&a, BinaryOp::Div, &b).to_gd(), \"a / b\");\n    assert_eq!(binary(&a, BinaryOp::Mod, &b).to_gd(), \"a % b\");\n    assert_eq!(binary(&a, BinaryOp::Add, &b).to_gd(), \"a + b\");\n    assert_eq!(binary(&a, BinaryOp::Sub, &b).to_gd(), \"a - b\");\n    assert_eq!(binary(&a, BinaryOp::LShift, &b).to_gd(), \"a << b\");\n    assert_eq!(binary(&a, BinaryOp::RShift, &b).to_gd(), \"a >> b\");\n    assert_eq!(binary(&a, BinaryOp::BitAnd, &b).to_gd(), \"a & b\");\n    assert_eq!(binary(&a, BinaryOp::BitXor, &b).to_gd(), \"a ^ b\");\n    assert_eq!(binary(&a, BinaryOp::BitOr, &b).to_gd(), \"a | b\");\n    assert_eq!(binary(&a, BinaryOp::LT, &b).to_gd(), \"a < b\");\n    assert_eq!(binary(&a, BinaryOp::GT, &b).to_gd(), \"a > b\");\n    assert_eq!(binary(&a, BinaryOp::Eq, &b).to_gd(), \"a == b\");\n    assert_eq!(binary(&a, BinaryOp::LE, &b).to_gd(), \"a <= b\");\n    assert_eq!(binary(&a, BinaryOp::GE, &b).to_gd(), \"a >= b\");\n    assert_eq!(binary(&a, BinaryOp::NE, &b).to_gd(), \"a != b\");\n    assert_eq!(binary(&a, BinaryOp::Is, &b).to_gd(), \"a is b\");\n    assert_eq!(binary(&a, BinaryOp::In, &b).to_gd(), \"a in b\");\n    assert_eq!(binary(&a, BinaryOp::And, &b).to_gd(), \"a && b\");\n    assert_eq!(binary(&a, BinaryOp::Or, &b).to_gd(), \"a || b\");\n    assert_eq!(binary(&a, BinaryOp::Cast, &b).to_gd(), \"a as b\");\n  }\n\n  #[test]\n  fn ternary_op() {\n    let a = Box::new(e(ExprF::from(1)));\n    let b = Box::new(e(ExprF::from(2)));\n    let c = Box::new(e(ExprF::from(3)));\n    assert_eq!(e(ExprF::TernaryIf(TernaryIf { true_case: a, cond: b, false_case: c })).to_gd(), \"1 if 2 else 3\");\n  }\n\n  #[test]\n  fn operator_precedence() {\n    let a = e(ExprF::Var(String::from(\"a\")));\n    let b = e(ExprF::Var(String::from(\"b\")));\n    let c = e(ExprF::Var(String::from(\"c\")));\n\n    let noparens = binary(&binary(&a, BinaryOp::Times, &b), BinaryOp::Add, &c);\n    let needparens = binary(&binary(&a, BinaryOp::Add, &b), BinaryOp::Times, &c);\n    assert_eq!(noparens.to_gd(), \"a * b + c\");\n    assert_eq!(needparens.to_gd(), \"(a + b) * c\");\n\n  }\n\n  #[test]\n  fn ternary_if_precedence_1() {\n    let a = Box::new(e(ExprF::from(1)));\n    let b = Box::new(e(ExprF::from(2)));\n    let c = Box::new(e(ExprF::from(3)));\n    let d = Box::new(e(ExprF::from(4)));\n    let f = Box::new(e(ExprF::from(5)));\n\n    let left_assoc = e(ExprF::from(TernaryIf {\n      true_case: Box::new(e(ExprF::from(TernaryIf { true_case: a.clone(), cond: b.clone(), false_case: c.clone() }))),\n      cond: d.clone(),\n      false_case: f.clone(),\n    }));\n    let right_assoc = e(ExprF::from(TernaryIf {\n      true_case: a.clone(),\n      cond: b.clone(),\n      false_case: Box::new(e(ExprF::from(TernaryIf { true_case: c.clone(), cond: d.clone(), false_case: f.clone() }))),\n    }));\n    assert_eq!(left_assoc.to_gd(), \"(1 if 2 else 3) if 4 else 5\");\n    assert_eq!(right_assoc.to_gd(), \"1 if 2 else 3 if 4 else 5\");\n  }\n\n  #[test]\n  fn ternary_if_precedence_2() {\n    let a = e(ExprF::Var(String::from(\"a\")));\n    let b = e(ExprF::Var(String::from(\"b\")));\n    let c = e(ExprF::Var(String::from(\"c\")));\n    let d = e(ExprF::Var(String::from(\"d\")));\n\n    let case1 = e(ExprF::from(TernaryIf {\n      true_case: Box::new(binary(&a, BinaryOp::Or, &b)),\n      cond: Box::new(c.clone()),\n      false_case: Box::new(d.clone()),\n    }));\n    assert_eq!(case1.to_gd(), \"a || b if c else d\");\n\n    let case2 = e(ExprF::from(TernaryIf {\n      true_case: Box::new(a.clone()),\n      cond: Box::new(binary(&b, BinaryOp::Or, &c)),\n      false_case: Box::new(d.clone()),\n    }));\n    assert_eq!(case2.to_gd(), \"a if b || c else d\");\n\n    let case3 = e(ExprF::from(TernaryIf {\n      true_case: Box::new(a.clone()),\n      cond: Box::new(b.clone()),\n      false_case: Box::new(binary(&c, BinaryOp::Or, &d)),\n    }));\n    assert_eq!(case3.to_gd(), \"a if b else c || d\");\n\n    let case4 = e(ExprF::from(TernaryIf {\n      true_case: Box::new(binary(&a, BinaryOp::Cast, &b)),\n      cond: Box::new(c.clone()),\n      false_case: Box::new(d.clone()),\n    }));\n    assert_eq!(case4.to_gd(), \"(a as b) if c else d\");\n\n    let case5 = e(ExprF::from(TernaryIf {\n      true_case: Box::new(a.clone()),\n      cond: Box::new(binary(&b, BinaryOp::Cast, &c)),\n      false_case: Box::new(d.clone()),\n    }));\n    assert_eq!(case5.to_gd(), \"a if b as c else d\");\n\n    let case6 = e(ExprF::from(TernaryIf {\n      true_case: Box::new(a.clone()),\n      cond: Box::new(b.clone()),\n      false_case: Box::new(binary(&c, BinaryOp::Cast, &d)),\n    }));\n    assert_eq!(case6.to_gd(), \"a if b else (c as d)\");\n\n  }\n\n  #[test]\n  fn arrays() {\n    assert_eq!(e(ExprF::ArrayLit(vec!())).to_gd(), \"[]\");\n    assert_eq!(e(ExprF::ArrayLit(vec!(e(ExprF::from(1))))).to_gd(), \"[1]\");\n    assert_eq!(e(ExprF::ArrayLit(vec!(e(ExprF::from(1)), e(ExprF::from(2))))).to_gd(), \"[1, 2]\");\n    assert_eq!(e(ExprF::ArrayLit(vec!(e(ExprF::from(1)), e(ExprF::from(2)), e(ExprF::from(3))))).to_gd(), \"[1, 2, 3]\");\n  }\n\n  #[test]\n  fn dictionaries() {\n    assert_eq!(e(ExprF::DictionaryLit(vec!())).to_gd(), \"{}\");\n    assert_eq!(e(ExprF::DictionaryLit(vec!((e(ExprF::from(1)), e(ExprF::from(2)))))).to_gd(), \"{1: 2}\");\n    assert_eq!(e(ExprF::DictionaryLit(vec!((e(ExprF::from(1)), e(ExprF::from(2))), (e(ExprF::from(3)), e(ExprF::from(4)))))).to_gd(), \"{1: 2, 3: 4}\");\n    assert_eq!(e(ExprF::DictionaryLit(vec!((e(ExprF::from(1)), e(ExprF::from(2))), (e(ExprF::from(3)), e(ExprF::from(4))), (e(ExprF::from(5)), e(ExprF::from(6)))))).to_gd(), \"{1: 2, 3: 4, 5: 6}\");\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/expr_wrapper.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Miscellaneous helpers for wrapping commonly-used operations as\n//! GDScript expressions.\n\nuse super::expr::{Expr, ExprF};\nuse super::literal::Literal;\n\n/// Call the GDScript function `int` on the expression, unless the\n/// expression is provably already an integer.\npub fn int(expr: Expr) -> Expr {\n  if let ExprF::Literal(Literal::Int(_)) = &expr.value {\n    expr\n  } else {\n    let pos = expr.pos;\n    Expr::simple_call(\"int\", vec!(expr), pos)\n  }\n}\n\n/// Call the GDScript function `float` on the expression, unless the\n/// expression is provably already a floating-point value.\npub fn float(expr: Expr) -> Expr {\n  if let ExprF::Literal(Literal::Float(_)) = &expr.value {\n    expr\n  } else {\n    let pos = expr.pos;\n    Expr::simple_call(\"float\", vec!(expr), pos)\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::source::SourceOffset;\n\n  #[test]\n  fn int_test() {\n    assert_eq!(int(Expr::var(\"a\", SourceOffset::default())),\n               Expr::call(None, \"int\", vec!(Expr::var(\"a\", SourceOffset::default())), SourceOffset::default()));\n    assert_eq!(int(Expr::from_value(10, SourceOffset::default())),\n               Expr::from_value(10, SourceOffset::default()));\n  }\n\n  #[test]\n  fn float_test() {\n    assert_eq!(float(Expr::var(\"a\", SourceOffset::default())),\n               Expr::call(None, \"float\", vec!(Expr::var(\"a\", SourceOffset::default())), SourceOffset::default()));\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/inner_class.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helpers for construction of GDLisp inner classes.\n//!\n//! Helpers to make sure GDLisp inner classes (and any feature of\n//! GDLisp that compiles to inner classes, like lambdas and lambda\n//! classes) can access statics from the enclosing scope.\n//!\n//! There are two key pieces of functionality in this module. The\n//! first is [`NeedsOuterClassRef`], a trait whose sole method\n//! determines whether a given type of declaration needs to retain a\n//! reference to the enclosing class. By default, inner classes in\n//! GDScript cannot access the outer class, so in the cases where we\n//! require this behavior, we have to go to special effort to get it.\n//! In the cases where we *do* need this behavior, the second core\n//! functionality of this module, [`add_outer_class_ref_named`] adds a\n//! reference to the outer class to the given scope.\n\nuse super::decl::{self, Decl, DeclF, VarDecl};\nuse crate::pipeline::can_load::CanLoad;\nuse crate::pipeline::source::SourceOffset;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::compile::symbol_table::local_var::VarName;\nuse crate::compile::preload_resolver::PreloadResolver;\nuse crate::ir;\nuse crate::ir::identifier::{Id, Namespace};\nuse crate::util::lattice::Lattice;\n\n/// By convention, this name is used as the basis for outer class\n/// reference names. Note that this name should not be used *directly*\n/// unless you first check that there are no conflicts. Normally, this\n/// would be passed to\n/// [`generate_with`](crate::compile::names::generator::NameGenerator::generate_with).\npub const OUTER_REFERENCE_NAME: &str = \"__gdlisp_outer_class\";\n\n/// Add a declaration to the outer class with the given name to\n/// `inner_class`.\n///\n/// When the class referenced by `inner_class` is constructed, the\n/// variable with name `var_name` will be initialized on the instance\n/// to be equal to the enclosing class resource. This resource will be\n/// loaded using `load`. The path to load is determined by `resolver`.\npub fn add_outer_class_ref_named(inner_class: &mut decl::ClassDecl, resolver: &dyn PreloadResolver, current_file: &impl CanLoad, var_name: String, pos: SourceOffset) {\n  let current_filename = get_current_filename(current_file, resolver)\n    .expect(\"Error identifying current file\");\n  let load_expr = VarName::load_expr(current_filename, pos);\n  let var_decl = Decl::new(DeclF::VarDecl(VarDecl::new(var_name, Some(load_expr))), pos);\n  inner_class.body.push(var_decl);\n}\n\n/// Gets the filename of the currently loading object from the\n/// pipeline, as per [`CanLoad::current_filename`], and then resolves\n/// that filename using `resolver`. Returns `None` if either step\n/// fails.\npub fn get_current_filename<L, R>(current_file: &L, resolver: &R) -> Option<String>\nwhere L : CanLoad + ?Sized,\n      R : PreloadResolver + ?Sized {\n  resolver.resolve_preload(&current_file.current_filename())\n}\n\n/// Trait for objects, such as declarations, which may need an outer\n/// class reference.\n///\n/// Before blindly throwing unnecessary references on every inner\n/// class (which, in addition to being hilariously inefficient, would\n/// cause significant memory leaks since GDScript resources are\n/// reference counted), the compiler should use this trait to check\n/// whether an outer reference is actually warranted.\npub trait NeedsOuterClassRef {\n  /// Given the names that are in scope at the current point in the\n  /// code, determine whether this declaration requires an outer class\n  /// reference.\n  fn needs_outer_class_ref(&self, table: &SymbolTable) -> bool;\n}\n\nfn check_dependencies_for_outer_class_ref(deps: impl Iterator<Item=Id>, table: &SymbolTable) -> bool {\n  for dep in deps {\n    // If the dependency is in the function namespace and refers to\n    // a function defined in the top-level of the current file, then\n    // we need an outer class reference.\n    if dep.namespace == Namespace::Function {\n      if let Some((func, _)) = table.get_fn(&dep.name) {\n        // TODO Abstract this check alongside the inner static update pattern into one place\n        if func.object.needs_inner_scope_reference() {\n          return true;\n        }\n      }\n    }\n  }\n  false\n}\n\nimpl NeedsOuterClassRef for ir::decl::ClassInnerDecl {\n  fn needs_outer_class_ref(&self, table: &SymbolTable) -> bool {\n    if self.is_static() {\n      // Static functions never use the outer class reference, since\n      // the reference itself is an instance variable and is\n      // inaccessible from static scope.\n      return false;\n    }\n    check_dependencies_for_outer_class_ref(self.dependencies().into_iter().map(|(k, _)| k), table)\n  }\n}\n\nimpl NeedsOuterClassRef for ir::decl::ConstructorDecl {\n  fn needs_outer_class_ref(&self, table: &SymbolTable) -> bool {\n    check_dependencies_for_outer_class_ref(self.dependencies().into_iter().map(|(k, _)| k), table)\n  }\n}\n\nimpl NeedsOuterClassRef for ir::decl::ClassDecl {\n  fn needs_outer_class_ref(&self, table: &SymbolTable) -> bool {\n    let ir::decl::ClassDecl { visibility: _, name: _, extends: _, main_class: _, constructor, decls } = self;\n    constructor.as_ref().map_or(false, |x| x.needs_outer_class_ref(table)) ||\n      decls.iter().any(|x| x.needs_outer_class_ref(table))\n  }\n}\n\nimpl NeedsOuterClassRef for ir::expr::LambdaClass {\n  fn needs_outer_class_ref(&self, table: &SymbolTable) -> bool {\n    let ir::expr::LambdaClass { extends: _, args: _, constructor, decls } = self;\n    constructor.as_ref().map_or(false, |x| x.needs_outer_class_ref(table)) ||\n      decls.iter().any(|x| x.needs_outer_class_ref(table))\n  }\n}\n\nimpl<T : Lattice> NeedsOuterClassRef for ir::closure_names::ClosureNames<T> {\n  fn needs_outer_class_ref(&self, table: &SymbolTable) -> bool {\n    let deps = self.names().map(|x| Id::new(Namespace::Function, x.to_owned()));\n    check_dependencies_for_outer_class_ref(deps, table)\n  }\n}\n"
  },
  {
    "path": "src/gdscript/library/cell.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helpers for constructing GDLisp `Cell` objects and wrapping\n//! variables in them.\n//!\n//! The GDLisp `Cell` class is a simple class with only one member\n//! variable (called `contents`), and a single 1-argument constructor.\n//!\n//! Any local variable (including function parameters) may need to be\n//! wrapped in a cell. A variable needs to be wrapped in a cell if its\n//! [access type](crate::ir::access_type::AccessType) is\n//! [`ClosedRW`](crate::ir::access_type::AccessType::ClosedRW). That\n//! is, a variable needs to be wrapped in a cell if either of the\n//! following is true.\n//!\n//! * We write to the variable inside of a closure that is strictly\n//!   smaller than the scope of the variable.\n//!\n//! * We write to the variable anywhere AND we read from the variable\n//!   inside a closure that is strictly smaller than the scope of the\n//!   variable.\n//!\n//! For details on the motivation for these rules and specifics on how\n//! their calculated, see the documentation for\n//! [`AccessType`](crate::ir::access_type::AccessType).\n//!\n//! If we decide that a cell is warranted, then the variable will be\n//! wrapped in a cell at construction time. For local variables, this\n//! means that the variable's initializer is wrapped in a call to\n//! `Cell.new`. For parameters, the first few lines of the function\n//! will be assignments of the form `argument_name =\n//! Cell.new(argument_name)`. In either case, all access or\n//! assignments to these variables will be targeted at\n//! `argument_name.contents`.\n//!\n//! The only exception to this is the construction of closures.\n//! Closures are explicitly expected to share cells with their\n//! enclosing scope, so when a closure is constructed, the constructor\n//! arguments to that closure will be passed as cells directly.\n\nuse super::on_gdlisp_root;\nuse crate::pipeline::source::SourceOffset;\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse crate::compile::body::builder::StmtBuilder;\n\n/// The name of the `Cell` class.\npub const CELL_CLASS: &str = \"Cell\";\n\n/// The name of the sole field in the `Cell` class.\npub const CELL_CONTENTS: &str = \"contents\";\n\n/// An expression representing the GDLisp `Cell` class.\npub fn cell_class(pos: SourceOffset) -> Expr {\n  on_gdlisp_root(String::from(\"Cell\"), pos)\n}\n\n/// Given a GDLisp expression, produce an expression which constructs\n/// a cell containing it.\npub fn construct_cell(expr: Expr) -> Expr {\n  let pos = expr.pos;\n  Expr::call(Some(cell_class(pos)), \"new\", vec!(expr), pos)\n}\n\n/// Constructs an assignment statement, assigning the variable with\n/// name `arg` to its own value, but wrapped in a cell.\npub fn wrap_var_in_cell(stmt_builder: &mut StmtBuilder, arg: &str, pos: SourceOffset) {\n  let var = Expr::var(arg, pos);\n  stmt_builder.append(Stmt::simple_assign(var.clone(), construct_cell(var), pos));\n}\n"
  },
  {
    "path": "src/gdscript/library/class_loader.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::gdnative::NativeClasses;\nuse super::gdnative::class::Class;\nuse crate::ir::decl::{DeclareDecl, DeclareType, ClassInnerDecl, ClassInnerDeclF, ClassVarDecl};\nuse crate::ir::export::Visibility;\nuse crate::ir::expr::{Expr, ExprF, AssignTarget};\nuse crate::compile::body::class_initializer::InitTime;\nuse crate::pipeline::source::SourceOffset;\n\nuse phf::{phf_map, phf_set};\n\n/// Classes which, for one reason or another, we do not want the\n/// bootstrapping engine to touch. This includes some \"fake\" classes\n/// like `GlobalConstants` which do not actually exist at runtime, as\n/// well as very primitive things like `Object` that we handle\n/// manually in `GDLisp.lisp`.\nconst CLASS_NAME_BLACKLIST: phf::Set<&'static str> = phf_set! {\n  \"GlobalConstants\", \"Object\",\n};\n\n// For some reason I have yet to fathom, the GDNative API and GDScript\n// itself have different names for these classes. We use the GDScript\n// names, but `get_class` still returns the GDNative one, so we have\n// to be prepared to deal with that here.\nconst PATCHED_CLASS_NAMES: phf::Map<&'static str, &'static str> = phf_map! {\n  \"_Directory\" => \"Directory\",\n  \"_File\" => \"File\",\n  \"_Mutex\" => \"Mutex\",\n  \"_Semaphore\" => \"Semaphore\",\n  \"_Thread\" => \"Thread\",\n};\n\npub fn get_all_non_singleton_classes(native: &NativeClasses) -> Vec<&Class> {\n  let mut classes: Vec<_> =\n    native.values()\n    .filter(|x| !x.singleton && !CLASS_NAME_BLACKLIST.contains(&*x.name))\n    .collect();\n  classes.sort_unstable_by(|a, b| a.name.cmp(&b.name));\n  classes\n}\n\npub fn get_all_singleton_classes(native: &NativeClasses) -> Vec<&Class> {\n  let mut classes: Vec<_> =\n    native.values()\n    .filter(|x| x.singleton && !CLASS_NAME_BLACKLIST.contains(&*x.name))\n    .collect();\n  classes.sort_unstable_by(|a, b| a.name.cmp(&b.name));\n  classes\n}\n\npub fn get_non_singleton_declarations(native: &NativeClasses) -> impl Iterator<Item=DeclareDecl> + '_ {\n  get_all_non_singleton_classes(native)\n    .into_iter()\n    .map(|cls| type_declaration_for_class(cls, DeclareType::Superglobal))\n}\n\npub fn get_singleton_declarations(native: &NativeClasses) -> Vec<DeclareDecl> {\n  let classes = get_all_singleton_classes(native);\n  let mut result: Vec<DeclareDecl> = Vec::with_capacity(classes.len() * 2);\n  for class in classes {\n    result.push(type_declaration_for_class(class, DeclareType::Value));\n    result.push(value_declaration_for_singleton(class, DeclareType::Superglobal));\n  }\n  result\n}\n\npub fn get_singleton_class_var_declarations(native: &NativeClasses, pos: SourceOffset) -> impl Iterator<Item=ClassInnerDecl> + '_ {\n  get_all_singleton_classes(native).into_iter().map(move |class| {\n    let name = backing_class_name_of(class);\n    let expr =\n      Expr::var(\"NamedSyntheticType\", pos)\n      .method_call(\n        \"new\",\n        vec!(Expr::from_value(class.name.clone(), pos)), // Note: *Original* class name, not the one we made in backing_class_name_of\n        pos,\n      );\n    ClassInnerDecl::new(\n      ClassInnerDeclF::ClassVarDecl(ClassVarDecl {\n        export: None,\n        name: name,\n        value: Some(expr),\n        init_time: InitTime::Init,\n      }),\n      pos,\n    )\n  })\n}\n\nfn type_declaration_for_class(class: &Class, declare_type: DeclareType) -> DeclareDecl {\n  let name = backing_class_name_of(class);\n  DeclareDecl {\n    visibility: Visibility::Public,\n    declare_type: declare_type,\n    name: name.clone(),\n    target_name: Some(name),\n  }\n}\n\nfn value_declaration_for_singleton(class: &Class, declare_type: DeclareType) -> DeclareDecl {\n  let singleton_name = class.singleton_name.to_owned();\n  DeclareDecl {\n    visibility: Visibility::Public,\n    declare_type: declare_type,\n    name: singleton_name.clone(),\n    target_name: Some(singleton_name),\n  }\n}\n\nfn backing_class_name_of(class: &Class) -> String {\n  // Some singletons in Godot have a class name and a distinct\n  // singleton name. For instance, `_Engine` is the class name for\n  // `Engine`. For these, it's fine to just use what Godot has. But\n  // some, like `ARVRServer`, have the *same* name for the type and\n  // object. In that case, we keep the name for the object and force\n  // an underscore at the beginning of the class name.\n  if let Some(translated_name) = PATCHED_CLASS_NAMES.get(&*class.name) {\n    (*translated_name).to_owned()\n  } else if class.singleton_name == class.name {\n    format!(\"_{}\", class.name)\n  } else {\n    class.name.to_owned()\n  }\n}\n\npub fn native_types_dictionary_literal(native: &NativeClasses, pos: SourceOffset) -> Expr {\n  let mut class_names: Vec<&Class> = native.values().collect();\n  class_names.sort_unstable_by(|a, b| a.name.cmp(&b.name));\n  let classes = class_names.into_iter()\n    .filter(|class| !CLASS_NAME_BLACKLIST.contains(&*class.name))\n    .map(|class| {\n      let original_name = class.name.to_owned();\n      let gdlisp_name = backing_class_name_of(class);\n      let value: Expr = {\n        if class.singleton {\n          // It's a singleton, so we need to expose our\n          // NamedSyntheticType object.\n          Expr::new(\n            ExprF::FieldAccess(\n              Box::new(Expr::var(\"self\", pos)),\n              gdlisp_name,\n            ),\n            pos,\n          )\n        } else {\n          // Not a singleton, so the name is globally available in\n          // GDScript; use that name.\n          Expr::var(gdlisp_name, pos)\n        }\n      };\n      (Expr::from_value(original_name, pos), value)\n    });\n  build_dict(classes, pos)\n}\n\nfn build_dict(terms: impl DoubleEndedIterator<Item=(Expr, Expr)>, pos: SourceOffset) -> Expr {\n  Expr::call(String::from(\"dict\"), terms.flat_map(|(k, v)| [k, v]).collect(), pos)\n}\n\npub fn native_types_dictionary_initializer(native: &NativeClasses, pos: SourceOffset) -> Expr {\n  let assign_target = AssignTarget::InstanceField(\n    pos,\n    Box::new(Expr::var(\"self\", pos)),\n    String::from(\"__gdlisp_Global_native_types_lookup\"),\n  );\n  let dict_literal = native_types_dictionary_literal(native, pos);\n  Expr::new(\n    ExprF::Assign(assign_target, Box::new(dict_literal)),\n    pos,\n  )\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  use std::collections::HashSet;\n\n  #[test]\n  fn test_non_singleton_classes() {\n    let native = NativeClasses::get_api_from_godot().unwrap();\n    let classes: HashSet<&str> = get_all_non_singleton_classes(&native)\n      .into_iter()\n      .map(|x| &*x.name)\n      .collect();\n    assert!(classes.contains(\"Node\"));\n    assert!(classes.contains(\"Node2D\"));\n    assert!(classes.contains(\"Spatial\"));\n    assert!(classes.contains(\"Texture\"));\n    assert!(!classes.contains(\"Object\"));\n    assert!(!classes.contains(\"_Engine\"));\n    assert!(!classes.contains(\"GlobalConstants\"));\n  }\n\n  #[test]\n  fn test_singleton_classes() {\n    let native = NativeClasses::get_api_from_godot().unwrap();\n    let classes: HashSet<&str> = get_all_singleton_classes(&native)\n      .into_iter()\n      .map(|x| &*x.name)\n      .collect();\n    assert!(!classes.contains(\"Node\"));\n    assert!(!classes.contains(\"Node2D\"));\n    assert!(!classes.contains(\"Spatial\"));\n    assert!(!classes.contains(\"Texture\"));\n    assert!(!classes.contains(\"Object\"));\n    assert!(classes.contains(\"_Engine\"));\n    assert!(!classes.contains(\"GlobalConstants\"));\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/constant_loader.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::gdnative::NativeClasses;\nuse crate::ir::decl::EnumDecl;\nuse crate::ir::export::Visibility;\nuse crate::ir::expr::Expr;\nuse crate::pipeline::source::SourceOffset;\nuse crate::util::prefix_matcher::PrefixMatcher;\n\nuse phf::{phf_map, phf_set};\n\nuse std::borrow::Borrow;\nuse std::collections::HashMap;\n\nconst ENUM_GROUPINGS_BY_PREFIX: phf::Map<&'static str, &'static str> = phf_map! {\n  \"BUTTON_\" => \"Mouse\",\n  \"CORNER_\" => \"Corner\",\n  \"ERR_\" => \"Err\",\n  \"HALIGN_\" => \"HAlign\",\n  \"JOY_\" => \"Joy\",\n  \"KEY_\" => \"Key\",\n  \"KEY_MASK_\" => \"KeyMask\",\n  \"MARGIN_\" => \"Margin\",\n  \"METHOD_FLAG_\" => \"MethodFlag\",\n  \"MIDI_MESSAGE_\" => \"MidiMessage\",\n  \"OP_\" => \"Op\",\n  \"PROPERTY_HINT_\" => \"PropertyHint\",\n  \"PROPERTY_USAGE_\" => \"PropertyUsage\",\n  \"TYPE_\" => \"Type\",\n  \"VALIGN_\" => \"VAlign\",\n};\n\n// Constants which are known in GDScript but which do not belong to an\n// enum.\nconst ENUM_BLACKLIST: phf::Set<&'static str> = phf_set! { \"SPKEY\" };\n\n// Names which do not follow the usual prefixing rules.\nconst ENUM_CUSTOM_NAMES: phf::Map<&'static str, &'static str> = phf_map! {\n  \"OK\" => \"Err\",\n  \"FAILED\" => \"Err\",\n  \"METHOD_FLAGS_DEFAULT\" => \"MethodFlag\",\n  \"HORIZONTAL\" => \"Orientation\",\n  \"VERTICAL\" => \"Orientation\",\n};\n\nlazy_static! {\n\n  static ref ENUM_GROUPINGS_TABLE: PrefixMatcher<'static> =\n    PrefixMatcher::build(ENUM_GROUPINGS_BY_PREFIX.keys());\n\n}\n\n/// An enumeration of GDScript-side constants. This can be converted\n/// (via [`From::from`]) into an [`EnumDecl`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ConstantEnum {\n  pub name: String,\n  pub clauses: Vec<(String, String)>,\n  pub pos: SourceOffset,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\nenum EnumGrouping {\n  Blacklist,\n  Unidentified,\n  // Matches the enum with the given enumeration name and item name.\n  Matched(String, String),\n}\n\npub fn get_all_constants(classes: &NativeClasses) -> impl Iterator<Item=&str> {\n  let global_constants = classes.get_global_constants();\n  global_constants.constants.keys().map(String::borrow)\n}\n\npub fn get_all_constant_enums(classes: &NativeClasses, pos: SourceOffset) -> Vec<ConstantEnum> {\n  get_all_constant_enums_with_leftovers(classes, pos).0\n}\n\nfn get_all_constant_enums_with_leftovers(classes: &NativeClasses, pos: SourceOffset) -> (Vec<ConstantEnum>, Vec<&str>) {\n  let table = &ENUM_GROUPINGS_TABLE;\n  let mut enums: HashMap<String, ConstantEnum> = HashMap::new();\n  let mut unidentified: Vec<&str> = Vec::new();\n\n  for constant_name in get_all_constants(classes) {\n    match identify_enum_grouping(table, constant_name) {\n      EnumGrouping::Blacklist => {\n        // Skip entirely, we are aware of it and don't want it in an\n        // enum.\n      }\n      EnumGrouping::Unidentified => {\n        // Unidentified\n        unidentified.push(constant_name);\n      }\n      EnumGrouping::Matched(enum_name, enum_item_name) => {\n        let enum_name_clone = enum_name.clone();\n        let enum_entry: &mut ConstantEnum = enums.entry(enum_name).or_insert_with(|| {\n          ConstantEnum {\n            name: enum_name_clone,\n            clauses: Vec::new(),\n            pos: pos,\n          }\n        });\n        enum_entry.clauses.push((enum_item_name, constant_name.to_owned()));\n      }\n    }\n  }\n\n  // For the sake of consistency, always return the enums (and their\n  // entries) in alphabetical order\n  let mut enums: Vec<_> = enums.into_values().collect();\n  enums.sort_unstable_by(|a, b| a.name.cmp(&b.name));\n  for constant_enum in &mut enums {\n    constant_enum.clauses.sort_unstable_by(|a, b| a.0.cmp(&b.0));\n  }\n\n  (enums, unidentified)\n\n}\n\nfn identify_enum_grouping(table: &PrefixMatcher<'_>, constant_name: &str) -> EnumGrouping {\n  if ENUM_BLACKLIST.contains(constant_name) {\n    // Blacklist, omit entirely\n    EnumGrouping::Blacklist\n  } else if let Some(custom_name) = ENUM_CUSTOM_NAMES.get(constant_name) {\n    // Match in custom names list\n    EnumGrouping::Matched((*custom_name).to_owned(), constant_name.to_owned())\n  } else if let Some(prefix) = table.identify_prefix(constant_name) {\n    // Match in prefix rules\n    let enum_name = ENUM_GROUPINGS_BY_PREFIX[prefix];\n    let enum_item_name = constant_name.strip_prefix(prefix).expect(\"Prefix did not match\");\n    EnumGrouping::Matched(enum_name.to_owned(), enum_item_name.to_owned())\n  } else {\n    // No match\n    EnumGrouping::Unidentified\n  }\n}\n\nimpl From<ConstantEnum> for EnumDecl {\n  fn from(constant_enum: ConstantEnum) -> EnumDecl {\n    let pos = constant_enum.pos;\n    let clauses: Vec<(String, Option<Expr>)> =\n      constant_enum.clauses.into_iter()\n      .map(|(name, value)| (name, Some(Expr::var(value, pos))))\n      .collect();\n    EnumDecl {\n      visibility: Visibility::Public,\n      name: constant_enum.name,\n      clauses: clauses,\n    }\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  // We should be able to classify, in some form or another, every\n  // single constant in GDScript. If there's one that's unknown (for\n  // instance, because we updated Godot versions and didn't add it),\n  // then this test will fail and will let us know.\n  #[test]\n  fn all_constants_classified_test() {\n    let native_classes = NativeClasses::get_api_from_godot().unwrap();\n    let (_, leftover_constants) = get_all_constant_enums_with_leftovers(&native_classes, SourceOffset(0));\n    assert!(leftover_constants.is_empty(), \"The following constants were leftover after classification: {:?}\", leftover_constants);\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/api_type.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! The [`ApiType`] enum, indicating whether a GDScript class is in\n//! the core library or intended for editor tooling.\n\nuse serde::{Serialize, Deserialize};\n\n/// The type of API to which a GDScript built-in class belongs.\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n#[serde(rename_all = \"lowercase\")]\npub enum ApiType {\n  /// A core class, which is always available.\n  Core,\n  /// A tooling class, available in Godot builds which have access to\n  /// the editor.\n  Tools,\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use serde_json;\n\n  #[test]\n  fn serialize_api_type_test() {\n    let core = serde_json::to_string(&ApiType::Core).unwrap();\n    assert_eq!(core, \"\\\"core\\\"\");\n    let tools = serde_json::to_string(&ApiType::Tools).unwrap();\n    assert_eq!(tools, \"\\\"tools\\\"\");\n  }\n\n  #[test]\n  fn deserialize_api_type_test() {\n    let core: ApiType = serde_json::from_str(\"\\\"core\\\"\").unwrap();\n    assert_eq!(core, ApiType::Core);\n    let tools: ApiType = serde_json::from_str(\"\\\"tools\\\"\").unwrap();\n    assert_eq!(tools, ApiType::Tools);\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/argument.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse serde::{Serialize, Deserialize};\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Argument {\n  pub name: String,\n  #[serde(rename = \"type\")]\n  pub argument_type: String,\n  pub has_default_value: bool,\n  pub default_value: String,\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use serde_json;\n\n  #[test]\n  fn serialize_roundtrip_argument_test() {\n    let argument = Argument {\n      name: String::from(\"mesh\"),\n      argument_type: String::from(\"Mesh\"),\n      has_default_value: false,\n      default_value: String::from(\"\"),\n    };\n    let serialized = serde_json::to_string(&argument).unwrap();\n    assert_eq!(serde_json::from_str::<Argument>(&serialized).unwrap(), argument);\n  }\n\n  #[test]\n  fn deserialize_argument_test() {\n    let argument_str = r#\"{\n\t\t\t\t\t\t\"name\": \"mesh\",\n\t\t\t\t\t\t\"type\": \"Mesh\",\n\t\t\t\t\t\t\"has_default_value\": false,\n\t\t\t\t\t\t\"default_value\": \"\"\n\t\t\t\t\t}\"#;\n    let argument = Argument {\n      name: String::from(\"mesh\"),\n      argument_type: String::from(\"Mesh\"),\n      has_default_value: false,\n      default_value: String::from(\"\"),\n    };\n    assert_eq!(serde_json::from_str::<Argument>(&argument_str).unwrap(), argument);\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/class.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::api_type::ApiType;\nuse super::property::Property;\nuse super::signal::Signal;\nuse super::method::Method;\nuse super::gdnative_enum::Enum;\n\nuse serde::{Serialize, Deserialize};\n\nuse std::collections::HashMap;\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Class {\n  pub name: String,\n  pub base_class: String,\n  pub api_type: ApiType,\n  pub singleton: bool,\n  pub singleton_name: String,\n  pub instanciable: bool,\n  pub is_reference: bool,\n  pub constants: HashMap<String, i32>,\n  pub properties: Vec<Property>,\n  pub signals: Vec<Signal>,\n  pub methods: Vec<Method>,\n  pub enums: Vec<Enum>,\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use serde_json;\n\n  // Note: This is not the actual _Engine class from any GDScript\n  // version. It's pared down from the real one but made smaller for\n  // testing purposes.\n  const SAMPLE_CLASS_JSON: &str = r#\"{\n\t\t\"name\": \"_Engine\",\n\t\t\"base_class\": \"Object\",\n\t\t\"api_type\": \"core\",\n\t\t\"singleton\": true,\n\t\t\"singleton_name\": \"Engine\",\n\t\t\"instanciable\": false,\n\t\t\"is_reference\": false,\n\t\t\"constants\": {\n\t\t},\n\t\t\"properties\": [\n\t\t\t{\n\t\t\t\t\"name\": \"editor_hint\",\n\t\t\t\t\"type\": \"bool\",\n\t\t\t\t\"getter\": \"is_editor_hint\",\n\t\t\t\t\"setter\": \"set_editor_hint\",\n\t\t\t\t\"index\": -1\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"iterations_per_second\",\n\t\t\t\t\"type\": \"int\",\n\t\t\t\t\"getter\": \"get_iterations_per_second\",\n\t\t\t\t\"setter\": \"set_iterations_per_second\",\n\t\t\t\t\"index\": -1\n\t\t\t}\n\t\t],\n\t\t\"signals\": [\n\t\t],\n\t\t\"methods\": [\n\t\t\t{\n\t\t\t\t\"name\": \"get_author_info\",\n\t\t\t\t\"return_type\": \"Dictionary\",\n\t\t\t\t\"is_editor\": false,\n\t\t\t\t\"is_noscript\": false,\n\t\t\t\t\"is_const\": true,\n\t\t\t\t\"is_reverse\": false,\n\t\t\t\t\"is_virtual\": false,\n\t\t\t\t\"has_varargs\": false,\n\t\t\t\t\"is_from_script\": false,\n\t\t\t\t\"arguments\": [\n\t\t\t\t]\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"name\": \"get_copyright_info\",\n\t\t\t\t\"return_type\": \"Array\",\n\t\t\t\t\"is_editor\": false,\n\t\t\t\t\"is_noscript\": false,\n\t\t\t\t\"is_const\": true,\n\t\t\t\t\"is_reverse\": false,\n\t\t\t\t\"is_virtual\": false,\n\t\t\t\t\"has_varargs\": false,\n\t\t\t\t\"is_from_script\": false,\n\t\t\t\t\"arguments\": [\n\t\t\t\t]\n\t\t\t}\n\t\t],\n\t\t\"enums\": [\n\t\t]\n\t}\"#;\n\n  #[test]\n  fn deserialize_class_test() {\n    let class: Class = serde_json::from_str(SAMPLE_CLASS_JSON).unwrap();\n    assert_eq!(class.name, String::from(\"_Engine\"));\n    assert_eq!(class.base_class, String::from(\"Object\"));\n    assert_eq!(class.api_type, ApiType::Core);\n    assert_eq!(class.singleton, true);\n    assert_eq!(class.singleton_name, String::from(\"Engine\"));\n    assert_eq!(class.instanciable, false);\n    assert_eq!(class.is_reference, false);\n    assert_eq!(class.constants.len(), 0);\n    assert_eq!(class.properties.len(), 2);\n    assert_eq!(class.signals.len(), 0);\n    assert_eq!(class.methods.len(), 2);\n    assert_eq!(class.enums.len(), 0);\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/gdnative_enum.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse serde::{Serialize, Deserialize};\n\nuse std::collections::HashMap;\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Enum {\n  pub name: String,\n  pub values: HashMap<String, i32>,\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use serde_json;\n\n  #[test]\n  fn deserialize_enum_test() {\n    let enum_str = r#\"{\n\t\t\t\t\"name\": \"TrackerHand\",\n\t\t\t\t\"values\": {\n\t\t\t\t\t\"TRACKER_HAND_UNKNOWN\": 0,\n\t\t\t\t\t\"TRACKER_LEFT_HAND\": 1,\n\t\t\t\t\t\"TRACKER_RIGHT_HAND\": 2\n\t\t\t\t}\n\t\t\t}\"#;\n    let enum_val = Enum {\n      name: String::from(\"TrackerHand\"),\n      values: HashMap::from([\n        (String::from(\"TRACKER_HAND_UNKNOWN\"), 0),\n        (String::from(\"TRACKER_LEFT_HAND\"), 1),\n        (String::from(\"TRACKER_RIGHT_HAND\"), 2),\n      ]),\n    };\n    assert_eq!(serde_json::from_str::<Enum>(&enum_str).unwrap(), enum_val);\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/method.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::argument::Argument;\n\nuse serde::{Serialize, Deserialize};\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Method {\n  pub name: String,\n  pub return_type: String,\n  pub is_editor: bool,\n  pub is_noscript: bool,\n  pub is_const: bool,\n  pub is_reverse: bool,\n  pub is_virtual: bool,\n  pub has_varargs: bool,\n  pub is_from_script: bool,\n  pub arguments: Vec<Argument>,\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use serde_json;\n\n  #[test]\n  fn deserialize_method_test() {\n    let method_str = r#\"{\n\t\t\t\t\"name\": \"add_interface\",\n\t\t\t\t\"return_type\": \"void\",\n\t\t\t\t\"is_editor\": false,\n\t\t\t\t\"is_noscript\": false,\n\t\t\t\t\"is_const\": false,\n\t\t\t\t\"is_reverse\": false,\n\t\t\t\t\"is_virtual\": false,\n\t\t\t\t\"has_varargs\": false,\n\t\t\t\t\"is_from_script\": false,\n\t\t\t\t\"arguments\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"interface\",\n\t\t\t\t\t\t\"type\": \"ARVRInterface\",\n\t\t\t\t\t\t\"has_default_value\": false,\n\t\t\t\t\t\t\"default_value\": \"\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\"#;\n    let result: Method = serde_json::from_str(method_str).unwrap();\n    assert_eq!(result, Method {\n      name: String::from(\"add_interface\"),\n      return_type: String::from(\"void\"),\n      is_editor: false,\n      is_noscript: false,\n      is_const: false,\n      is_reverse: false,\n      is_virtual: false,\n      has_varargs: false,\n      is_from_script: false,\n      arguments: vec![\n        Argument {\n          name: String::from(\"interface\"),\n          argument_type: String::from(\"ARVRInterface\"),\n          has_default_value: false,\n          default_value: String::from(\"\"),\n        },\n      ],\n    });\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Classes for reading the output of `godot\n//! --gdnative-generate-json-api`.\n\npub mod argument;\npub mod api_type;\npub mod class;\npub mod gdnative_enum;\npub mod method;\npub mod property;\npub mod signal;\n\nuse class::Class;\nuse crate::runner::dump_json_api;\n\nuse serde_json;\n\nuse std::io;\nuse std::collections::HashMap;\n\npub const GLOBAL_CONSTANTS_CLASS: &str = \"GlobalConstants\";\n\n#[derive(Debug, Clone)]\npub struct NativeClasses {\n  mapping: HashMap<String, Class>,\n}\n\nimpl NativeClasses {\n\n  pub fn get_api_from_godot() -> io::Result<NativeClasses> {\n    let tempfile = dump_json_api()?;\n    let classes: Vec<Class> = serde_json::from_reader(tempfile)?;\n    Ok(\n      NativeClasses {\n        mapping: classes.into_iter().map(|cls| (cls.name.clone(), cls)).collect(),\n      }\n    )\n  }\n\n  pub fn len(&self) -> usize {\n    self.mapping.len()\n  }\n\n  pub fn is_empty(&self) -> bool {\n    self.mapping.is_empty()\n  }\n\n  pub fn get(&self, class_name: &str) -> Option<&Class> {\n    self.mapping.get(class_name)\n  }\n\n  pub fn get_global_constants(&self) -> &Class {\n    self.get(GLOBAL_CONSTANTS_CLASS).expect(\"Could not read global constants from GDNative API\")\n  }\n\n  pub fn values(&self) -> impl Iterator<Item=&Class> {\n    self.mapping.values()\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn test_get_api_from_godot() {\n    let classes = NativeClasses::get_api_from_godot().unwrap();\n    // Assert only a few basic facts about the classes, as we want to\n    // maximize compatibility with forked or custom Godot builds that\n    // have different classes available.\n\n    // There should exist at least one class.\n    assert!(classes.len() > 0);\n\n    // There should be an Object class with the desired properties.\n    let object = classes.get(\"Object\").unwrap();\n    assert_eq!(object.name, \"Object\");\n    assert_eq!(object.base_class, \"\");\n    assert_eq!(object.api_type, api_type::ApiType::Core);\n    assert_eq!(object.singleton, false);\n    assert_eq!(object.singleton_name, \"\");\n    assert_eq!(object.instanciable, true);\n    assert_eq!(object.is_reference, false);\n\n    // There should be an Reference class with the desired properties.\n    let object = classes.get(\"Reference\").unwrap();\n    assert_eq!(object.name, \"Reference\");\n    assert_eq!(object.base_class, \"Object\");\n    assert_eq!(object.api_type, api_type::ApiType::Core);\n    assert_eq!(object.singleton, false);\n    assert_eq!(object.singleton_name, \"\");\n    assert_eq!(object.instanciable, true);\n    assert_eq!(object.is_reference, true);\n\n    // There should be an Reference class with the desired properties.\n    let object = classes.get(\"Node\").unwrap();\n    assert_eq!(object.name, \"Node\");\n    assert_eq!(object.base_class, \"Object\");\n    assert_eq!(object.api_type, api_type::ApiType::Core);\n    assert_eq!(object.singleton, false);\n    assert_eq!(object.singleton_name, \"\");\n    assert_eq!(object.instanciable, true);\n    assert_eq!(object.is_reference, false);\n\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/property.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse serde::{Serialize, Deserialize};\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Property {\n  pub name: String,\n  #[serde(rename = \"type\")]\n  pub property_type: String,\n  pub getter: String,\n  pub setter: String,\n  pub index: i32,\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use serde_json;\n\n  #[test]\n  fn deserialize_property_test() {\n    let property_str = r#\"{\n\t\t\t\t\"name\": \"editor_hint\",\n\t\t\t\t\"type\": \"bool\",\n\t\t\t\t\"getter\": \"is_editor_hint\",\n\t\t\t\t\"setter\": \"set_editor_hint\",\n\t\t\t\t\"index\": -1\n\t\t\t}\"#;\n    let result: Property = serde_json::from_str(property_str).unwrap();\n    assert_eq!(result, Property {\n      name: String::from(\"editor_hint\"),\n      property_type: String::from(\"bool\"),\n      getter: String::from(\"is_editor_hint\"),\n      setter: String::from(\"set_editor_hint\"),\n      index: -1,\n    });\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/gdnative/signal.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::argument::Argument;\n\nuse serde::{Serialize, Deserialize};\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct Signal {\n  pub name: String,\n  pub arguments: Vec<Argument>,\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use serde_json;\n\n  #[test]\n  fn deserialize_signal_test() {\n    let signal_str = r#\"{\n\t\t\t\t\"name\": \"mesh_updated\",\n\t\t\t\t\"arguments\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"name\": \"mesh\",\n\t\t\t\t\t\t\"type\": \"Mesh\",\n\t\t\t\t\t\t\"has_default_value\": false,\n\t\t\t\t\t\t\"default_value\": \"\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\"#;\n    let result: Signal = serde_json::from_str(signal_str).unwrap();\n    assert_eq!(result, Signal {\n      name: String::from(\"mesh_updated\"),\n      arguments: vec![\n        Argument {\n          name: String::from(\"mesh\"),\n          argument_type: String::from(\"Mesh\"),\n          has_default_value: false,\n          default_value: String::from(\"\"),\n        },\n      ],\n    });\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/library/magic.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDLisp call magic.\n//!\n//! Call magic can be thought of as a sort of optimization and\n//! bootstrapping technique. In essence, a function call like `(+ a\n//! b)` in GDLisp should, in full generality, compile to a GDScript\n//! function call of the form `GDLisp.plus(GDLisp.cons(a,\n//! GDLisp.cons(b, null)))`. However, for obvious reasons, we would\n//! prefer that this simply compile to `a + b`.\n//!\n//! This is exactly what call magic accomplishes. The function `+` in\n//! GDLisp is marked with a special flag which indicates that it is\n//! eligible for call magic. There is a `GDLisp.gd` implementation of\n//! `+` in full generality which will get called if the programmer\n//! ever uses `+` as a first-class function (i.e. `(function +)` or\n//! `#'+`). However, if the programmer ever calls `+` directly, then\n//! the call magic is guaranteed to fire and replace the call with the\n//! correct syntax.\n//!\n//! As mentioned above, this is not *merely* an optimization. The `+`\n//! function in GDLisp could ostensibly be implemented in terms of\n//! itself. Since call magic is guaranteed behavior, this bootstrapped\n//! function would be guaranteed to compile to actual addition and not\n//! be recursive.\n\nuse crate::compile::symbol_table::call_magic::{CallMagic, Assoc};\nuse crate::compile::symbol_table::call_magic::table::MagicTable;\nuse crate::gdscript::op;\nuse crate::gdscript::literal::Literal;\n\n/// Bind all GDLisp call magic to the magic table given.\npub fn bind_magic(table: &mut MagicTable) {\n\n  // Default magic (used by default for all user-defined functions and\n  // for any builtins which don't request other magic)\n  table.set(String::from(\"DEFAULT\"), CallMagic::DefaultCall);\n\n  // Addition (+)\n  table.set(String::from(\"ADDITION\"),\n            CallMagic::CompileToBinOp(Literal::from(0), op::BinaryOp::Add, Assoc::Left));\n\n  // Multiplication (*)\n  table.set(String::from(\"MULTIPLICATION\"),\n            CallMagic::CompileToBinOp(Literal::from(1), op::BinaryOp::Times, Assoc::Left));\n\n  // Subtraction (-)\n  table.set(String::from(\"SUBTRACTION\"), CallMagic::MinusOperation);\n\n  // Division (/)\n  table.set(String::from(\"DIVISION\"), CallMagic::DivOperation);\n\n  // Integer Division (div)\n  table.set(String::from(\"INTEGER-DIVISION\"), CallMagic::IntDivOperation);\n\n  // Modulo Division (mod)\n  table.set(String::from(\"MODULO\"), CallMagic::ModOperation);\n\n  // Min function (min)\n  table.set(String::from(\"MIN-FUNCTION\"), CallMagic::MinFunction);\n\n  // Max function (max)\n  table.set(String::from(\"MAX-FUNCTION\"), CallMagic::MaxFunction);\n\n  // Equality (=)\n  table.set(String::from(\"EQUAL\"), CallMagic::CompileToTransCmp(op::BinaryOp::Eq));\n\n  // Less Than (<)\n  table.set(String::from(\"LESS-THAN\"), CallMagic::CompileToTransCmp(op::BinaryOp::LT));\n\n  // Greater Than (>)\n  table.set(String::from(\"GREATER-THAN\"), CallMagic::CompileToTransCmp(op::BinaryOp::GT));\n\n  // Less Than or Equal (<=)\n  table.set(String::from(\"LESS-THAN-OR-EQUAL\"), CallMagic::CompileToTransCmp(op::BinaryOp::LE));\n\n  // Greater Than or Equal (>=)\n  table.set(String::from(\"GREATER-THAN-OR-EQUAL\"), CallMagic::CompileToTransCmp(op::BinaryOp::GE));\n\n  // Not Equal (/=)\n  table.set(String::from(\"NOT-EQUAL\"),\n            CallMagic::NEqOperation(Box::new(CallMagic::DefaultCall)));\n\n  // Boolean Not (not)\n  table.set(String::from(\"BOOLEAN-NOT\"), CallMagic::BooleanNotOperation);\n\n  // List (list)\n  table.set(String::from(\"LIST\"), CallMagic::ListOperation);\n\n  // Array (array)\n  table.set(String::from(\"ARRAY\"), CallMagic::ArrayOperation);\n\n  // Dictionary (dict)\n  table.set(String::from(\"DICT\"), CallMagic::DictOperation);\n\n  // Vector (vector)\n  table.set(String::from(\"VECTOR\"), CallMagic::VectorOperation);\n\n  // Array Subscript (elt)\n  table.set(String::from(\"ARRAY-SUBSCRIPT\"), CallMagic::ArraySubscript);\n\n  // Array Subscript Assignment (set-elt)\n  table.set(String::from(\"ARRAY-SUBSCRIPT-ASSIGNMENT\"), CallMagic::ArraySubscriptAssign);\n\n  // Dictionary Subscript (dict/elt; same as elt)\n  table.set(String::from(\"DICT-SUBSCRIPT\"), CallMagic::ArraySubscript);\n\n  // Dictionary Subscript Assignment (set-dict/elt; same as set-elt)\n  table.set(String::from(\"DICT-SUBSCRIPT-ASSIGNMENT\"), CallMagic::ArraySubscriptAssign);\n\n  // Direct Instance Check (sys/instance_direct?)\n  table.set(String::from(\"DIRECT-INSTANCE-CHECK\"), CallMagic::InstanceOf);\n\n  // Array Membership Check (member?)\n  table.set(String::from(\"ARRAY-MEMBER-CHECK\"), CallMagic::ElementOf);\n\n  // Node access (sys/get-node)\n  table.set(String::from(\"GET-NODE-SYNTAX\"), CallMagic::GetNodeSyntax);\n\n  // str function\n  table.set(String::from(\"VARARG-STR\"), CallMagic::CompileToVarargCall(String::from(\"str\")));\n\n  // printerr function\n  table.set(String::from(\"VARARG-PRINTERR\"), CallMagic::CompileToVarargCall(String::from(\"printerr\")));\n\n  // printraw function\n  table.set(String::from(\"VARARG-PRINTRAW\"), CallMagic::CompileToVarargCall(String::from(\"printraw\")));\n\n  // print-debug function\n  table.set(String::from(\"VARARG-PRINTDEBUG\"), CallMagic::CompileToVarargCall(String::from(\"print_debug\")));\n\n  // printt function\n  table.set(String::from(\"VARARG-PRINTT\"), CallMagic::CompileToVarargCall(String::from(\"printt\")));\n\n  // prints function\n  table.set(String::from(\"VARARG-PRINTS\"), CallMagic::CompileToVarargCall(String::from(\"prints\")));\n\n  // print function\n  table.set(String::from(\"VARARG-PRINT\"), CallMagic::CompileToVarargCall(String::from(\"print\")));\n\n  // range function\n  table.set(String::from(\"VARARG-RANGE\"), CallMagic::CompileToVarargCall(String::from(\"range\")));\n\n  // Color8 function\n  table.set(String::from(\"VARARG-COLOR8\"), CallMagic::CompileToVarargCall(String::from(\"Color8\")));\n\n  // ColorN function\n  table.set(String::from(\"VARARG-COLORN\"), CallMagic::CompileToVarargCall(String::from(\"ColorN\")));\n\n  // bytes2var function\n  table.set(String::from(\"VARARG-BYTES2VAR\"), CallMagic::CompileToVarargCall(String::from(\"bytes2var\")));\n\n  // var2bytes function\n  table.set(String::from(\"VARARG-VAR2BYTES\"), CallMagic::CompileToVarargCall(String::from(\"var2bytes\")));\n\n  // Rect2 function\n  table.set(String::from(\"VARARG-RECT2\"), CallMagic::CompileToVarargCall(String::from(\"Rect2\")));\n\n  // Transform2D function\n  table.set(String::from(\"VARARG-TRANSFORM2D\"), CallMagic::CompileToVarargCall(String::from(\"Transform2D\")));\n\n  // Plane function\n  table.set(String::from(\"VARARG-PLANE\"), CallMagic::CompileToVarargCall(String::from(\"Plane\")));\n\n  // Quat function\n  table.set(String::from(\"VARARG-QUAT\"), CallMagic::CompileToVarargCall(String::from(\"Quat\")));\n\n  // Basis function\n  table.set(String::from(\"VARARG-BASIS\"), CallMagic::CompileToVarargCall(String::from(\"Basis\")));\n\n  // Transform function\n  table.set(String::from(\"VARARG-TRANSFORM\"), CallMagic::CompileToVarargCall(String::from(\"Transform\")));\n\n  // Color function\n  table.set(String::from(\"VARARG-COLOR\"), CallMagic::CompileToVarargCall(String::from(\"Color\")));\n\n  // NodePath function\n  table.set(String::from(\"NODEPATH-SYNTAX\"),\n            CallMagic::NodePathConstructor(Box::new(CallMagic::DefaultCall)));\n\n}\n\n/// Produce a new [`MagicTable`] with all of the magic bound as though\n/// via [`bind_magic`].\npub fn standard_magic_table() -> MagicTable {\n  let mut table = MagicTable::new();\n  bind_magic(&mut table);\n  table\n}\n"
  },
  {
    "path": "src/gdscript/library/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Convenient access to the builtins in `GDLisp.gd`.\n//!\n//! The most commonly-used functions from this module are\n//! [`bind_builtins`], [`bind_builtin_macros`], and\n//! [`all_builtin_names`], though the module also provides several\n//! simpler helper functions for commonly-used GDLisp builtins.\n\npub mod cell;\npub mod class_loader;\npub mod constant_loader;\npub mod gdnative;\npub mod magic;\n\nuse super::expr::{Expr, ExprF};\nuse crate::compile::Compiler;\nuse crate::compile::names;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::compile::symbol_table::local_var::VarName;\nuse crate::ir::identifier::{Id, Namespace};\nuse crate::ir::macros::MacroData;\nuse crate::runner::version::{VersionInfo, get_godot_version};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::translation_unit::TranslationUnit;\nuse crate::pipeline::stdlib_unit::StdlibUnit;\nuse crate::pipeline::config::ProjectConfig;\nuse crate::pipeline::source::{SourceOffset, SourcedValue};\n\nuse rmp_serde::{encode, decode};\n\nuse std::collections::{HashSet, HashMap};\nuse std::path::PathBuf;\nuse std::fs::File;\nuse std::env::current_exe;\n\n/// The name of the top-level GDLisp singleton object.\npub const GDLISP_NAME: &str = \"GDLisp\";\n\n/// The name of a GDScript constructor.\npub const CONSTRUCTOR_NAME: &str = \"_init\";\n\n/// The name of a GDScript _ready function which runs when a node is\n/// added to the scene tree.\npub const READY_NAME: &str = \"_ready\";\n\n/// The name of the default superclass, when one is not specified.\npub const REFERENCE_NAME: &str = \"Reference\";\n\n/// An expression which accesses the global GDLisp singleton object.\npub fn gdlisp_root(pos: SourceOffset) -> Expr {\n  Expr::var(GDLISP_NAME, pos)\n}\n\n/// A variable name which accesses the global GDLisp singleton object.\npub fn gdlisp_root_var_name() -> VarName {\n  VarName::Superglobal(GDLISP_NAME.to_owned())\n}\n\n/// An expression which accesses a specific field on [`gdlisp_root`].\npub fn on_gdlisp_root(name: String, pos: SourceOffset) -> Expr {\n  Expr::new(ExprF::Attribute(Box::new(gdlisp_root(pos)), name), pos)\n}\n\n/// Given a vector of expressions `vec`, produce an expression which\n/// produces a GDLisp list containing those expressions in order.\npub fn construct_list(vec: Vec<Expr>, pos: SourceOffset) -> Expr {\n  vec.into_iter().rev().fold(Expr::null(pos), |rest, first| {\n    Expr::call(Some(gdlisp_root(pos)), \"cons\", vec!(first, rest), pos)\n  })\n}\n\n/// An appropriate [`ProjectConfig`] for the `GDLisp.gd` source file.\npub fn gdlisp_project_config(godot_version: VersionInfo) -> ProjectConfig {\n  ProjectConfig {\n    root_directory: PathBuf::from(\".\"),\n    optimizations: true,\n    godot_version: godot_version,\n  }\n}\n\nfn get_stdlib() -> StdlibUnit {\n  let exe_path = current_exe().expect(\"Could not locate current executable\");\n  let exe_dir = exe_path.parent().expect(\"Could not locate executable path\");\n  let file = File::open(exe_dir.join(\"GDLisp.msgpack\")).expect(\"I/O error reading GDLisp.msgpack (this file should have been built as part of the GDLisp build process; are you sure you have compiled GDLisp fully?)\");\n  decode::from_read(file).expect(\"Error deserializing stdlib (this is likely a GDLisp error and should be reported as a bug)\")\n}\n\npub fn load_stdlib_to_file() -> StdlibUnit {\n  let godot_version = get_godot_version().expect(\"I/O error loading stdlib\");\n  let mut pipeline = Pipeline::new(gdlisp_project_config(godot_version));\n  let unit = load_stdlib_file(&mut pipeline);\n  let stdlib = StdlibUnit::from(unit.clone_detached());\n  let mut file = File::create(\"GDLisp.msgpack\").expect(\"I/O error loading stdlib (does GDLisp have write permissions for this folder?)\");\n  encode::write(&mut file, &stdlib).expect(\"Error during serialization of stdlib\");\n  stdlib\n}\n\nfn load_stdlib_file(pipeline: &mut Pipeline) -> &TranslationUnit {\n  match pipeline.load_file(&PathBuf::from(\"GDLisp.lisp\"), SourceOffset(0)) {\n    Ok(unit) => unit,\n    Err(err) => {\n      // This is basically .expect() but with a much better error\n      // message.\n      let err = SourcedValue::from_file(&err, \"GDLisp.lisp\").expect(\"Error loading stdlib (failed to get further diagnostic information)\");\n      panic!(\"Error loading stdlib: {}\", err);\n    }\n  }\n}\n\n/// Ensure that the standard library `GDLisp.lisp` has been compiled.\n/// If it has not, then this function compiles it. As with other\n/// `library` functions, this will panic in case of error. After this\n/// function returns, a file named `GDLisp.gd` will exist in the\n/// project directory.\npub fn ensure_stdlib_loaded() {\n  let _ = get_stdlib();\n}\n\n/// Bind all GDLisp and GDScript built-in names to the given symbol\n/// table.\n///\n/// This function does *not* bind built-in macros.\n/// [`bind_builtin_macros`] is a separate function provided for that\n/// behavior.\n///\n/// If `minimalist` is true, then the GDLisp standard library will not\n/// be bound. Note that this does *not* make `bind_builtins` a no-op;\n/// GDScript global names like top-level classes (`Reference`, `Node`,\n/// etc.) will be bound regardless of `minimalist`, but the GDLisp\n/// standard library will not be loaded. `minimalist` should generally\n/// be false and is intended as a means of compiling the standard\n/// library itself.\npub fn bind_builtins(table: &mut SymbolTable, minimalist: bool) {\n  let unit =\n    if minimalist {\n      None\n    } else {\n      Some(get_stdlib())\n    };\n  bind_builtins_unchecked(table, unit.as_ref());\n}\n\nfn bind_builtins_unchecked(table: &mut SymbolTable, unit: Option<&StdlibUnit>) {\n\n  // (Assumes minimalist compile iff unit is None)\n\n  // TODO Do we need to bind built-in macros here? Macros should have\n  // no runtime presence so that makes me think no, but at the same\n  // time we do bind user-defined macros to the symbol table.\n\n  if let Some(unit) = unit {\n    for id in &unit.exports {\n      match id.namespace {\n        Namespace::Value => {\n          let value = unit.table.get_var(&id.name).unwrap_or_else(|| panic!(\"Exported value name {} does not appear\", id.name));\n          let mut value = value.to_owned();\n          value.name = value.name.into_imported(String::from(\"GDLisp\"));\n          table.set_var(id.name.to_owned(), value);\n        }\n        Namespace::Function => {\n          let (call, magic) = unit.table.get_fn(&id.name).unwrap_or_else(|| panic!(\"Exported function name {} does not appear\", id.name));\n          let call = Compiler::translate_call(String::from(\"GDLisp\"), call.clone());\n          let magic = magic.clone();\n          table.set_fn(id.name.to_owned(), call, magic);\n        }\n      }\n    }\n  }\n\n}\n\n/// Get a collection of all of the built-in names in GDLisp and\n/// GDScript, in no particular order.\n///\n/// `minimalist` is passed onto [`bind_builtins`] as-is and will be\n/// interpreted in the same way it is in that function.\npub fn all_builtin_names(minimalist: bool) -> HashSet<Id> {\n  // bind_builtins is the single source of truth for built-in names.\n  // So if we want them in a HashSet, we should let bind_builtins\n  // build a symbol table, and then we should convert that into a\n  // HashSet.\n  let mut table = SymbolTable::new();\n  bind_builtins(&mut table, minimalist);\n  table.into_keys().collect()\n}\n\n/// Bind all of the built-in GDLisp macros and symbol macros to the\n/// macro table given.\n///\n/// If the standard library has not been loaded, this function loads\n/// the standard library. As such, this function should not be called\n/// in the process of loading the standard library, as that will\n/// result in a double lock on the stdlib mutex.\npub fn bind_builtin_macros(macros: &mut HashMap<Id, MacroData>,\n                           pipeline: &mut Pipeline) {\n  let unit = get_stdlib();\n\n  for name in &unit.exports {\n    if let Some(data) = unit.macros.get(name) {\n      macros.insert(name.to_owned(), data.to_imported());\n      pipeline.get_server_mut().add_reserved_macro(names::lisp_to_gd(&name.name));\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/literal.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDScript literal values.\n//!\n//! This module represents the subset of [GDScript\n//! expressions](super::expr) which are literal values, i.e. which\n//! evaluate to themselves. This includes literal integers, strings,\n//! and the special value `null`.\n\nuse super::is_valid_node_path;\nuse crate::ir::literal::{Literal as IRLiteral};\nuse crate::sxp::string::insert_escapes;\n\nuse ordered_float::OrderedFloat;\nuse serde::{Serialize, Deserialize};\n\nuse std::convert::TryFrom;\nuse std::ops::Deref;\nuse std::fmt;\n\n/// The type of GDScript literal values.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum Literal {\n  Int(i32),\n  Float(LiteralFloat),\n  String(String),\n  NodeLiteral(String),\n  NodePathLiteral(String),\n  Null,\n  Bool(bool),\n}\n\n/// A literal floating-point value, with reflexive equality semantics.\n/// The only difference between this type and [`OrderedFloat`] is that\n/// the former supports serde.\n#[derive(Clone, Copy, Serialize, Deserialize)]\npub struct LiteralFloat(pub f32);\n\n/// In most cases, we can convert an\n/// [`ir::literal::Literal`](IRLiteral) to a [`Literal`]. However,\n/// there are a handful of corner cases where `ir::literal::Literal`\n/// compiles to something nontrivial in GDScript. In those cases, this\n/// error will be returned.\n#[derive(Debug, Clone, Default)]\npub struct IRToExprLiteralError;\n\nimpl Literal {\n\n  /// Convert a GDScript literal to a string. The result will contain\n  /// valid GDScript syntax.\n  pub fn to_gd(&self) -> String {\n    match self {\n      Literal::Int(n) => n.to_string(),\n      Literal::String(s) => format!(\"\\\"{}\\\"\", insert_escapes(s)),\n      Literal::NodeLiteral(s) => {\n        if is_valid_node_path(s) {\n          format!(\"${}\", s)\n        } else {\n          format!(\"$\\\"{}\\\"\", insert_escapes(s))\n        }\n      }\n      Literal::NodePathLiteral(s) => format!(\"@\\\"{}\\\"\", insert_escapes(s)),\n      Literal::Null => String::from(\"null\"),\n      Literal::Bool(b) => if *b { String::from(\"true\") } else { String::from(\"false\") },\n      Literal::Float(f) => format!(\"{:e}\", **f),\n    }\n  }\n\n}\n\nimpl From<i32> for Literal {\n  fn from(x: i32) -> Literal {\n    Literal::Int(x)\n  }\n}\n\nimpl From<OrderedFloat<f32>> for Literal {\n  fn from(x: OrderedFloat<f32>) -> Literal {\n    Literal::Float(LiteralFloat(*x))\n  }\n}\n\nimpl From<String> for Literal {\n  fn from(x: String) -> Literal {\n    Literal::String(x)\n  }\n}\n\nimpl<'a> From<&'a str> for Literal {\n  fn from(x: &'a str) -> Literal {\n    Literal::String(String::from(x))\n  }\n}\n\nimpl From<bool> for Literal {\n  fn from(x: bool) -> Literal {\n    Literal::Bool(x)\n  }\n}\n\nimpl TryFrom<IRLiteral> for Literal {\n  type Error = IRToExprLiteralError;\n\n  fn try_from(value: IRLiteral) -> Result<Literal, Self::Error> {\n    match value {\n      IRLiteral::Nil => Ok(Literal::Null),\n      IRLiteral::Int(n) => Ok(Literal::Int(n)),\n      IRLiteral::Float(f) => Ok(Literal::Float(LiteralFloat(*f))),\n      IRLiteral::String(s) => Ok(Literal::String(s)),\n      IRLiteral::Symbol(_) => Err(IRToExprLiteralError), // Doesn't compile to a GDScript literal\n      IRLiteral::Bool(b) => Ok(Literal::Bool(b)),\n    }\n  }\n\n}\n\nimpl PartialEq for LiteralFloat {\n\n  /// Compares the two floats for equality using [`OrderedFloat`]\n  /// semantics, so NaN compares equal to itself.\n  fn eq(&self, other: &Self) -> bool {\n    OrderedFloat(**self) == OrderedFloat(**other)\n  }\n\n}\n\nimpl Eq for LiteralFloat {}\n\nimpl Deref for LiteralFloat {\n  type Target = f32;\n\n  fn deref(&self) -> &f32 {\n    &self.0\n  }\n\n}\n\nimpl fmt::Debug for LiteralFloat {\n\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"{}\", self.0)\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn literal_test() {\n    assert_eq!(Literal::Int(10).to_gd(), \"10\");\n    assert_eq!(Literal::Null.to_gd(), \"null\");\n    assert_eq!(Literal::Bool(false).to_gd(), \"false\");\n    assert_eq!(Literal::Bool(true).to_gd(), \"true\");\n  }\n\n  #[test]\n  fn string_test() {\n    assert_eq!(Literal::String(\"foo\".to_owned()).to_gd(), \"\\\"foo\\\"\");\n    assert_eq!(Literal::String(\"foo\\\"bar\".to_owned()).to_gd(), \"\\\"foo\\\\\\\"bar\\\"\");\n  }\n\n  #[test]\n  fn node_path_test() {\n    assert_eq!(Literal::NodeLiteral(\"foo\".to_owned()).to_gd(), \"$foo\");\n    assert_eq!(Literal::NodeLiteral(\"foo\\\"bar\".to_owned()).to_gd(), \"$\\\"foo\\\\\\\"bar\\\"\");\n    assert_eq!(Literal::NodeLiteral(\"foo bar\".to_owned()).to_gd(), \"$\\\"foo bar\\\"\");\n  }\n\n  #[test]\n  fn node_path_literal_test() {\n    assert_eq!(Literal::NodePathLiteral(\"foo\".to_owned()).to_gd(), \"@\\\"foo\\\"\");\n    assert_eq!(Literal::NodePathLiteral(\"foo\\\"bar\".to_owned()).to_gd(), \"@\\\"foo\\\\\\\"bar\\\"\");\n    assert_eq!(Literal::NodePathLiteral(\"foo bar\".to_owned()).to_gd(), \"@\\\"foo bar\\\"\");\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/metadata.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helpers for using GDScript object and script metadata.\n//!\n//! Every object in Godot has metadata fields that can be modified.\n//! These fields exist independently of the usual instance variables\n//! on an object and can even be defined on built-in class instances\n//! such as `GDScript` objects themselves. GDLisp uses this capability\n//! to define various data needed for more advanced languages\n//! features.\n//!\n//! This module provides helpers to read, write, and check whether\n//! metadata exists. It also provides naming conventions for managing\n//! the names of GDLisp-useful metadata, to avoid conflicts. All\n//! GDLisp metadata keys will always begin with the prefix `__gdlisp`\n//! followed by some more specific string. The specific strings used\n//! are implementation details and should not be relied upon in GDLisp\n//! code.\n\nuse super::expr::Expr;\nuse crate::pipeline::source::SourceOffset;\n\n/// The prefix applied to all GDLisp metadata.\npub const PREFIX: &str = \"__gdlisp\";\n\n/// The metadata field that exists (and is truthy) on all cons cells\n/// in GDLisp.\npub const CONS_META: &str = \"__gdlisp_Primitive_Cons\";\n\n/// The metadata field that exists (and is truthy) on all symbol\n/// values in GDLisp.\npub const SYMBOL_META: &str = \"__gdlisp_Primitive_Symbol\";\n\n/// The name of the global function we define in a REPL file to\n/// indicate the REPL code to be run.\npub const REPL_FUNCTION_NAME: &str = \"__gdlisp_Repl_runner\";\n\n/// The name of the dictionary on an object's metadata that lists any\n/// objects being kept alive for the purposes of signal dispatch.\npub const SIGNAL_META: &str = \"__gdlisp_signals\";\n\n/// Given a GDScript function name, prefix it appropriately for a\n/// symbol macro with the given name.\npub fn symbol_macro(name: &str) -> String {\n  format!(\"{}_SymbolMacroFunction_{}\", PREFIX, name)\n}\n\n/// Given a GDLisp lazy val, this is the name of the metadata with\n/// suffix `name`.\n///\n/// Note that, under the current implementation, the `deflazy` macro\n/// does *not* use the GDScript name but rather a gensym to store the\n/// metadata. This behavior may be changed in the future.\npub fn lazy(name: &str) -> String {\n  format!(\"{}_Lazy_{}\", PREFIX, name)\n}\n\n/// A `MetadataCompiler` is used as a helper for compiling calls to\n/// GDScript metadata functions such as `get_meta`, `has_meta`, and\n/// `set_meta`.\n#[derive(Clone, Debug)]\npub struct MetadataCompiler {\n  object_ref: Expr,\n}\n\nimpl MetadataCompiler {\n\n  /// A new `MetadataCompiler` which will compile to metadata calls on\n  /// the given expression.\n  pub fn new(expr: Expr) -> MetadataCompiler {\n    MetadataCompiler { object_ref: expr }\n  }\n\n  fn get_pos(&self) -> SourceOffset {\n    self.object_ref.pos\n  }\n\n  /// Compiles to a `get_meta` GDScript call.\n  pub fn get_meta(&self, meta_name: &str) -> Expr {\n    let meta_name_expr = Expr::from_value(meta_name, self.get_pos());\n    Expr::call(Some(self.object_ref.clone()), \"get_meta\", vec!(meta_name_expr), self.get_pos())\n  }\n\n  /// Compiles to a `set_meta` GDScript call.\n  pub fn set_meta(&self, meta_name: &str, value: Expr) -> Expr {\n    let meta_name_expr = Expr::from_value(meta_name, self.get_pos());\n    Expr::call(Some(self.object_ref.clone()), \"set_meta\", vec!(meta_name_expr, value), self.get_pos())\n  }\n\n  /// Compiles to a `has_meta` GDScript call.\n  pub fn has_meta(&self, meta_name: &str) -> Expr {\n    let meta_name_expr = Expr::from_value(meta_name, self.get_pos());\n    Expr::call(Some(self.object_ref.clone()), \"has_meta\", vec!(meta_name_expr), self.get_pos())\n  }\n\n  /// Compiles to a `remove_meta` GDScript call.\n  pub fn remove_meta(&self, meta_name: &str) -> Expr {\n    let meta_name_expr = Expr::from_value(meta_name, self.get_pos());\n    Expr::call(Some(self.object_ref.clone()), \"remove_meta\", vec!(meta_name_expr), self.get_pos())\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::expr::ExprF;\n\n  #[test]\n  fn naming_test() {\n    assert_eq!(lazy(\"ABC\"), \"__gdlisp_Lazy_ABC\");\n    assert_eq!(lazy(\"Foobar\"), \"__gdlisp_Lazy_Foobar\");\n  }\n\n  #[test]\n  fn compiler_test() {\n    let expr = Expr::var(\"reference\", SourceOffset(0));\n    let value_expr = Expr::from_value(100, SourceOffset(0));\n    let compiler = MetadataCompiler::new(expr);\n\n    // Basic compilation\n    assert_eq!(compiler.get_meta(\"foobar\").to_gd(), \"reference.get_meta(\\\"foobar\\\")\");\n    assert_eq!(compiler.has_meta(\"foobar\").to_gd(), \"reference.has_meta(\\\"foobar\\\")\");\n    assert_eq!(compiler.remove_meta(\"foobar\").to_gd(), \"reference.remove_meta(\\\"foobar\\\")\");\n    assert_eq!(compiler.set_meta(\"foobar\", value_expr.clone()).to_gd(), \"reference.set_meta(\\\"foobar\\\", 100)\");\n\n  }\n\n  #[test]\n  fn compiler_offset_test() {\n    let expr = Expr::var(\"reference\", SourceOffset(42));\n    let value_expr = Expr::from_value(100, SourceOffset(10));\n    let compiler = MetadataCompiler::new(expr);\n\n    // Check that the generated source offsets are correct\n    let result_expr = compiler.set_meta(\"foobar\", value_expr);\n    assert_eq!(result_expr.pos, SourceOffset(42));\n    match result_expr {\n      Expr { value: ExprF::Call(Some(lhs), _, args), pos: _ } => {\n        assert_eq!(lhs.pos, SourceOffset(42));\n        assert_eq!(args.len(), 2);\n        assert_eq!(args[0].pos, SourceOffset(42));\n        assert_eq!(args[1].pos, SourceOffset(10));\n      }\n      _ => {\n        panic!(\"Wrong shape of result, got {:?}\", result_expr);\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Data structures for representing and manipulating valid GDScript\n//! code.\n\npub mod arglist;\npub mod class_extends;\npub mod decl;\npub mod expr;\npub mod expr_wrapper;\npub mod inner_class;\npub mod library;\npub mod literal;\npub mod metadata;\npub mod op;\npub mod pattern;\npub mod spacing;\npub mod stmt;\n\nuse regex::Regex;\n\nuse std::fmt;\n\n/// Indent to the given position with spaces.\n///\n/// # Examples\n///\n/// ```\n/// # use gdlisp::gdscript::indent;\n/// let mut str = String::new();\n/// indent(&mut str, 4);\n/// assert_eq!(str, \"    \");\n/// ```\npub fn indent<W: fmt::Write>(w: &mut W, ind: u32) -> Result<(), fmt::Error> {\n  let spaces = \" \".repeat(ind as usize);\n  write!(w, \"{}\", spaces)\n}\n\n/// Determines whether the string is a valid GDScript identifier, by\n/// the rules of the language. A GDScript identifier consists only of\n/// ASCII alphanumeric characters and underscore and cannot begin with\n/// an underscore.\npub fn is_valid_identifier(s: &str) -> bool {\n  lazy_static! {\n    static ref RE: Regex = Regex::new(r#\"\\A[A-Za-z_][A-Za-z0-9_]*\\z\"#).unwrap();\n  }\n  RE.is_match(s)\n}\n\n/// Determines whether the string is a valid GDScript node path. A\n/// valid GDScript node path is a sequence of one or more valid\n/// identifiers, delimited by forward slashes.\n///\n/// Note: This function checks the *path* portion of the node path\n/// syntax, so a string containing a `$` will not be accepted.\n/// Additionally, this function is not designed to check the extended\n/// quoted node path syntax, so a string containing `\"` will not be\n/// accepted.\npub fn is_valid_node_path(s: &str) -> bool {\n  s.split('/').all(is_valid_identifier)\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  fn do_indent(ind: u32) -> String {\n    let mut x = String::new();\n    indent(&mut x, ind).unwrap();\n    x\n  }\n\n  #[test]\n  fn test_indent() {\n    assert_eq!(do_indent(0), \"\");\n    assert_eq!(do_indent(1), \" \");\n    assert_eq!(do_indent(2), \"  \");\n    assert_eq!(do_indent(3), \"   \");\n    assert_eq!(do_indent(4), \"    \");\n  }\n\n  #[test]\n  fn test_identifier() {\n\n    // Valid\n    assert_eq!(is_valid_identifier(\"foo\"), true);\n    assert_eq!(is_valid_identifier(\"foo99\"), true);\n    assert_eq!(is_valid_identifier(\"FOO99\"), true);\n    assert_eq!(is_valid_identifier(\"_\"), true);\n    assert_eq!(is_valid_identifier(\"_a_b_c\"), true);\n    assert_eq!(is_valid_identifier(\"_00_00_\"), true);\n\n    // Invalid\n    assert_eq!(is_valid_identifier(\"\"), false);\n    assert_eq!(is_valid_identifier(\"0\"), false);\n    assert_eq!(is_valid_identifier(\"0.0\"), false);\n    assert_eq!(is_valid_identifier(\"a.b\"), false);\n    assert_eq!(is_valid_identifier(\"c/d\"), false);\n    assert_eq!(is_valid_identifier(\"-\"), false);\n\n  }\n\n  #[test]\n  fn test_path() {\n\n    // Valid\n    assert_eq!(is_valid_node_path(\"foo\"), true);\n    assert_eq!(is_valid_node_path(\"foo99\"), true);\n    assert_eq!(is_valid_node_path(\"_\"), true);\n    assert_eq!(is_valid_node_path(\"_a_b_c\"), true);\n    assert_eq!(is_valid_node_path(\"_00_00_\"), true);\n    assert_eq!(is_valid_node_path(\"a/b\"), true);\n    assert_eq!(is_valid_node_path(\"a/b/cdef\"), true);\n    assert_eq!(is_valid_node_path(\"A/B/C\"), true);\n\n    // Invalid\n    assert_eq!(is_valid_node_path(\"\"), false);\n    assert_eq!(is_valid_node_path(\"0\"), false);\n    assert_eq!(is_valid_node_path(\"0.0\"), false);\n    assert_eq!(is_valid_node_path(\"a.b\"), false);\n    assert_eq!(is_valid_node_path(\"c//d\"), false);\n    assert_eq!(is_valid_node_path(\"/a\"), false);\n    assert_eq!(is_valid_node_path(\"a/\"), false);\n    assert_eq!(is_valid_node_path(\"/\"), false);\n    assert_eq!(is_valid_node_path(\"/0\"), false);\n    assert_eq!(is_valid_node_path(\"0/\"), false);\n    assert_eq!(is_valid_node_path(\"foo/bar/0\"), false);\n    assert_eq!(is_valid_node_path(\"-\"), false);\n\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/op.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! All of the operators recognized by GDScript.\n\nuse serde::{Serialize, Deserialize};\n\n/// Unary operators.\n#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug, Serialize, Deserialize)]\npub enum UnaryOp {\n  BitNot, Negate, Not,\n}\n\n/// Binary operators.\n#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug, Serialize, Deserialize)]\npub enum BinaryOp {\n  Times, Div, Mod, Add, Sub, LShift, RShift, BitAnd, BitXor,\n  BitOr, LT, GT, Eq, NE, LE, GE, Is, In, And, Or, Cast,\n}\n\n/// Assignment operators.\n#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug, Serialize, Deserialize)]\npub enum AssignOp {\n  Eq, Add, Sub, Times, Div, Mod, BitAnd, BitOr,\n}\n\n/// Ternary operators.\n///\n/// There is only one ternary op in Godot: if conditionals. Hence,\n/// this struct is a singleton whose unique value represents ternary\n/// if conditions.\n#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]\npub struct TernaryOp;\n\n/// Information about an operator.\n#[derive(PartialEq, Eq, Clone, Hash, Debug)]\npub struct OperatorInfo {\n  /// The operator's precedence. Operators with higher precedence will\n  /// bind more tightly.\n  pub precedence: i32,\n  /// The operator's name, as a string.\n  pub name: &'static str,\n  /// How spaces should be applied around an operator.\n  pub padding: Padding,\n}\n\n/// This enum describes different mechanisms for providing padding\n/// around an operator.\n///\n/// There are some operators in GDScript that require spaces around\n/// them, such as `is`. There are many more that, while surrounding\n/// spaces are not required, tend to be more readable if padding is\n/// added. Finally, there are some (especially unary operators) which\n/// simply do not require spaces for readability.\n///\n/// An [operator's info](OperatorInfo) contains a [Padding] which\n/// describes how that operator would like to be spaced.\n#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Debug)]\npub enum Padding {\n  /// The operator is ambivalent to padding. It may be added if needed\n  /// for some other reason but is not required.\n  NotRequired,\n  /// The operator is more readable if spaces are added. It is not\n  /// incorrect to omit spaces, but they should be added unless there\n  /// is a good reason not to.\n  Preferred,\n  /// The operator requires spaces around it. Failing to include them\n  /// may result in incorrect code.\n  Required,\n}\n\nfn info(prec: i32, name: &'static str, padding: Padding) -> OperatorInfo {\n  OperatorInfo { precedence: prec, name: name, padding: padding }\n}\n\n/// All of the operator types in this module can produce an\n/// [OperatorInfo] value describing their properties. This trait\n/// describes any type which can produce such an [OperatorInfo].\npub trait OperatorHasInfo {\n  /// Produce [OperatorInfo].\n  fn op_info(&self) -> OperatorInfo;\n}\n\nimpl OperatorHasInfo for UnaryOp {\n  fn op_info(&self) -> OperatorInfo {\n    match self {\n      UnaryOp::BitNot => info(17, \"~\", Padding::NotRequired),\n      UnaryOp::Negate => info(16, \"-\", Padding::NotRequired),\n      UnaryOp::Not => info(6, \"!\", Padding::NotRequired),\n    }\n  }\n}\n\nimpl OperatorHasInfo for BinaryOp {\n  fn op_info(&self) -> OperatorInfo {\n    match self {\n      BinaryOp::Times => info(15, \"*\", Padding::Preferred),\n      BinaryOp::Div => info(15, \"/\", Padding::Preferred),\n      BinaryOp::Mod => info(15, \"%\", Padding::Preferred),\n      BinaryOp::Add => info(14, \"+\", Padding::Preferred),\n      BinaryOp::Sub => info(13, \"-\", Padding::Preferred),\n      BinaryOp::LShift => info(12, \"<<\", Padding::Preferred),\n      BinaryOp::RShift => info(12, \">>\", Padding::Preferred),\n      BinaryOp::BitAnd => info(11, \"&\", Padding::Preferred),\n      BinaryOp::BitXor => info(10, \"^\", Padding::Preferred),\n      BinaryOp::BitOr => info(9, \"|\", Padding::Preferred),\n      BinaryOp::LT => info(8, \"<\", Padding::Preferred),\n      BinaryOp::GT => info(8, \">\", Padding::Preferred),\n      BinaryOp::Eq => info(8, \"==\", Padding::Preferred),\n      BinaryOp::NE => info(8, \"!=\", Padding::Preferred),\n      BinaryOp::LE => info(8, \"<=\", Padding::Preferred),\n      BinaryOp::GE => info(8, \">=\", Padding::Preferred),\n      BinaryOp::Is => info(18, \"is\", Padding::Required),\n      BinaryOp::In => info(7, \"in\", Padding::Required),\n      BinaryOp::And => info(5, \"&&\", Padding::Preferred),\n      BinaryOp::Or => info(4, \"||\", Padding::Preferred),\n      BinaryOp::Cast => info(2, \"as\", Padding::Preferred),\n    }\n  }\n}\n\nimpl OperatorHasInfo for AssignOp {\n  fn op_info(&self) -> OperatorInfo {\n    let prec = 1; // All assignments are of the lowest precedence in GDScript\n    let pad = Padding::Preferred;\n    let name = match self {\n      AssignOp::Eq => \"=\",\n      AssignOp::Add => \"+=\",\n      AssignOp::Sub => \"-=\",\n      AssignOp::Times => \"*=\",\n      AssignOp::Div => \"/=\",\n      AssignOp::Mod => \"%=\",\n      AssignOp::BitAnd => \"&=\",\n      AssignOp::BitOr => \"|=\",\n    };\n    info(prec, name, pad)\n  }\n}\n\nimpl OperatorHasInfo for TernaryOp {\n  fn op_info(&self) -> OperatorInfo {\n    info(3, \"if-else\", Padding::Required)\n  }\n}\n"
  },
  {
    "path": "src/gdscript/pattern.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDScript patterns for use in pattern matching.\n//!\n//! This module defines a [datatype](Pattern) for representing\n//! patterns in the GDScript language, as well as [`Pattern::to_gd`]\n//! for converting to GDScript syntax.\n\nuse crate::gdscript::literal::Literal;\n\n/// The type of GDScript patterns.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Pattern {\n  Literal(Literal),\n  Var(String),\n  Wildcard,\n  BindingVar(String),\n  Array(Vec<Pattern>, Wildcard),\n  Dictionary(Vec<(Literal, Pattern)>, Wildcard),\n}\n\n/// This type is isomorphic to [`bool`] and indicates whether or not a\n/// wildcard was supplied to a pattern.\n#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]\npub enum Wildcard {\n  NoWildcard, Wildcard,\n}\n\nimpl Pattern {\n\n  /// Convert a GDScript pattern to a string. The result will contain\n  /// valid GDScript syntax.\n  pub fn to_gd(&self) -> String {\n    match self {\n      Pattern::Literal(lit) => lit.to_gd(),\n      Pattern::Var(s) => s.clone(),\n      Pattern::Wildcard => String::from(\"_\"), // TODO Make sure to handle the case of a variable called _, as that's technically weirdly ambiguous here.\n      Pattern::BindingVar(s) => format!(\"var {}\", s),\n      Pattern::Array(ptns, wild) => {\n        let mut inner = ptns.iter().map(|ptn| ptn.to_gd()).collect::<Vec<_>>();\n        if *wild == Wildcard::Wildcard {\n          inner.push(String::from(\"..\"));\n        }\n        format!(\"[{}]\", inner.join(\", \"))\n      },\n      Pattern::Dictionary(d, wild) => {\n        let mut inner = d.iter().map(|x| format!(\"{}: {}\", x.0.to_gd(), x.1.to_gd())).collect::<Vec<_>>();\n        if *wild == Wildcard::Wildcard {\n          inner.push(String::from(\"..\"));\n        }\n        format!(\"{{{}}}\", inner.join(\", \"))\n      },\n    }\n  }\n\n}\n\nimpl From<Wildcard> for bool {\n  fn from(w: Wildcard) -> bool {\n    w == Wildcard::Wildcard\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn atomic_patterns() {\n    assert_eq!(Pattern::Literal(Literal::Int(3)).to_gd(), \"3\");\n    assert_eq!(Pattern::Var(String::from(\"var_name\")).to_gd(), \"var_name\");\n    assert_eq!(Pattern::BindingVar(String::from(\"var_name\")).to_gd(), \"var var_name\");\n    assert_eq!(Pattern::Wildcard.to_gd(), \"_\");\n  }\n\n  #[test]\n  fn compound_patterns() {\n    let lit1 = Literal::Int(100);\n    let lit2 = Literal::Int(200);\n\n    let ptn1 = Pattern::Literal(Literal::Int(1));\n    let ptn2 = Pattern::Literal(Literal::Int(2));\n\n    assert_eq!(Pattern::Array(vec!(), Wildcard::NoWildcard).to_gd(), \"[]\");\n    assert_eq!(Pattern::Array(vec!(), Wildcard::Wildcard).to_gd(), \"[..]\");\n    assert_eq!(Pattern::Array(vec!(ptn1.clone()), Wildcard::NoWildcard).to_gd(), \"[1]\");\n    assert_eq!(Pattern::Array(vec!(ptn1.clone()), Wildcard::Wildcard).to_gd(), \"[1, ..]\");\n    assert_eq!(Pattern::Array(vec!(ptn1.clone(), ptn2.clone()), Wildcard::NoWildcard).to_gd(), \"[1, 2]\");\n    assert_eq!(Pattern::Array(vec!(ptn1.clone(), ptn2.clone()), Wildcard::Wildcard).to_gd(), \"[1, 2, ..]\");\n\n    assert_eq!(Pattern::Dictionary(vec!(), Wildcard::NoWildcard).to_gd(), \"{}\");\n    assert_eq!(Pattern::Dictionary(vec!(), Wildcard::Wildcard).to_gd(), \"{..}\");\n    assert_eq!(Pattern::Dictionary(vec!((lit1.clone(), ptn1.clone())), Wildcard::NoWildcard).to_gd(), \"{100: 1}\");\n    assert_eq!(Pattern::Dictionary(vec!((lit1.clone(), ptn1.clone())), Wildcard::Wildcard).to_gd(), \"{100: 1, ..}\");\n    assert_eq!(Pattern::Dictionary(vec!((lit1.clone(), ptn1.clone()), (lit2.clone(), ptn2.clone())), Wildcard::NoWildcard).to_gd(), \"{100: 1, 200: 2}\");\n    assert_eq!(Pattern::Dictionary(vec!((lit1.clone(), ptn1.clone()), (lit2.clone(), ptn2.clone())), Wildcard::Wildcard).to_gd(), \"{100: 1, 200: 2, ..}\");\n\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/spacing.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helper type for printing out [`Decl`](super::decl::Decl) instances\n//! with good spacing.\n\nuse crate::util::group_by::group_by;\nuse super::decl::{Decl, DeclF};\n\nuse std::fmt::{self, Write};\n\n#[derive(Debug)]\npub struct SpacedDeclPrinter {\n  spacing: u32,\n  indentation: u32,\n}\n\nimpl SpacedDeclPrinter {\n\n  /// A default [`SpacedDeclPrinter`], suitable for top-level\n  /// pretty-printing.\n  pub fn new() -> Self {\n    Self::default()\n  }\n\n  /// Builder method which sets the spacing between non-grouped items\n  /// in the declaration list.\n  pub fn with_spacing(mut self, spacing: u32) -> Self {\n    self.spacing = spacing;\n    self\n  }\n\n  /// Builder method which sets the base indentation level for the\n  /// declarations.\n  pub fn with_indentation(mut self, indentation: u32) -> Self {\n    self.indentation = indentation;\n    self\n  }\n\n  /// Write the sequence of declarations, in order, inserting extra\n  /// newlines where it makes sense for readability.\n  pub fn write_gd<'a, W: Write, I: Iterator<Item=&'a Decl>>(&self, w: &mut W, iter: I) -> Result<(), fmt::Error> {\n    let mut first = true;\n    for group in group_by(iter, |a, b| group_predicate(a, b)) {\n      if !first {\n        write_newlines(w, self.spacing)?;\n      }\n      first = false;\n      for decl in group {\n        decl.write_gd(w, self.indentation)?;\n      }\n    }\n    Ok(())\n  }\n\n}\n\n/// Given two adjacent declarations, determine if they can be grouped.\n/// Two declarations can be grouped together if and only if both of\n/// the following are true: (a) The two declarations are of the same\n/// type, and (b) Both declarations are considered \"short\"\n/// declarations.\n///\n/// The following declarations are considered \"short\": Variables,\n/// constants, signals, and `pass`.\nfn group_predicate(a: &Decl, b: &Decl) -> bool {\n  #[allow(clippy::match_like_matches_macro)] // Looks much cleaner this way\n  match (&a.value, &b.value) {\n    (DeclF::VarDecl(_), DeclF::VarDecl(_)) => true,\n    (DeclF::ConstDecl(_, _), DeclF::ConstDecl(_, _)) => true,\n    (DeclF::SignalDecl(_, _), DeclF::SignalDecl(_, _)) => true,\n    (DeclF::PassDecl, DeclF::PassDecl) => true,\n    _ => false,\n  }\n}\n\n/// Writes the given number of newlines to the `Write` object.\nfn write_newlines(w: &mut impl Write, count: u32) -> Result<(), fmt::Error> {\n  let newlines = \"\\n\".repeat(count as usize);\n  write!(w, \"{}\", newlines)\n}\n\nimpl Default for SpacedDeclPrinter {\n\n  fn default() -> Self {\n    SpacedDeclPrinter {\n      spacing: 2,\n      indentation: 0,\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/gdscript/stmt.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDScript statements.\n//!\n//! This module defines a [datatype](Stmt) for representing statements\n//! in the GDScript language, as well as [`Stmt::write_gd`] for\n//! writing statements as GDScript syntax to a [`fmt::Write`]\n//! instance.\n\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::op::{self, AssignOp, OperatorHasInfo};\nuse crate::gdscript::pattern::Pattern;\nuse crate::gdscript::indent;\nuse crate::pipeline::source::{SourceOffset, Sourced};\n\nuse std::fmt;\n\n/// The type of GDScript statements.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum StmtF {\n  /// An expression alone can stand as a statement.\n  Expr(Expr),\n  IfStmt(IfStmt),\n  ForLoop(ForLoop),\n  WhileLoop(WhileLoop),\n  PassStmt,\n  BreakStmt,\n  ContinueStmt,\n  MatchStmt(Expr, Vec<(Pattern, Vec<Stmt>)>),\n  /// A variable declaration. Note that while GDScript allows variable\n  /// declarations which do *not* assign a value, here we require that\n  /// all declarations assign an initial value.\n  VarDecl(String, Expr),\n  ReturnStmt(Expr),\n  /// An assignment. Note that GDScript restricts what can appear on\n  /// the left-hand side of an assignment, whereas this type allows\n  /// any [`Expr`]. Care must be taken to ensure that the left-hand\n  /// side is syntactically valid.\n  Assign(Box<Expr>, AssignOp, Box<Expr>),\n}\n\n/// GDScript statement with its source offset. See [`Sourced`].\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Stmt {\n  pub value: StmtF,\n  pub pos: SourceOffset,\n}\n\n/// The type of if statements, used in [`StmtF::IfStmt`].\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct IfStmt {\n  pub if_clause: (Expr, Vec<Stmt>),\n  pub elif_clauses: Vec<(Expr, Vec<Stmt>)>,\n  pub else_clause: Option<Vec<Stmt>>,\n}\n\n/// The type of for loops, used in [`StmtF::ForLoop`].\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ForLoop {\n  pub iter_var: String,\n  pub collection: Expr,\n  pub body: Vec<Stmt>,\n}\n\n/// The type of while loops, used in [`StmtF::WhileLoop`].\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct WhileLoop {\n  pub condition: Expr,\n  pub body: Vec<Stmt>,\n}\n\n/// Construct an `if` statement with no `elif` or `else` branches.\npub fn if_then(cond: Expr, true_branch: Vec<Stmt>, pos: SourceOffset) -> Stmt {\n  Stmt::new(\n    StmtF::IfStmt(IfStmt {\n      if_clause: (cond, true_branch),\n      elif_clauses: vec!(),\n      else_clause: None,\n    }),\n    pos,\n  )\n}\n\n/// Construct an `if` statement with an `else` branch but no `elif`\n/// branches.\npub fn if_else(cond: Expr, true_branch: Vec<Stmt>, false_branch: Vec<Stmt>, pos: SourceOffset) -> Stmt {\n  Stmt::new(\n    StmtF::IfStmt(IfStmt {\n      if_clause: (cond, true_branch),\n      elif_clauses: vec!(),\n      else_clause: Some(false_branch),\n    }),\n    pos,\n  )\n}\n\n/// General-purpose `if` statement constructor.\n///\n/// Construct a sequence of statements which represents the branches.\n/// The first element of `cases` will be the `if_clause` of the\n/// statement, all remaining `cases` will be `elif_clauses`, and the\n/// `default` shall be `else_clause`.\n///\n/// As a special corner case, if `cases` is empty, then `default` is\n/// returned unmodified. This is the only case in which more than one\n/// value might be returned. If `cases` is nonempty, then\n/// `if_branches` will always return a vector containing a single\n/// statement.\npub fn if_branches(cases: Vec<(Expr, Vec<Stmt>)>, default: Vec<Stmt>, pos: SourceOffset) -> Vec<Stmt> {\n  if cases.is_empty() {\n    default\n  } else {\n    let if_clause = cases[0].clone();\n    let elif_clauses = cases[1..].to_vec();\n    vec!(\n      Stmt::new(\n        StmtF::IfStmt(IfStmt {\n          if_clause,\n          elif_clauses,\n          else_clause: Some(default),\n        }),\n        pos,\n      )\n    )\n  }\n}\n\nimpl Stmt {\n\n  /// A new `Stmt` at the given position in the source.\n  pub fn new(value: StmtF, pos: SourceOffset) -> Stmt {\n    Stmt { value, pos }\n  }\n\n  /// An expression, as a statement.\n  pub fn expr(expr: Expr) -> Stmt {\n    let pos = expr.pos;\n    Stmt::new(StmtF::Expr(expr), pos)\n  }\n\n  /// A return statement.\n  pub fn return_stmt(expr: Expr, pos: SourceOffset) -> Stmt {\n    Stmt::new(StmtF::ReturnStmt(expr), pos)\n  }\n\n  /// Simple assignment to a given target.\n  pub fn simple_assign(lhs: Expr, rhs: Expr, pos: SourceOffset) -> Stmt {\n    Stmt::new(StmtF::Assign(Box::new(lhs), AssignOp::Eq, Box::new(rhs)), pos)\n  }\n\n  /// Declaration of a new variable.\n  pub fn var_decl(var_name: String, value: Expr, pos: SourceOffset) -> Stmt {\n    Stmt::new(StmtF::VarDecl(var_name, value), pos)\n  }\n\n  /// A `break` statement.\n  pub fn break_stmt(pos: SourceOffset) -> Stmt {\n    Stmt::new(StmtF::BreakStmt, pos)\n  }\n\n  /// A `continue` statement.\n  pub fn continue_stmt(pos: SourceOffset) -> Stmt {\n    Stmt::new(StmtF::ContinueStmt, pos)\n  }\n\n  /// A `pass` statement.\n  pub fn pass_stmt(pos: SourceOffset) -> Stmt {\n    Stmt::new(StmtF::PassStmt, pos)\n  }\n\n  /// Write the statement, as GDScript code, to the [`fmt::Write`]\n  /// instance `w`.\n  ///\n  /// We are assumed to be at the indentation level `ind`, so that all\n  /// lines in the result will be indented to that level.\n  ///\n  /// The writer `w` should currently be either empty or immediately\n  /// after a newline. The statement will always end by printing a\n  /// newline, making it suitable for writing a subsequent statement\n  /// immediately after.\n  pub fn write_gd<W : fmt::Write>(&self, w: &mut W, ind: u32) -> Result<(), fmt::Error> {\n    indent(w, ind)?;\n    match &self.value {\n      StmtF::Expr(expr) => {\n        writeln!(w, \"{}\", expr.to_gd())\n      }\n      StmtF::IfStmt(IfStmt { if_clause, elif_clauses, else_clause }) => {\n        writeln!(w, \"if {}:\", if_clause.0.to_gd())?;\n        Stmt::write_gd_stmts(&if_clause.1, w, ind + 4)?;\n        for clause in elif_clauses {\n          indent(w, ind)?;\n          writeln!(w, \"elif {}:\", clause.0.to_gd())?;\n          Stmt::write_gd_stmts(&clause.1, w, ind + 4)?;\n        }\n        if let Some(else_clause) = else_clause {\n          indent(w, ind)?;\n          writeln!(w, \"else:\")?;\n          Stmt::write_gd_stmts(else_clause, w, ind + 4)?;\n        }\n        Ok(())\n      }\n      StmtF::PassStmt => writeln!(w, \"pass\"),\n      StmtF::BreakStmt => writeln!(w, \"break\"),\n      StmtF::ContinueStmt => writeln!(w, \"continue\"),\n      StmtF::ForLoop(ForLoop { iter_var, collection, body }) => {\n        writeln!(w, \"for {} in {}:\", iter_var, collection.to_gd())?;\n        Stmt::write_gd_stmts(body, w, ind + 4)\n      }\n      StmtF::WhileLoop(WhileLoop { condition, body }) => {\n        writeln!(w, \"while {}:\", condition.to_gd())?;\n        Stmt::write_gd_stmts(body, w, ind + 4)\n      }\n      StmtF::MatchStmt(expr, clauses) => {\n        writeln!(w, \"match {}:\", expr.to_gd())?;\n        if clauses.is_empty() {\n          // If you try to have an empty match body, you kinda deserve\n          // the program to crash. But hey, I'm in a good mood, so\n          // I'll handle the wonky corner case. :)\n          indent(w, ind + 4)?;\n          writeln!(w, \"{}:\", Pattern::Wildcard.to_gd())?;\n          Stmt::write_gd_stmts(vec!(), w, ind + 8)\n        } else {\n          for (ptn, body) in clauses {\n            indent(w, ind + 4)?;\n            writeln!(w, \"{}:\", ptn.to_gd())?;\n            Stmt::write_gd_stmts(body, w, ind + 8)?;\n          }\n          Ok(())\n        }\n      }\n      StmtF::VarDecl(name, expr) => {\n        writeln!(w, \"var {} = {}\", name, expr.to_gd())\n      }\n      StmtF::ReturnStmt(expr) => {\n        writeln!(w, \"return {}\", expr.to_gd())\n      }\n      StmtF::Assign(lhs, op, rhs) => {\n        let info = op.op_info();\n        let lhs = lhs.to_gd();\n        let rhs = rhs.to_gd();\n        if info.padding == op::Padding::NotRequired {\n          writeln!(w, \"{}{}{}\", lhs, info.name, rhs)\n        } else {\n          writeln!(w, \"{} {} {}\", lhs, info.name, rhs)\n        }\n      },\n    }\n  }\n\n  /// Write several statements in sequence, using [`Stmt::write_gd`].\n  ///\n  /// If `iter` is empty, then `\"pass\\n\"` will be written.\n  pub fn write_gd_stmts<'a, W, I>(iter: I, w: &mut W, ind: u32) -> Result<(), fmt::Error>\n  where W : fmt::Write,\n        I : IntoIterator<Item = &'a Stmt> {\n    let mut empty = true;\n    for stmt in iter {\n      stmt.write_gd(w, ind)?;\n      empty = false;\n    }\n    if empty {\n      Stmt::new(StmtF::PassStmt, SourceOffset::default()).write_gd(w, ind)?;\n    }\n    Ok(())\n  }\n\n  /// Write the statement to a string, using [`Stmt::write_gd`].\n  ///\n  /// # Panics\n  ///\n  /// This function panics if there is a write error to the string. If\n  /// you wish to handle that case yourself, use [`Stmt::write_gd`]\n  /// explicitly.\n  pub fn to_gd(&self, ind: u32) -> String {\n    let mut string = String::new();\n    self.write_gd(&mut string, ind).expect(\"Could not write to string in Stmt::to_gd\");\n    string\n  }\n\n}\n\nimpl Sourced for Stmt {\n  type Item = StmtF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &StmtF {\n    &self.value\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::expr::{Expr, ExprF};\n  use crate::gdscript::literal::Literal;\n  use crate::pipeline::source::SourceOffset;\n\n  fn assign(a: &Expr, op: AssignOp, b: &Expr) -> Stmt {\n    s(StmtF::Assign(Box::new(a.clone()), op, Box::new(b.clone())))\n  }\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  #[test]\n  fn expr_stmt() {\n    assert_eq!(Stmt::expr(e(ExprF::Var(String::from(\"foobar\")))).to_gd(0), \"foobar\\n\");\n    assert_eq!(Stmt::expr(e(ExprF::from(9))).to_gd(0), \"9\\n\");\n  }\n\n  #[test]\n  fn basic_indent() {\n    assert_eq!(Stmt::expr(e(ExprF::Var(String::from(\"foobar\")))).to_gd(0), \"foobar\\n\");\n    assert_eq!(Stmt::expr(e(ExprF::Var(String::from(\"foobar\")))).to_gd(4), \"    foobar\\n\");\n    assert_eq!(Stmt::expr(e(ExprF::Var(String::from(\"foobar\")))).to_gd(8), \"        foobar\\n\");\n  }\n\n  #[test]\n  fn simple_stmts() {\n    let expr = e(ExprF::from(1000));\n    assert_eq!(s(StmtF::VarDecl(String::from(\"var_name\"), expr.clone())).to_gd(0), \"var var_name = 1000\\n\");\n    assert_eq!(s(StmtF::ReturnStmt(expr.clone())).to_gd(0), \"return 1000\\n\");\n  }\n\n  #[test]\n  fn if_stmt() {\n    let cond1 = e(ExprF::Var(String::from(\"condition1\")));\n    let cond2 = e(ExprF::Var(String::from(\"condition2\")));\n    let cond3 = e(ExprF::Var(String::from(\"condition3\")));\n\n    let stmt1 = Stmt::expr(e(ExprF::from(1)));\n    let stmt2 = Stmt::expr(e(ExprF::from(2)));\n    let stmt3 = Stmt::expr(e(ExprF::from(3)));\n    let stmt4 = Stmt::expr(e(ExprF::from(4)));\n    let stmt5 = Stmt::expr(e(ExprF::from(5)));\n\n    let if1 = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!()),\n      elif_clauses: vec!(),\n      else_clause: None\n    }));\n    assert_eq!(if1.to_gd(0), \"if condition1:\\n    pass\\n\");\n\n    let if2 = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!(stmt1.clone())),\n      elif_clauses: vec!(),\n      else_clause: None\n    }));\n    assert_eq!(if2.to_gd(0), \"if condition1:\\n    1\\n\");\n\n    let if3 = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!(stmt1.clone(), stmt2.clone())),\n      elif_clauses: vec!(),\n      else_clause: None\n    }));\n    assert_eq!(if3.to_gd(0), \"if condition1:\\n    1\\n    2\\n\");\n\n    let if4 = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!(stmt1.clone(), stmt2.clone())),\n      elif_clauses: vec!(),\n      else_clause: Some(vec!())\n    }));\n    assert_eq!(if4.to_gd(0), \"if condition1:\\n    1\\n    2\\nelse:\\n    pass\\n\");\n\n    let if5 = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!(stmt1.clone(), stmt2.clone())),\n      elif_clauses: vec!(),\n      else_clause: Some(vec!(stmt3.clone()))\n    }));\n    assert_eq!(if5.to_gd(0), \"if condition1:\\n    1\\n    2\\nelse:\\n    3\\n\");\n\n    let if6 = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!(stmt1.clone(), stmt2.clone())),\n      elif_clauses: vec!((cond2.clone(), vec!(stmt3.clone()))),\n      else_clause: Some(vec!(stmt4.clone()))\n    }));\n    assert_eq!(if6.to_gd(0), \"if condition1:\\n    1\\n    2\\nelif condition2:\\n    3\\nelse:\\n    4\\n\");\n\n    let if7 = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!(stmt1.clone(), stmt2.clone())),\n      elif_clauses: vec!((cond2.clone(), vec!(stmt3.clone())), (cond3.clone(), vec!(stmt4.clone()))),\n      else_clause: Some(vec!(stmt5.clone()))\n    }));\n    assert_eq!(if7.to_gd(0), \"if condition1:\\n    1\\n    2\\nelif condition2:\\n    3\\nelif condition3:\\n    4\\nelse:\\n    5\\n\");\n\n  }\n\n  #[test]\n  fn nested_if() {\n    let cond1 = e(ExprF::Var(String::from(\"condition1\")));\n    let cond2 = e(ExprF::Var(String::from(\"condition2\")));\n\n    let stmt1 = Stmt::expr(e(ExprF::from(1)));\n    let stmt2 = Stmt::expr(e(ExprF::from(2)));\n    let stmt3 = Stmt::expr(e(ExprF::from(3)));\n    let stmt4 = Stmt::expr(e(ExprF::from(4)));\n\n    let inner = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond2.clone(), vec!(stmt2.clone(), stmt3.clone())),\n      elif_clauses: vec!(),\n      else_clause: None,\n    }));\n    let outer = s(StmtF::IfStmt(IfStmt {\n      if_clause: (cond1.clone(), vec!(stmt1.clone(), inner, stmt4.clone())),\n      elif_clauses: vec!(),\n      else_clause: None,\n    }));\n    assert_eq!(outer.to_gd(0), \"if condition1:\\n    1\\n    if condition2:\\n        2\\n        3\\n    4\\n\");\n\n  }\n\n  #[test]\n  fn for_loop() {\n    let expr = e(ExprF::Var(String::from(\"collection\")));\n    let stmt = Stmt::expr(e(ExprF::from(1)));\n\n    let for1 = s(StmtF::ForLoop(ForLoop {\n      iter_var: String::from(\"i\"),\n      collection: expr.clone(),\n      body: vec!(),\n    }));\n    assert_eq!(for1.to_gd(0), \"for i in collection:\\n    pass\\n\");\n\n    let for2 = s(StmtF::ForLoop(ForLoop {\n      iter_var: String::from(\"i\"),\n      collection: expr.clone(),\n      body: vec!(stmt.clone()),\n    }));\n    assert_eq!(for2.to_gd(0), \"for i in collection:\\n    1\\n\");\n\n  }\n\n  #[test]\n  fn while_loop() {\n    let expr = e(ExprF::Var(String::from(\"condition\")));\n    let stmt = Stmt::expr(e(ExprF::from(1)));\n\n    let while1 = s(StmtF::WhileLoop(WhileLoop {\n      condition: expr.clone(),\n      body: vec!(),\n    }));\n    assert_eq!(while1.to_gd(0), \"while condition:\\n    pass\\n\");\n\n    let while2 = s(StmtF::WhileLoop(WhileLoop {\n      condition: expr.clone(),\n      body: vec!(stmt.clone()),\n    }));\n    assert_eq!(while2.to_gd(0), \"while condition:\\n    1\\n\");\n\n  }\n\n  #[test]\n  fn match_stmt() {\n    let expr = e(ExprF::Var(String::from(\"expr\")));\n\n    let ptn1 = Pattern::Literal(Literal::Int(100));\n    let ptn2 = Pattern::Literal(Literal::Int(200));\n\n    let body1 = Stmt::expr(e(ExprF::Var(String::from(\"body1\"))));\n    let body2 = Stmt::expr(e(ExprF::Var(String::from(\"body2\"))));\n\n    let match1 = s(StmtF::MatchStmt(expr.clone(), vec!()));\n    assert_eq!(match1.to_gd(0), \"match expr:\\n    _:\\n        pass\\n\");\n\n    let match2 = s(StmtF::MatchStmt(expr.clone(), vec!((ptn1.clone(), vec!(body1.clone())))));\n    assert_eq!(match2.to_gd(0), \"match expr:\\n    100:\\n        body1\\n\");\n\n    let match3 = s(StmtF::MatchStmt(expr.clone(), vec!((ptn1.clone(), vec!(body1.clone())),\n                                                      (ptn2.clone(), vec!(body2.clone())))));\n    assert_eq!(match3.to_gd(0), \"match expr:\\n    100:\\n        body1\\n    200:\\n        body2\\n\");\n\n  }\n\n  #[test]\n  fn assign_ops() {\n    let a = e(ExprF::Var(String::from(\"a\")));\n    let b = e(ExprF::Var(String::from(\"b\")));\n    assert_eq!(assign(&a, AssignOp::Eq, &b).to_gd(0), \"a = b\\n\");\n    assert_eq!(assign(&a, AssignOp::Add, &b).to_gd(0), \"a += b\\n\");\n    assert_eq!(assign(&a, AssignOp::Sub, &b).to_gd(0), \"a -= b\\n\");\n    assert_eq!(assign(&a, AssignOp::Times, &b).to_gd(0), \"a *= b\\n\");\n    assert_eq!(assign(&a, AssignOp::Div, &b).to_gd(0), \"a /= b\\n\");\n    assert_eq!(assign(&a, AssignOp::Mod, &b).to_gd(0), \"a %= b\\n\");\n    assert_eq!(assign(&a, AssignOp::BitAnd, &b).to_gd(0), \"a &= b\\n\");\n    assert_eq!(assign(&a, AssignOp::BitOr, &b).to_gd(0), \"a |= b\\n\");\n  }\n\n}\n"
  },
  {
    "path": "src/graph/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Functions for working with\n//! [graphs](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)).\n//!\n//! The [`Graph`] datatype provides the core functionality for\n//! representing graphs.\n\npub mod top_sort;\npub mod tarjan;\n\nuse std::collections::HashMap;\nuse std::borrow::Borrow;\nuse std::hash::Hash;\n\n// TODO Really, we should be storing the edge sets with some kind of\n// numerical index, rather than assuming the type T can be safely\n// cloned.\n\n/// A graph consists of nodes and edges. Here, we represent directed\n/// unlabeled multigraphs with loopback edges.\n///\n/// That is, every edge has a head and a tail, there can be multiple\n/// edges with the same head and tail, and an edge can point from a\n/// node to itself.\n///\n/// The type `T` of nodes must be [`Eq`] and [`Hash`] in order to\n/// perform any of the `Graph` operations on it. Generally, you will\n/// want it to have a sane [`Clone`] implementation as well, since a\n/// given node will need to be stored several times within the data\n/// structure.\n#[derive(Debug, Clone)]\npub struct Graph<T> {\n  edges: HashMap<T, Vec<T>>,\n}\n\nimpl<T> Graph<T> where T : Eq + Hash {\n\n  /// Given an iterator of edges, produce a [`Graph`]. Each element of\n  /// the iterator shall be a pair, where the first term of the pair\n  /// is the head of the edge and the second is the tail.\n  pub fn from_edges<I>(iter: I) -> Graph<T>\n  where T : Clone,\n        I : Iterator<Item=(T, T)> {\n    let mut edges = HashMap::new();\n    for (x, y) in iter {\n      // Make sure the other node is present too.\n      if !edges.contains_key(&y) {\n        edges.insert(y.clone(), Vec::new());\n      }\n      // Then put an entry for the edge\n      let vec = edges.entry(x).or_insert_with(Vec::new);\n      vec.push(y);\n    }\n    Graph { edges }\n  }\n\n  /// Produce an [empty\n  /// graph](https://en.wikipedia.org/wiki/Empty_graph) containing the\n  /// given node set.\n  ///\n  /// In the case of duplicate nodes, only the first will be kept.\n  pub fn from_nodes<I>(iter: I) -> Graph<T>\n  where I : Iterator<Item=T> {\n    Graph { edges: iter.map(|x| (x, Vec::new())).collect() }\n  }\n\n  /// Produce an empty graph with no edges or nodes.\n  pub fn new() -> Graph<T> {\n    Graph::default()\n  }\n\n  /// An iterator over the nodes of the graph, in an unspecified\n  /// order.\n  pub fn nodes(&self) -> impl Iterator<Item=&T> {\n    self.edges.keys()\n  }\n\n  /// Given a value `x`, get a reference to the unique node in the\n  /// graph with is equal to `x`. If there is no such node, returns\n  /// [`None`].\n  pub fn get_node<U>(&self, x: &U) -> Option<&T> where U : Borrow<T> {\n    self.edges.get_key_value(x.borrow()).map(|(k, _)| k)\n  }\n\n  /// Returns whether or not the graph contains a node equal to `x`.\n  pub fn has_node<U>(&self, x: &U) -> bool where U : Borrow<T> {\n    self.get_node(x).is_some()\n  }\n\n  /// An iterator over all edges of the graph, in an unspecified\n  /// order.\n  pub fn all_edges(&self) -> impl Iterator<Item=(&T, &T)> {\n    self.edges.iter().flat_map(|(x, ys)| ys.iter().map(move |y| (x, y)))\n  }\n\n  /// A vector over all of the outgoing edges from a given node, in an\n  /// unspecified order. Returns [`None`] if the node does not exist.\n  pub fn outgoing_edges(&self, node: impl Borrow<T>) -> Option<&Vec<T>> {\n    self.edges.get(node.borrow())\n  }\n\n  /// Add a new node to the graph. If there is already a node equal to\n  /// `node` in the graph, this method does nothing.\n  pub fn add_node(&mut self, node: T) {\n    self.edges.entry(node).or_insert_with(Vec::new);\n  }\n\n  /// Add a new edge to the graph. Both `x` and `y` should already be\n  /// in the graph, or the result is undefined.\n  pub fn add_edge(&mut self, x: T, y: T) {\n    let entry = self.edges.entry(x).or_insert_with(Vec::new);\n    entry.push(y);\n  }\n\n  /// Add a new edge in the graph. If there is already an edge from\n  /// `x` to `y`, do not add another. Both `x` and `y` should already\n  /// be in the graph, or the result is undefined.\n  pub fn add_edge_no_dup(&mut self, x: T, y: T) {\n    if !self.has_edge(&x, &y) {\n      self.add_edge(x, y);\n    }\n  }\n\n  /// Check whether the graph has an edge from `x` to `y`.\n  pub fn has_edge<U, V>(&self, x: &U, y: &V) -> bool where U : Borrow<T>, V : Borrow<T> {\n    match self.outgoing_edges(x.borrow()) {\n      None => false,\n      Some(vec) => vec.iter().any(|node| node == y.borrow()),\n    }\n  }\n\n  /// Remove one edge from the graph pointing from `x` to `y`. If no\n  /// such edge exists, then the graph is unchanged.\n  pub fn remove_edge<U, V>(&mut self, x: &U, y: &V) where U : Borrow<T>, V : Borrow<T> {\n    if let Some(vec) = self.edges.get_mut(x.borrow()) {\n      if let Some(pos) = vec.iter().position(|node| node == y.borrow()) {\n        vec.swap_remove(pos);\n      }\n    }\n  }\n\n  /// The total number of nodes in the graph.\n  pub fn node_count(&self) -> usize {\n    self.edges.len()\n  }\n\n  /// The graph with all edges flipped. The resulting graph will have\n  /// the same nodes as `self`, but every edge will have its head and\n  /// tail swapped.\n  pub fn transpose(&self) -> Graph<T>\n  where T : Clone {\n    let mut graph = Graph::from_nodes(self.nodes().cloned());\n    for (x, y) in self.all_edges() {\n      graph.add_edge(y.clone(), x.clone());\n    }\n    graph\n  }\n\n}\n\nimpl<T> Default for Graph<T> {\n\n  fn default() -> Self {\n    Graph { edges: HashMap::default() }\n  }\n\n}\n"
  },
  {
    "path": "src/graph/tarjan.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Implementation of [Tarjan's SCC Algorithm](https://en.wikipedia.org/wiki/Tarjan%E2%80%99s_strongly_connected_components_algorithm)\n//!\n//! Given a graph, we say that a nonempty set of nodes is a *strongly\n//! connected component* if, given any `u` and `v` in that set, there\n//! is a path in the graph from `u` to `v`. This module implements an\n//! algorithm to find all strongly connected components in a graph.\n\nuse super::*;\n\nuse std::collections::HashMap;\nuse std::collections::HashSet;\nuse std::cmp::min;\nuse std::mem::swap;\n\n/// A strongly connected component is a nonempty set of nodes.\n#[derive(Clone, Debug)]\npub struct SCC<'a, T>(pub HashSet<&'a T>);\n\n/// The result type of [`find_scc`].\n#[derive(Clone, Debug)]\npub struct SCCSummary<'a, T> {\n  sccs: Vec<SCC<'a, T>>,\n  scc_lookup: HashMap<&'a T, usize>,\n}\n\nstruct Algorithm<'a, T> {\n  graph: &'a Graph<T>,\n  sccs: Vec<SCC<'a, T>>,\n  indices: HashMap<&'a T, usize>,\n  lowlinks: HashMap<&'a T, usize>,\n  index: usize,\n  stack: Vec<&'a T>,\n  current_scc: SCC<'a, T>,\n}\n\nimpl<'a, T> PartialEq for SCC<'a, T> where T : Eq + Hash {\n  fn eq(&self, other: &Self) -> bool {\n    self.0 == other.0\n  }\n}\n\nimpl<'a, T> Default for SCC<'a, T> {\n  fn default() -> Self {\n    SCC(HashSet::new())\n  }\n}\n\nimpl<'a, T> SCCSummary<'a, T>\nwhere T : Eq + Hash {\n\n  /// Given an ID value, get the strongly connected component\n  /// associated to that ID.\n  ///\n  /// The ID values are integers from 0 up to (and excluding) the\n  /// total number of SCCs. The order of ID values is unspecified.\n  /// Given a node, the relevant ID value for the SCC can be obtained\n  /// from [`SCCSummary::get_scc_id`], which this function serves as a spiritual\n  /// inverse to.\n  pub fn get_scc_by_id(&self, id: usize) -> Option<&SCC<'a, T>> {\n    self.sccs.get(id)\n  }\n\n  /// Given a node, get the ID value of the SCC containing that node.\n  pub fn get_scc_id(&self, node: &'a T) -> Option<usize> {\n    self.scc_lookup.get(node).copied()\n  }\n\n  /// Given a node, get the SCC containing that node.\n  pub fn get_scc(&self, node: &'a T) -> Option<&SCC<'a, T>> {\n    self.scc_lookup.get(node).map(|idx| &self.sccs[*idx])\n  }\n\n  /// Return whether two nodes are in the same SCC or not.\n  ///\n  /// If either node is not in the graph, returns false.\n  pub fn is_in_same(&self, a: &'a T, b: &'a T) -> bool {\n    match self.get_scc(a) {\n      None => false,\n      Some(SCC(nodes)) => nodes.contains(b),\n    }\n  }\n\n  /// Return the total number of SCCs.\n  ///\n  /// The valid ID values for [`SCCSummary::get_scc_by_id`] range from 0 up to\n  /// (and excluding) [`SCCSummary::count()`].\n  pub fn count(&self) -> usize {\n    self.sccs.len()\n  }\n\n}\n\nimpl<'a, T> Algorithm<'a, T>\nwhere T : Eq + Hash {\n\n  fn new(graph: &'a Graph<T>) -> Algorithm<'a, T> {\n    Algorithm {\n      graph: graph,\n      sccs: Vec::new(),\n      indices: HashMap::new(),\n      lowlinks: HashMap::new(),\n      index: 0,\n      stack: Vec::new(),\n      current_scc: SCC::default(),\n    }\n  }\n\n  fn strongconnect(&mut self, v: &'a T) {\n    self.indices.insert(v, self.index);\n    self.lowlinks.insert(v, self.index);\n    self.index += 1;\n    self.stack.push(v);\n    for w in self.graph.outgoing_edges(v).expect(\"Node not found\") {\n      if !self.indices.contains_key(w) {\n        self.strongconnect(w);\n        let a = *self.lowlinks.get(w).expect(\"No lowlink for w\");\n        let b = *self.lowlinks.get(v).expect(\"No lowlink for v\");\n        self.lowlinks.insert(v, min(a, b));\n      } else if self.stack.iter().any(|x| x == &w) {\n        let a = *self.indices.get(w).expect(\"No index for w\");\n        let b = *self.lowlinks.get(v).expect(\"No lowlink for v\");\n        self.lowlinks.insert(v, min(a, b));\n      }\n    }\n    if self.indices.get(v) == self.lowlinks.get(v) {\n      while {\n        let w = self.stack.pop().expect(\"Ran out of stack in strongconnect\");\n        self.current_scc.0.insert(w);\n        w != v\n      } {}\n      let mut curr = SCC::default();\n      swap(&mut curr, &mut self.current_scc);\n      self.sccs.push(curr);\n    }\n  }\n\n}\n\nimpl<'a, T> From<Algorithm<'a, T>> for SCCSummary<'a, T>\nwhere T : Eq + Hash {\n\n  fn from(alg: Algorithm<'a, T>) -> SCCSummary<'a, T> {\n    let sccs = alg.sccs;\n    let mut scc_lookup = HashMap::new();\n    for (idx, SCC(nodes)) in sccs.iter().enumerate() {\n      for node in nodes {\n        scc_lookup.insert(*node, idx);\n      }\n    }\n    SCCSummary { sccs, scc_lookup }\n  }\n\n}\n\n/// Given a graph, identify all of its strongly connected components.\npub fn find_scc<T>(graph: &Graph<T>) -> SCCSummary<'_, T>\nwhere T : Eq + Hash {\n  let mut alg = Algorithm::new(graph);\n  for v in graph.nodes() {\n    if !alg.indices.contains_key(v) {\n      alg.strongconnect(v);\n    }\n  }\n  alg.into()\n}\n\n/// Given a graph, produce a graph where the nodes are SCCs.\n///\n/// The `summary` argument must be equivalent to `find_scc(graph)`.\n///\n/// The resulting graph contains nodes from 0 up to (and excluding)\n/// `summary.count()`. The graph contains no loopback edges and no\n/// duplicate edges. Given two SCCs `u` and `v`, the graph has an edge\n/// from `u` to `v` iff there is a path from some node in `u` to some\n/// node in `v`.\npub fn build_scc_graph<'a, 'b, T>(graph: &'a Graph<T>, summary: &'b SCCSummary<'a, T>) -> Graph<usize>\nwhere T : Eq + Hash {\n  let mut new_graph = Graph::from_nodes(0..summary.count());\n  for (x, y) in graph.all_edges() {\n    let xscc = summary.get_scc_id(x).expect(\"Node not found in SCCSummary\");\n    let yscc = summary.get_scc_id(y).expect(\"Node not found in SCCSummary\");\n    if xscc != yscc {\n      new_graph.add_edge_no_dup(xscc, yscc);\n    }\n  }\n  new_graph\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn scc_test_isolated() {\n    let graph = Graph::from_nodes(vec!(1, 2, 4, 3).into_iter());\n    let result = find_scc(&graph);\n    assert_eq!(result.count(), 4);\n    assert!(!result.is_in_same(&1, &2));\n    assert!(!result.is_in_same(&1, &3));\n    assert!(!result.is_in_same(&1, &4));\n    assert!(!result.is_in_same(&2, &3));\n    assert!(!result.is_in_same(&2, &4));\n  }\n\n  #[test]\n  fn scc_test_path() {\n    let graph = Graph::from_edges(vec!((1, 2), (2, 3), (3, 4)).into_iter());\n    let result = find_scc(&graph);\n    assert_eq!(result.count(), 4);\n    assert!(!result.is_in_same(&1, &2));\n    assert!(!result.is_in_same(&1, &3));\n    assert!(!result.is_in_same(&1, &4));\n    assert!(!result.is_in_same(&2, &3));\n    assert!(!result.is_in_same(&2, &4));\n  }\n\n  #[test]\n  fn scc_test_cycle_rhs() {\n    let graph = Graph::from_edges(vec!((1, 2), (2, 3), (3, 4), (4, 2)).into_iter());\n    let result = find_scc(&graph);\n    assert_eq!(result.count(), 2);\n    assert!(!result.is_in_same(&1, &2));\n    assert!(!result.is_in_same(&1, &3));\n    assert!(!result.is_in_same(&1, &4));\n    assert!(result.is_in_same(&2, &3));\n    assert!(result.is_in_same(&2, &4));\n  }\n\n  #[test]\n  fn scc_test_cycle_lhs() {\n    let graph = Graph::from_edges(vec!((1, 2), (2, 3), (3, 4), (3, 1)).into_iter());\n    let result = find_scc(&graph);\n    assert_eq!(result.count(), 2);\n    assert!(result.is_in_same(&1, &2));\n    assert!(result.is_in_same(&1, &3));\n    assert!(!result.is_in_same(&1, &4));\n    assert!(result.is_in_same(&2, &3));\n    assert!(!result.is_in_same(&2, &4));\n  }\n\n  #[test]\n  fn scc_test_one_big_cycle() {\n    let graph = Graph::from_edges(vec!((1, 2), (2, 3), (3, 4), (4, 1)).into_iter());\n    let result = find_scc(&graph);\n    assert_eq!(result.count(), 1);\n  }\n\n  #[test]\n  fn scc_test_two_connected_cycles() {\n    let graph = Graph::from_edges(vec!((1, 2), (2, 3), (3, 4), (4, 5), (3, 1), (5, 3)).into_iter());\n    let result = find_scc(&graph);\n    assert_eq!(result.count(), 1);\n  }\n\n}\n"
  },
  {
    "path": "src/graph/top_sort.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Topological sorting of a graph.\n\nuse super::*;\n\nuse std::collections::HashSet;\n\n/// Error type produced if [`top_sort()`] finds a cycle.\n#[derive(Clone, Copy, Eq, PartialEq, Debug)]\npub struct CycleInTopSortError {}\n\n/// Any directed acyclic graph can be topologically sorted. A valid\n/// topological sorting of an acyclic graph is a total order of the\n/// nodes in that graph such that, for any edge from `u` to `v` in the\n/// graph, `u <= v` in the total order.\n///\n/// This function finds some valid topological sorting of the nodes in\n/// the graph. If there is a cycle in the graph, then an error is\n/// returned.\npub fn top_sort<T>(graph: &Graph<T>) -> Result<Vec<&T>, CycleInTopSortError>\nwhere T : Eq + Hash {\n  let nodes: HashSet<_> = graph.nodes().collect();\n  let mut visited: HashSet<&T> = HashSet::new();\n  let mut result = Vec::new();\n  while visited.len() < nodes.len() {\n    match nodes.iter().find(|node| {\n      // To be a valid candidate, every outgoing node must be in visited.\n      !visited.contains(*node) &&\n        graph.outgoing_edges(**node).expect(\"Node not found\").iter().all(|out| {\n          visited.contains(out)\n        })\n    }) {\n      None => { return Err(CycleInTopSortError {}) }\n      Some(node) => {\n        result.push(*node);\n        visited.insert(node);\n      }\n    }\n  }\n  result.reverse();\n  Ok(result)\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn test_top_sort_1() {\n    // There's only one topological sort possible.\n    let graph = Graph::from_edges(vec!((0, 1), (1, 2), (2, 3)).into_iter());\n    let result = top_sort(&graph);\n    assert_eq!(result, Ok(vec!(&0, &1, &2, &3)));\n  }\n\n  #[test]\n  fn test_top_sort_2() {\n    // There's only one topological sort possible.\n    let graph = Graph::from_edges(vec!((0, 1), (1, 2), (0, 2)).into_iter());\n    let result = top_sort(&graph);\n    assert_eq!(result, Ok(vec!(&0, &1, &2)));\n  }\n\n  #[test]\n  fn test_top_sort_3() {\n    // Two options; as long as it's one of them, we're happy.\n    let graph = Graph::from_edges(vec!((0, 1), (1, 2), (0, 3), (3, 2)).into_iter());\n    let result = top_sort(&graph).expect(\"No topological sort found\");\n    assert_eq!(result[0], &0);\n    assert_eq!(result[3], &2);\n  }\n\n  #[test]\n  fn test_top_sort_4() {\n    // No topological sort.\n    let graph = Graph::from_edges(vec!((0, 1), (1, 2), (2, 3), (3, 1)).into_iter());\n    let result = top_sort(&graph);\n    assert_eq!(result, Err(CycleInTopSortError {}));\n  }\n\n}\n"
  },
  {
    "path": "src/ir/access_type.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Defines the [`AccessType`] enum, for describing methods of\n//! accessing a local variable.\n\nuse serde::{Serialize, Deserialize};\n\nuse crate::util::lattice::Lattice;\n\n/// `AccessType` describes the different ways we can access a variable.\n///\n/// Note that, generally speaking, an `AccessType` should be regarded\n/// as context-sensitive. That is, an access type of\n/// [`None`](AccessType::None) does *not* necessarily mean that the\n/// variable is irrelevant to the whole program; it simply means that\n/// the variable is never accessed in the scope we're worried about.\n///\n/// These possibilities do *not* form a total ordering, but they do\n/// form a [lattice](crate::util::lattice::Lattice) as follows.\n///\n/// ```text\n///   ClosedRW\n///   /      \\\n///  RW    ClosedRead\n///   \\      /\n///     Read\n///      |\n///     None\n/// ```\n///\n/// This lattice is realized by the [`AccessType::meet`] and\n/// [`AccessType::join`] functions.\n///\n/// The logic for the lattice is as follows. If a variable is accessed\n/// in two ways, `a` and `b` (both `AccessType` values), then we need\n/// at least `AccessType::join(a, b)` control over the variable to be\n/// able to implement both accesses correctly. For most access types,\n/// this is straightforward: if a variable is read (`Read`) and\n/// written to (`RW`) but never in a closure, then we need `RW`. If a\n/// variable is read in (`ClosedRead`) and out (`Read`) of a closure\n/// but never written to, then we need `ClosedRead`. The one slightly\n/// more provocative corner case is when a variable is written to\n/// outside of a closure (`RW`) and read within one (`ClosedRead`).\n/// This is equivalent in power to writing to a variable within a\n/// closure (`ClosedRW`), since in both cases we will need an explicit\n/// cell wrapper around the variable to implement the read-write\n/// correctly.\n///\n/// Note that, for the purposes of `AccessType`, we only care about\n/// actual modifications to the *value* of the variable. Internal\n/// mutability, such as modifying the *fields* of an instance stored\n/// in a variable, are irrelevant. That is,\n///\n/// ```text\n/// var x = 1\n/// x = 2\n/// var y = [1]\n/// y[0] = 2\n/// ```\n///\n/// In this example, `x` requires `RW` as the variable is directly\n/// written to. However, `y` only requires `Read`, because the\n/// variable, while internally mutated, it never directly rewritten.\n/// We draw this distinction because `AccessType` is intended for\n/// determining what sort of closure we need to construct for a\n/// variable for lambda purposes, and a closure does not care about\n/// internal mutability, only the top-level value and how constant it\n/// remains.\n#[derive(PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]\npub enum AccessType {\n  /// The variable in question is never accessed in the relevant\n  /// scope.\n  None,\n  /// The variable is read *directly* in the relevant scope.\n  Read,\n  /// The variable is written to *directly* in the relevant scope.\n  ///\n  /// This includes direct assignment to the variable itself or to an\n  /// instance variable *on* this variable. Nested assignment to an\n  /// instance variable of an instance variable (or deeper) is\n  /// excluded specifically. This condition is included so as to\n  /// ensure that COW data structures such as Vector behave correctly\n  /// inside closures. Specifically, COW structures should be wrapped\n  /// in cells if a slot on them is ever changed, not just if the\n  /// variable itself is assigned to.\n  RW,\n  /// The variable is read within a closure in the relevant scope.\n  ClosedRead,\n  /// The variable is written to within a closure in the relevant\n  /// scope.\n  ClosedRW,\n} // TODO We could get PartialOrd on this (not the derived instance but a custom one).\n\nimpl AccessType {\n\n  /// Given an access type which occurs inside of a lambda, convert it\n  /// to the access type observed from outside the lambda. This method\n  /// converts `Read` into `ClosedRead` and `RW` into `ClosedRW`,\n  /// leaving all other inputs alone.\n  ///\n  /// # Examples\n  ///\n  /// ```\n  /// # use gdlisp::ir::access_type::AccessType;\n  /// assert_eq!(AccessType::None.closed(), AccessType::None);\n  /// assert_eq!(AccessType::Read.closed(), AccessType::ClosedRead);\n  /// assert_eq!(AccessType::RW.closed(), AccessType::ClosedRW);\n  /// assert_eq!(AccessType::ClosedRead.closed(), AccessType::ClosedRead);\n  /// assert_eq!(AccessType::ClosedRW.closed(), AccessType::ClosedRW);\n  /// ```\n  pub fn closed(&self) -> AccessType {\n    match *self {\n      AccessType::None => AccessType::None,\n      AccessType::Read | AccessType::ClosedRead => AccessType::ClosedRead,\n      AccessType::RW | AccessType::ClosedRW => AccessType::ClosedRW,\n    }\n  }\n\n  /// Returns whether the access type requires a cell.\n  ///\n  /// Only [`AccessType::ClosedRW`] requires a cell. Any variables\n  /// which are never used within a closure are [`AccessType::RW`] or\n  /// less, and those variables can simply be written to directly, as\n  /// the standard Godot local variable semantics will handle this\n  /// case correctly. Any variables which are read-only (possibly\n  /// within a closure) are at most [`AccessType::ClosedRead`], and\n  /// those variables can likewise be read directly, since they'll\n  /// never be changed, so Godot's built-in pointer copying is\n  /// harmless.\n  ///\n  /// However, if a variable is written to within a closure, or if a\n  /// variable is written to and (separately) read within a closure,\n  /// then the standard process of closing around the variable will\n  /// implicitly copy the pointer to the variable, which means that\n  /// changes to the original will not be reflected in the closure and\n  /// vice versa. Hence, in this situation, we need to wrap the local\n  /// variable in a cell. `Cell` is a GDLisp standard library class\n  /// which has a single field and no methods. Its only purpose is\n  /// adding one layer of indirection to avoid this implicit copy\n  /// problem.\n  pub fn requires_cell(&self) -> bool {\n    *self == AccessType::ClosedRW\n  }\n\n  /// Returns whether or not the access type involves modifying the\n  /// variable. [`AccessType::ClosedRW`] and [`AccessType::RW`]\n  /// involves\n  pub fn is_written_to(&self) -> bool {\n    *self == AccessType::ClosedRW || *self == AccessType::RW\n  }\n\n}\n\nimpl Lattice for AccessType {\n\n  /// The least-upper-bound of `a` and `b`, under the lattice\n  /// described in [`AccessType`].\n  fn join(self, b: AccessType) -> AccessType {\n    if self == AccessType::None {\n      return b;\n    }\n    if b == AccessType::None {\n      return self;\n    }\n    if self == AccessType::Read {\n      return b;\n    }\n    if b == AccessType::Read {\n      return self;\n    }\n    if self == b {\n      return self;\n    }\n    AccessType::ClosedRW\n  }\n\n  /// The greatest-lower-bound of `a` and `b`, under the lattice\n  /// described in [`AccessType`].\n  fn meet(self, b: AccessType) -> AccessType {\n    if self == AccessType::ClosedRW {\n      return b;\n    }\n    if b == AccessType::ClosedRW {\n      return self;\n    }\n    if self == b {\n      return self;\n    }\n    if self == AccessType::None {\n      return self;\n    }\n    if b == AccessType::None {\n      return b;\n    }\n    AccessType::Read\n  }\n\n}\n"
  },
  {
    "path": "src/ir/arglist/constructor.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`ConstructorArgList`] type, the type of argument\n//! lists which allow for instance variables to be initialized\n//! directly from them.\n\nuse super::error::{ArgListParseError, ArgListParseErrorF};\nuse super::simple::SimpleArgList;\nuse super::general::{GeneralArgList, GeneralArg};\nuse super::parser;\nuse crate::sxp::ast::AST;\nuse crate::compile::names::NameTrans;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::pipeline::source::SourceOffset;\nuse crate::gdscript::arglist::ArgList as GDArgList;\n\nuse std::borrow::Borrow;\nuse std::convert::TryFrom;\n\n/// A constructor argument list consists of only required arguments,\n/// similar to [`SimpleArgList`]. However, some of the arguments can\n/// be marked as instance fields. These will be assigned to the\n/// corresponding instance variable on the class at runtime.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ConstructorArgList {\n  /// The list of required arguments, together with whether or not\n  /// they've been marked as instance variables.\n  pub args: Vec<(String, bool)>,\n}\n\nimpl ConstructorArgList {\n\n  /// Converts the argument list into a GDScript argument list, using\n  /// the given name generator to produce unique names, similar to\n  /// [`ArgList::into_gd_arglist`](super::ordinary::ArgList::into_gd_arglist).\n  ///\n  /// The second return value of this function includes, in addition\n  /// to the name translations, whether or not each argument is marked\n  /// as a constructor instance field.\n  pub fn into_gd_arglist(self, gen: &mut impl NameGenerator) -> (GDArgList, Vec<(NameTrans, bool)>) {\n    let (args, instance_fields): (Vec<_>, Vec<_>) = self.args.into_iter().unzip();\n    let arglist = SimpleArgList { args };\n    let (gd_arglist, trans) = arglist.into_gd_arglist(gen);\n    let names: Vec<(NameTrans, bool)> = trans.into_iter().zip(instance_fields.into_iter()).collect();\n    (gd_arglist, names)\n  }\n\n  /// Returns true iff any arguments are marked as instance fields.\n  pub fn has_any_instance_fields(&self) -> bool {\n    self.args.iter().any(|(_, b)| *b)\n  }\n\n  /// An iterator over all variable names, together with whether or\n  /// not they are instance variables, mentioned in the argument list,\n  /// in order.\n  pub fn iter_vars(&self) -> impl Iterator<Item = (&str, bool)> {\n    self.args.iter().map(|(name, field)| (name.borrow(), *field))\n  }\n\n  /// An iterator over all variable names, together with whether or\n  /// not they are instance variables, as a mutable iterator.\n  pub fn iter_vars_mut(&mut self) -> impl Iterator<Item = &mut (String, bool)> {\n    self.args.iter_mut()\n  }\n\n  /// Attempts to parse a constructor argument list from the iterator.\n  /// A constructor argument list can contain ordinary names, just\n  /// like a [`SimpleArgList`], but those names can also be written in\n  /// one of the following three equivalent forms.\n  ///\n  /// ```text\n  /// @foobar\n  /// self:foobar\n  /// (access-slot self foobar)\n  /// ```\n  ///\n  /// In that case, the argument will be marked as a constructor\n  /// instance field argument.\n  pub fn parse<'a>(args: impl IntoIterator<Item = &'a AST>, pos: SourceOffset)\n                   -> Result<ConstructorArgList, ArgListParseError> {\n    parser::parse(args).and_then(|arglist| {\n      ConstructorArgList::try_from(arglist).map_err(|err| ArgListParseError::new(err, pos))\n    })\n  }\n\n  /// The length of the argument list.\n  pub fn len(&self) -> usize {\n    self.args.len()\n  }\n\n  /// Whether the argument list consists of zero arguments.\n  pub fn is_empty(&self) -> bool {\n    self.args.is_empty()\n  }\n\n}\n\nimpl From<ConstructorArgList> for GeneralArgList {\n  fn from(arglist: ConstructorArgList) -> GeneralArgList {\n    let required_args: Vec<_> = arglist.args.into_iter().map(|(name, is_instance_field)| {\n      GeneralArg { name, is_instance_field }\n    }).collect();\n    GeneralArgList {\n      required_args: required_args,\n      optional_args: vec!(),\n      rest_arg: None,\n    }\n  }\n}\n\nimpl TryFrom<GeneralArgList> for ConstructorArgList {\n  type Error = ArgListParseErrorF;\n\n  fn try_from(arglist: GeneralArgList) -> Result<Self, Self::Error> {\n    if arglist.optional_args.is_empty() && arglist.rest_arg.is_none() {\n      let required_args: Vec<_> = arglist.required_args.into_iter().map(|x| {\n        (x.name, x.is_instance_field)\n      }).collect();\n      Ok(ConstructorArgList { args: required_args })\n    } else {\n      Err(ArgListParseErrorF::ConstructorArgListExpected)\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/ir/arglist/error.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides [`ArgListParseError`], the type of argument list parse\n//! errors.\n\nuse crate::sxp::ast::AST;\nuse crate::pipeline::source::{Sourced, SourceOffset};\n\nuse std::fmt;\nuse std::error::Error;\n\n/// `ArgListParseErrorF` describes the types of errors that can occur\n/// when parsing an [`AST`] argument list.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub enum ArgListParseErrorF {\n  /// An argument of some specific type was expected but something\n  /// else was provided. (TODO Remove this in favor of more specific\n  /// errors)\n  InvalidArgument(AST),\n  /// An `@` (or `self:`) argument was found in a non-constructor\n  /// context.\n  BadSelf(AST),\n  /// An `&` directive was provided but the name was unknown.\n  UnknownDirective(String),\n  /// An `&` directive appeared in the wrong place in an argument\n  /// list, such as attempting to specify `&opt` arguments after\n  /// `&rest`.\n  DirectiveOutOfOrder(String),\n  /// A simple argument list with no directives was expected, but\n  /// directives were used.\n  SimpleArgListExpected,\n  /// A simple (unmodified) argument was expected, but an instance\n  /// field was named.\n  SimpleArgExpected,\n  /// A constructor argument list was expected, but directives were\n  /// used.\n  ConstructorArgListExpected,\n}\n\n/// An [`ArgListParseErrorF`] together with [`SourceOffset`] data.\n#[derive(Debug, Clone, Eq, PartialEq)]\npub struct ArgListParseError {\n  pub value: ArgListParseErrorF,\n  pub pos: SourceOffset,\n}\n\nimpl ArgListParseError {\n  pub fn new(value: ArgListParseErrorF, pos: SourceOffset) -> ArgListParseError {\n    ArgListParseError { value, pos }\n  }\n}\n\nimpl fmt::Display for ArgListParseError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match &self.value {\n      ArgListParseErrorF::InvalidArgument(ast) => {\n        write!(f, \"Invalid arglist argument {}\", ast)\n      }\n      ArgListParseErrorF::BadSelf(_) => {\n        write!(f, \"'@' arguments are not supported here\")\n      }\n      ArgListParseErrorF::UnknownDirective(s) => {\n        write!(f, \"Unknown arglist directive {}\", s)\n      }\n      ArgListParseErrorF::DirectiveOutOfOrder(s) => {\n        write!(f, \"Arglist directive appeared out of order {}\", s)\n      }\n      ArgListParseErrorF::SimpleArgListExpected => {\n        write!(f, \"Only simple arglists are allowed in this context\")\n      }\n      ArgListParseErrorF::SimpleArgExpected => {\n        write!(f, \"Only simple arguments (not instance variables) are allowed in this context\")\n      }\n      ArgListParseErrorF::ConstructorArgListExpected => {\n        write!(f, \"Only constructor arglists are allowed in this context\")\n      }\n    }\n  }\n}\n\nimpl Sourced for ArgListParseError {\n  type Item = ArgListParseErrorF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &ArgListParseErrorF {\n    &self.value\n  }\n\n}\n\nimpl Error for ArgListParseError {}\n"
  },
  {
    "path": "src/ir/arglist/general.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`GeneralArgList`] type, for the most general type of\n//! argument list available.\n\nuse crate::sxp::ast::{ASTF, AST};\nuse crate::ir::special_form::access_slot::AccessSlotSyntax;\nuse super::vararg::VarArg;\nuse super::error::{ArgListParseError, ArgListParseErrorF};\n\n/// `GeneralArgList` is the largest argument list type available in\n/// this crate. It is not used directly in GDLisp and is instead\n/// converted to one of the several more restrictive types after\n/// parsing. Every single argument list in GDLisp is capable of being\n/// converted losslessly to `GeneralArgList`.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct GeneralArgList {\n  /// The list of required arguments.\n  pub required_args: Vec<GeneralArg>,\n  /// The list of optional arguments.\n  pub optional_args: Vec<GeneralArg>,\n  /// The \"rest\" argument.\n  pub rest_arg: Option<(GeneralArg, VarArg)>,\n}\n\n/// An argument in a [`GeneralArgList`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct GeneralArg {\n  pub name: String,\n  pub is_instance_field: bool,\n}\n\nimpl GeneralArgList {\n\n  /// An empty argument list, taking no arguments and accepting no\n  /// \"rest\" argument.\n  pub fn empty() -> GeneralArgList {\n    GeneralArgList {\n      required_args: vec!(),\n      optional_args: vec!(),\n      rest_arg: None,\n    }\n  }\n\n}\n\nimpl GeneralArg {\n\n  /// A simple argument, with no instance field modifiers.\n  pub fn simple(name: String) -> GeneralArg {\n    GeneralArg { name, is_instance_field: false }\n  }\n\n  /// Parse the argument from an AST of the form `name` or\n  /// `(access-slot self name)`. Any other form will be rejected with\n  /// an error.\n  pub fn parse(ast: &AST) -> Result<GeneralArg, ArgListParseError> {\n    if let Some(name) = ast.as_symbol_ref() {\n      // Ordinary argument\n      Ok(GeneralArg {\n        name: name.to_owned(),\n        is_instance_field: false,\n      })\n    } else {\n      // Try to parse as `self:foobar`.\n      let AccessSlotSyntax { object, slot_name } = parse_access_slot(ast)?;\n      verify_self(object)?;\n      Ok(GeneralArg {\n        name: slot_name,\n        is_instance_field: true,\n      })\n    }\n  }\n\n  /// Returns the name, if there are no adornments or modifiers\n  /// applied. Returns an error otherwise. This is the retraction of\n  /// [`GeneralArg::simple`].\n  pub fn into_simple_name(self) -> Result<String, ArgListParseErrorF> {\n    if self.is_instance_field {\n      Err(ArgListParseErrorF::SimpleArgExpected)\n    } else {\n      Ok(self.name)\n    }\n  }\n\n}\n\n/// Equivalent to [`AccessSlotSyntax::parse_ast`], but with the error\n/// type converted to [`ArgListParseError`]. The original error's\n/// [`SourceOffset`](crate::pipeline::source::SourceOffset) will be\n/// preserved.\nfn parse_access_slot(ast: &AST) -> Result<AccessSlotSyntax<'_>, ArgListParseError> {\n  AccessSlotSyntax::parse_ast(ast)\n    .map_err(|err| ArgListParseError::new(ArgListParseErrorF::InvalidArgument(ast.clone()), err.pos))\n}\n\n/// Verifies that the AST is literally the symbol `self`.\nfn verify_self(ast: &AST) -> Result<(), ArgListParseError> {\n  if ast.value == ASTF::symbol(String::from(\"self\")) {\n    Ok(())\n  } else {\n    Err(ArgListParseError::new(ArgListParseErrorF::InvalidArgument(ast.clone()), ast.pos))\n  }\n}\n"
  },
  {
    "path": "src/ir/arglist/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Argument list types as supported by the IR.\n//!\n//! See [`crate::gdscript::arglist`] for the companion module on the\n//! GDScript side.\n\npub mod constructor;\npub mod error;\npub mod general;\npub mod ordinary;\npub mod parser;\npub mod simple;\npub mod vararg;\n\n// TODO Split these tests up into the new submodules.\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  use ordinary::ArgList;\n  use error::ArgListParseError;\n  use vararg::VarArg;\n  use crate::AST_PARSER;\n  use crate::sxp::ast::AST;\n  use crate::sxp::dotted::DottedExpr;\n  use crate::compile::names::fresh::FreshNameGenerator;\n  use crate::compile::names::NameTrans;\n  use crate::gdscript::arglist::ArgList as GDArgList;\n  use crate::pipeline::source::SourceOffset;\n\n  use std::convert::TryInto;\n\n  fn parse_ast(input: &str) -> AST {\n    AST_PARSER.parse(input).unwrap()\n  }\n\n  fn parse_arglist(input: &str) -> Result<ArgList, ArgListParseError> {\n    let ast = parse_ast(input);\n    let dotted: Vec<_> = DottedExpr::new(&ast).try_into().unwrap();\n    ArgList::parse(dotted, SourceOffset(0))\n  }\n\n  fn arglist(req: Vec<&str>, opt: Vec<&str>, rest: Option<(&str, VarArg)>) -> ArgList {\n    ArgList {\n      required_args: req.into_iter().map(|x| x.to_owned()).collect(),\n      optional_args: opt.into_iter().map(|x| x.to_owned()).collect(),\n      rest_arg: rest.map(|(x, y)| (x.to_owned(), y)),\n    }\n  }\n\n  fn gdarglist(req: Vec<&str>) -> GDArgList {\n    GDArgList::required(req.into_iter().map(|x| x.to_owned()).collect())\n  }\n\n  fn into_gd(args: ArgList) -> (GDArgList, Vec<NameTrans>) {\n    let mut tmp = FreshNameGenerator::new(vec!());\n    args.into_gd_arglist(&mut tmp)\n  }\n\n  #[test]\n  fn test_parsing() {\n    assert_eq!(parse_arglist(\"()\").unwrap(), arglist(vec!(), vec!(), None));\n    assert_eq!(parse_arglist(\"(a)\").unwrap(), arglist(vec!(\"a\"), vec!(), None));\n    assert_eq!(parse_arglist(\"(a b)\").unwrap(), arglist(vec!(\"a\", \"b\"), vec!(), None));\n    assert_eq!(parse_arglist(\"(a b &opt c)\").unwrap(), arglist(vec!(\"a\", \"b\"), vec!(\"c\"), None));\n    assert_eq!(parse_arglist(\"(a &rest rest)\").unwrap(), arglist(vec!(\"a\"), vec!(), Some((\"rest\", VarArg::RestArg))));\n    assert_eq!(parse_arglist(\"(a b c &opt d &rest e)\").unwrap(), arglist(vec!(\"a\", \"b\", \"c\"), vec!(\"d\"), Some((\"e\", VarArg::RestArg))));\n    assert_eq!(parse_arglist(\"(a b c &opt d e &rest f)\").unwrap(), arglist(vec!(\"a\", \"b\", \"c\"), vec!(\"d\", \"e\"), Some((\"f\", VarArg::RestArg))));\n    assert_eq!(parse_arglist(\"(a b c &opt d e &arr f)\").unwrap(), arglist(vec!(\"a\", \"b\", \"c\"), vec!(\"d\", \"e\"), Some((\"f\", VarArg::ArrArg))));\n    assert_eq!(parse_arglist(\"(a b c &opt d e)\").unwrap(), arglist(vec!(\"a\", \"b\", \"c\"), vec!(\"d\", \"e\"), None));\n  }\n\n  #[test]\n  fn test_invalid_parse() {\n    assert!(parse_arglist(\"(&silly-name)\").is_err());\n    assert!(parse_arglist(\"(&opt a &opt b)\").is_err());\n    assert!(parse_arglist(\"(&rest a &opt b)\").is_err());\n    assert!(parse_arglist(\"(&arr a &opt b)\").is_err());\n    assert!(parse_arglist(\"(&rest a &rest b)\").is_err());\n    assert!(parse_arglist(\"(&rest a b)\").is_err());\n    assert!(parse_arglist(\"(&rest a &arr b)\").is_err());\n    assert!(parse_arglist(\"(&arr a &rest b)\").is_err());\n  }\n\n  #[test]\n  fn test_arglist_gen() {\n    assert_eq!(into_gd(arglist(vec!(), vec!(), None)).0, gdarglist(vec!()));\n    assert_eq!(into_gd(arglist(vec!(\"a\"), vec!(), None)).0, gdarglist(vec!(\"a_0\")));\n    assert_eq!(into_gd(arglist(vec!(\"a\", \"b\"), vec!(), None)).0, gdarglist(vec!(\"a_0\", \"b_1\")));\n    assert_eq!(into_gd(arglist(vec!(\"a\"), vec!(\"b\"), None)).0, gdarglist(vec!(\"a_0\", \"b_1\")));\n    assert_eq!(into_gd(arglist(vec!(\"a\"), vec!(\"b\"), Some((\"r\", VarArg::RestArg)))).0, gdarglist(vec!(\"a_0\", \"b_1\", \"r_2\")));\n    assert_eq!(into_gd(arglist(vec!(\"a\"), vec!(\"b\"), Some((\"r\", VarArg::ArrArg)))).0, gdarglist(vec!(\"a_0\", \"b_1\", \"r_2\")));\n  }\n\n  // TODO Test constructor and simple arglists\n\n}\n"
  },
  {
    "path": "src/ir/arglist/ordinary.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the ordinary argument list type [`ArgList`] for\n//! module-level functions and for lambda expressions.\n\nuse crate::sxp::ast::AST;\nuse crate::compile::names::{self, NameTrans};\nuse crate::compile::names::generator::NameGenerator;\nuse crate::compile::symbol_table::function_call::FnSpecs;\nuse crate::gdscript::arglist::ArgList as GDArgList;\nuse crate::pipeline::source::SourceOffset;\nuse super::error::{ArgListParseError, ArgListParseErrorF};\nuse super::vararg::VarArg;\nuse super::general::{GeneralArg, GeneralArgList};\nuse super::parser;\n\nuse std::convert::TryFrom;\nuse std::borrow::Borrow;\n\n/// An argument list in GDLisp consists of a sequence of zero or more\n/// required arguments, followed by zero or more optional arguments,\n/// followed by (optionally) a \"rest\" argument.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct ArgList {\n  /// The list of required argument names.\n  pub required_args: Vec<String>,\n  /// The list of optional argument names. Note that optional\n  /// arguments in GDLisp always default to `nil`, so no default value\n  /// is explicitly mentioned here.\n  pub optional_args: Vec<String>,\n  /// The \"rest\" argument. If present, this indicates the name of the\n  /// argument and the type of \"rest\" argument.\n  pub rest_arg: Option<(String, VarArg)>,\n}\n\nimpl ArgList {\n\n  /// An empty argument list, taking no required or optional arguments\n  /// and having no \"rest\" argument.\n  pub fn empty() -> ArgList {\n    ArgList {\n      required_args: vec!(),\n      optional_args: vec!(),\n      rest_arg: None,\n    }\n  }\n\n  /// An argument list consisting only of a single [`VarArg::RestArg`]\n  /// argument with a default name.\n  #[deprecated(note=\"Directly construct the arglist with an explicit rest name instead\")]\n  pub fn rest() -> ArgList {\n    ArgList {\n      required_args: vec!(),\n      optional_args: vec!(),\n      rest_arg: Some((String::from(\"rest-arg\"), VarArg::RestArg)),\n    }\n  }\n\n  /// An argument list consisting only of required arguments with the\n  /// given names.\n  pub fn required(args: Vec<String>) -> ArgList {\n    ArgList {\n      required_args: args,\n      optional_args: vec!(),\n      rest_arg: None,\n    }\n  }\n\n  /// Converts an [`FnSpecs`] to an [`ArgList`] with dummy names for\n  /// the variables.\n  ///\n  /// The names of the generated arguments may change and should not\n  /// be relied upon; only the shape and lengths should be considered\n  /// stable.\n  pub fn from_specs(specs: FnSpecs) -> ArgList {\n    // Uses dummy names for variables.\n    let required_args = (0..specs.required).into_iter().map(|i| format!(\"required_arg{}\", i)).collect();\n    let optional_args = (0..specs.optional).into_iter().map(|i| format!(\"optional_arg{}\", i)).collect();\n    let rest_arg = specs.rest.map(|arg| (String::from(\"rest_arg\"), arg));\n    ArgList { required_args, optional_args, rest_arg }\n  }\n\n  /// Parse an argument list from an iterator of `AST` values. Returns\n  /// either the [`GeneralArgList`] or an appropriate error.\n  pub fn parse<'a>(args: impl IntoIterator<Item = &'a AST>, pos: SourceOffset)\n                   -> Result<ArgList, ArgListParseError> {\n    let general_arglist = parser::parse(args)?;\n    ArgList::try_from(general_arglist).map_err(|err| ArgListParseError::new(err, pos))\n  }\n\n  /// Converts the argument list into a GDScript argument list, using\n  /// the given name generator to produce unique GDScript names.\n  ///\n  /// * Each required argument will be translated into a GDScript\n  /// argument.\n  ///\n  /// * Each optional argument, likewise, will be translated into a\n  /// GDScript argument. On the GDScript side, required and optional\n  /// arguments are indistinguishable.\n  ///\n  /// * If there is a \"rest\" argument of any kind, it is translated to\n  /// a single GDScript argument as well.\n  pub fn into_gd_arglist(self, gen: &mut impl NameGenerator) -> (GDArgList, Vec<NameTrans>) {\n    let cap = 1 + self.required_args.len() + self.optional_args.len();\n    let mut name_translations = Vec::with_capacity(cap);\n    let mut args = Vec::with_capacity(cap);\n    for arg in self.required_args {\n      let gd = gen.generate_with(&names::lisp_to_gd(&arg));\n      name_translations.push(NameTrans { lisp_name: arg, gd_name: gd.clone() });\n      args.push(gd);\n    }\n    for arg in self.optional_args {\n      let gd = gen.generate_with(&names::lisp_to_gd(&arg));\n      name_translations.push(NameTrans { lisp_name: arg, gd_name: gd.clone() });\n      args.push(gd);\n    }\n    if let Some((arg, _)) = self.rest_arg {\n      let gd = gen.generate_with(&names::lisp_to_gd(&arg));\n      name_translations.push(NameTrans { lisp_name: arg, gd_name: gd.clone() });\n      args.push(gd);\n    }\n    (GDArgList::required(args), name_translations)\n  }\n\n  /// An iterator over all variable names mentioned in the argument\n  /// list, in order.\n  pub fn iter_vars(&self) -> impl Iterator<Item = &str> {\n    self.required_args.iter()\n      .chain(self.optional_args.iter())\n      .chain(self.rest_arg.iter().map(|x| &x.0))\n      .map(|x| x.borrow())\n  }\n\n}\n\nimpl From<ArgList> for FnSpecs {\n\n  /// [`FnSpecs`] is simply an [`ArgList`] without the argument names;\n  /// it merely preserves the shape. From an `ArgList` we can always\n  /// construct an `FnSpecs` in a canonical way.\n  fn from(arglist: ArgList) -> FnSpecs {\n    // TODO We need to define an upper limit on argument list length\n    // (and check if Godot already has one we need to respect)\n    FnSpecs::new(\n      arglist.required_args.len(),\n      arglist.optional_args.len(),\n      arglist.rest_arg.map(|x| x.1),\n    )\n  }\n\n}\n\nimpl From<ArgList> for GeneralArgList {\n\n  fn from(arglist: ArgList) -> GeneralArgList {\n    let required_args: Vec<_> = arglist.required_args.into_iter().map(GeneralArg::simple).collect();\n    let optional_args: Vec<_> = arglist.optional_args.into_iter().map(GeneralArg::simple).collect();\n    let rest_arg: Option<_> = arglist.rest_arg.map(|(name, vararg)| (GeneralArg::simple(name), vararg));\n    GeneralArgList {\n      required_args,\n      optional_args,\n      rest_arg,\n    }\n  }\n\n}\n\nimpl TryFrom<GeneralArgList> for ArgList {\n  type Error = ArgListParseErrorF;\n\n  fn try_from(arglist: GeneralArgList) -> Result<Self, Self::Error> {\n    let required_args: Vec<_> = arglist.required_args.into_iter().map(|x| x.into_simple_name()).collect::<Result<_, _>>()?;\n    let optional_args: Vec<_> = arglist.optional_args.into_iter().map(|x| x.into_simple_name()).collect::<Result<_, _>>()?;\n    let rest_arg: Option<_> = arglist.rest_arg.map(|(arg, vararg)| Ok((arg.into_simple_name()?, vararg))).transpose()?;\n    Ok(ArgList {\n      required_args,\n      optional_args,\n      rest_arg,\n    })\n  }\n\n}\n"
  },
  {
    "path": "src/ir/arglist/parser.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! The general-purpose parsing module of [`GeneralArgList`].\n\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::literal::Literal;\nuse super::general::{GeneralArgList, GeneralArg};\nuse super::error::{ArgListParseError, ArgListParseErrorF};\nuse super::vararg::VarArg;\n\nuse std::cmp::Ordering;\nuse std::borrow::Borrow;\n\n/// The current type of argument we're looking for when parsing an\n/// argument list.\n///\n/// This is an internal type to this module and, generally, callers\n/// from outside the module should not need to interface with it.\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum ParseState {\n  /// We're expecting required arguments. This is the state we begin\n  /// in.\n  Required,\n  /// We're expecting optional arguments. This is the state following\n  /// `&opt`.\n  Optional,\n  /// We're expecting the single `&rest` argument.\n  Rest,\n  /// We're expecting the single `&arr` argument.\n  Arr,\n  /// We have passed the \"rest\" argument and are expecting the end of\n  /// an argument list. If *any* arguments occur in this state, an\n  /// error will be issued.\n  RestInvalid,\n}\n\n/// `ParseState` implements `PartialOrd` to indicate valid orderings\n/// in which the argument type directives can occur. Specifically, we\n/// can transition from a state `u` to a state `v` if and only if `u\n/// <= v` is true. If we attempt a state transition where that is not\n/// true, then an [`ArgListParseErrorF::DirectiveOutOfOrder`] error\n/// will be issued.\n///\n/// There are two chains in this ordering:\n///\n/// * `Required < Optional < Rest < RestInvalid`\n///\n/// * `Required < Optional < Arr < RestInvalid`\n///\n/// The two states `Rest` and `Arr` are incomparable.\nimpl PartialOrd for ParseState {\n  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {\n    if *self == *other {\n      return Some(Ordering::Equal);\n    }\n    if *self == ParseState::Required {\n      return Some(Ordering::Less);\n    }\n    if *other == ParseState::Required {\n      return Some(Ordering::Greater);\n    }\n    if *self == ParseState::Optional {\n      return Some(Ordering::Less);\n    }\n    if *other == ParseState::Optional {\n      return Some(Ordering::Greater);\n    }\n    if *self == ParseState::RestInvalid {\n      return Some(Ordering::Greater);\n    }\n    if *other == ParseState::RestInvalid {\n      return Some(Ordering::Less);\n    }\n    None\n  }\n}\n\nimpl ParseState {\n\n  /// The state to begin parsing an argument list in.\n  pub const START_STATE: ParseState =\n    ParseState::Required;\n\n  /// Given an argument directive, returns the state represented by\n  /// that directive, or `None` if the string is not a valid argument\n  /// directive.\n  ///\n  /// Note that [`ParseState::Required`] is not represented by an\n  /// argument directive, as it is the start state and never requires\n  /// (or admits) an `&` directive to transition *to* that state.\n  pub fn state_transition(self, arg: &str) -> Option<ParseState> {\n    match arg.borrow() {\n      \"&opt\" => Some(ParseState::Optional),\n      \"&rest\" => Some(ParseState::Rest),\n      \"&arr\" => Some(ParseState::Arr),\n      _ => None\n    }\n  }\n\n  /// Parse the given [`AST`] value, modifying the current parse\n  /// state, and the [`GeneralArgList`] being built, as necessary.\n  pub fn parse_once(&mut self, arglist: &mut GeneralArgList, arg: &AST) -> Result<(), ArgListParseError> {\n    if self.parse_state_transition(arg)? {\n      Ok(())\n    } else {\n      let pos = arg.pos;\n      match self {\n        ParseState::Required => {\n          let general_arg = GeneralArg::parse(arg)?;\n          arglist.required_args.push(general_arg);\n        }\n        ParseState::Optional => {\n          let general_arg = GeneralArg::parse(arg)?;\n          arglist.optional_args.push(general_arg);\n        }\n        ParseState::Rest => {\n          let general_arg = GeneralArg::parse(arg)?;\n          arglist.rest_arg = Some((general_arg, VarArg::RestArg));\n          *self = ParseState::RestInvalid;\n        }\n        ParseState::Arr => {\n          let general_arg = GeneralArg::parse(arg)?;\n          arglist.rest_arg = Some((general_arg, VarArg::ArrArg));\n          *self = ParseState::RestInvalid;\n        }\n        ParseState::RestInvalid => {\n          return Err(ArgListParseError::new(ArgListParseErrorF::InvalidArgument(arg.clone()), pos));\n        }\n      }\n      Ok(())\n    }\n  }\n\n  fn parse_state_transition(&mut self, arg: &AST) -> Result<bool, ArgListParseError> {\n    // Returns whether or not a transition was parsed.\n    let pos = arg.pos;\n    match &arg.value {\n      ASTF::Atom(Literal::Symbol(arg)) if arg.starts_with('&') => {\n        let new_state = self.state_transition(arg.borrow());\n        match new_state {\n          None => {\n            Err(ArgListParseError::new(ArgListParseErrorF::UnknownDirective(arg.to_owned()), pos))\n          }\n          Some(new_state) => {\n            if *self < new_state {\n              *self = new_state;\n              Ok(true)\n            } else {\n              Err(ArgListParseError::new(ArgListParseErrorF::DirectiveOutOfOrder(arg.to_owned()), pos))\n            }\n          }\n        }\n      }\n      _ => {\n        Ok(false)\n      }\n    }\n  }\n\n}\n\n/// Parse an argument list from an iterator of `AST` values. Returns\n/// either the [`GeneralArgList`] or an appropriate error.\npub fn parse<'a>(args: impl IntoIterator<Item = &'a AST>)\n                 -> Result<GeneralArgList, ArgListParseError> {\n  let mut state = ParseState::START_STATE;\n  let mut result = GeneralArgList::empty();\n  for arg in args {\n    state.parse_once(&mut result, arg)?;\n  }\n  Ok(result)\n}\n"
  },
  {
    "path": "src/ir/arglist/simple.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides [`SimpleArgList`], the type of argument lists which\n//! consist of simple names and no other features.\n\nuse super::error::{ArgListParseError, ArgListParseErrorF};\nuse super::general::{GeneralArgList, GeneralArg};\nuse super::ordinary::ArgList;\nuse super::parser;\nuse crate::sxp::ast::AST;\nuse crate::gdscript::arglist::ArgList as GDArgList;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::compile::names::NameTrans;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::convert::TryFrom;\nuse std::borrow::Borrow;\n\n/// A simple argument list consists only of required arguments and\n/// nothing more. This is required in contexts where GDLisp cannot\n/// determine the arity of a call, such as when invoking instance\n/// methods on an unknown object in GDLisp.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct SimpleArgList {\n  /// The list of required arguments.\n  pub args: Vec<String>,\n}\n\nimpl SimpleArgList {\n\n  /// Converts the argument list into a GDScript argument list, using\n  /// the given name generator to produce unique names, similar to\n  /// [`ArgList::into_gd_arglist`].\n  pub fn into_gd_arglist(self, gen: &mut impl NameGenerator) -> (GDArgList, Vec<NameTrans>) {\n    ArgList::from(self).into_gd_arglist(gen)\n  }\n\n  /// An iterator over all variable names mentioned in the argument\n  /// list, in order.\n  pub fn iter_vars(&self) -> impl Iterator<Item = &str> {\n    self.args.iter().map(|x| x.borrow())\n  }\n\n  /// Attempts to parse a simple argument list from the iterator, as\n  /// though by [`ArgList::parse`], and then attempts to convert that\n  /// argument list into a [`SimpleArgList`]. If either step fails, an\n  /// error is reported.\n  pub fn parse<'a>(args: impl IntoIterator<Item = &'a AST>, pos: SourceOffset)\n                   -> Result<SimpleArgList, ArgListParseError> {\n    parser::parse(args).and_then(|arglist| {\n      SimpleArgList::try_from(arglist).map_err(|err| ArgListParseError::new(err, pos))\n    })\n  }\n\n  /// The length of the argument list.\n  pub fn len(&self) -> usize {\n    self.args.len()\n  }\n\n  /// Whether the argument list consists of zero arguments.\n  pub fn is_empty(&self) -> bool {\n    self.args.is_empty()\n  }\n\n}\n\nimpl From<SimpleArgList> for GeneralArgList {\n  fn from(arglist: SimpleArgList) -> GeneralArgList {\n    let required_args: Vec<_> = arglist.args.into_iter().map(GeneralArg::simple).collect();\n    GeneralArgList {\n      required_args: required_args,\n      optional_args: vec!(),\n      rest_arg: None,\n    }\n  }\n}\n\nimpl TryFrom<GeneralArgList> for SimpleArgList {\n  type Error = ArgListParseErrorF;\n\n  fn try_from(arglist: GeneralArgList) -> Result<Self, Self::Error> {\n    if arglist.optional_args.is_empty() && arglist.rest_arg.is_none() {\n      let required_args: Vec<_> = arglist.required_args.into_iter().map(|x| x.into_simple_name()).collect::<Result<_, _>>()?;\n      Ok(SimpleArgList { args: required_args })\n    } else {\n      Err(ArgListParseErrorF::SimpleArgListExpected)\n    }\n  }\n\n}\n\nimpl From<SimpleArgList> for ArgList {\n  fn from(arglist: SimpleArgList) -> ArgList {\n    let required_args: Vec<_> = arglist.args;\n    ArgList {\n      required_args: required_args,\n      optional_args: vec!(),\n      rest_arg: None,\n    }\n  }\n}\n"
  },
  {
    "path": "src/ir/arglist/vararg.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Defines the [`VarArg`] enum, which indicates the types of variable\n//! argument tails available to lambda lists.\n\nuse serde::{Serialize, Deserialize};\n\n/// The type of \"rest\" argument which accumulates any extra arguments\n/// to a function call.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub enum VarArg {\n  /// A `&rest` argument accumulates the arguments into a GDLisp list.\n  RestArg,\n  /// An `&arr` argument accumulates the arguments into a Godot array.\n  ArrArg,\n}\n\nimpl VarArg {\n\n  /// An [`i32`] constant representing no variable argument at all.\n  pub const NONE: i32 = 0;\n\n  /// Converts `self` into a numerical value, suitable for\n  /// communication with GDScript. This method is guaranteed to never\n  /// return [`VarArg::NONE`].\n  pub fn into_constant(self) -> i32 {\n    match self {\n      VarArg::RestArg => 1,\n      VarArg::ArrArg => 2,\n    }\n  }\n\n  /// Returns [`VarArg::NONE`] if `opt` is `None`, or calls\n  /// [`VarArg::into_constant`] otherwise.\n  pub fn arg_to_const(opt: Option<VarArg>) -> i32 {\n    opt.map_or(VarArg::NONE, VarArg::into_constant)\n  }\n\n}\n"
  },
  {
    "path": "src/ir/bootstrapping.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::incremental::IncCompiler;\nuse super::decl::{Decl, DeclF, DeclareDecl, DeclareType, EnumDecl, ClassDecl};\nuse super::expr::Expr;\nuse super::export::Visibility;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::gdscript::library::gdnative::NativeClasses;\nuse crate::gdscript::library::{constant_loader, class_loader};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::source::SourceOffset;\nuse crate::util::one::One;\n\npub fn compile_bootstrapping_decl(\n  _icompiler: &mut IncCompiler,\n  pipeline: &mut Pipeline,\n  acc: &mut impl Extend<Decl>,\n  directive: &str,\n  pos: SourceOffset,\n) -> Result<(), GDError> {\n  let native = pipeline.get_native_classes();\n  match directive {\n    \"constants\" => {\n      bootstrap_constant_names(native, acc, pos);\n    }\n    \"constant-enums\" => {\n      bootstrap_constant_enums(native, acc, pos);\n    }\n    \"non-singleton-types\" => {\n      bootstrap_non_singletons(native, acc, pos);\n    }\n    \"singleton-types\" => {\n      bootstrap_singletons(native, acc, pos);\n    }\n    _ => {\n      return Err(GDError::new(GDErrorF::BadBootstrappingDirective(directive.to_owned()), pos));\n    }\n  }\n  Ok(())\n}\n\npub fn compile_bootstrapping_class_inner_decl(\n  _icompiler: &mut IncCompiler,\n  pipeline: &mut Pipeline,\n  acc: &mut ClassDecl,\n  directive: &str,\n  pos: SourceOffset,\n) -> Result<(), GDError> {\n  let native = pipeline.get_native_classes();\n  match directive {\n    \"singleton-backing-types\" => {\n      bootstrap_singleton_backing_types(native, acc, pos);\n    }\n    _ => {\n      return Err(GDError::new(GDErrorF::BadBootstrappingDirective(directive.to_owned()), pos));\n    }\n  }\n  Ok(())\n}\n\npub fn compile_bootstrapping_expr(\n  _icompiler: &mut IncCompiler,\n  pipeline: &mut Pipeline,\n  directive: &str,\n  pos: SourceOffset,\n) -> Result<Expr, GDError> {\n  let native = pipeline.get_native_classes();\n  match directive {\n    \"native-types-table\" => {\n      Ok(class_loader::native_types_dictionary_initializer(native, pos))\n    }\n    _ => {\n      Err(GDError::new(GDErrorF::BadBootstrappingDirective(directive.to_owned()), pos))\n    }\n  }\n}\n\nfn bootstrap_constant_names(\n  native: &NativeClasses,\n  acc: &mut impl Extend<Decl>,\n  pos: SourceOffset,\n) {\n  let all_constant_names = constant_loader::get_all_constants(native);\n  acc.extend(all_constant_names.map(|name| declare_superglobal(name, pos)));\n}\n\nfn declare_superglobal(name: &str, pos: SourceOffset) -> Decl {\n  Decl::new(\n    DeclF::DeclareDecl(\n      DeclareDecl {\n        visibility: Visibility::Public,\n        declare_type: DeclareType::Superglobal,\n        name: name.to_owned(),\n        target_name: Some(name.to_owned()),\n      }\n    ),\n    pos,\n  )\n}\n\nfn bootstrap_constant_enums(\n  native: &NativeClasses,\n  acc: &mut impl Extend<Decl>,\n  pos: SourceOffset,\n) {\n  let all_enums = constant_loader::get_all_constant_enums(native, pos);\n  for constant_enum in all_enums {\n    let decl = EnumDecl::from(constant_enum);\n    acc.extend(One(Decl::new(DeclF::EnumDecl(decl), pos)));\n  }\n}\n\nfn bootstrap_non_singletons(\n  native: &NativeClasses,\n  acc: &mut impl Extend<Decl>,\n  pos: SourceOffset,\n) {\n  let all_non_singleton_classes = class_loader::get_non_singleton_declarations(native);\n  acc.extend(all_non_singleton_classes.map(|decl| Decl::new(DeclF::DeclareDecl(decl), pos)));\n}\n\nfn bootstrap_singletons(\n  native: &NativeClasses,\n  acc: &mut impl Extend<Decl>,\n  pos: SourceOffset,\n) {\n  let all_singleton_classes = class_loader::get_singleton_declarations(native);\n  acc.extend(all_singleton_classes.into_iter().map(|decl| Decl::new(DeclF::DeclareDecl(decl), pos)));\n}\n\nfn bootstrap_singleton_backing_types(\n  native: &NativeClasses,\n  acc: &mut ClassDecl,\n  pos: SourceOffset,\n) {\n  let all_singleton_classes = class_loader::get_singleton_class_var_declarations(native, pos);\n  acc.decls.extend(all_singleton_classes);\n}\n"
  },
  {
    "path": "src/ir/call_name.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! [`CallName`] is the type of valid cars in a call [`AST`]\n//! expression.\n\nuse super::incremental::IncCompiler;\nuse super::expr::Expr;\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::dotted::DottedExpr;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::error::PError;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::convert::TryFrom;\n\n/// GDLisp is fairly conservative about what sort of [`AST`] values\n/// are allowed as the subject of a call. Excluding special forms and\n/// other quoted constructs, an `AST` appearing in evaluation context\n/// must have a car of one of the forms permitted by `CallName`.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum CallName {\n  /// A simple\n  /// [`Literal::Symbol`](crate::sxp::literal::Literal::Symbol) name.\n  SimpleName(String),\n  /// An `access-slot` qualified call.\n  MethodName(Box<Expr>, String),\n  /// A `literally` call.\n  AtomicName(String),\n  /// A call on the special `super` keyword, to invoke the superclass\n  /// method with the given name.\n  SuperName(String),\n}\n\nimpl CallName {\n\n  /// Identifies the type of call being referred to by a particular\n  /// AST. `ast` shall be the AST we're calling, excluding any\n  /// arguments or enclosing structures.\n  pub fn resolve_call_name(icompiler: &mut IncCompiler,\n                           pipeline: &mut Pipeline,\n                           ast: &AST)\n                           -> Result<CallName, PError> {\n    if let Some((lhs, name)) = CallName::try_resolve_method_name(ast) {\n      // Might be a super call; check for that first.\n      if lhs.value == ASTF::symbol(\"super\") {\n        Ok(CallName::SuperName(name.to_owned()))\n      } else {\n        let lhs = icompiler.compile_expr(pipeline, lhs)?;\n        Ok(CallName::MethodName(Box::new(lhs), name.to_owned()))\n      }\n    } else if let Some(name) = CallName::try_resolve_atomic_name(ast) {\n      Ok(CallName::AtomicName(name.to_owned()))\n    } else if let Some(s) = ast.as_symbol_ref() {\n      Ok(CallName::SimpleName(s.to_owned()))\n    } else {\n      Err(PError::from(GDError::new(GDErrorF::CannotCall(ast.clone()), ast.pos)))\n    }\n  }\n\n  pub fn into_expr(self,\n                   icompiler: &mut IncCompiler,\n                   pipeline: &mut Pipeline,\n                   tail: &[&AST],\n                   pos: SourceOffset)\n                   -> Result<Expr, PError> {\n    match self {\n      CallName::SimpleName(head) => {\n        icompiler.resolve_simple_call(pipeline, &head, tail, pos)\n      }\n      CallName::MethodName(target, head) => {\n        let args = tail.iter().map(|x| icompiler.compile_expr(pipeline, x)).collect::<Result<Vec<_>, _>>()?;\n        Ok(target.method_call(head, args, pos))\n      }\n      CallName::AtomicName(head) => {\n        let args = tail.iter().map(|x| icompiler.compile_expr(pipeline, x)).collect::<Result<Vec<_>, _>>()?;\n        Ok(Expr::atomic_call(head, args, pos))\n      }\n      CallName::SuperName(head) => {\n        let args = tail.iter().map(|x| icompiler.compile_expr(pipeline, x)).collect::<Result<Vec<_>, _>>()?;\n        Ok(Expr::super_call(head, args, pos))\n      }\n    }\n  }\n\n  /// Attempts to resolve `ast` as an `access-slot` pair, with an\n  /// `AST` left-hand side and a string method name.\n  fn try_resolve_method_name(ast: &AST) -> Option<(&AST, &str)> {\n    if let Ok(vec) = Vec::<&AST>::try_from(DottedExpr::new(ast)) {\n      if vec.len() == 3 && vec[0].value == ASTF::symbol(\"access-slot\") {\n        if let Some(name) = vec[2].as_symbol_ref() {\n          return Some((vec[1], name));\n        }\n      }\n    }\n    None\n  }\n\n  /// Attempts to resolve `ast` as a `literally` name with a single\n  /// symbol argument.\n  fn try_resolve_atomic_name(ast: &AST) -> Option<&str> {\n    if let Ok(vec) = Vec::<&AST>::try_from(DottedExpr::new(ast)) {\n      if vec.len() == 2 && vec[0].value == ASTF::symbol(\"literally\") {\n        if let Some(name) = vec[1].as_symbol_ref() {\n          return Some(name);\n        }\n      }\n    }\n    None\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::AST_PARSER;\n  use crate::pipeline::Pipeline;\n  use crate::pipeline::source::SourceOffset;\n  use crate::pipeline::config::ProjectConfig;\n  use crate::pipeline::resolver::PanickingNameResolver;\n  use crate::runner::version::VersionInfo;\n\n  use std::path::PathBuf;\n\n  fn parse_ast(input: &str) -> AST {\n    AST_PARSER.parse(input).unwrap()\n  }\n\n  fn dummy_config() -> ProjectConfig {\n    ProjectConfig {\n      root_directory: PathBuf::from(r\".\"),\n      optimizations: false,\n      godot_version: VersionInfo::default(),\n    }\n  }\n\n  fn dummy_pipeline() -> Pipeline {\n    Pipeline::with_resolver(dummy_config(), Box::new(PanickingNameResolver))\n  }\n\n  fn resolve_call(input: &str) -> Result<CallName, PError> {\n    let ast = parse_ast(input);\n    let mut icompiler = IncCompiler::new(vec!());\n    CallName::resolve_call_name(&mut icompiler, &mut dummy_pipeline(), &ast)\n  }\n\n  #[test]\n  fn simple_call_name() {\n    assert_eq!(resolve_call(\"foobar\"), Ok(CallName::SimpleName(String::from(\"foobar\"))));\n    assert_eq!(resolve_call(\"some-complicated-name\"), Ok(CallName::SimpleName(String::from(\"some-complicated-name\"))));\n\n    // Built-in names are still ordinary calls according to CallName.\n    assert_eq!(resolve_call(\"if\"), Ok(CallName::SimpleName(String::from(\"if\"))));\n\n    // `self` is just a name like any other.\n    assert_eq!(resolve_call(\"self\"), Ok(CallName::SimpleName(String::from(\"self\"))));\n\n    // Although probably a user mistake, `super` is a valid function\n    // name.\n    assert_eq!(resolve_call(\"super\"), Ok(CallName::SimpleName(String::from(\"super\"))));\n\n  }\n\n  #[test]\n  fn method_name() {\n    assert_eq!(resolve_call(\"foo:bar\"), Ok(CallName::MethodName(Box::new(Expr::var(\"foo\", SourceOffset(0))), String::from(\"bar\"))));\n    assert_eq!(resolve_call(\"self:bar\"), Ok(CallName::MethodName(Box::new(Expr::var(\"self\", SourceOffset(0))), String::from(\"bar\"))));\n  }\n\n  #[test]\n  fn super_name() {\n    assert_eq!(resolve_call(\"super:bar\"), Ok(CallName::SuperName(String::from(\"bar\"))));\n  }\n\n  #[test]\n  fn atomic_name() {\n    assert_eq!(resolve_call(\"(literally bar)\"), Ok(CallName::AtomicName(String::from(\"bar\"))));\n  }\n\n}\n"
  },
  {
    "path": "src/ir/classification.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Convenience functions for checking the type of thing we're looking\n//! at, for instance whether a given [`AST`] is a declaration or an\n//! expression.\n\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::dotted::DottedExpr;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::convert::TryInto;\nuse std::borrow::Borrow;\n\n/// A `super` call detected by [`detect_super`].\n#[derive(Clone, Debug)]\npub struct DetectedSuperCall<'a> {\n  pub arguments: Vec<&'a AST>,\n  pub pos: SourceOffset,\n}\n\n/// Here, we list the heads for all valid declaration types. Note that\n/// `progn` is specifically not included here; `progn` is an\n/// expression-level special form which is also a deeply magical\n/// construct treated in a special way by the compiler during parsing.\n/// It is *not* a declaration, even though it can look like one\n/// syntactically.\npub const DECL_HEADS: [&str; 9] = [\n  \"defn\", \"defmacro\", \"defconst\", \"defclass\", \"defenum\", \"sys/declare\",\n  \"define-symbol-macro\", \"sys/bootstrap\", \"sys/min-godot-version\",\n];\n\n/// A simple check to see whether a given AST should be parsed as a\n/// declaration or not.\n///\n/// There are many contexts (including the very top-level of a file)\n/// where either a declaration or an expression is acceptable. This\n/// function is used to determine whether the AST should be parsed as\n/// a declaration or an expression. Note that a `true` result for\n/// `is_decl` is *not* a guarantee that parsing as a declaration will\n/// be error-free; it is merely an indication that the declaration\n/// parse should be attempted.\n///\n/// If `decl` is not a proper list (as per the definition in\n/// [`DottedExpr`](crate::sxp::dotted::DottedExpr)), then `is_decl`\n/// returns false. Otherwise, the first term of the list is checked\n/// against several known symbol values ([`DECL_HEADS`]) to determine\n/// if the AST represents a declaration.\npub fn is_decl(decl: &AST) -> bool {\n  let vec: Result<Vec<&AST>, _> = DottedExpr::new(decl).try_into();\n  if let Ok(vec) = vec {\n    if let Some(head) = vec.get(0).and_then(|x| x.as_symbol_ref()) {\n      return DECL_HEADS.contains(&head.borrow());\n    }\n  }\n  false\n}\n\n/// Given the body of a [`FnDecl`](super::decl::FnDecl) or a possible\n/// [`ConstructorDecl`](super::decl::ConstructorDecl), check to see if\n/// the first expression present is a \"super\" call.\n///\n/// If a \"super\" call is present, its arguments, as well as a slice of\n/// the rest of the body, is returned. Otherwise, the whole slice is\n/// returned untouched.\npub fn detect_super<'a, 'b>(body: &'a [&'b AST]) -> (Option<DetectedSuperCall<'b>>, &'a [&'b AST]) {\n  if !body.is_empty() {\n    let first: Result<Vec<_>, _> = DottedExpr::new(body[0]).try_into();\n    if let Ok(mut first) = first {\n      if !first.is_empty() && first[0].value == ASTF::symbol(String::from(\"super\")) {\n        let super_symbol = first.remove(0);\n        let super_args = first;\n        let super_call = DetectedSuperCall {\n          arguments: super_args,\n          pos: super_symbol.pos,\n        };\n        return (Some(super_call), &body[1..]);\n      }\n    }\n  }\n  (None, body)\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::AST_PARSER;\n\n  fn parse_ast(input: &str) -> AST {\n    AST_PARSER.parse(input).unwrap()\n  }\n\n  #[test]\n  fn is_decl_test() {\n    assert!(is_decl(&parse_ast(\"(defn foo ())\")));\n    assert!(is_decl(&parse_ast(\"(defclass Example (Reference) (defn bar (x)))\")));\n    assert!(is_decl(&parse_ast(\"(defmacro foo ())\")));\n    assert!(is_decl(&parse_ast(\"(defconst MY_CONST 3)\")));\n    assert!(is_decl(&parse_ast(\"(defenum MyEnum A B C)\")));\n    assert!(is_decl(&parse_ast(\"(define-symbol-macro my-macro 3)\")));\n    assert!(is_decl(&parse_ast(\"(sys/declare value xyz)\")));\n    assert!(is_decl(&parse_ast(\"(sys/min-godot-version 3030000)\")));\n  }\n\n  #[test]\n  fn is_not_decl_test() {\n    assert!(!is_decl(&parse_ast(\"100\")));\n    assert!(!is_decl(&parse_ast(\"((defn foo ()))\")));\n    assert!(!is_decl(&parse_ast(\"abc\")));\n    assert!(!is_decl(&parse_ast(\"(progn 1 2 3)\")));\n    assert!(!is_decl(&parse_ast(\"(progn (defconst MY_CONST 3))\")));\n    assert!(!is_decl(&parse_ast(\"#t\")));\n  }\n\n}\n"
  },
  {
    "path": "src/ir/closure_names.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helper for storing collections of values to be closed over.\n//!\n//! This module defines the [`ClosureNames`] type, which maintains a\n//! collection of names and some reference to how they're used. If a\n//! name is \"used\" multiple times (i.e. [`ClosureNames::visit`] is\n//! called multiple times with the same name), then the usage\n//! references will be combined via [`Lattice::join`].\n\nuse crate::util::lattice::Lattice;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::collections::HashMap;\nuse std::iter::IntoIterator;\n\n/// A collection of names and usage information. The usage information\n/// shall be an instance of [`Lattice`]. The `Lattice` instance is\n/// used for combining duplicate keys in the table.\n#[derive(PartialEq, Eq, Debug, Clone)]\npub struct ClosureNames<T : Lattice>(HashMap<String, (T, SourceOffset)>);\n\nimpl<T : Lattice> ClosureNames<T> {\n\n  /// Constructs a new, empty `ClosureNames`.\n  pub fn new() -> Self {\n    ClosureNames::default()\n  }\n\n  /// Constructs a `ClosureNames` from a hashmap mapping names to\n  /// usage information.\n  pub fn from_hashmap(map: HashMap<String, (T, SourceOffset)>) -> Self {\n    // TODO Do we ever use this function? Its signature is kinda... unfortunately complicated.\n    ClosureNames(map)\n  }\n\n  /// Gets the usage information associated to the name, or [`None`]\n  /// if the name is unknown to `self`.\n  pub fn get(&self, name: &str) -> Option<&T> {\n    self.0.get(name).map(|x| &x.0)\n  }\n\n  /// Whether or not the `ClosureNames` contains the given name.\n  /// Equivalent to `self.get(name).is_some()`.\n  pub fn contains(&self, name: &str) -> bool {\n    self.get(name).is_some()\n  }\n\n  /// Marks the given name as being used in the given way. If the name\n  /// was not known to `self` before, it is added to the table. If the\n  /// variable was known, then the previous usage information and\n  /// `usage` are joined via [`Lattice::join`] to produce the new\n  /// usage.\n  ///\n  /// In case of a conflict, the minimum of the two source positions\n  /// is chosen, since errors reported earlier in the file are\n  /// generally more intuitive than those reported later, all other\n  /// things being equal.\n  pub fn visit(&mut self, name: String, usage: T, pos: SourceOffset) {\n    match self.0.remove(&name) {\n      None => self.0.insert(name, (usage, pos)),\n      Some((prev_usage, prev_pos)) => self.0.insert(name, (prev_usage.join(usage), prev_pos.min(pos))),\n    };\n  }\n\n  /// Removes the name from the table, returning its usage type if one\n  /// existed.\n  pub fn remove(&mut self, name: &str) -> Option<T> {\n    self.0.remove(name).map(|x| x.0)\n  }\n\n  /// An iterator over all names known to the `ClosureNames` instance.\n  pub fn names(&self) -> impl Iterator<Item=&str> {\n    self.0.keys().map(|x| &x[..])\n  }\n\n  /// As [`ClosureNames::names`] but takes ownership over the values\n  /// in `self`.\n  pub fn into_names(self) -> impl Iterator<Item=String> {\n    self.0.into_iter().map(|x| x.0)\n  }\n\n  /// Returns whether `self` is devoid of any names.\n  pub fn is_empty(&self) -> bool {\n    self.0.is_empty()\n  }\n\n  /// Adds all of the names from `b` into `self`. Any names which did\n  /// not exist in `self` are added, and those which did will have\n  /// their two usage values joined via [`Lattice::join`].\n  pub fn merge_with(&mut self, b: ClosureNames<T>) {\n    for (name, data, pos) in b.into_iter_with_offset() {\n      self.visit(name, data, pos);\n    }\n  }\n\n  /// Filters the `ClosureNames` instance in-place, retaining only\n  /// those values for which `f` returns true.\n  pub fn retain(&mut self, mut f: impl FnMut(&str, &mut T) -> bool) {\n    self.0.retain(|x, (y, _)| f(x, y))\n  }\n\n  // TODO \"with offset\" versions of iter(), iter_mut(), and a \"non-with-offset\" version of into_iter().\n\n  /// Iterates over the entries in the table.\n  pub fn iter(&self) -> impl Iterator<Item=(&str, &T)> {\n    self.0.iter().map(|(x, (y, _))| (&x[..], y))\n  }\n\n  /// Iterates over the entries in the table, allowing mutation on the\n  /// usage information.\n  pub fn iter_mut(&mut self) -> impl Iterator<Item=(&str, &mut T)> {\n    self.0.iter_mut().map(|(x, (y, _))| (&x[..], y))\n  }\n\n  /// Iterates over the entries in the table, taking ownership during iteration.\n  pub fn into_iter_with_offset(self) -> impl Iterator<Item=(String, T, SourceOffset)> {\n    self.0.into_iter().map(|(s, (t, p))| (s, t, p))\n  }\n\n  /// Iterates over the entries in the table, by immutable reference.\n  pub fn iter_with_offset(&self) -> impl Iterator<Item=(&str, &T, SourceOffset)> {\n    self.0.iter().map(|(s, (t, p))| (s.as_str(), t, *p))\n  }\n\n}\n\nimpl<T : Lattice> Default for ClosureNames<T> {\n  fn default() -> Self {\n    ClosureNames(HashMap::default())\n  }\n}\n"
  },
  {
    "path": "src/ir/decl.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::arglist::ordinary::ArgList;\nuse super::arglist::constructor::ConstructorArgList;\nuse super::arglist::simple::SimpleArgList;\nuse super::expr::{self, Expr, Locals, Functions};\nuse super::literal::Literal;\nuse super::import::ImportDecl;\nuse super::identifier::{Namespace, ClassNamespace, Id, IdLike};\nuse super::export::Visibility;\nuse crate::sxp::ast::AST;\nuse crate::sxp::dotted::DottedExpr;\nuse crate::gdscript::decl::Static;\nuse crate::gdscript::library;\nuse crate::pipeline::source::{SourceOffset, Sourced};\nuse crate::compile::body::class_initializer::InitTime;\nuse crate::compile::body::synthetic_field::{Getter, Setter};\n\nuse std::collections::HashMap;\nuse std::borrow::Cow;\nuse std::convert::TryInto;\nuse std::iter;\n\n#[derive(Clone, Debug, Eq, PartialEq, Default)]\npub struct TopLevel {\n  pub imports: Vec<ImportDecl>,\n  pub decls: Vec<Decl>,\n  pub minimalist_flag: bool,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum DeclF {\n  FnDecl(FnDecl),\n  MacroDecl(MacroDecl),\n  SymbolMacroDecl(SymbolMacroDecl),\n  ConstDecl(ConstDecl),\n  ClassDecl(ClassDecl),\n  EnumDecl(EnumDecl),\n  DeclareDecl(DeclareDecl),\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct Decl {\n  pub value: DeclF,\n  pub pos: SourceOffset,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct FnDecl {\n  pub visibility: Visibility,\n  pub call_magic: Option<String>,\n  pub name: String,\n  pub args: ArgList,\n  pub body: Expr,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct MacroDecl {\n  pub visibility: Visibility,\n  pub name: String,\n  pub args: ArgList,\n  pub body: Expr,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct SymbolMacroDecl {\n  pub visibility: Visibility,\n  pub name: String,\n  pub body: Expr,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ConstDecl {\n  pub visibility: Visibility,\n  pub name: String,\n  pub value: Expr,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct EnumDecl {\n  pub visibility: Visibility,\n  pub name: String,\n  pub clauses: Vec<(String, Option<Expr>)>,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ClassDecl {\n  pub visibility: Visibility,\n  pub name: String,\n  pub extends: String,\n  pub main_class: bool,\n  pub constructor: Option<ConstructorDecl>,\n  pub decls: Vec<ClassInnerDecl>,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ConstructorDecl {\n  pub args: ConstructorArgList,\n  pub super_call: SuperCall,\n  pub body: Expr,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct SuperCall {\n  pub call: Vec<Expr>,\n  pub pos: SourceOffset,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum ClassInnerDeclF {\n  ClassSignalDecl(ClassSignalDecl),\n  ClassConstDecl(ConstDecl),\n  ClassVarDecl(ClassVarDecl),\n  ClassFnDecl(ClassFnDecl),\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ClassInnerDecl {\n  pub value: ClassInnerDeclF,\n  pub pos: SourceOffset,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ClassSignalDecl {\n  pub name: String,\n  pub args: SimpleArgList,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ClassVarDecl {\n  pub export: Option<Export>,\n  pub name: String,\n  pub value: Option<Expr>,\n  pub init_time: InitTime,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ClassFnDecl {\n  /// Indicates whether or not the function is a static instance function.\n  pub is_static: Static,\n  /// If this field is true, then all of the arguments to this\n  /// function will be compiled to optional GDScript arguments whose\n  /// default values are `null`.\n  ///\n  /// There is no user-exposed way to set this flag to true. This is\n  /// used in `GDLisp.lisp` to emulate variable arguments for small\n  /// argument counts.\n  pub is_nullargs: bool,\n  /// The name of the function, which can be an ordinary name or a\n  /// getter or setter.\n  pub name: InstanceFunctionName,\n  /// The list of arguments to the function. These arguments are\n  /// always required on the GDLisp side. When compiled, these\n  /// arguments compile to required arguments if `is_nullargs` is\n  /// false, or optional ones otherwise.\n  pub args: SimpleArgList,\n  /// The body of the function, as a single [`Expr`]. For functions\n  /// which require multiple expressions, [`expr::ExprF::Progn`] can be\n  /// used.\n  pub body: Expr,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct DeclareDecl {\n  pub visibility: Visibility,\n  pub declare_type: DeclareType,\n  pub name: String,\n  // If this doesn't exist, it will be treated as\n  // `lisp_to_gd(self.name)` after compilation. The declaration is\n  // allowed to override this, however.\n  pub target_name: Option<String>,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum DeclareType {\n  /// A value, with no hint as to its value or `const` status.\n  Value,\n  /// A value, equivalent to [`DeclareType::Value`], but with a value\n  /// hint indicating that its value is immutable at runtime.\n  Constant,\n  /// A function available at the top-level of the current module.\n  Function(ArgList),\n  /// A superglobal value, available from everywhere in the Godot\n  /// ecosystem without import.\n  Superglobal,\n  /// A superglobal function, available from everywhere in the Godot\n  /// ecosystem without import.\n  SuperglobalFn(ArgList),\n}\n\n// TODO This is a bit confusing, since \"export\" is used in GDLisp to\n// mean \"exported from a module\", not \"exported to the interface\". We\n// should probably rename this so that GDScript \"exports\" are called\n// something else.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct Export {\n  pub args: Vec<Expr>,\n}\n\n/// The name of an instance function in GDLisp, which can either be an\n/// ordinary string or a special accessor.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum InstanceFunctionName {\n  /// An ordinary string name, which will compile to an ordinary\n  /// method in GDScript.\n  Ordinary(String),\n  /// A setter for the field with the given name.\n  Setter(String),\n  /// A getter for the field with the given name.\n  Getter(String),\n}\n\n#[derive(Clone, Debug, Copy, Eq, PartialEq)]\npub struct DuplicateMainClassError(pub SourceOffset);\n\nimpl TopLevel {\n\n  pub fn new() -> TopLevel {\n    TopLevel::default()\n  }\n\n  /// Find the class in `self` for which [`ClassDecl::main_class`] is\n  /// true. If there is no such class, then `None` is returned. If\n  /// there are multiple such classes, then\n  /// [`DuplicateMainClassError`] is returned.\n  pub fn find_main_class(&self) -> Result<Option<&ClassDecl>, DuplicateMainClassError> {\n    let all_main_classes: Vec<_> = self.decls.iter().filter(|decl| {\n      if let DeclF::ClassDecl(cdecl) = &decl.value {\n        cdecl.main_class\n      } else {\n        false\n      }\n    }).collect();\n    match all_main_classes.len() {\n      0 => {\n        // No main class; return None\n        Ok(None)\n      }\n      1 => {\n        // Single main class; return it\n        if let DeclF::ClassDecl(cdecl) = &all_main_classes[0].value {\n          Ok(Some(cdecl))\n        } else {\n          panic!(\"Internal error in TopLevel::find_main_class (this is a bug in GDLisp)\")\n        }\n      }\n      _ => {\n        // Duplicate main classes; produce an error at the position of\n        // the second\n        let second_main_class = all_main_classes[1];\n        Err(DuplicateMainClassError(second_main_class.pos))\n      }\n    }\n  }\n\n  pub fn inner_exprs(&self) -> impl Iterator<Item=&Expr> + '_ {\n    self.decls.iter().flat_map(|x| x.inner_exprs())\n  }\n\n}\n\nimpl Decl {\n\n  pub fn new(value: DeclF, pos: SourceOffset) -> Decl {\n    Decl { value, pos }\n  }\n\n  pub fn to_id(&self) -> Id {\n    Id::new(self.namespace(), self.name().to_owned())\n  }\n\n  pub fn id_like<'a>(&'a self) -> Box<dyn IdLike<NS=Namespace> + 'a> {\n    Id::build(self.namespace(), self.name())\n  }\n\n  pub fn name(&self) -> &str {\n    match &self.value {\n      DeclF::FnDecl(decl) => &decl.name,\n      DeclF::MacroDecl(decl) => &decl.name,\n      DeclF::SymbolMacroDecl(decl) => &decl.name,\n      DeclF::ConstDecl(decl) => &decl.name,\n      DeclF::ClassDecl(decl) => &decl.name,\n      DeclF::EnumDecl(decl) => &decl.name,\n      DeclF::DeclareDecl(decl) => &decl.name,\n    }\n  }\n\n  // Gets the direct dependencies required by the declaration.\n  pub fn dependencies(&self) -> HashMap<Id, SourceOffset> {\n    match &self.value {\n      DeclF::FnDecl(f) => {\n        let mut ids: HashMap<Id, SourceOffset> = f.body.get_ids().collect();\n        for name in f.args.iter_vars() {\n          ids.remove(&*Id::build(Namespace::Value, name));\n        }\n        ids\n      }\n      DeclF::MacroDecl(m) => {\n        let mut ids: HashMap<Id, SourceOffset> = m.body.get_ids().collect();\n        for name in m.args.iter_vars() {\n          ids.remove(&*Id::build(Namespace::Value, name));\n        }\n        ids\n      }\n      DeclF::SymbolMacroDecl(m) => {\n        m.body.get_ids().collect()\n      }\n      DeclF::ConstDecl(c) => {\n        c.value.get_ids().collect()\n      }\n      DeclF::ClassDecl(c) => {\n        let mut ids = HashMap::new();\n        ids.insert(Id::new(Namespace::Value, c.extends.to_owned()), self.pos);\n        ids.extend(c.constructor_or_default(SourceOffset::from(0)).dependencies());\n        for d in &c.decls {\n          ids.extend(d.dependencies());\n        }\n        ids.remove(&Id::new(Namespace::Value, String::from(\"self\")));\n        ids\n      }\n      DeclF::EnumDecl(enum_decl) => {\n        let mut ids = HashMap::new();\n        for (_, expr) in &enum_decl.clauses {\n          if let Some(expr) = expr {\n            ids.extend(expr.get_ids());\n          }\n        }\n        ids\n      }\n      DeclF::DeclareDecl(_) => {\n        // Declare declarations have no dependencies; they are\n        // assertions to the compiler.\n        HashMap::new()\n      }\n    }\n  }\n\n  pub fn is_macro(&self) -> bool {\n    matches!(&self.value, DeclF::MacroDecl(_) | DeclF::SymbolMacroDecl(_))\n  }\n\n  #[deprecated(note=\"Use visibility() or export::Visibility constants instead\")]\n  pub fn is_exported_by_default(&self) -> bool {\n    // (sys/declare ...) statements are never exported and are always\n    // file-local by default.\n    !(matches!(&self.value, DeclF::DeclareDecl(_)))\n  }\n\n  pub fn namespace(&self) -> Namespace {\n    match &self.value {\n      DeclF::FnDecl(_) => Namespace::Function,\n      DeclF::MacroDecl(_) => Namespace::Function,\n      DeclF::SymbolMacroDecl(_) => Namespace::Value,\n      DeclF::ConstDecl(_) => Namespace::Value,\n      DeclF::ClassDecl(_) => Namespace::Value,\n      DeclF::EnumDecl(_) => Namespace::Value,\n      DeclF::DeclareDecl(d) => d.declare_type.namespace(),\n    }\n  }\n\n  pub fn visibility(&self) -> Visibility {\n    match &self.value {\n      DeclF::FnDecl(d) => d.visibility,\n      DeclF::MacroDecl(d) => d.visibility,\n      DeclF::SymbolMacroDecl(d) => d.visibility,\n      DeclF::ConstDecl(d) => d.visibility,\n      DeclF::ClassDecl(d) => d.visibility,\n      DeclF::EnumDecl(d) => d.visibility,\n      DeclF::DeclareDecl(d) => d.visibility,\n    }\n  }\n\n  pub fn visibility_mut(&mut self) -> &mut Visibility {\n    match &mut self.value {\n      DeclF::FnDecl(d) => &mut d.visibility,\n      DeclF::MacroDecl(d) => &mut d.visibility,\n      DeclF::SymbolMacroDecl(d) => &mut d.visibility,\n      DeclF::ConstDecl(d) => &mut d.visibility,\n      DeclF::ClassDecl(d) => &mut d.visibility,\n      DeclF::EnumDecl(d) => &mut d.visibility,\n      DeclF::DeclareDecl(d) => &mut d.visibility,\n    }\n  }\n\n  pub fn inner_exprs(&self) -> Box<dyn Iterator<Item=&Expr> + '_> {\n    match &self.value {\n      DeclF::FnDecl(cdecl) => Box::new(iter::once(&cdecl.body)),\n      DeclF::MacroDecl(cdecl) => Box::new(iter::once(&cdecl.body)),\n      DeclF::SymbolMacroDecl(cdecl) => Box::new(iter::once(&cdecl.body)),\n      DeclF::ConstDecl(cdecl) => Box::new(iter::once(&cdecl.value)),\n      DeclF::DeclareDecl(_) => Box::new(iter::empty()),\n      DeclF::EnumDecl(cdecl) => {\n        Box::new(cdecl.clauses.iter().flat_map(|(_name, value)| value))\n      }\n      DeclF::ClassDecl(cdecl) => {\n        let constructor_exprs = cdecl.constructor.iter().flat_map(|constructor| {\n          constructor.super_call.call.iter()\n            .chain(iter::once(&constructor.body))\n        });\n        let inner_exprs = cdecl.decls.iter().flat_map(ClassInnerDecl::inner_exprs);\n        Box::new(constructor_exprs.chain(inner_exprs))\n      }\n    }\n  }\n\n}\n\nimpl EnumDecl {\n\n  pub fn value_names(&self) -> impl Iterator<Item=&str> {\n    self.clauses.iter().map(|(x, _)| &**x)\n  }\n\n}\n\nimpl ClassDecl {\n\n  pub fn new(name: String, extends: String) -> ClassDecl {\n    ClassDecl {\n      visibility: Visibility::CLASS,\n      name: name,\n      extends: extends,\n      main_class: false,\n      constructor: None,\n      decls: vec!(),\n    }\n  }\n\n  /// The class' constructor, or an empty constructor if there is no\n  /// constructor. In the latter case, the empty constructor will be\n  /// reported as being at source position default_pos, which should\n  /// be the position of the start of the class declaration.\n  pub fn constructor_or_default(&self, default_pos: SourceOffset) -> Cow<ConstructorDecl> {\n    self.constructor.as_ref().map_or_else(|| Cow::Owned(ConstructorDecl::empty(default_pos)), Cow::Borrowed)\n  }\n\n}\n\nimpl ConstructorDecl {\n\n  /// An empty constructor, marked as starting at `pos`. `pos` should\n  /// be the position of the start of the class declaration.\n  pub fn empty(pos: SourceOffset) -> ConstructorDecl {\n    ConstructorDecl {\n      args: ConstructorArgList { args: vec!() },\n      super_call: SuperCall::empty(pos),\n      body: Expr::literal(Literal::Nil, pos),\n    }\n  }\n\n  pub fn dependencies(&self) -> HashMap<Id, SourceOffset> {\n    let mut ids: HashMap<Id, SourceOffset> = self.body.get_ids().collect();\n    for expr in &self.super_call.call {\n      for (id, pos) in expr.get_ids() {\n        ids.insert(id, pos);\n      }\n    }\n    for (name, is_instance_field) in self.args.iter_vars() {\n      // Instance field arguments are accessed directly on `self` and\n      // are not available as GDLisp-side local variables.\n      if !is_instance_field {\n        ids.remove(&*Id::build(Namespace::Value, name));\n      }\n    }\n    ids.remove(&*Id::build(Namespace::Value, \"self\"));\n    ids\n  }\n\n  pub fn get_names(&self) -> (Locals, Functions) {\n    let (mut loc, func) = self.body.get_names();\n    for (name, is_instance_field) in self.args.iter_vars() {\n      // Instance field arguments are accessed directly on `self` and\n      // are not available as GDLisp-side local variables.\n      if !is_instance_field {\n        loc.remove(name);\n      }\n    }\n    loc.remove(\"self\");\n    (loc, func)\n  }\n\n}\n\nimpl SuperCall {\n\n  /// An empty super call, which invokes the super constructor with no\n  /// arguments.\n  pub fn empty(pos: SourceOffset) -> SuperCall {\n    SuperCall { call: vec!(), pos: pos }\n  }\n\n}\n\nimpl ClassInnerDecl {\n\n  pub fn new(value: ClassInnerDeclF, pos: SourceOffset) -> ClassInnerDecl {\n    ClassInnerDecl { value, pos }\n  }\n\n  pub fn dependencies(&self) -> HashMap<Id, SourceOffset> {\n    match &self.value {\n      ClassInnerDeclF::ClassSignalDecl(_) => HashMap::new(),\n      ClassInnerDeclF::ClassConstDecl(_) => HashMap::new(),\n      ClassInnerDeclF::ClassVarDecl(_) => HashMap::new(),\n      ClassInnerDeclF::ClassFnDecl(func) => {\n        let mut ids: HashMap<Id, SourceOffset> = func.body.get_ids().collect();\n        for name in func.args.iter_vars() {\n          ids.remove(&*Id::build(Namespace::Value, name));\n        }\n        ids.remove(&*Id::build(Namespace::Value, \"self\"));\n        ids\n      }\n    }\n  }\n\n  pub fn name(&self) -> Cow<str> {\n    match &self.value {\n      ClassInnerDeclF::ClassSignalDecl(signal) => Cow::Borrowed(&signal.name),\n      ClassInnerDeclF::ClassConstDecl(constant) => Cow::Borrowed(&constant.name),\n      ClassInnerDeclF::ClassVarDecl(var) => Cow::Borrowed(&var.name),\n      ClassInnerDeclF::ClassFnDecl(func) => func.name.method_name(),\n    }\n  }\n\n  pub fn namespace(&self) -> ClassNamespace {\n    match &self.value {\n      ClassInnerDeclF::ClassSignalDecl(_) => ClassNamespace::Signal,\n      ClassInnerDeclF::ClassConstDecl(_) => ClassNamespace::Value,\n      ClassInnerDeclF::ClassVarDecl(_) => ClassNamespace::Value,\n      ClassInnerDeclF::ClassFnDecl(_) => ClassNamespace::Function,\n    }\n  }\n\n  pub fn get_names(&self) -> (Locals, Functions) {\n    match &self.value {\n      ClassInnerDeclF::ClassSignalDecl(_) | ClassInnerDeclF::ClassConstDecl(_) => {\n        (Locals::new(), Functions::new())\n      }\n      ClassInnerDeclF::ClassVarDecl(vdecl) => {\n        if let Some(initial_value) = &vdecl.value {\n          initial_value.get_names()\n        } else {\n          (Locals::new(), Functions::new())\n        }\n      }\n      ClassInnerDeclF::ClassFnDecl(fndecl) => {\n        let (mut loc, func) = fndecl.body.get_names();\n        for name in fndecl.args.iter_vars() {\n          loc.remove(name);\n        }\n        if !bool::from(fndecl.is_static) {\n          loc.remove(\"self\");\n        }\n        (loc, func)\n      }\n    }\n  }\n\n  pub fn is_static(&self) -> bool {\n    match &self.value {\n      ClassInnerDeclF::ClassSignalDecl(_) | ClassInnerDeclF::ClassVarDecl(_) => false,\n      ClassInnerDeclF::ClassConstDecl(_) => true,\n      ClassInnerDeclF::ClassFnDecl(decl) => decl.is_static.into(),\n    }\n  }\n\n  pub fn inner_exprs(&self) -> Box<dyn Iterator<Item=&Expr> + '_> {\n    match &self.value {\n      ClassInnerDeclF::ClassSignalDecl(_) => Box::new(iter::empty()),\n      ClassInnerDeclF::ClassConstDecl(cdecl) => Box::new(iter::once(&cdecl.value)),\n      ClassInnerDeclF::ClassVarDecl(cdecl) => Box::new(cdecl.value.iter()),\n      ClassInnerDeclF::ClassFnDecl(cdecl) => Box::new(iter::once(&cdecl.body)),\n    }\n  }\n\n}\n\nimpl DeclareType {\n\n  pub fn namespace(&self) -> Namespace {\n    match self {\n      DeclareType::Value | DeclareType::Constant | DeclareType::Superglobal => Namespace::Value,\n      DeclareType::Function(_) | DeclareType::SuperglobalFn(_) => Namespace::Function,\n    }\n  }\n\n}\n\nimpl InstanceFunctionName {\n\n  /// Given an [`AST`] representing the name of an instance function,\n  /// returns the name as an [`InstanceFunctionName`]. Specifically,\n  /// the following are attempted in order.\n  ///\n  /// 1. If the [`AST`] argument holds a\n  /// [`Literal::Symbol`](crate::sxp::literal::Literal::Symbol), then\n  /// this is an [`InstanceFunctionName::Ordinary`] name.\n  ///\n  /// 2. Otherwise, if the argument is a (proper) list of two\n  /// elements, both of which are symbols, and if the first symbol is\n  /// `set`, then this is an [`InstanceFunctionName::Setter`].\n  ///\n  /// 3. If the argument is a (proper) list of two elements, both of\n  /// which are symbols, and if the first symbol is `get`, then this\n  /// is an [`InstanceFunctionName::Getter`].\n  ///\n  /// 4. Otherwise, parsing fails and `None` is returned.\n  pub fn parse(ast: &AST) -> Option<InstanceFunctionName> {\n    if let Some(name) = ast.as_symbol_ref() {\n      return Some(InstanceFunctionName::Ordinary(name.to_owned()));\n    }\n    let list: Vec<_> = DottedExpr::new(ast).try_into().ok()?;\n    if let [accessor_type, field_name] = &*list {\n      if let Some(field_name) = field_name.as_symbol_ref() {\n        if let Some(accessor_type) = accessor_type.as_symbol_ref() {\n          match accessor_type {\n            \"set\" => { return Some(InstanceFunctionName::Setter(field_name.to_owned())); }\n            \"get\" => { return Some(InstanceFunctionName::Getter(field_name.to_owned())); }\n            _ => {}\n          }\n        }\n      }\n    }\n    None\n  }\n\n  /// The name of the method being defined, using the GDLisp naming\n  /// conventions (e.g., characters such as `-` will *not* be\n  /// converted in the returned value). For\n  /// [`InstanceFunctionName::Ordinary`], this is simply the declared\n  /// name of the method. For other types of method names, specialized\n  /// prefixes will be added to compute the runtime name.\n  pub fn method_name(&self) -> Cow<str> {\n    match self {\n      InstanceFunctionName::Ordinary(name) => Cow::Borrowed(name),\n      InstanceFunctionName::Setter(field_name) => Cow::Owned(Setter::method_name(field_name)),\n      InstanceFunctionName::Getter(field_name) => Cow::Owned(Getter::method_name(field_name)),\n    }\n  }\n\n  /// Returns true if this is the name of a constructor function, i.e.\n  /// if this is an ordinary function whose name is equal to\n  /// [`CONSTRUCTOR_NAME`](library::CONSTRUCTOR_NAME).\n  pub fn is_constructor_function(&self) -> bool {\n    if let InstanceFunctionName::Ordinary(fname) = self {\n      fname == library::CONSTRUCTOR_NAME\n    } else {\n      false\n    }\n  }\n\n}\n\nimpl Sourced for Decl {\n  type Item = DeclF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &DeclF {\n    &self.value\n  }\n\n}\n\nimpl Sourced for ClassInnerDecl {\n  type Item = ClassInnerDeclF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &ClassInnerDeclF {\n    &self.value\n  }\n\n}\n\nimpl From<DeclareType> for Namespace {\n  fn from(d: DeclareType) -> Namespace {\n    d.namespace()\n  }\n}\n\nimpl From<SymbolMacroDecl> for MacroDecl {\n  fn from(decl: SymbolMacroDecl) -> MacroDecl {\n    MacroDecl {\n      visibility: decl.visibility,\n      name: decl.name,\n      args: ArgList::empty(),\n      body: decl.body,\n    }\n  }\n}\n\nimpl From<(ClassDecl, Vec<Expr>)> for expr::LambdaClass {\n  fn from(arg: (ClassDecl, Vec<Expr>)) -> expr::LambdaClass {\n    let (decl, args) = arg;\n    expr::LambdaClass {\n      extends: decl.extends,\n      args: args,\n      constructor: decl.constructor,\n      decls: decl.decls,\n    }\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::AST_PARSER;\n\n  fn parse_ast(input: &str) -> AST {\n    AST_PARSER.parse(input).unwrap()\n  }\n\n  fn sample_class(class_name: &str, main_class: bool) -> ClassDecl {\n    ClassDecl {\n      visibility: Visibility::Public,\n      name: String::from(class_name),\n      extends: String::from(\"Reference\"),\n      main_class,\n      constructor: None,\n      decls: vec!(),\n    }\n  }\n\n  #[test]\n  fn find_main_class_test_1() {\n    let example = TopLevel {\n      imports: vec!(),\n      decls: vec!(\n        Decl::new(DeclF::ClassDecl(sample_class(\"Foo\", false)), SourceOffset::default()),\n        Decl::new(DeclF::ClassDecl(sample_class(\"Bar\", false)), SourceOffset::default()),\n      ),\n      minimalist_flag: false,\n    };\n    assert_eq!(example.find_main_class(), Ok(None));\n  }\n\n  #[test]\n  fn find_main_class_test_2() {\n    let example = TopLevel {\n      imports: vec!(),\n      decls: vec!(\n        Decl::new(DeclF::ClassDecl(sample_class(\"Foo\", true)), SourceOffset::default()),\n        Decl::new(DeclF::ClassDecl(sample_class(\"Bar\", false)), SourceOffset::default()),\n      ),\n      minimalist_flag: false,\n    };\n    assert_eq!(example.find_main_class(), Ok(Some(&sample_class(\"Foo\", true))));\n  }\n\n  #[test]\n  fn find_main_class_test_3() {\n    let example = TopLevel {\n      imports: vec!(),\n      decls: vec!(\n        Decl::new(DeclF::ClassDecl(sample_class(\"Foo\", false)), SourceOffset::default()),\n        Decl::new(DeclF::ClassDecl(sample_class(\"Bar\", true)), SourceOffset::default()),\n      ),\n      minimalist_flag: false,\n    };\n    assert_eq!(example.find_main_class(), Ok(Some(&sample_class(\"Bar\", true))));\n  }\n\n  #[test]\n  fn find_main_class_test_4() {\n    let example = TopLevel {\n      imports: vec!(),\n      decls: vec!(\n        Decl::new(DeclF::ClassDecl(sample_class(\"Foo\", true)), SourceOffset::default()),\n        Decl::new(DeclF::ClassDecl(sample_class(\"Bar\", true)), SourceOffset::default()),\n      ),\n      minimalist_flag: false,\n    };\n    assert_eq!(example.find_main_class(), Err(DuplicateMainClassError(SourceOffset(0))));\n  }\n\n  #[test]\n  fn find_main_class_test_5() {\n    // Nontrivial source offset\n    let example = TopLevel {\n      imports: vec!(),\n      decls: vec!(\n        Decl::new(DeclF::ClassDecl(sample_class(\"Foo\", true)), SourceOffset(10)),\n        Decl::new(DeclF::ClassDecl(sample_class(\"Bar\", true)), SourceOffset(20)),\n      ),\n      minimalist_flag: false,\n    };\n    // Should be reported at the source position of the *second* main class\n    assert_eq!(example.find_main_class(), Err(DuplicateMainClassError(SourceOffset(20))));\n  }\n\n  #[test]\n  fn parse_instance_function_name_test() {\n\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"abc\")),\n               Some(InstanceFunctionName::Ordinary(String::from(\"abc\"))));\n\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"foo-bar\")),\n               Some(InstanceFunctionName::Ordinary(String::from(\"foo-bar\"))));\n\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"(set pizza)\")),\n               Some(InstanceFunctionName::Setter(String::from(\"pizza\"))));\n\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"(get pizza)\")),\n               Some(InstanceFunctionName::Getter(String::from(\"pizza\"))));\n\n  }\n\n  #[test]\n  fn parse_instance_function_name_failures_test() {\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"0\")), None);\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"(get a b c)\")), None);\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"(put abc)\")), None);\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"(get 100)\")), None);\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"\\\"alpha\\\"\")), None);\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"()\")), None);\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"(set)\")), None);\n    assert_eq!(InstanceFunctionName::parse(&parse_ast(\"(get)\")), None);\n  }\n\n  #[test]\n  fn is_constructor_name_test() {\n    assert!(InstanceFunctionName::Ordinary(String::from(\"_init\")).is_constructor_function());\n    assert!(!InstanceFunctionName::Ordinary(String::from(\"init\")).is_constructor_function());\n    assert!(!InstanceFunctionName::Ordinary(String::from(\"foobar\")).is_constructor_function());\n    assert!(!InstanceFunctionName::Setter(String::from(\"_init\")).is_constructor_function());\n    assert!(!InstanceFunctionName::Getter(String::from(\"_init\")).is_constructor_function());\n    assert!(!InstanceFunctionName::Setter(String::from(\"abcdef\")).is_constructor_function());\n    assert!(!InstanceFunctionName::Getter(String::from(\"baz\")).is_constructor_function());\n  }\n\n}\n"
  },
  {
    "path": "src/ir/declaration_table.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! A [`DeclarationTable`] is a sequence of declarations which can be\n//! efficiently indexed by [`Id`].\n\nuse super::Decl;\nuse super::identifier::{Id, IdLike, Namespace};\nuse crate::compile::error::{GDError, GDErrorF};\n\nuse std::collections::HashMap;\nuse std::mem;\n\n/// A `DeclarationTable` stores [`Decl`] declarations, indexed by\n/// their names. A `DeclarationTable` should be thought of as a\n/// `Vec<Decl>`, but with efficient access indexed by the\n/// [identifier](Id) of a declaration. There are `From` and `Into`\n/// instances converting from and to `Vec<Decl>`.\n///\n/// Note that, for indexing purposes, the identifier of a `Decl` is\n/// given by [`Decl::to_id`].\n#[derive(Clone, Debug, Default)]\npub struct DeclarationTable {\n  values: HashMap<Id, usize>,\n  in_order: Vec<Decl>,\n}\n\nimpl DeclarationTable {\n\n  /// Constructs a new, empty `DeclarationTable`.\n  pub fn new() -> DeclarationTable {\n    DeclarationTable::default()\n  }\n\n  /// Gets the declaration with the given identifier.\n  pub fn get<'a>(&self, id: &(dyn IdLike<NS=Namespace> + 'a)) -> Option<&Decl> {\n    self.values.get(id).map(|idx| &self.in_order[*idx])\n  }\n\n  /// Adds a [`Decl`] to the `DeclarationTable`, to be indexed via its\n  /// [`Decl::to_id`] value. If a declaration with that ID is already\n  /// present in this table, then that declaration is replaced with\n  /// `value`. Otherwise, `value` is added at the end of the table.\n  #[allow(clippy::map_entry)] // Using the Entry API would require that value be cloned.\n  pub fn add(&mut self, value: Decl) -> Option<Decl> {\n    let id = value.to_id();\n    let new_idx = self.in_order.len();\n    if self.values.contains_key(&id) {\n      let idx = *self.values.get(&id).unwrap();\n      Some(mem::replace(&mut self.in_order[idx], value))\n    } else {\n      self.values.insert(id, new_idx);\n      self.in_order.push(value);\n      None\n    }\n  }\n\n  /// Adds a [`Decl`] to the `DeclarationTable`, if no declaration\n  /// with that name already exists. If a declaration with the given\n  /// name already exists, an error is reported and the table is\n  /// unmodified.\n  pub fn add_unless_exists(&mut self, value: Decl) -> Result<(), GDError> {\n    if self.get(&*value.id_like()).is_some() {\n      Err(\n        GDError::new(GDErrorF::DuplicateName(value.namespace().into(), value.name().to_owned()), value.pos),\n      )\n    } else {\n      let result = self.add(value);\n      assert!(result.is_none(), \"Internal error in DeclarationTable::add_unless_exists\");\n      Ok(())\n    }\n  }\n\n  /// Removes the declaration with identifier `id` from the table and\n  /// returns it. Returns `None` if no matching declaration exists.\n  pub fn del<'a>(&mut self, id: &(dyn IdLike<NS=Namespace> + 'a)) -> Option<Decl> {\n    if let Some(idx) = self.values.remove(id) {\n      let decl = self.in_order.remove(idx);\n      for v in self.values.values_mut() {\n        // We shifted declarations over, so we need to update all\n        // indices that were to the right of the removed one.\n        if *v > idx {\n          *v -= 1;\n        }\n      }\n      Some(decl)\n    } else {\n      None\n    }\n  }\n\n  /// Checks whether a declaration with the given identifier exists in\n  /// the table. Equivalent to `self.get(id).is_some()`.\n  pub fn has<'a>(&self, id: &(dyn IdLike<NS=Namespace> + 'a)) -> bool {\n    self.get(id).is_some()\n  }\n\n  /// Filters the `DeclarationTable` by the predicate `condition`,\n  /// returning a new table where only the entries which returned true\n  /// under the predicate are kept.\n  pub fn filter(&self, condition: impl FnMut(&Decl) -> bool) -> DeclarationTable {\n    let vec = Vec::from(self.clone());\n    let filtered = vec.into_iter().filter(condition).collect::<Vec<_>>();\n    filtered.into()\n  }\n\n  /// Iterates over the elements of `self` in order.\n  pub fn iter(&self) -> impl Iterator<Item=&Decl> {\n    self.in_order.iter()\n  }\n\n}\n\nimpl From<DeclarationTable> for Vec<Decl> {\n\n  fn from(table: DeclarationTable) -> Vec<Decl> {\n    table.in_order\n  }\n\n}\n\nimpl From<Vec<Decl>> for DeclarationTable {\n\n  fn from(decls: Vec<Decl>) -> DeclarationTable {\n    let mut table = DeclarationTable::new();\n    for decl in decls {\n      table.add(decl);\n    }\n    table\n  }\n\n}\n"
  },
  {
    "path": "src/ir/depends.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::declaration_table::DeclarationTable;\nuse super::identifier::{IdLike, Id, Namespace};\nuse crate::pipeline::source::SourceOffset;\n\nuse std::collections::{HashSet, HashMap};\n\n// TODO Make these fields private\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct Dependencies {\n  pub known: HashMap<Id, SourceOffset>,\n  pub imports: HashMap<Id, SourceOffset>,\n  pub unknown: HashMap<Id, SourceOffset>,\n}\n\n#[derive(Debug)]\npub enum DependencyError {\n  UnknownName(Id, SourceOffset),\n}\n\nimpl Dependencies {\n\n  pub fn identify<'a>(table: &DeclarationTable,\n                      known_imports: &HashSet<Id>,\n                      id: &(dyn IdLike<NS=Namespace> + 'a),\n                      pos: SourceOffset)\n                      -> Dependencies {\n    let mut visited = HashMap::new();\n    let mut imports = HashMap::new();\n    let mut unknown = HashMap::new();\n    match table.get(id) {\n      None => {\n        // No traversal necessary.\n        unknown.insert(id.to_owned(), pos);\n      }\n      Some(initial) => {\n        let mut frontier = vec!((initial, pos));\n        while let Some((current, pos)) = frontier.pop() {\n          if visited.contains_key(&*current.id_like()) {\n            visited.entry(current.to_id()).and_modify(|old_pos: &mut SourceOffset| {\n              *old_pos = (*old_pos).min(pos);\n            });\n            continue;\n          }\n          let deps = current.dependencies();\n          for (dep, pos) in deps {\n            if let Some(next) = table.get(&dep) {\n              frontier.push((next, pos));\n            } else if known_imports.contains(&dep) {\n              imports.insert(dep, pos);\n            } else {\n              unknown.insert(dep, pos);\n            }\n          }\n          visited.insert(current.to_id(), pos);\n      }\n      }\n    }\n    let mut known = visited;\n    for imported_name in imports.keys() {\n      known.remove(imported_name);\n    }\n    Dependencies { known, imports, unknown }\n  }\n\n  // TODO Rather than purge after the fact, we can use the imports\n  // argument above to account for built-ins.\n  pub fn purge_unknowns<'a, 'b, I>(&mut self, purge: I)\n  where I : Iterator<Item=&'a (dyn IdLike<NS=Namespace> + 'b)>,\n        'b : 'a {\n    for s in purge {\n      self.unknown.remove(s);\n    }\n  }\n\n  pub fn try_into_knowns(self) -> Result<HashSet<Id>, DependencyError> {\n    // Note: We explicitly don't care about imports here. As long as\n    // all names are either knowns or imports (and there are no\n    // unknowns), then we can safely discard the imports and keep only\n    // the knowns, as the imports are already loaded elsewhere.\n    if let Some((unknown, pos)) = self.unknown.into_iter().next() {\n      Err(DependencyError::UnknownName(unknown, pos))\n    } else {\n      Ok(self.known.into_iter().map(|(k, _)| k).collect())\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/ir/export.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Export and visibility rules for GDLisp modules.\n\nuse super::decl::Decl;\nuse super::identifier::Id;\n\n/// A name defined in a GDLisp module is either public or private. A\n/// private name is only accessible from the current module and cannot\n/// be imported into other modules. A public name can be imported and\n/// used in other modules.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum Visibility { Public, Private }\n\nimpl Visibility {\n\n  /// Default visibility for function declarations.\n  pub const FUNCTION: Visibility = Visibility::Public;\n\n  /// Default visibility for macro declarations.\n  pub const MACRO: Visibility = Visibility::Public;\n\n  /// Default visibility for symbol macro declarations.\n  pub const SYMBOL_MACRO: Visibility = Visibility::Public;\n\n  /// Default visibility for constant declarations.\n  pub const CONST: Visibility = Visibility::Public;\n\n  /// Default visibility for class declarations.\n  pub const CLASS: Visibility = Visibility::Public;\n\n  /// Default visibility for object declarations.\n  pub const OBJECT: Visibility = Visibility::Public;\n\n  /// Default visibility for enum declarations.\n  pub const ENUM: Visibility = Visibility::Public;\n\n  /// Default visibility for `sys/declare` declarations.\n  pub const DECLARE: Visibility = Visibility::Private;\n\n}\n\n/// Returns a vector of identifiers exported from the declarations\n/// `decls`. Private identifiers are *not* included in this vector.\npub fn get_export_list<'a>(decls: impl IntoIterator<Item=&'a Decl>) -> Vec<Id> {\n  let mut exports = Vec::new();\n  for decl in decls {\n    if decl.visibility() == Visibility::Public {\n      exports.push(decl.to_id());\n    }\n  }\n  exports\n}\n"
  },
  {
    "path": "src/ir/expr.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::literal;\nuse super::decl;\nuse super::arglist::ordinary::ArgList;\nuse super::closure_names::ClosureNames;\nuse super::access_type::AccessType;\nuse super::identifier::{Namespace, Id};\nuse super::special_ref::SpecialRef;\nuse super::special_form::local_binding;\nuse crate::sxp::ast::AST;\nuse crate::sxp::literal::{Literal as ASTLiteral};\nuse crate::compile::names;\nuse crate::pipeline::source::{SourceOffset, Sourced};\nuse crate::runner::path::RPathBuf;\n\nuse std::collections::HashSet;\nuse std::collections::hash_map::RandomState;\nuse std::borrow::Cow;\n\npub const DEFAULT_SPLIT_NAME: &str = \"_split\";\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum ExprF {\n  BareName(BareName), // A (possibly atomic) bare name, referring to a variable.\n  Call(CallTarget, String, Vec<Expr>),\n  Literal(literal::Literal),\n  Progn(Vec<Expr>),\n  CondStmt(Vec<(Expr, Option<Expr>)>),\n  WhileStmt(Box<Expr>, Box<Expr>),\n  ForStmt(String, Box<Expr>, Box<Expr>),\n  Let(Vec<LocalVarClause>, Box<Expr>),\n  FunctionLet(FunctionBindingType, Vec<LocalFnClause>, Box<Expr>),\n  Lambda(ArgList, Box<Expr>),\n  FuncRef(FuncRefTarget),\n  Assign(AssignTarget, Box<Expr>),\n  Quote(AST),\n  FieldAccess(Box<Expr>, String),\n  LambdaClass(Box<LambdaClass>),\n  Yield(Option<(Box<Expr>, Box<Expr>)>),\n  Assert(Box<Expr>, Option<Box<Expr>>),\n  Return(Box<Expr>),\n  Break,\n  Continue,\n  SpecialRef(SpecialRef),\n  ContextualFilename(RPathBuf),\n  Split(String, Box<Expr>), // Compiles the inner expression, but forces it to be stored in a local variable with a generated name (the string argument is a prefix for the name)\n  Preload(String),\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct Expr {\n  pub value: ExprF,\n  pub pos: SourceOffset,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum AssignTarget {\n  Variable(SourceOffset, String),\n  InstanceField(SourceOffset, Box<Expr>, String),\n}\n\n/// The object on which a function call is being made, if any.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum CallTarget {\n  /// The call is being made on the current scope itself, not on any\n  /// particular object.\n  Scoped,\n  /// The call is being made on the special `super` object.\n  ///\n  /// This does *not* include superclass *constructor* calls, which\n  /// are handled by a special method modifier in the syntax itself.\n  /// See\n  /// [`compile_class_inner_decl`](crate::ir::incremental::IncCompiler::compile_class_inner_decl)\n  /// for details on those super calls.\n  Super,\n  /// The call is being made on a function whose name is considered\n  /// atomic. This is similar to `Scoped` but the name will not be\n  /// considered during any semantic analysis and will be passed\n  /// through to GDScript unmodified.\n  Atomic,\n  /// The call is being made on an ordinary object in GDLisp. That is,\n  /// this is a method call.\n  Object(Box<Expr>),\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum BareName {\n  /// An ordinary name, referring to a variable in GDLisp.\n  Plain(String),\n  /// An atomic name, which will be translated literally into GDScript\n  /// without any regard for semantics.\n  Atomic(String),\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum FuncRefTarget {\n  SimpleName(String),\n}\n\n/// The type of binding to use in a function-namespaced let-binding,\n/// i.e. [`ExprF::FunctionLet`].\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum FunctionBindingType {\n  /// Outer-scoped binding, a la `flet`. Each function name is bound\n  /// in an inner scope, with the function body interpreted in the\n  /// outer scope that `flet` was invoked in. That is, functions\n  /// cannot see themselves or other functions in the same `flet`\n  /// block.\n  OuterScoped,\n  /// Recursive binding, a la `labels`. Each function name is bound in\n  /// an inner scope, with the function body interpreted in that same\n  /// inner scope. Functions in this binding type can see their own\n  /// name and other functions from the same block.\n  Recursive,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct LambdaClass {\n  pub extends: String,\n  pub args: Vec<Expr>,\n  pub constructor: Option<decl::ConstructorDecl>,\n  pub decls: Vec<decl::ClassInnerDecl>,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct LocalFnClause {\n  pub name: String,\n  pub args: ArgList,\n  pub body: Expr,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct LocalVarClause {\n  pub name: String,\n  pub value: Expr,\n}\n\n/// A collection of local variables, as well as the broadest\n/// [`AccessType`] the variables need.\npub type Locals = ClosureNames<AccessType>;\n\n/// A collection of functions, either local or global.\n///\n/// Unlike [`Locals`], `Functions` does not need to keep track of\n/// access types, since it is impossible to reassign a function in the\n/// function namespace after its been declared. It's possible to\n/// shadow functions with local ones, but this doesn't mutate the\n/// existing one and a closure around the existing function will still\n/// reflect the old value of the name.\npub type Functions = ClosureNames<()>;\n\nimpl Expr {\n\n  pub fn new(value: ExprF, pos: SourceOffset) -> Expr {\n    Expr { value, pos }\n  }\n\n  pub fn var(name: impl Into<String>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::BareName(BareName::Plain(name.into())), pos)\n  }\n\n  pub fn atomic_var(name: impl Into<String>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::BareName(BareName::Atomic(name.into())), pos)\n  }\n\n  pub fn while_stmt(cond: Expr, body: Expr, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::WhileStmt(Box::new(cond), Box::new(body)), pos)\n  }\n\n  pub fn for_stmt(name: String, iter: Expr, body: Expr, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::ForStmt(name, Box::new(iter), Box::new(body)), pos)\n  }\n\n  pub fn literal(literal: literal::Literal, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Literal(literal), pos)\n  }\n\n  pub fn nil(pos: SourceOffset) -> Expr {\n    Expr::literal(literal::Literal::Nil, pos)\n  }\n\n  pub fn progn(body: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Progn(body), pos)\n  }\n\n  pub fn call(name: impl Into<String>, args: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Call(CallTarget::Scoped, name.into(), args), pos)\n  }\n\n  pub fn atomic_call(name: impl Into<String>, args: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Call(CallTarget::Atomic, name.into(), args), pos)\n  }\n\n  pub fn super_call(name: impl Into<String>, args: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Call(CallTarget::Super, name.into(), args), pos)\n  }\n\n  pub fn method_call(self, name: impl Into<String>, args: Vec<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Call(CallTarget::Object(Box::new(self)), name.into(), args), pos)\n  }\n\n  pub fn yield_none(pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Yield(None), pos)\n  }\n\n  pub fn yield_some(lhs: Expr, rhs: Expr, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Yield(Some((Box::new(lhs), Box::new(rhs)))), pos)\n  }\n\n  pub fn assert_expr(cond: Expr, message: Option<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Assert(Box::new(cond), message.map(Box::new)), pos)\n  }\n\n  /// If `self` is a [`BareName::Plain`], then returns a reference to\n  /// the inside of the name. If not, returns `None`.\n  pub fn as_plain_name(&self) -> Option<&str> {\n    if let ExprF::BareName(BareName::Plain(s)) = &self.value {\n      Some(s)\n    } else {\n      None\n    }\n  }\n\n  /// Converts the AST [`Literal`](crate::sxp::literal::Literal) value\n  /// into an [`Expr`].\n  pub fn from_ast_literal(ast_literal: &ASTLiteral, pos: SourceOffset) -> Expr {\n    match ast_literal {\n      ASTLiteral::Nil => {\n        Expr::new(ExprF::Literal(literal::Literal::Nil), pos)\n      }\n      ASTLiteral::Int(n) => {\n        Expr::new(ExprF::Literal(literal::Literal::Int(*n)), pos)\n      }\n      ASTLiteral::Bool(b) => {\n        Expr::new(ExprF::Literal(literal::Literal::Bool(*b)), pos)\n      }\n      ASTLiteral::Float(f) => {\n        Expr::new(ExprF::Literal(literal::Literal::Float(*f)), pos)\n      }\n      ASTLiteral::String(s) => {\n        Expr::new(ExprF::Literal(literal::Literal::String(s.to_owned())), pos)\n      }\n      ASTLiteral::Symbol(s) => {\n        Expr::new(ExprF::Literal(literal::Literal::Symbol(s.to_owned())), pos)\n      }\n    }\n  }\n\n  /// Wraps the expression in a 0-ary lambda which is immediately\n  /// invoked.\n  ///\n  /// If `expr` is the starting expression, then the result is\n  /// conceptually `(funcall (lambda () expr))`. This is used in\n  /// certain contexts in compilation, such as in a parent constructor\n  /// invocation, where we have expression context but have nowhere to\n  /// place helper statements produced by a\n  /// [`StmtBuilder`](crate::compile::body::builder::StmtBuilder).\n  pub fn self_evaluating_lambda(self) -> Expr {\n    let pos = self.pos;\n    Expr::call(\n      String::from(\"sys/funcall\"),\n      vec!(Expr::new(ExprF::Lambda(ArgList::empty(), Box::new(self)), pos)),\n      pos,\n    )\n  }\n\n  pub fn from_value<T>(value: T, pos: SourceOffset) -> Expr\n  where ExprF : From<T> {\n    Expr::new(ExprF::from(value), pos)\n  }\n\n  pub fn named_split(self, name: &str, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::Split(name.to_owned(), Box::new(self)), pos)\n  }\n\n  pub fn split(self, pos: SourceOffset) -> Expr {\n    self.named_split(DEFAULT_SPLIT_NAME, pos)\n  }\n\n  fn walk_locals(&self, acc_vars: &mut Locals, acc_fns: &mut Functions) {\n    match &self.value {\n      ExprF::BareName(name) => {\n        match name {\n          BareName::Plain(s) => {\n            acc_vars.visit(s.to_owned(), AccessType::Read, self.pos);\n          }\n          BareName::Atomic(_) => {\n            // Never try to reason about these; they have no semantics\n            // by definition.\n          }\n        }\n      }\n      ExprF::Literal(_) => {}\n      ExprF::Progn(exprs) => {\n        for expr in exprs {\n          expr.walk_locals(acc_vars, acc_fns);\n        }\n      }\n      ExprF::CondStmt(clauses) => {\n        for clause in clauses {\n          clause.0.walk_locals(acc_vars, acc_fns);\n          if let Some(body) = &clause.1 {\n            body.walk_locals(acc_vars, acc_fns);\n          }\n        }\n      }\n      ExprF::WhileStmt(cond, body) => {\n        cond.walk_locals(acc_vars, acc_fns);\n        body.walk_locals(acc_vars, acc_fns);\n      }\n      ExprF::ForStmt(var, iter, body) => {\n        let mut local_vars = Locals::new();\n        iter.walk_locals(acc_vars, acc_fns);\n        body.walk_locals(&mut local_vars, acc_fns);\n        local_vars.remove(var);\n        acc_vars.merge_with(local_vars);\n      }\n      ExprF::Call(object, name, args) => {\n        match object {\n          CallTarget::Scoped => {\n            acc_fns.visit(name.to_owned(), (), self.pos);\n          }\n          CallTarget::Atomic => {\n            // Do not log any additional names, other than those\n            // visited in the arguments.\n          }\n          CallTarget::Super => {\n            // A super call implicitly requires read access to a\n            // `self` variable.\n            acc_vars.visit(String::from(\"self\"), AccessType::Read, self.pos);\n          }\n          CallTarget::Object(object) => {\n            object.walk_locals(acc_vars, acc_fns);\n          }\n        }\n        for expr in args {\n          expr.walk_locals(acc_vars, acc_fns);\n        }\n      }\n      ExprF::Let(clauses, body) => {\n        let mut vars = HashSet::new();\n        for clause in clauses {\n          vars.insert(clause.name.to_owned());\n          clause.value.walk_locals(acc_vars, acc_fns);\n        }\n        let mut local_scope = Locals::new();\n        body.walk_locals(&mut local_scope, acc_fns);\n        for (var, access_type, pos) in local_scope.into_iter_with_offset() {\n          if !vars.contains(&var) {\n            acc_vars.visit(var, access_type, pos);\n          }\n        }\n      }\n      ExprF::FunctionLet(FunctionBindingType::OuterScoped, clauses, body) => {\n        let mut fns = HashSet::new();\n        for clause in clauses {\n          let LocalFnClause { name, args, body: fbody } = clause;\n          fns.insert(name.to_owned());\n          let lambda_body = ExprF::Lambda(args.to_owned(), Box::new(fbody.to_owned()));\n          Expr::new(lambda_body, self.pos).walk_locals(acc_vars, acc_fns);\n        }\n        let mut local_scope = Functions::new();\n        body.walk_locals(acc_vars, &mut local_scope);\n        for (func, (), pos) in local_scope.into_iter_with_offset() {\n          if !fns.contains(&func) {\n            acc_fns.visit(func, (), pos);\n          }\n        }\n      }\n      ExprF::FunctionLet(FunctionBindingType::Recursive, clauses, body) => {\n        let mut fns = HashSet::new();\n        let mut local_scope = Functions::new();\n        for clause in clauses {\n          let LocalFnClause { name, args, body: fbody } = clause;\n          fns.insert(name.to_owned());\n          let lambda_body = ExprF::Lambda(args.to_owned(), Box::new(fbody.to_owned()));\n          Expr::new(lambda_body, self.pos).walk_locals(acc_vars, &mut local_scope);\n        }\n        body.walk_locals(acc_vars, &mut local_scope);\n        for (func, (), pos) in local_scope.into_iter_with_offset() {\n          if !fns.contains(&func) {\n            acc_fns.visit(func, (), pos);\n          }\n        }\n      }\n      ExprF::Lambda(args, body) => {\n        let vars: HashSet<_, RandomState> = args.iter_vars().map(|x| x.to_owned()).collect();\n        let mut local_scope = Locals::new();\n        body.walk_locals(&mut local_scope, acc_fns);\n        for (var, access_type, pos) in local_scope.into_iter_with_offset() {\n          if !vars.contains(&var) {\n            acc_vars.visit(var, access_type.closed(), pos);\n          }\n        }\n      }\n      ExprF::Assign(target, expr) => {\n        match target {\n          AssignTarget::Variable(pos, s) => {\n            acc_vars.visit(s.to_owned(), AccessType::RW, *pos);\n          }\n          AssignTarget::InstanceField(_, lhs, _) => {\n            lhs.walk_locals(acc_vars, acc_fns);\n            // If the LHS is specifically a variable, then that\n            // variable also becomes RW, since it might contain a COW\n            // value.\n            if let Some(v) = lhs.as_plain_name() {\n              acc_vars.visit(v.to_owned(), AccessType::RW, lhs.pos);\n            }\n          }\n        }\n        expr.walk_locals(acc_vars, acc_fns);\n      }\n      ExprF::FuncRef(target) => {\n        match target {\n          FuncRefTarget::SimpleName(name) => acc_fns.visit(name.to_owned(), (), self.pos),\n        }\n      }\n      ExprF::Quote(_) => {}\n      ExprF::FieldAccess(lhs, _) => {\n        lhs.walk_locals(acc_vars, acc_fns);\n      }\n      ExprF::LambdaClass(cls) => {\n        let LambdaClass { extends, args, constructor, decls } = &**cls;\n        for arg in args {\n          arg.walk_locals(acc_vars, acc_fns);\n        }\n        acc_vars.visit(extends.to_owned(), AccessType::ClosedRead, self.pos);\n\n        let (mut con_vars, con_fns) =\n          constructor.as_ref().map_or_else(|| (ClosureNames::new(), ClosureNames::new()),\n                                           |x| x.get_names());\n        for (_, access_type) in con_vars.iter_mut() {\n          *access_type = access_type.closed();\n        }\n        acc_vars.merge_with(con_vars);\n        acc_fns.merge_with(con_fns);\n\n        for decl in decls {\n          let (mut decl_vars, decl_fns) = decl.get_names();\n          for (_, access_type) in decl_vars.iter_mut() {\n            *access_type = access_type.closed();\n          }\n          acc_vars.merge_with(decl_vars);\n          acc_fns.merge_with(decl_fns);\n        }\n      }\n      ExprF::Yield(arg) => {\n        if let Some((x, y)) = arg {\n          x.walk_locals(acc_vars, acc_fns);\n          y.walk_locals(acc_vars, acc_fns);\n        }\n      }\n      ExprF::Assert(cond, message) => {\n        cond.walk_locals(acc_vars, acc_fns);\n        if let Some(message) = message {\n          message.walk_locals(acc_vars, acc_fns);\n        }\n      }\n      ExprF::Return(expr) => {\n        expr.walk_locals(acc_vars, acc_fns);\n      }\n      ExprF::Break => {}\n      ExprF::Continue => {}\n      ExprF::SpecialRef(_) => {}\n      ExprF::ContextualFilename(_) => {}\n      ExprF::Split(_, expr) => {\n        // The \"name\" of the split is not a GDLisp-level name; it's a\n        // hint to the later stages of the compiler. So it doesn't\n        // matter for our purposes.\n        expr.walk_locals(acc_vars, acc_fns);\n      }\n      ExprF::Preload(_) => {}\n    };\n  }\n\n  // Returns all of the variable names which appear unbound in the\n  // current scope. Crucially, this excludes names which are bound to\n  // lambda arguments or let instantiations.\n  pub fn get_locals(&self) -> Locals {\n    self.get_names().0\n  }\n\n  // Returns all of the function names which appear unbound in the\n  // current scope.\n  pub fn get_functions(&self) -> Functions {\n    self.get_names().1\n  }\n\n  pub fn get_names(&self) -> (Locals, Functions) {\n    let mut vars = Locals::new();\n    let mut fns = Functions::new();\n    self.walk_locals(&mut vars, &mut fns);\n    (vars, fns)\n  }\n\n  pub fn get_ids(&self) -> impl Iterator<Item=(Id, SourceOffset)> {\n    let (vars, fns) = self.get_names();\n    let vars = vars.into_iter_with_offset().map(|(name, _, pos)| (Id::new(Namespace::Value, name), pos));\n    let fns = fns.into_iter_with_offset().map(|(name, (), pos)| (Id::new(Namespace::Function, name), pos));\n    Iterator::chain(vars, fns)\n  }\n\n}\n\nimpl LambdaClass {\n\n  /// See [`decl::ClassDecl::constructor_or_default`].\n  pub fn constructor_or_default(&self, default_pos: SourceOffset) -> Cow<decl::ConstructorDecl> {\n    self.constructor.as_ref().map_or_else(|| Cow::Owned(decl::ConstructorDecl::empty(default_pos)), Cow::Borrowed)\n  }\n\n}\n\nimpl Sourced for Expr {\n  type Item = ExprF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &ExprF {\n    &self.value\n  }\n\n}\n\nimpl<T> From<T> for ExprF\nwhere literal::Literal: From<T> {\n  fn from(value: T) -> ExprF {\n    ExprF::Literal(literal::Literal::from(value))\n  }\n}\n\nimpl BareName {\n\n  pub fn to_gd_name(&self) -> String {\n    match self {\n      BareName::Plain(name) => names::lisp_to_gd(name),\n      BareName::Atomic(name) => name.to_owned(),\n    }\n  }\n\n  pub fn to_gd_name_bare(&self) -> String {\n    match self {\n      BareName::Plain(name) => names::lisp_to_gd_bare(name),\n      BareName::Atomic(name) => name.to_owned(),\n    }\n  }\n\n}\n\nimpl FunctionBindingType {\n\n  /// Returns a correct [`local_binding::LocalBinding`] implementation\n  /// for the function binding type.\n  ///\n  /// This function is the two-sided inverse of\n  /// [`local_binding::LocalBinding::function_binding_type`].\n  pub fn into_local_binding(self) -> Box<dyn local_binding::LocalBinding> {\n    match self {\n      FunctionBindingType::OuterScoped => Box::new(local_binding::FLetLocalBinding),\n      FunctionBindingType::Recursive => Box::new(local_binding::LabelsLocalBinding),\n    }\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use literal::Literal;\n\n  fn lhash(vec: Vec<String>) -> Locals {\n    Locals::from_hashmap(vec.into_iter().map(|x| (x, (AccessType::Read, SourceOffset(0)))).collect())\n  }\n\n  fn lhash_rw(vec: Vec<(String, AccessType)>) -> Locals {\n    Locals::from_hashmap(vec.into_iter().map(|(x, t)| (x, (t, SourceOffset(0)))).collect())\n  }\n\n  fn fhash(vec: Vec<String>) -> Functions {\n    Functions::from_hashmap(vec.into_iter().map(|x| (x, ((), SourceOffset(0)))).collect())\n  }\n\n  fn lvc(name: &str, value: Expr) -> LocalVarClause {\n    LocalVarClause {\n      name: name.to_owned(),\n      value,\n    }\n  }\n\n  // For most of these tests, we don't care about SourceOffset, so\n  // let's just make it easier to construct values with SourceOffset\n  // of 0.\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  #[test]\n  fn test_locals_simple() {\n    assert_eq!(Expr::var(\"foobar\", SourceOffset(0)).get_locals(), lhash(vec!(\"foobar\".to_owned())));\n    assert_eq!(Expr::var(\"aaa\", SourceOffset(0)).get_locals(), lhash(vec!(\"aaa\".to_owned())));\n    assert_eq!(e(ExprF::Literal(Literal::Int(99))).get_locals(), lhash(vec!()));\n    assert_eq!(e(ExprF::Literal(Literal::Nil)).get_locals(), lhash(vec!()));\n    assert_eq!(e(ExprF::Progn(vec!())).get_locals(), lhash(vec!()));\n  }\n\n  #[test]\n  fn test_locals_compound() {\n    let progn = e(ExprF::Progn(vec!(Expr::var(\"aa\", SourceOffset(0)),\n                                    Expr::var(\"bb\", SourceOffset(0)))));\n    assert_eq!(progn.get_locals(), lhash(vec!(\"aa\".to_owned(), \"bb\".to_owned())));\n  }\n\n  #[test]\n  fn test_locals_super_call() {\n    let super_call = Expr::super_call(\"foobar\", vec!(Expr::var(\"aa\", SourceOffset(0))), SourceOffset(0));\n    assert_eq!(super_call.get_locals(), lhash(vec!(\"aa\".to_owned(), \"self\".to_owned())));\n  }\n\n  #[test]\n  fn test_locals_let() {\n\n    // Declared variable\n    let e1 = e(ExprF::Let(vec!(lvc(\"var\", e(ExprF::Literal(Literal::Nil)))),\n                          Box::new(e(ExprF::Literal(Literal::Nil)))));\n    assert_eq!(e1.get_locals(), lhash(vec!()));\n\n    // Declared and used variable\n    let e2 = e(ExprF::Let(vec!(lvc(\"var\", e(ExprF::Literal(Literal::Nil)))),\n                          Box::new(Expr::var(\"var\", SourceOffset(0)))));\n    assert_eq!(e2.get_locals(), lhash(vec!()));\n\n    // Different variable\n    let e3 = e(ExprF::Let(vec!(lvc(\"var_unused\", e(ExprF::Literal(Literal::Nil)))),\n                          Box::new(Expr::var(\"var1\", SourceOffset(0)))));\n    assert_eq!(e3.get_locals(), lhash(vec!(\"var1\".to_owned())));\n\n    // Variable in decl\n    let e4 = e(ExprF::Let(vec!(lvc(\"var_unused\", Expr::var(\"var\", SourceOffset(0)))),\n                          Box::new(e(ExprF::Literal(Literal::Nil)))));\n    assert_eq!(e4.get_locals(), lhash(vec!(\"var\".to_owned())));\n\n    // Variable in decl (soon to be shadowed)\n    let e4 = e(ExprF::Let(vec!(lvc(\"var\", Expr::var(\"var\", SourceOffset(0)))),\n                          Box::new(e(ExprF::Literal(Literal::Nil)))));\n    assert_eq!(e4.get_locals(), lhash(vec!(\"var\".to_owned())));\n\n  }\n\n  #[test]\n  fn test_locals_assignment() {\n\n    // Simple assignment\n    let e1 = e(ExprF::Assign(AssignTarget::Variable(SourceOffset::default(), String::from(\"var\")), Box::new(e(ExprF::Literal(Literal::Nil)))));\n    assert_eq!(e1.get_locals(), lhash_rw(vec!((\"var\".to_owned(), AccessType::RW))));\n\n    // Assignment including RHS\n    let e2 = e(ExprF::Assign(AssignTarget::Variable(SourceOffset::default(), String::from(\"var1\")), Box::new(Expr::var(\"var2\", SourceOffset(0)))));\n    assert_eq!(e2.get_locals(), lhash_rw(vec!((\"var1\".to_owned(), AccessType::RW), (\"var2\".to_owned(), AccessType::Read))));\n\n    // Reading and writing (I)\n    let e3 = e(ExprF::Progn(vec!(\n      e(ExprF::Assign(AssignTarget::Variable(SourceOffset::default(), String::from(\"var\")), Box::new(e(ExprF::Literal(Literal::Nil))))),\n      Expr::var(\"var\", SourceOffset(0)),\n    )));\n    assert_eq!(e3.get_locals(), lhash_rw(vec!((\"var\".to_owned(), AccessType::RW))));\n\n    // Reading and writing (II)\n    let e4 = e(ExprF::Progn(vec!(\n      Expr::var(\"var\", SourceOffset(0)),\n      e(ExprF::Assign(AssignTarget::Variable(SourceOffset::default(), String::from(\"var\")), Box::new(e(ExprF::Literal(Literal::Nil))))),\n    )));\n    assert_eq!(e4.get_locals(), lhash_rw(vec!((\"var\".to_owned(), AccessType::RW))));\n\n  }\n\n  #[test]\n  fn test_locals_slot_assignment() {\n\n    // Simple slot assignment\n    let e1 = e(ExprF::Assign(\n      AssignTarget::InstanceField(SourceOffset::default(), Box::new(Expr::var(\"var\", SourceOffset(0))), String::from(\"foo\")),\n      Box::new(e(ExprF::Literal(Literal::Nil))),\n    ));\n    assert_eq!(e1.get_locals(), lhash_rw(vec!((\"var\".to_owned(), AccessType::RW))));\n\n    // Nested slot assignment\n    let e2 = e(ExprF::Assign(\n      AssignTarget::InstanceField(SourceOffset::default(),\n                                  Box::new(e(ExprF::FieldAccess(Box::new(Expr::var(\"var\", SourceOffset(0))), String::from(\"foo\")))),\n                                  String::from(\"baro\")),\n      Box::new(e(ExprF::Literal(Literal::Nil))),\n    ));\n    assert_eq!(e2.get_locals(), lhash_rw(vec!((\"var\".to_owned(), AccessType::Read))));\n\n  }\n\n  #[test]\n  fn test_functions_trivial() {\n    let e1 = e(ExprF::Literal(Literal::Int(1)));\n    assert_eq!(e1.get_functions(), fhash(vec!()));\n  }\n\n  #[test]\n  fn test_functions_calls() {\n    let e1 = Expr::call(\"abc\", vec!(Expr::call(\"def\", vec!(), SourceOffset(0))), SourceOffset(0));\n    assert_eq!(e1.get_functions(), fhash(vec!(\"abc\".to_owned(), \"def\".to_owned())));\n  }\n\n  #[test]\n  fn test_functions_ref() {\n    let e1 = e(ExprF::FuncRef(FuncRefTarget::SimpleName(\"abc\".to_owned())));\n    assert_eq!(e1.get_functions(), fhash(vec!(\"abc\".to_owned())));\n  }\n\n  #[test]\n  fn test_function_binding_type_local_binding_inverse() {\n    assert_eq!(FunctionBindingType::OuterScoped.into_local_binding().function_binding_type(),\n               FunctionBindingType::OuterScoped);\n    assert_eq!(FunctionBindingType::Recursive.into_local_binding().function_binding_type(),\n               FunctionBindingType::Recursive);\n  }\n\n}\n"
  },
  {
    "path": "src/ir/identifier.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! This module defines the [`Id`] type, for namespaced identifiers.\n\nuse serde::{Serialize, Deserialize};\n\nuse std::borrow::{Borrow, ToOwned};\nuse std::hash::{Hash, Hasher};\n\n/// An identifier consists of a namespace and a name. Two identifiers\n/// which happen to share a name but are in different namespaces are\n/// considered unrelated and distinct names.\n#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]\npub struct Id {\n  /// The identifier namespace.\n  pub namespace: Namespace,\n  /// The identifier name.\n  pub name: String,\n}\n\n/// There are two namespaces in GDLisp: the value namespace and the\n/// function namespace.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]\npub enum Namespace {\n  /// The value namespace consists of variables (local and global),\n  /// class declarations, enums, and constants.\n  Value,\n  /// The function namespace consists of functions (local and global)\n  /// and macros.\n  Function,\n}\n\n/// While [`Namespace`] suffices for most of GDLisp, there is\n/// technically a third scope that sometimes applies, that of signals.\n/// Signals cannot be referenced by name at runtime, as they don't\n/// actually exist in GDScript in that form. But for the purposes of\n/// name resolution, there are occasions where we need to check them\n/// for correctness.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]\npub enum ClassNamespace {\n  /// See [`Namespace::Value`].\n  Value,\n  /// See [`Namespace::Function`].\n  Function,\n  /// A signal declaration, i.e. the result of a `defsignal` declaration.\n  Signal,\n}\n\n// This trait is a specialized version of KeyPair from this\n// StackOverflow answer. We specialize it to work on &str.\n//\n// https://stackoverflow.com/a/45795699/2288659\n\n/// [`Id`] is frequently used as the key type in some kind of\n/// associative data structure. `Id`, however, requires ownership of\n/// its string value, which is not always ideal for simple lookups.\n///\n/// Similar to how `String` keys in a hashmap can be queried with a\n/// `&str`, this trait allows `Id` keys in a hashmap or similar\n/// structure to be queried by any `IdLike` implementor, most namely a\n/// tuple `(Namespace, &str)`. Any type which has a [`Namespace`] and\n/// a `&str` name can implement this trait. The trait object type `dyn\n/// IdLike` implements the necessary [`Borrow`] and [`ToOwned`] traits\n/// to be used to reference `Id` keys in a hashmap.\npub trait IdLike {\n\n  /// The underlying namespace type.\n  type NS;\n\n  /// Gets the namespace for `self`. [`Namespace`] is a [`Copy`] type,\n  /// so we return by value here, since returning a `&Namespace` is\n  /// pointless and verbose.\n  fn namespace(&self) -> Self::NS;\n\n  /// Gets the name for `self`, by reference.\n  fn name(&self) -> &str;\n\n}\n\nimpl From<Namespace> for ClassNamespace {\n\n  fn from(ns: Namespace) -> ClassNamespace {\n    match ns {\n      Namespace::Value => ClassNamespace::Value,\n      Namespace::Function => ClassNamespace::Function,\n    }\n  }\n\n}\n\nimpl Id {\n\n  /// A new `Id` value with the given name and namespace.\n  pub fn new(namespace: Namespace, name: String) -> Self {\n    Id { namespace, name }\n  }\n\n  /// A new [`IdLike`] value with the given name and namespace. This\n  /// function does not take ownership of `name`, nor does it clone\n  /// it.\n  pub fn build<'a, NS: Clone + 'a>(namespace: NS, name: &'a str) -> Box<dyn IdLike<NS=NS> + 'a> {\n    Box::new((namespace, name))\n  }\n\n}\n\nimpl Namespace {\n\n  pub fn name(self) -> &'static str {\n    ClassNamespace::from(self).name()\n  }\n\n}\n\n\nimpl ClassNamespace {\n\n  pub fn name(self) -> &'static str {\n    match self {\n      ClassNamespace::Value => \"value\",\n      ClassNamespace::Function => \"function\",\n      ClassNamespace::Signal => \"signal\",\n    }\n  }\n\n}\n\nimpl IdLike for Id {\n\n  type NS = Namespace;\n\n  fn namespace(&self) -> Namespace {\n    self.namespace\n  }\n\n  fn name(&self) -> &str {\n    &self.name\n  }\n\n}\n\nimpl<NS: Clone> IdLike for (NS, &str) {\n\n  type NS = NS;\n\n  fn namespace(&self) -> NS {\n    self.0.clone()\n  }\n\n  fn name(&self) -> &str {\n    self.1\n  }\n\n}\n\nimpl<NS: Clone> IdLike for (NS, String) {\n\n  type NS = NS;\n\n  fn namespace(&self) -> NS {\n    self.0.clone()\n  }\n\n  fn name(&self) -> &str {\n    &self.1\n  }\n\n}\n\nimpl<'a> Borrow<dyn IdLike<NS=Namespace> + 'a> for Id {\n  fn borrow(&self) -> &(dyn IdLike<NS=Namespace> + 'a) {\n    self\n  }\n}\n\nimpl<'a, NS: Clone + 'a> Borrow<dyn IdLike<NS=NS> + 'a> for (NS, String) {\n  fn borrow(&self) -> &(dyn IdLike<NS=NS> + 'a) {\n    self\n  }\n}\n\nimpl<'a, NS: Hash> Hash for (dyn IdLike<NS=NS> + 'a) {\n  fn hash<H : Hasher>(&self, state: &mut H) {\n    self.namespace().hash(state);\n    self.name().hash(state);\n  }\n}\n\nimpl<'a, NS: PartialEq> PartialEq for (dyn IdLike<NS=NS> + 'a) {\n  fn eq(&self, other: &Self) -> bool {\n    self.namespace() == other.namespace() && self.name() == other.name()\n  }\n}\n\nimpl<'a, NS: PartialEq> Eq for (dyn IdLike<NS=NS> + 'a) {}\n\nimpl<'a> ToOwned for (dyn IdLike<NS=Namespace> + 'a) {\n  type Owned = Id;\n  fn to_owned(&self) -> Id {\n    Id::new(self.namespace(), self.name().to_owned())\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use std::collections::{HashMap, HashSet};\n\n  #[test]\n  fn test_id_in_hashmap() {\n    let mut container: HashMap<Id, i32> = HashMap::new();\n    container.insert(Id::new(Namespace::Function, String::from(\"foobar\")), 945);\n    assert_eq!(container.get(&*Id::build(Namespace::Function, \"foobar\")), Some(&945));\n  }\n\n  #[test]\n  fn test_id_in_hashset() {\n    let mut container: HashSet<Id> = HashSet::new();\n    container.insert(Id::new(Namespace::Function, String::from(\"foobar\")));\n    assert!(container.contains(&*Id::build(Namespace::Function, \"foobar\")));\n  }\n\n}\n"
  },
  {
    "path": "src/ir/import.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::literal::Literal;\nuse crate::sxp::dotted::DottedExpr;\nuse crate::runner::path::{RPathBuf, PathSrc};\nuse crate::pipeline::source::SourceOffset;\nuse super::identifier::{Namespace, Id, IdLike};\n\nuse std::convert::{TryInto, TryFrom};\nuse std::fmt;\nuse std::error::Error;\n\n// Import syntax:\n//\n// (1) Qualified import\n// (use \"res://example/foo.lisp\")\n// Imports \"example/foo.lisp\" as example/foo\n//\n// (2) Qualified import (aliased)\n// (use \"res://example/foo.lisp\" as renamed-foo)\n// Imports \"example/foo.lisp\" as renamed-foo\n//\n// (3) Explicit import\n// (use \"res://example/foo.lisp\" (a b c (d function)))\n// Imports functions a, b, c from \"example/foo.lisp\"\n//\n// (4) Explicit import (aliased)\n// (use \"res://example/foo.lisp\" ((a as a1) (b as b1) (c as c1) (d value as d1)))\n// Imports functions a, b, c as a1, b1, c1 from \"example/foo.lisp\"\n//\n// (5) Wildcard import\n// (use \"res://example/foo.lisp\" open)\n// Imports all names into the current scope\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ImportDecl {\n  pub filename: RPathBuf,\n  pub details: ImportDetails,\n  pub pos: SourceOffset,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum ImportDetails {\n  Named(String),                                  // (1) and (2) above\n  Restricted(Vec<ImportName<Option<Namespace>>>), // (3) and (4) above\n  Open,                                           // (5) above\n}\n\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ImportName<NS> {\n  pub namespace: NS,\n  pub in_name: String,\n  pub out_name: String,\n}\n\n#[derive(Debug, PartialEq, Eq)]\npub enum ImportDeclParseError {\n  NoFilename,\n  BadFilename(AST),\n  InvalidPath(String),\n  MalformedFunctionImport(AST),\n  InvalidEnding(AST),\n}\n\n#[derive(Debug)]\npub enum ImportNameResolutionError {\n  UnknownName(Id),\n  AmbiguousNamespace(String),\n}\n\nimpl ImportDecl {\n\n  pub fn default_import_name(path: &RPathBuf) -> String {\n    let mut path = path.clone();\n    path.path_mut().set_extension(\"\");\n    path\n      .components_no_root()\n      .filter_map(|x| x.as_os_str().to_str())\n      .last()\n      .unwrap_or(\"/\")\n      .to_owned()\n  }\n\n  pub fn parse_path_param(arg: &str) -> Option<RPathBuf> {\n    // Paths must start with \"res://\"\n    RPathBuf::try_from(String::from(arg)).ok().filter(|path| {\n      path.source() == PathSrc::Res\n    })\n  }\n\n  pub fn named(filename: RPathBuf, name: Option<String>, pos: SourceOffset) -> ImportDecl {\n    let name = name.unwrap_or_else(|| ImportDecl::default_import_name(&filename));\n    ImportDecl {\n      filename: filename,\n      details: ImportDetails::Named(name),\n      pos,\n    }\n  }\n\n  pub fn restricted(filename: RPathBuf, imports: Vec<ImportName<Option<Namespace>>>, pos: SourceOffset) -> ImportDecl {\n    ImportDecl {\n      filename: filename,\n      details: ImportDetails::Restricted(imports),\n      pos,\n    }\n  }\n\n  pub fn open(filename: RPathBuf, pos: SourceOffset) -> ImportDecl {\n    ImportDecl {\n      filename: filename,\n      details: ImportDetails::Open,\n      pos,\n    }\n  }\n\n  pub fn names(&self, exports: &[Id]) -> Vec<ImportName<Namespace>> {\n    exports.iter().cloned().filter_map(|export| {\n      let Id { namespace: export_namespace, name: export_name } = export;\n      let import_name = match &self.details {\n        ImportDetails::Named(s) => {\n          Some(format!(\"{}/{}\", s, export_name))\n        }\n        ImportDetails::Open => {\n          Some(export_name.clone())\n        }\n        ImportDetails::Restricted(vec) => {\n          // Find it in the import list.\n          vec.iter()\n            .find(|x| x.namespace.map_or(true, |ns| ns == export_namespace) && x.out_name == *export_name)\n            .map(|name_match| name_match.in_name.clone())\n        }\n      };\n      import_name.map(|import_name| {\n        ImportName::new(export_namespace, import_name, export_name)\n      })\n    }).collect()\n  }\n\n  pub fn parse(tail: &[&AST]) -> Result<ImportDecl, ImportDeclParseError> {\n    if tail.is_empty() {\n      return Err(ImportDeclParseError::NoFilename);\n    }\n    let filename = match &tail[0].value {\n      ASTF::Atom(Literal::String(s)) => ImportDecl::parse_path_param(s).ok_or_else(|| {\n        ImportDeclParseError::InvalidPath(s.clone())\n      }),\n      _ => Err(ImportDeclParseError::BadFilename(tail[0].clone())),\n    }?;\n    match tail.len() {\n      0 => { unreachable!() } // We checked tail.is_empty() already\n      1 => {\n        // (1) Qualified import\n        Ok(ImportDecl::named(filename, None, tail[0].pos))\n      }\n      2 => {\n        match &tail[1].value {\n          ASTF::Atom(Literal::Symbol(open)) if open == \"open\" => {\n            // (5) Wildcard import\n            Ok(ImportDecl::open(filename, tail[0].pos))\n          }\n          ASTF::Atom(Literal::Nil) | ASTF::Cons(_, _) => {\n            // (3) or (4) Explicit import (possibly aliased)\n            let imports: Vec<_> = DottedExpr::new(tail[1]).try_into().map_err(|_| invalid_ending_err(&tail[1..], tail[1].pos))?;\n            let imports = imports.into_iter()\n              .map(ImportName::<Option<Namespace>>::parse)\n              .collect::<Result<Vec<_>, _>>()?;\n            Ok(ImportDecl::restricted(filename, imports, tail[0].pos))\n          }\n          _ => {\n            Err(invalid_ending_err(&tail[1..], tail[1].pos))\n          }\n        }\n      }\n      3 => {\n        // (2) Qualified import (aliased)\n        if tail[1].value != ASTF::symbol(\"as\") {\n          return Err(invalid_ending_err(&tail[1..], tail[1].pos));\n        }\n        match &tail[2].value {\n          ASTF::Atom(Literal::Symbol(s)) => Ok(ImportDecl::named(filename, Some(s.clone()), tail[0].pos)),\n          _ => Err(invalid_ending_err(&tail[1..], tail[1].pos))\n        }\n      }\n      _ => {\n        Err(invalid_ending_err(&tail[1..], tail[1].pos))\n      }\n    }\n  }\n\n}\n\nimpl<NS> ImportName<NS> {\n\n  pub fn new(namespace: NS, in_name: String, out_name: String) -> ImportName<NS> {\n    ImportName { namespace, in_name, out_name }\n  }\n\n  pub fn simple(namespace: NS, in_name: String) -> ImportName<NS> {\n    let out_name = in_name.clone();\n    ImportName { namespace, in_name, out_name }\n  }\n\n  fn symbol_to_namespace(symbol: &str, pos: SourceOffset) -> Result<Namespace, ImportDeclParseError> {\n    match symbol {\n      \"value\" => Ok(Namespace::Value),\n      \"function\" => Ok(Namespace::Function),\n      _ => Err(ImportDeclParseError::MalformedFunctionImport(AST::new(ASTF::symbol(symbol), pos))),\n    }\n  }\n\n  pub fn parse(clause: &AST) -> Result<ImportName<Option<Namespace>>, ImportDeclParseError> {\n    match &clause.value {\n      ASTF::Atom(Literal::Symbol(s)) => {\n        Ok(ImportName::simple(None, s.clone()))\n      }\n      ASTF::Cons(_, _) => {\n        let vec: Vec<_> = DottedExpr::new(clause).try_into().map_err(|_| ImportDeclParseError::MalformedFunctionImport(clause.clone()))?;\n        let shape_vec: Option<Vec<&str>> = vec\n          .into_iter()\n          .map(|x| x.as_symbol_ref())\n          .collect();\n        match shape_vec.as_deref() {\n          Some([o, \"as\", i]) => {\n            Ok(ImportName::new(None, (*i).to_owned(), (*o).to_owned()))\n          }\n          Some([o, ns, \"as\", i]) => {\n            let ns = ImportName::<Option<Namespace>>::symbol_to_namespace(ns, clause.pos)?;\n            Ok(ImportName::new(Some(ns), (*i).to_owned(), (*o).to_owned()))\n          }\n          Some([o, ns]) => {\n            let ns = ImportName::<Option<Namespace>>::symbol_to_namespace(ns, clause.pos)?;\n            Ok(ImportName::new(Some(ns), (*o).to_owned(), (*o).to_owned()))\n          }\n          _ => {\n            Err(ImportDeclParseError::MalformedFunctionImport(clause.clone()))\n          }\n        }\n      }\n      _ => {\n        Err(ImportDeclParseError::MalformedFunctionImport(clause.clone()))\n      }\n    }\n  }\n\n}\n\nimpl ImportName<Option<Namespace>> {\n\n  pub fn refine(&self, exports: &[Id]) -> Result<ImportName<Namespace>, ImportNameResolutionError> {\n    let mut matches = Vec::new();\n    for export_id in exports {\n      if self.namespace.map_or(true, |ns| ns == export_id.namespace) && export_id.name == self.out_name {\n        matches.push(export_id);\n      }\n    }\n    if matches.is_empty() {\n      Err(ImportNameResolutionError::UnknownName(Id::new(self.namespace.unwrap_or(Namespace::Function), self.out_name.to_owned())))\n    } else if matches.len() == 1 {\n      Ok(ImportName::new(matches[0].namespace, self.in_name.to_owned(), self.out_name.to_owned()))\n    } else {\n      Err(ImportNameResolutionError::AmbiguousNamespace(self.out_name.to_owned()))\n    }\n  }\n\n}\n\nimpl ImportName<Namespace> {\n\n  pub fn into_imported_id(self) -> Id {\n    Id::new(self.namespace, self.in_name)\n  }\n\n  pub fn into_exported_id(self) -> Id {\n    Id::new(self.namespace, self.out_name)\n  }\n\n  pub fn to_imported_id<'a>(&'a self) -> Box<dyn IdLike<NS=Namespace> + 'a> {\n    Id::build(self.namespace, &self.in_name)\n  }\n\n  pub fn to_exported_id<'a>(&'a self) -> Box<dyn IdLike<NS=Namespace> + 'a> {\n    Id::build(self.namespace, &self.out_name)\n  }\n\n}\n\nfn invalid_ending_err(tail: &[&AST], pos: SourceOffset) -> ImportDeclParseError {\n  let ending: Vec<AST> = tail.iter().map(|x| (*x).clone()).collect();\n  ImportDeclParseError::InvalidEnding(AST::dotted_list(ending, AST::nil(pos)))\n}\n\nimpl fmt::Display for ImportDeclParseError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      ImportDeclParseError::NoFilename => {\n        write!(f, \"Expected filename in import\")\n      }\n      ImportDeclParseError::BadFilename(ast) => {\n        write!(f, \"Not a valid filename in import {}\", ast)\n      }\n      ImportDeclParseError::InvalidPath(s) => {\n        write!(f, \"Not a valid path in import {}\", s)\n      }\n      ImportDeclParseError::MalformedFunctionImport(ast) => {\n        write!(f, \"Malformed function import {}\", ast)\n      }\n      ImportDeclParseError::InvalidEnding(ast) => {\n        write!(f, \"Invalid end of import {}\", ast)\n      }\n    }\n  }\n}\n\nimpl fmt::Display for ImportNameResolutionError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      ImportNameResolutionError::UnknownName(id) => {\n        write!(f, \"Unknown name in import resolution {}\", id.name)\n      }\n      ImportNameResolutionError::AmbiguousNamespace(name) => {\n        write!(f, \"Ambiguous namespace at {} in import\", name)\n      }\n    }\n  }\n}\n\nimpl Error for ImportDeclParseError {}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::AST_PARSER;\n\n  fn parse_ast(input: &str) -> AST {\n    AST_PARSER.parse(input).unwrap()\n  }\n\n  fn parse_import(input: &str) -> Result<ImportDecl, ImportDeclParseError> {\n    let ast = parse_ast(input);\n    let dotted: Vec<_> = DottedExpr::new(&ast).try_into().unwrap();\n    ImportDecl::parse(&dotted)\n  }\n\n  fn str_to_rpathbuf(input: &str) -> RPathBuf {\n    RPathBuf::try_from(String::from(input)).unwrap()\n  }\n\n  fn so(x: usize) -> SourceOffset {\n    SourceOffset(x)\n  }\n\n  #[test]\n  fn default_import_name_test_relative() {\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"res://foo/bar\")), \"bar\");\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"res://foo/bar.lisp\")), \"bar\");\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"res://foo/bar.gd\")), \"bar\");\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"res://\")), \"/\"); // Degenerate case\n  }\n\n  #[test]\n  #[cfg(target_family = \"windows\")]\n  fn default_import_name_test_absolute_windows() {\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"C:/a/b/c\")), \"c\");\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"C:/abcd\")), \"abcd\");\n  }\n\n  #[test]\n  #[cfg(target_family = \"unix\")]\n  fn default_import_name_test_absolute_unix() {\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"/a/b/c\")), \"c\");\n    assert_eq!(ImportDecl::default_import_name(&str_to_rpathbuf(\"/abcd\")), \"abcd\");\n  }\n\n  #[test]\n  fn test_parsing() {\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\")\"#).unwrap(),\n               ImportDecl::named(str_to_rpathbuf(\"res://foo/bar\"), None, so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\")\"#).unwrap(),\n               ImportDecl::named(str_to_rpathbuf(\"res://foo/bar\"), Some(String::from(\"bar\")), so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\" as foo)\"#).unwrap(),\n               ImportDecl::named(str_to_rpathbuf(\"res://foo/bar\"), Some(String::from(\"foo\")), so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\" as foo.baz)\"#).unwrap(),\n               ImportDecl::named(str_to_rpathbuf(\"res://foo/bar\"), Some(String::from(\"foo.baz\")), so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\" open)\"#).unwrap(),\n               ImportDecl::open(str_to_rpathbuf(\"res://foo/bar\"), so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\" (a b))\"#).unwrap(),\n               ImportDecl::restricted(str_to_rpathbuf(\"res://foo/bar\"),\n                                      vec!(ImportName::simple(None, String::from(\"a\")),\n                                           ImportName::simple(None, String::from(\"b\"))),\n                                      so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\" ())\"#).unwrap(),\n               ImportDecl::restricted(str_to_rpathbuf(\"res://foo/bar\"),\n                                      vec!(),\n                                      so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\" ((a as a1) b))\"#).unwrap(),\n               ImportDecl::restricted(str_to_rpathbuf(\"res://foo/bar\"),\n                                      vec!(ImportName::new(None, String::from(\"a1\"), String::from(\"a\")),\n                                           ImportName::simple(None, String::from(\"b\"))),\n                                      so(1)));\n    assert_eq!(parse_import(r#\"(\"res://foo/bar\" ((a function as a1) (b value)))\"#).unwrap(),\n               ImportDecl::restricted(str_to_rpathbuf(\"res://foo/bar\"),\n                                      vec!(ImportName::new(Some(Namespace::Function), String::from(\"a1\"), String::from(\"a\")),\n                                           ImportName::simple(Some(Namespace::Value), String::from(\"b\"))),\n                                      so(1)));\n  }\n\n  #[test]\n  fn test_invalid_parsing() {\n    assert!(parse_import(r#\"(10)\"#).is_err());\n    assert!(parse_import(r#\"(\"foo/bar\")\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" as a as b)\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" as 1)\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" open-NOT-CORRECT)\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" (1))\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" ((a as)))\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" ((a b c)))\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" ([]))\"#).is_err());\n    assert!(parse_import(r#\"(\"res://foo/bar\" (()))\"#).is_err());\n  }\n\n}\n"
  },
  {
    "path": "src/ir/incremental.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n// Incremental compilation (supplies backbone for macro resolution)\n\nuse super::declaration_table::DeclarationTable;\nuse super::main_function::MainFunctionHandler;\nuse super::call_name::CallName;\nuse super::classification;\nuse super::arglist;\nuse super::arglist::error::ArgListParseError;\nuse super::arglist::ordinary::ArgList;\nuse super::arglist::constructor::ConstructorArgList;\nuse super::arglist::simple::SimpleArgList;\nuse super::import::{ImportDecl, ImportName};\nuse super::expr::Expr;\nuse super::special_form;\nuse super::depends::Dependencies;\nuse super::decl::{self, Decl, DeclF, InstanceFunctionName};\nuse super::macros::{self, MacroData};\nuse super::identifier::{Id, IdLike, Namespace};\nuse super::modifier::{self, ParseRule};\nuse super::export::Visibility;\nuse super::bootstrapping::{compile_bootstrapping_decl, compile_bootstrapping_class_inner_decl};\nuse crate::util::one::One;\nuse crate::sxp::dotted::{DottedExpr, TryFromDottedExprError};\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::literal::{Literal as ASTLiteral};\nuse crate::sxp::reify::pretty::reify_pretty_expr;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::compile::symbol_table::function_call::FnSpecs;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::compile::resource_type::ResourceType;\nuse crate::compile::args::{self, Expecting, ExpectedShape};\nuse crate::compile::names;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::compile::frame::MAX_QUOTE_REIFY_DEPTH;\nuse crate::compile::body::class_initializer::InitTime;\nuse crate::gdscript::library;\nuse crate::gdscript::metadata;\nuse crate::gdscript::decl::Static;\nuse crate::pipeline::error::{PError, IOError};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::translation_unit::TranslationUnit;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::convert::{TryFrom, TryInto};\nuse std::borrow::{Cow, Borrow};\nuse std::hash::Hash;\nuse std::collections::{HashMap, HashSet};\n\npub struct IncCompiler {\n  names: FreshNameGenerator,\n  table: DeclarationTable,\n  macros: HashMap<Id, MacroData>,\n  imports: Vec<ImportDecl>,\n  ambient_symbols: SymbolTable,\n  minimalist: bool, // A minimalist compiler doesn't use stdlib and doesn't do macro expansion\n}\n\n#[allow(clippy::new_without_default)]\nimpl IncCompiler {\n\n  pub fn new(names: Vec<&str>) -> IncCompiler {\n    IncCompiler::with_ambient_symbols(names, SymbolTable::new())\n  }\n\n  pub fn with_ambient_symbols(names: Vec<&str>, ambient_symbols: SymbolTable) -> IncCompiler {\n    let names = FreshNameGenerator::new(names);\n    IncCompiler {\n      names: names,\n      table: DeclarationTable::new(),\n      macros: HashMap::new(),\n      imports: vec!(),\n      ambient_symbols: ambient_symbols,\n      minimalist: false,\n    }\n  }\n\n  fn resolve_macro_call<K: IdLike>(&mut self,\n                                   pipeline: &mut Pipeline,\n                                   head: &K,\n                                   tail: &[&AST],\n                                   macro_data: &MacroData,\n                                   pos: SourceOffset)\n                                   -> Result<AST, PError>\n  where Id : Borrow<K>,\n        K : Hash + Eq + ToOwned<Owned=Id> + ?Sized {\n    // If we're in a minimalist file, then macro expansion is automatically an error\n    if self.minimalist {\n      let head = head.to_owned().name;\n      return Err(PError::from(GDError::new(GDErrorF::MacroInMinimalistError(head), pos)));\n    }\n\n    // Local name generator that will be shared among the\n    // arguments but not used outside of this function.\n    let mut local_gen = FreshNameGenerator::new(vec!());\n\n    let mut prelude = vec!();\n    let mut args = vec!();\n    for arg in tail {\n      let (stmts, expr) = reify_pretty_expr(arg, MAX_QUOTE_REIFY_DEPTH, &mut local_gen);\n      prelude.extend(stmts.into_iter());\n      args.push(expr);\n    }\n    let server = pipeline.get_server_mut();\n    server.set_global_name_generator(&self.names).map_err(|err| IOError::new(err, pos))?;\n\n    let ast = server.run_server_file(macro_data.id, prelude, macro_data.specs, args, pos);\n\n    server.reset_global_name_generator().map_err(|err| IOError::new(err, pos))?;\n\n    // Set the source position for the entire macro expansion to\n    // be the source of the macro call\n    let mut ast = ast?;\n    ast.each_source_mut(|_| pos);\n\n    Ok(ast)\n  }\n\n  fn try_resolve_macro_call(&mut self, pipeline: &mut Pipeline, ast: &AST) -> Result<Option<AST>, PError> {\n    let vec: Vec<&AST> = match DottedExpr::new(ast).try_into() {\n      Err(TryFromDottedExprError { pos: _ }) => return Ok(None),\n      Ok(v) => v,\n    };\n    if !vec.is_empty() {\n      let head = CallName::resolve_call_name(self, pipeline, vec[0])?;\n      if let CallName::SimpleName(head) = head {\n        let tail = &vec[1..];\n        if let Some(macro_data) = self.macros.get(&*Id::build(Namespace::Function, &head)) {\n          let macro_data = macro_data.to_owned();\n          return self.resolve_macro_call(pipeline, &*Id::build(Namespace::Function, &head), tail, &macro_data, ast.pos).map(Some);\n        }\n      }\n    }\n    Ok(None)\n  }\n\n  fn try_resolve_symbol_macro_call(&mut self, pipeline: &mut Pipeline, head: &str, pos: SourceOffset) -> Result<Option<AST>, PError> {\n    if let Some(macro_data) = self.macros.get(&*Id::build(Namespace::Value, head)) {\n      let macro_data = macro_data.to_owned();\n      return self.resolve_macro_call(pipeline, &*Id::build(Namespace::Value, head), &[], &macro_data, pos).map(Some);\n    }\n    Ok(None)\n  }\n\n  pub fn resolve_simple_call(&mut self, pipeline: &mut Pipeline, head: &str, tail: &[&AST], pos: SourceOffset)\n                             -> Result<Expr, PError> {\n    if let Some(sf) = special_form::dispatch_form(self, pipeline, head, tail, pos)? {\n      Ok(sf)\n    } else if let Some(macro_data) = self.macros.get(&*Id::build(Namespace::Function, head)) {\n      let macro_data = macro_data.to_owned();\n      let result = self.resolve_macro_call(pipeline, &*Id::build(Namespace::Function, head), tail, &macro_data, pos)?;\n      self.compile_expr(pipeline, &result)\n    } else {\n      let args = tail.iter().map(|x| self.compile_expr(pipeline, x)).collect::<Result<Vec<_>, _>>()?;\n      Ok(Expr::call(head, args, pos))\n    }\n  }\n\n  pub fn compile_expr(&mut self, pipeline: &mut Pipeline, expr: &AST) -> Result<Expr, PError> {\n    match &expr.value {\n      ASTF::Cons(_, _) => {\n        let vec: Vec<&AST> = DottedExpr::new(expr).try_into()?;\n        assert!(!vec.is_empty(), \"Internal error in compile_expr, expecting non-empty list, got {:?}\", &expr);\n        let head = CallName::resolve_call_name(self, pipeline, vec[0])?;\n        let tail = &vec[1..];\n        head.into_expr(self, pipeline, tail, expr.pos)\n      }\n      ASTF::Atom(lit) => {\n        // We handle symbols specially, but for any other literal, we\n        // simply delegate to [`Expr::from_ast_literal`].\n        if let ASTLiteral::Symbol(s) = lit {\n          // Symbol macro resolution\n          let macro_result = self.try_resolve_symbol_macro_call(pipeline, s, expr.pos)?;\n          if let Some(macro_result) = macro_result {\n            self.compile_expr(pipeline, &macro_result)\n          } else {\n            Ok(Expr::var(s, expr.pos))\n          }\n        } else {\n          Ok(Expr::from_ast_literal(lit, expr.pos))\n        }\n      }\n    }\n  }\n\n  pub fn compile_decl(&mut self,\n                      pipeline: &mut Pipeline,\n                      acc: &mut impl Extend<Decl>,\n                      decl: &AST)\n                      -> Result<(), PError> {\n    let vec: Vec<&AST> = DottedExpr::new(decl).try_into()?;\n    if vec.is_empty() {\n      return Err(PError::from(GDError::new(GDErrorF::UnknownDecl(decl.clone()), decl.pos)));\n    }\n    match &vec[0].value {\n      ASTF::Atom(ASTLiteral::Symbol(s)) => {\n        match s.borrow() {\n          \"sys/min-godot-version\" => {\n            // TODO In principle, this should be a macro that the user\n            // can use directly (`ifc`), but that requires us to be\n            // able to use macros in stdlib (#103), which is not\n            // currently possible. So it's a sys/ directive for now.\n            Expecting::at_least(1).validate(\"sys/min-godot-version\", decl.pos, &vec[1..])?;\n            let min_version = ExpectedShape::extract_i32(\"sys/min-godot-version\", vec[1].clone())?;\n            let actual_version = pipeline.config().godot_version.version;\n            if actual_version.into_i32() >= min_version {\n              for decl in &vec[2..] {\n                self.compile_decl(pipeline, acc, decl)?;\n              }\n            }\n            Ok(())\n          }\n          \"defn\" => {\n            Expecting::at_least(2).validate(\"defn\", decl.pos, &vec[1..])?;\n            let name = ExpectedShape::extract_symbol(\"defn\", vec[1].clone())?;\n            let args: Vec<_> = DottedExpr::new(vec[2]).try_into()?;\n            let args = ArgList::parse(args, decl.pos)?;\n            let (mods, body) = modifier::function::parser().parse(&vec[3..])?;\n            let body = body.iter().map(|expr| self.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n            let mut decl = decl::FnDecl {\n              visibility: Visibility::FUNCTION,\n              call_magic: None,\n              name: name,\n              args: args,\n              body: Expr::progn(body, vec[0].pos),\n            };\n            for m in mods {\n              m.apply(&mut decl);\n            }\n            acc.extend(One(Decl::new(DeclF::FnDecl(decl), vec[0].pos)));\n            Ok(())\n          }\n          \"defmacro\" => {\n            Expecting::at_least(2).validate(\"defmacro\", decl.pos, &vec[1..])?;\n            let name = ExpectedShape::extract_symbol(\"defmacro\", vec[1].clone())?;\n            let args: Vec<_> = DottedExpr::new(vec[2]).try_into()?;\n            let args = ArgList::parse(args, decl.pos)?;\n            let (mods, body) = modifier::macros::parser().parse(&vec[3..])?;\n            let body = body.iter().map(|expr| self.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n            let mut decl = decl::MacroDecl {\n              visibility: Visibility::MACRO,\n              name: name,\n              args: args,\n              body: Expr::progn(body, vec[0].pos),\n            };\n            for m in mods {\n              m.apply(&mut decl);\n            }\n            acc.extend(One(Decl::new(DeclF::MacroDecl(decl), vec[0].pos)));\n            Ok(())\n          }\n          \"define-symbol-macro\" => {\n            Expecting::at_least(2).validate(\"define-symbol-macro\", decl.pos, &vec[1..])?;\n            let name = ExpectedShape::extract_symbol(\"define-symbol-macro\", vec[1].clone())?;\n            let value = self.compile_expr(pipeline, vec[2])?;\n            let (mods, body) = modifier::macros::parser().parse(&vec[3..])?;\n            ExpectedShape::validate_end_of_list(\"define-symbol-macro\", body, decl.pos)?;\n            let mut decl = decl::SymbolMacroDecl {\n              visibility: Visibility::SYMBOL_MACRO,\n              name: name,\n              body: value,\n            };\n            for m in mods {\n              m.apply_to_symbol_macro(&mut decl);\n            }\n            acc.extend(One(Decl::new(DeclF::SymbolMacroDecl(decl), vec[0].pos)));\n            Ok(())\n          }\n          \"defconst\" => {\n            Expecting::at_least(2).validate(\"defconst\", decl.pos, &vec[1..])?;\n            let name = ExpectedShape::extract_symbol(\"defconst\", vec[1].clone())?;\n            let value = self.compile_expr(pipeline, vec[2])?;\n            let (mods, body) = modifier::constant::parser().parse(&vec[3..])?;\n            ExpectedShape::validate_end_of_list(\"defconst\", body, decl.pos)?;\n            let mut decl = decl::ConstDecl { visibility: Visibility::CONST, name, value };\n            for m in mods {\n              m.apply(&mut decl);\n            }\n            acc.extend(One(Decl::new(DeclF::ConstDecl(decl), vec[0].pos)));\n            Ok(())\n          }\n          \"defclass\" => {\n            Expecting::at_least(2).validate(\"defclass\", decl.pos, &vec[1..])?;\n            let name = ExpectedShape::extract_symbol(\"defclass\", vec[1].clone())?;\n            let superclass = match &vec[2].value {\n              ASTF::Atom(ASTLiteral::Nil) =>\n                library::REFERENCE_NAME.to_owned(),\n              ASTF::Cons(car, cdr) =>\n                match (&car.value, &cdr.value) {\n                  (ASTF::Atom(ASTLiteral::Symbol(superclass_name)), ASTF::Atom(ASTLiteral::Nil)) => superclass_name.to_owned(),\n                  (_, ASTF::Atom(ASTLiteral::Nil)) => return Err(PError::from(GDError::new(GDErrorF::BadExtendsClause, vec[2].pos))),\n                  _ => return Err(PError::from(GDError::new(GDErrorF::BadExtendsClause, vec[2].pos))),\n                },\n              _ => return Err(PError::from(GDError::new(GDErrorF::BadExtendsClause, decl.pos))),\n            };\n            let (mods, decl_body) = modifier::class::parser().parse(&vec[3..])?;\n            let mut class = decl::ClassDecl::new(name, superclass);\n            for m in mods {\n              m.apply(&mut class);\n            }\n            for decl in decl_body {\n              self.compile_class_inner_decl(pipeline, &mut class, decl)?;\n            }\n            acc.extend(One(Decl::new(DeclF::ClassDecl(class), vec[0].pos)));\n            Ok(())\n          }\n          \"defenum\" => {\n            Expecting::at_least(1).validate(\"defenum\", decl.pos, &vec[1..])?;\n            let name = ExpectedShape::extract_symbol(\"defenum\", vec[1].clone())?;\n            let (mods, body) = modifier::enums::parser().parse(&vec[2..])?;\n            let clauses = body.iter().map(|clause| {\n              let clause = match &clause.value {\n                ASTF::Atom(ASTLiteral::Symbol(_)) => AST::dotted_list(vec!((*clause).clone()), AST::nil(clause.pos)),\n                _ => (*clause).clone(),\n              };\n              let clause = Vec::try_from(DottedExpr::new(&clause))?;\n              let (name, value) = match &clause[..] {\n                [name] => (name, None),\n                [name, value] => (name, Some(value)),\n                _ => return Err(PError::from(GDError::new(GDErrorF::BadEnumClause, decl.pos))),\n              };\n              let name = ExpectedShape::extract_symbol(\"defenum\", (*name).to_owned())?;\n              let value = value.map(|v| self.compile_expr(pipeline, v)).transpose()?;\n              Ok((name, value))\n            }).collect::<Result<_, _>>()?;\n            let mut enum_decl = decl::EnumDecl { visibility: Visibility::ENUM, name, clauses };\n            for m in mods {\n              m.apply(&mut enum_decl);\n            }\n            acc.extend(One(Decl::new(DeclF::EnumDecl(enum_decl), vec[0].pos)));\n            Ok(())\n          }\n          \"sys/declare\" => {\n            // (sys/declare value name)\n            // (sys/declare function name (args...))\n            Expecting::at_least(2).validate(\"sys/declare\", decl.pos, &vec[1..])?;\n            let value_type = ExpectedShape::extract_symbol(\"sys/declare\", vec[1].clone())?;\n            let (mut declare, body) = match &*value_type {\n              \"value\" | \"constant\" | \"superglobal\" => {\n                let (name, target_name) = IncCompiler::get_declare_decl_name(vec[2])?;\n                let declare_type =\n                  match &*value_type {\n                    \"value\" => decl::DeclareType::Value,\n                    \"constant\" => decl::DeclareType::Constant,\n                    \"superglobal\" => decl::DeclareType::Superglobal,\n                    _ => unreachable!(),\n                  };\n                let decl = decl::DeclareDecl {\n                  visibility: Visibility::DECLARE,\n                  declare_type,\n                  name,\n                  target_name,\n                };\n                (decl, &vec[3..])\n              }\n              \"function\" | \"superfunction\" => {\n                Expecting::at_least(3).validate(\"sys/declare\", decl.pos, &vec[1..])?;\n                let (name, target_name) = IncCompiler::get_declare_decl_name(vec[2])?;\n                let args: Vec<_> = DottedExpr::new(vec[3]).try_into()?;\n                let args = ArgList::parse(args, decl.pos)?;\n                let declare_type =\n                  if value_type == \"superfunction\" {\n                    decl::DeclareType::SuperglobalFn(args)\n                  } else {\n                    decl::DeclareType::Function(args)\n                  };\n                let decl = decl::DeclareDecl {\n                  visibility: Visibility::DECLARE,\n                  declare_type,\n                  name,\n                  target_name,\n                };\n                (decl, &vec[4..])\n              }\n              value_type => {\n                return Err(PError::from(GDError::new(GDErrorF::BadSysDeclare(value_type.to_owned()), decl.pos)))\n              }\n            };\n            let (mods, body) = modifier::declare::parser().parse(body)?;\n            ExpectedShape::validate_end_of_list(\"sys/declare\", body, decl.pos)?;\n            for m in mods {\n              m.apply(&mut declare);\n            }\n            acc.extend(One(Decl::new(DeclF::DeclareDecl(declare), vec[0].pos)));\n            Ok(())\n          }\n          \"sys/bootstrap\" => {\n            // (sys/bootstrap directive)\n            Expecting::exactly(1).validate(\"sys/bootstrap\", decl.pos, &vec[1..])?;\n            let directive = ExpectedShape::extract_symbol(\"sys/bootstrap\", vec[1].clone())?;\n            compile_bootstrapping_decl(self, pipeline, acc, &directive, decl.pos)?;\n            Ok(())\n          }\n          _ => {\n            Err(PError::from(GDError::new(GDErrorF::UnknownDecl(decl.clone()), decl.pos)))\n          }\n        }\n      }\n      _ => {\n        Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"(declaration)\"), vec[0].clone(), ExpectedShape::Symbol), decl.pos)))\n      }\n    }\n  }\n\n  // TODO Do we need to take two tables here (static and non-static) like we do in compile?\n  pub fn compile_class_inner_decl(&mut self,\n                                  pipeline: &mut Pipeline,\n                                  acc: &mut decl::ClassDecl,\n                                  curr: &AST)\n                                  -> Result<(), PError> {\n\n    // Deal with macros\n    let mut candidate: Option<AST>;\n    let mut curr = curr;\n    while let Some(ast) = self.try_resolve_macro_call(pipeline, curr)? {\n      candidate = Some(ast);\n      curr = candidate.as_ref().unwrap();\n    }\n\n    let vec = Vec::try_from(DottedExpr::new(curr)).map_err(|x| GDError::from_value(x, curr.pos))?;\n    if vec.is_empty() {\n      return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"(declaration)\"), curr.clone(), ExpectedShape::NonemptyList), curr.pos)));\n    }\n    if let Some(s) = vec[0].as_symbol_ref() {\n      match s {\n        \"progn\" => {\n          // Top-level magic progn\n          for d in &vec[1..] {\n            self.compile_class_inner_decl(pipeline, acc, d)?;\n          }\n          Ok(())\n        }\n        \"defconst\" => {\n          // TODO Combine this with the other defconst in a helper function\n          Expecting::exactly(2).validate(\"defconst\", curr.pos, &vec[1..])?;\n          let name = ExpectedShape::extract_symbol(\"defconst\", vec[1].clone())?;\n          let value = self.compile_expr(pipeline, vec[2])?;\n          acc.decls.push(decl::ClassInnerDecl::new(decl::ClassInnerDeclF::ClassConstDecl(decl::ConstDecl { visibility: Visibility::CONST, name, value }), vec[0].pos));\n          Ok(())\n        }\n        \"defvar\" => {\n          Expecting::at_least(1).validate(\"defvar\", curr.pos, &vec[1..])?;\n          let name = ExpectedShape::extract_symbol(\"defvar\", vec[1].clone())?;\n\n          // Parse value and export\n          let mut value = None;\n          let mut export = None;\n          let mut idx = 2;\n          // Value\n          if let Some(v) = vec.get(idx) {\n            if !(matches!(&v.value, ASTF::Cons(car, _) if car.value == ASTF::symbol(\"export\"))) {\n              let e = self.compile_expr(pipeline, v)?;\n              value = Some(e);\n              idx += 1;\n            }\n          };\n          // Export\n          if let Some(v) = vec.get(idx) {\n            if let Ok(elements) = Vec::<&AST>::try_from(DottedExpr::new(v)) {\n              if elements.get(0).map(|x| &x.value) == Some(&ASTF::symbol(\"export\")) {\n                let args = elements[1..].iter().map(|x| self.compile_expr(pipeline, x)).collect::<Result<_, _>>()?;\n                export = Some(decl::Export { args });\n                idx += 1;\n              }\n            }\n          }\n          // Modifiers\n          let (mods, body) = modifier::var::parser().parse(&vec[idx..])?;\n          // (Extra)\n          ExpectedShape::validate_end_of_list(\"defvar\", body, curr.pos)?;\n\n          // Exports are only allowed on the main class\n          if export.is_some() && !acc.main_class {\n            return Err(PError::from(GDError::new(GDErrorF::ExportOnInnerClassVar(name), curr.pos)));\n          }\n\n          let decl = {\n            let mut decl = decl::ClassVarDecl { export, name, value, init_time: InitTime::default() };\n            for m in mods {\n              m.apply(&mut decl);\n            }\n            decl\n          };\n          acc.decls.push(decl::ClassInnerDecl::new(decl::ClassInnerDeclF::ClassVarDecl(decl), vec[0].pos));\n          Ok(())\n        }\n        \"defn\" => {\n          // TODO Unify some of this with the equivalent code from compile_decl?\n          Expecting::at_least(2).validate(\"defn\", curr.pos, &vec[1..])?;\n          if let Some(fname) = InstanceFunctionName::parse(vec[1]) {\n            let args: Vec<_> = DottedExpr::new(vec[2]).try_into()?;\n            let args = arglist::parser::parse(args)?;\n\n            // Determine if static\n            let (mods, body) = modifier::instance_method::parser().parse(&vec[3..])?;\n\n            // Check for super call (only valid in constructor, but we\n            // run the check unconditionally)\n            let (super_call, body) = classification::detect_super(body);\n            let super_call = match super_call {\n              None => None,\n              Some(super_call) => {\n                let call =\n                  super_call.arguments.iter()\n                    .map(|expr| self.compile_expr(pipeline, expr))\n                    .collect::<Result<Vec<_>, _>>()?;\n                Some(decl::SuperCall { call, pos: super_call.pos })\n              }\n            };\n\n            let body = body.iter().map(|expr| self.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n            if fname.is_constructor_function() {\n              // Constructor\n\n              // There can only be one constructor defined in the class\n              if acc.constructor.is_some() {\n                return Err(PError::from(GDError::new(GDErrorF::DuplicateConstructor, vec[0].pos)));\n              }\n\n              let args = ConstructorArgList::try_from(args).map_err(|err| {\n                ArgListParseError::new(err, vec[0].pos)\n              })?;\n\n              let super_call = super_call.unwrap_or_else(|| decl::SuperCall::empty(vec[0].pos));\n              let mut constructor = decl::ConstructorDecl { args, super_call, body: Expr::progn(body, vec[0].pos) };\n              for m in mods {\n                m.apply_to_constructor(&mut constructor)?;\n              }\n              acc.constructor = Some(constructor);\n\n            } else {\n              // Ordinary functions cannot have init super calls (The\n              // `(super:foo)` syntax is handled separately).\n\n              let args = SimpleArgList::try_from(args).map_err(|err| {\n                ArgListParseError::new(err, vec[0].pos)\n              })?;\n\n              if let Some(super_call) = &super_call {\n                return Err(PError::from(GDError::new(GDErrorF::BadSuperCall(String::from(\"(init)\")), super_call.pos)));\n              }\n              let mut decl = decl::ClassFnDecl {\n                is_static: Static::NonStatic,\n                is_nullargs: false,\n                name: fname,\n                args,\n                body: Expr::progn(body, vec[0].pos),\n              };\n              for m in mods {\n                m.apply(&mut decl);\n              }\n              acc.decls.push(decl::ClassInnerDecl::new(decl::ClassInnerDeclF::ClassFnDecl(decl), vec[0].pos));\n\n            }\n            Ok(())\n          } else {\n            Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"defn\"), vec[1].clone(), ExpectedShape::InstanceFnName), vec[1].pos)))\n          }\n        }\n        \"defsignal\" => {\n          Expecting::between(1, 2).validate(\"defsignal\", curr.pos, &vec[1..])?;\n          let name = ExpectedShape::extract_symbol(\"defsignal\", vec[1].clone())?;\n          let nil = AST::nil(vec[0].pos);\n          let args = vec.get(2).map_or(&nil, |x| *x);\n          let args_pos = args.pos;\n          let args: Vec<_> = DottedExpr::new(args).try_into()?;\n          let args = SimpleArgList::parse(args, args_pos)?;\n          acc.decls.push(decl::ClassInnerDecl::new(decl::ClassInnerDeclF::ClassSignalDecl(decl::ClassSignalDecl { name, args }), vec[0].pos));\n          Ok(())\n        }\n        \"sys/bootstrap\" => {\n          // (sys/bootstrap directive)\n          Expecting::exactly(1).validate(\"sys/bootstrap\", curr.pos, &vec[1..])?;\n          let directive = ExpectedShape::extract_symbol(\"sys/bootstrap\", vec[1].clone())?;\n          compile_bootstrapping_class_inner_decl(self, pipeline, acc, &directive, curr.pos)?;\n          Ok(())\n        }\n        _ => {\n          Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"(declaration)\"), vec[0].clone(), ExpectedShape::Symbol), curr.pos)))\n        }\n      }\n    } else {\n      Err(PError::from(GDError::new(GDErrorF::UnknownDecl(curr.clone()), curr.pos)))\n    }\n  }\n\n  pub fn compile_import(&mut self, curr: &AST) -> Result<Option<ImportDecl>, GDError> {\n    if let Ok(vec) = Vec::try_from(DottedExpr::new(curr)) {\n      if !vec.is_empty() && vec[0].value == ASTF::symbol(\"use\") {\n        return ImportDecl::parse(&vec[1..]).map_err(|x| GDError::from_value(x, curr.pos)).map(Some);\n      }\n    }\n    Ok(None)\n  }\n\n  fn import_macros_from(&mut self, unit: &TranslationUnit, import: &ImportDecl) {\n    for imp in import.names(&unit.exports) {\n      let ImportName { namespace: namespace, in_name: import_name, out_name: export_name } = imp;\n      if let Some(data) = unit.macros.get(&*Id::build(namespace, &export_name)) {\n        self.macros.insert(Id::new(namespace, import_name), data.to_imported());\n      };\n    }\n  }\n\n  fn compile_decl_or_expr(&mut self, pipeline: &mut Pipeline, main: &mut Vec<Expr>, curr: &AST)\n                          -> Result<(), PError> {\n    let mut candidate: Option<AST>; // Just need somewhere to store the intermediate.\n    let mut curr = curr; // Change lifetime :)\n    while let Some(ast) = self.try_resolve_macro_call(pipeline, curr)? {\n      candidate = Some(ast);\n      curr = candidate.as_ref().unwrap();\n    }\n    // Check if we're looking at a top-level progn.\n    if let Ok(vec) = Vec::try_from(DottedExpr::new(curr)) {\n      if !vec.is_empty() && vec[0].value == ASTF::symbol(\"progn\") {\n        for inner in &vec[1..] {\n          self.compile_decl_or_expr(pipeline, main, inner)?;\n        }\n        return Ok(());\n      }\n    }\n    if let Some(imp) = self.compile_import(curr)? { // TODO Consider doing the is_decl thing with imports so we have a nice, pure function called is_import to call here instead?\n      let res_type = ResourceType::from(&imp);\n      if res_type.can_have_macros() {\n        let file = pipeline.load_file(imp.filename.path(), curr.pos)?;\n        self.import_macros_from(file, &imp);\n      }\n      self.imports.push(imp);\n    } else if classification::is_decl(curr) {\n      let mut new_decls: Vec<Decl> = Vec::new();\n      // Most declaration-level constructs produce exactly one\n      // declaration, but there are a handful (mainly bootstrapping\n      // primitives) that can produce several.\n      self.compile_decl(pipeline, &mut new_decls, curr)?;\n      for d in new_decls {\n        self.table.add_unless_exists(d.clone())?;\n        if let DeclF::MacroDecl(mdecl) = d.value {\n          self.bind_macro(pipeline, mdecl, d.pos, false, Namespace::Function)?;\n        } else if let DeclF::SymbolMacroDecl(mdecl) = d.value {\n          let mdecl = decl::MacroDecl::from(mdecl);\n          self.bind_macro(pipeline, mdecl, d.pos, true, Namespace::Value)?;\n        }\n      }\n    } else {\n      main.push(self.compile_expr(pipeline, curr)?);\n    }\n    Ok(())\n  }\n\n  pub fn compile_toplevel(mut self, pipeline: &mut Pipeline, body: &AST, main_function_handler: &impl MainFunctionHandler)\n                          -> Result<(decl::TopLevel, HashMap<Id, MacroData>), PError> {\n    let body: Result<Vec<_>, TryFromDottedExprError> = DottedExpr::new(body).try_into();\n    let body: Vec<_> = body?; // *sigh* Sometimes the type checker just doesn't get it ...\n\n    // File-level modifiers\n    let (mods, body) = modifier::file::parser().parse(&body)?;\n    for m in mods {\n      m.apply(&mut self);\n    }\n\n    self.bind_builtin_macros(pipeline); // No-op if minimalist is true.\n\n    // Compile\n    let mut main: Vec<Expr> = Vec::new();\n    for curr in body {\n      self.compile_decl_or_expr(pipeline, &mut main, curr)?;\n    }\n    main_function_handler.handle_main(&mut self, &main)?;\n\n    Ok(self.into())\n  }\n\n  pub fn bind_macro(&mut self, pipeline: &mut Pipeline, mut decl: decl::MacroDecl, pos: SourceOffset, generate_name: bool, namespace: Namespace) -> Result<(), PError> {\n    let orig_name = decl.name.to_owned();\n\n    let tmp_name = if generate_name {\n      self.names.generate_with(\"_macro\")\n    } else {\n      orig_name.to_owned()\n    };\n\n    // bind_macro is a no-op in a minimalist compile\n    if self.minimalist {\n      let id = pipeline.get_server_mut().add_reserved_macro(names::lisp_to_gd(&orig_name));\n      self.macros.insert(Id::new(namespace, orig_name), MacroData { id, specs: FnSpecs::from(decl.args), imported: false });\n      return Ok(());\n    }\n\n    let translation_names = self.imports.iter().map(|import| {\n      let res_type = ResourceType::from(import);\n      if res_type.can_have_macros() {\n        let unit = pipeline.load_file(&import.filename.path(), pos)?;\n        Ok(import.names(&unit.exports))\n      } else {\n        Ok(vec!())\n      }\n    }).collect::<Result<Vec<_>, PError>>()?;\n    let mut imported_names: HashSet<_> =\n      translation_names.into_iter().flatten().map(ImportName::into_imported_id).collect();\n\n    // Now add any ambient symbols (most commonly, names defined in\n    // prior lines in a REPL) to the known imports.\n    for (name, _) in self.ambient_symbols.vars() {\n      imported_names.insert(Id::new(Namespace::Value, name.to_owned()));\n    }\n    for (name, _, _) in self.ambient_symbols.fns() {\n      imported_names.insert(Id::new(Namespace::Function, name.to_owned()));\n    }\n\n    // If we're generating a name, then we need to modify the symbol\n    // table to reflect that name.\n    decl.name = tmp_name.to_owned();\n    let table = if generate_name {\n      let mut table = self.table.clone();\n      match namespace {\n        Namespace::Function => {\n          table.add_unless_exists(Decl::new(DeclF::MacroDecl(decl.to_owned()), pos))?;\n        }\n        Namespace::Value => {\n          let symdecl = decl::SymbolMacroDecl { visibility: decl.visibility, name: decl.name.to_owned(), body: decl.body.clone() };\n          table.add_unless_exists(Decl::new(DeclF::SymbolMacroDecl(symdecl), pos))?;\n        }\n      }\n      Cow::Owned(table)\n    } else {\n      Cow::Borrowed(&self.table)\n    };\n\n    // Now we need to find the dependencies and spawn up the\n    // server for the macro itself.\n    let mut deps = Dependencies::identify(table.borrow(), &imported_names, &*Id::build(namespace, &tmp_name), pos);\n    deps.purge_unknowns(library::all_builtin_names(self.minimalist).iter().map(|x| x as &dyn IdLike<NS=Namespace>));\n\n    let runtime_name =\n      if namespace == Namespace::Value {\n        metadata::symbol_macro(&tmp_name)\n      } else {\n        tmp_name\n      };\n\n    // Aside from built-in functions, it must be the case that\n    // all referenced functions are already defined.\n    let names = deps.try_into_knowns()?;\n    let tmpfile = macros::create_macro_file(pipeline, self.imports.clone(), table.borrow(), names, &self.ambient_symbols, pos, self.minimalist)?;\n    let m_id = pipeline.get_server_mut().stand_up_macro(runtime_name, tmpfile).map_err(|err| IOError::new(err, pos))?;\n    self.macros.insert(Id::new(namespace, orig_name), MacroData { id: m_id, specs: FnSpecs::from(decl.args), imported: false });\n\n    Ok(())\n  }\n\n  pub fn locally_save_macro<B, K>(&mut self, name: &K, func: impl FnOnce(&mut Self) -> B) -> B\n  where Id : Borrow<K>,\n        K : Hash + Eq + ToOwned<Owned=Id> + ?Sized {\n    let saved_value = self.macros.remove(name);\n    let result = func(self);\n    if let Some(saved_value) = saved_value {\n      self.macros.insert(name.to_owned(), saved_value);\n    }\n    result\n  }\n\n  pub fn has_macro<K>(&self, name: &K) -> bool\n  where Id : Borrow<K>,\n    K : Hash + Eq + ToOwned<Owned=Id> + ?Sized {\n    self.macros.contains_key(name)\n  }\n\n  pub fn unbind_macro<K>(&mut self, name: &K)\n  where Id : Borrow<K>,\n        K : Hash + Eq + ToOwned<Owned=Id> + ?Sized {\n    self.macros.remove(name);\n  }\n\n  pub fn bind_builtin_macros(&mut self, pipeline: &mut Pipeline) {\n    if !self.minimalist {\n      library::bind_builtin_macros(&mut self.macros, pipeline);\n    }\n  }\n\n  pub fn bind_macros_from(&mut self, existing_macros: impl IntoIterator<Item=(Id, MacroData)>) {\n    self.macros.extend(existing_macros.into_iter())\n  }\n\n  pub fn declaration_table(&mut self) -> &mut DeclarationTable {\n    &mut self.table\n  }\n\n  pub fn mark_as_minimalist(&mut self) {\n    self.minimalist = true;\n  }\n\n  pub fn name_generator(&mut self) -> &mut FreshNameGenerator {\n    &mut self.names\n  }\n\n  /// The name of the declaration in a `sys/declare` form can take one\n  /// of two forms. It can either be a single symbol literal or a list\n  /// of two symbol literals. In the former case, the symbol is\n  /// considered to be both the GDLisp name and the target GDScript\n  /// name, with the latter being escaped using `lisp_to_gd`. In the\n  /// latter case, the first symbol is the GDLisp name, and the second\n  /// is the target GDScript name, escaped with `lisp_to_gd_bare`\n  /// (note that we use the \"bare\" variant in this case, to allow\n  /// explicit overriding of GDScript identifiers in this case).\n  ///\n  /// This function returns the GDLisp name and, if present, the\n  /// GDScript target name. No escaping is done by this function.\n  fn get_declare_decl_name(form: &AST) -> Result<(String, Option<String>), GDError> {\n    fn inner(form: &AST) -> Option<(String, Option<String>)> {\n      if let Some(name) = form.as_symbol_ref() {\n        // Single symbol; target GDScript name should be determined\n        // automatically.\n        Some((name.to_owned(), None))\n      } else {\n        let list: Vec<&AST> = DottedExpr::new(form).try_into().ok()?;\n        Expecting::exactly(2).validate(\"sys/declare\", form.pos, &list).ok()?;\n        let (name, target_name) = args::two(list);\n        if let (Some(name), Some(target_name)) = (name.as_symbol_ref(), target_name.as_symbol_ref()) {\n          Some((name.to_owned(), Some(target_name.to_owned())))\n        } else {\n          None\n        }\n      }\n    }\n    // Replace any errors with the expected shape, since that will be\n    // more helpful in this case than a vague \"unexpected dotted list\"\n    // or something.\n    inner(form).ok_or_else(|| {\n      GDError::new(\n        GDErrorF::InvalidArg(String::from(\"sys/declare\"), form.to_owned(), ExpectedShape::SymbolOrPairOfSymbols),\n        form.pos,\n      )\n    })\n  }\n\n}\n\nimpl From<IncCompiler> for (decl::TopLevel, HashMap<Id, MacroData>) {\n\n  fn from(compiler: IncCompiler) -> (decl::TopLevel, HashMap<Id, MacroData>) {\n    let toplevel = decl::TopLevel {\n      imports: compiler.imports,\n      decls: compiler.table.into(),\n      minimalist_flag: compiler.minimalist,\n    };\n    let macros: HashMap<_, _> = compiler.macros.into_iter().filter(|(_, x)| !x.imported).collect();\n    (toplevel, macros)\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::Pipeline;\n  use crate::pipeline::config::ProjectConfig;\n  use crate::pipeline::resolver::PanickingNameResolver;\n  use crate::runner::version::VersionInfo;\n\n  use std::path::PathBuf;\n\n  // TODO More tests\n\n  fn dummy_config() -> ProjectConfig {\n    ProjectConfig {\n      root_directory: PathBuf::from(r\".\"),\n      optimizations: false,\n      godot_version: VersionInfo::default(),\n    }\n  }\n\n  fn dummy_pipeline() -> Pipeline {\n    Pipeline::with_resolver(dummy_config(), Box::new(PanickingNameResolver))\n  }\n\n  #[test]\n  fn bad_call_test() {\n\n    let mut icompiler = IncCompiler::new(vec!());\n    icompiler.mark_as_minimalist();\n\n    let mut pipeline = dummy_pipeline();\n\n    let ast = AST::list(\n      vec!(\n        AST::from_value(5, SourceOffset(500)),\n        AST::from_value(6, SourceOffset(600)),\n      ),\n      SourceOffset(0),\n    );\n\n    assert_eq!(\n      icompiler.compile_expr(&mut pipeline, &ast),\n      Err(PError::from(GDError::new(GDErrorF::CannotCall(AST::from_value(5, SourceOffset(500))), SourceOffset(500)))),\n    );\n\n  }\n\n}\n"
  },
  {
    "path": "src/ir/literal.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! GDLisp literal values.\n\nuse ordered_float::OrderedFloat;\n\n/// A GDLisp literal value.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum Literal {\n  Nil,\n  Int(i32),\n  Float(OrderedFloat<f32>),\n  String(String),\n  Symbol(String),\n  Bool(bool),\n}\n\nimpl From<i32> for Literal {\n  fn from(value: i32) -> Literal {\n    Literal::Int(value)\n  }\n}\n\nimpl From<OrderedFloat<f32>> for Literal {\n  fn from(value: OrderedFloat<f32>) -> Literal {\n    Literal::Float(value)\n  }\n}\n\nimpl From<String> for Literal {\n  fn from(value: String) -> Literal {\n    Literal::String(value)\n  }\n}\n\nimpl<'a> From<&'a str> for Literal {\n  fn from(value: &'a str) -> Literal {\n    Literal::String(String::from(value))\n  }\n}\n\nimpl From<bool> for Literal {\n  fn from(value: bool) -> Literal {\n    Literal::Bool(value)\n  }\n}\n"
  },
  {
    "path": "src/ir/loops/error.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Error types during loop primitive validation.\n\nuse crate::pipeline::source::{Sourced, SourceOffset};\n\nuse std::fmt;\nuse std::error::Error;\n\n/// A `LoopPrimitiveErrorF` indicates that a looping construct, either\n/// `break` or `continue`, was found outside of the scope of a loop.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct LoopPrimitiveErrorF {\n  /// The primitive construct that was invalid.\n  pub primitive: LoopPrimitive,\n  /// If this is true, then the error occurred inside the lexical\n  /// scope of a loop, but there was a closure in between the loop and\n  /// the primitive. This is used to produce a better error message in\n  /// this specific situation.\n  pub is_in_closure: bool,\n}\n\n/// A [`LoopPrimitiveErrorF`] together with [`SourceOffset`] data.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct LoopPrimitiveError {\n  pub value: LoopPrimitiveErrorF,\n  pub pos: SourceOffset,\n}\n\n#[derive(Clone, Debug, Copy, PartialEq, Eq)]\npub enum LoopPrimitive {\n  Break, Continue,\n}\n\nimpl LoopPrimitiveError {\n\n  pub fn new(value: LoopPrimitiveErrorF, pos: SourceOffset) -> LoopPrimitiveError {\n    LoopPrimitiveError { value, pos }\n  }\n\n  pub fn break_error(pos: SourceOffset) -> LoopPrimitiveError {\n    LoopPrimitiveError::new(LoopPrimitiveErrorF {\n      primitive: LoopPrimitive::Break,\n      is_in_closure: false,\n    }, pos)\n  }\n\n  pub fn continue_error(pos: SourceOffset) -> LoopPrimitiveError {\n    LoopPrimitiveError::new(LoopPrimitiveErrorF {\n      primitive: LoopPrimitive::Continue,\n      is_in_closure: false,\n    }, pos)\n  }\n\n  pub fn in_closure(mut self) -> Self {\n    self.value.is_in_closure = true;\n    self\n  }\n\n}\n\nimpl Sourced for LoopPrimitiveError {\n  type Item = LoopPrimitiveErrorF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &LoopPrimitiveErrorF {\n    &self.value\n  }\n\n}\n\nimpl fmt::Display for LoopPrimitiveError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    let LoopPrimitiveErrorF { primitive, is_in_closure } = &self.value;\n    write!(f, \"Loop primitive '{}' is not allowed outside of loops\", primitive)?;\n    if *is_in_closure {\n    write!(f, \" (Note: '{}' was found inside of a lambda or anonymous class that is nested inside of a loop; this nesting is currently not allowed)\", primitive)?;\n    }\n    Ok(())\n  }\n}\n\nimpl fmt::Display for LoopPrimitive {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      LoopPrimitive::Break => write!(f, \"break\"),\n      LoopPrimitive::Continue => write!(f, \"continue\"),\n    }\n  }\n}\n\nimpl Error for LoopPrimitiveError {}\n"
  },
  {
    "path": "src/ir/loops/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Functions for validating the use of looping primitives,\n//! specifically `break` and `continue`.\n\npub mod error;\n\nuse super::decl::TopLevel;\nuse super::expr::{Expr, ExprF, AssignTarget, LambdaClass, CallTarget};\nuse error::{LoopPrimitiveError, LoopPrimitive, LoopPrimitiveErrorF};\nuse crate::pipeline::source::SourceOffset;\n\n#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]\nenum LoopWalker {\n  /// `LoopWalker::None` is the default state. If there are no loops\n  /// that the current expression is inside, either directly or\n  /// indirectly, then we are in this state.\n  #[default]\n  None,\n  /// The state of expressions that are inside a loop, and for which\n  /// there are no closures strictly between the current expression\n  /// and the innermost enclosing loop. This is the only state in\n  /// which it is permitted to use loop primitives.\n  Within,\n  /// The state of expressions that are inside a loop, but for which\n  /// there is a closure strictly between the current expression and\n  /// the innermost enclosing loop. Due to limitations in GDScript, we\n  /// are not capable of compiling loop primitives in this state.\n  ClosureWithin,\n}\n\npub fn check_expr(expr: &Expr) -> Result<(), LoopPrimitiveError> {\n  LoopWalker::new().check(expr)\n}\n\npub fn check_all_exprs(toplevel: &TopLevel) -> Result<(), LoopPrimitiveError> {\n  for expr in toplevel.inner_exprs() {\n    check_expr(expr)?;\n  }\n  Ok(())\n}\n\nimpl LoopWalker {\n\n  fn new() -> Self {\n    Self::default()\n  }\n\n  fn enter_loop(self) -> Self {\n    LoopWalker::Within\n  }\n\n  fn enter_closure(self) -> Self {\n    if self == LoopWalker::None {\n      // There's no loop at all, so don't change that fact.\n      LoopWalker::None\n    } else {\n      LoopWalker::ClosureWithin\n    }\n  }\n\n  fn check(self, expr: &Expr) -> Result<(), LoopPrimitiveError> {\n    match &expr.value {\n      ExprF::BareName(_) => {}\n      ExprF::Literal(_) => {}\n      ExprF::Progn(exprs) => {\n        for inner in exprs {\n          self.check(inner)?;\n        }\n      }\n      ExprF::CondStmt(clauses) => {\n        for (cond, body) in clauses {\n          self.check(cond)?;\n          if let Some(body) = body {\n            self.check(body)?;\n          }\n        }\n      }\n      ExprF::WhileStmt(cond, body) => {\n        // Note: Both the condition *and* the body count as being\n        // inside the loop. We *can* break inside of a `while`\n        // condition; we'll just end up compiling to the \"full\" while\n        // loop, where the condition is a sequence of statements in a\n        // `while True`.\n        let walker = self.enter_loop();\n        walker.check(cond)?;\n        walker.check(body)?;\n      }\n      ExprF::ForStmt(_, iter, body) => {\n        // Contrary to `while`, the iteratee of a `for` loop is\n        // *outside* the loop. A `break` in the iteratee expression is\n        // meaningless.\n        self.check(iter)?;\n        self.enter_loop().check(body)?;\n      }\n      ExprF::Call(object, _, args) => {\n        match object {\n          CallTarget::Scoped | CallTarget::Super | CallTarget::Atomic => {}\n          CallTarget::Object(inner) => { self.check(inner)?; }\n        }\n        for inner in args {\n          self.check(inner)?;\n        }\n      }\n      ExprF::Let(clauses, body) => {\n        for clause in clauses {\n          self.check(&clause.value)?;\n        }\n        self.check(body)?;\n      }\n      ExprF::FunctionLet(_, clauses, body) => {\n        let closure_walker = self.enter_closure();\n        for clause in clauses {\n          closure_walker.check(&clause.body)?;\n        }\n        self.check(body)?;\n      }\n      ExprF::Lambda(_, body) => {\n        self.enter_closure().check(body)?;\n      }\n      ExprF::FuncRef(_) => {}\n      ExprF::Assign(lhs, rhs) => {\n        match lhs {\n          AssignTarget::Variable(_, _) => {}\n          AssignTarget::InstanceField(_, lhs, _) => { self.check(lhs)?; }\n        }\n        self.check(rhs)?;\n      }\n      ExprF::Quote(_) => {}\n      ExprF::FieldAccess(lhs, _) => {\n        self.check(lhs)?;\n      }\n      ExprF::LambdaClass(class) => {\n        let walker = self.enter_closure();\n        let LambdaClass { extends: _, args, constructor, decls } = class.as_ref();\n        for inner in args {\n          // Not inside the lambda class yet.\n          self.check(inner)?;\n        }\n        if let Some(constructor) = constructor {\n          for inner in &constructor.super_call.call {\n            walker.check(inner)?;\n          }\n          walker.check(&constructor.body)?;\n        }\n        for expr in decls.iter().flat_map(|d| d.inner_exprs()) {\n          walker.check(expr)?;\n        }\n      }\n      ExprF::Yield(args) => {\n        if let Some((a, b)) = args {\n          self.check(a)?;\n          self.check(b)?;\n        }\n      }\n      ExprF::Assert(a, b) => {\n        self.check(a)?;\n        if let Some(b) = b {\n          self.check(b)?;\n        }\n      }\n      ExprF::Return(arg) => {\n        self.check(arg)?;\n      }\n      ExprF::Break => {\n        self.check_loop_primitive(LoopPrimitive::Break, expr.pos)?;\n      }\n      ExprF::Continue => {\n        self.check_loop_primitive(LoopPrimitive::Continue, expr.pos)?;\n      }\n      ExprF::SpecialRef(_) => {}\n      ExprF::ContextualFilename(_) => {}\n      ExprF::Split(_, expr) => {\n        self.check(expr)?;\n      }\n      ExprF::Preload(_) => {}\n    };\n    Ok(())\n  }\n\n  /// This function is called when the walker encounters a loop\n  /// primitive, either `break` or `continue`. For a loop primitive to\n  /// succeed, `self` *must* be [`LoopWalker::Within`]. If it's in\n  /// either of the other states, then an appropriate error is issued.\n  fn check_loop_primitive(self, primitive: LoopPrimitive, pos: SourceOffset) -> Result<(), LoopPrimitiveError> {\n    match self {\n      LoopWalker::None => {\n        Err(LoopPrimitiveError::new(\n          LoopPrimitiveErrorF { primitive, is_in_closure: false },\n          pos,\n        ))\n      }\n      LoopWalker::Within => {\n        // Everything is fine :)\n        Ok(())\n      }\n      LoopWalker::ClosureWithin => {\n        Err(LoopPrimitiveError::new(\n          LoopPrimitiveErrorF { primitive, is_in_closure: true },\n          pos,\n        ))\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/ir/macros.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::compile::Compiler;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::compile::body::builder::CodeBuilder;\nuse crate::compile::body::class_scope::OutsideOfClass;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::compile::symbol_table::function_call::FnSpecs;\nuse crate::gdscript::library;\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::runner::into_gd_file::IntoGDFile;\nuse crate::runner::macro_server::named_file_server::MacroID;\nuse crate::pipeline::error::{PError, IOError};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::can_load::CanLoad;\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir;\nuse crate::ir::import::ImportDecl;\nuse crate::ir::decl::TopLevel;\nuse crate::ir::identifier::Id;\nuse crate::ir::declaration_table::DeclarationTable;\n\nuse tempfile::{NamedTempFile, Builder};\nuse serde::{Serialize, Deserialize};\n\nuse std::io::{self, Write};\nuse std::collections::HashSet;\n\n#[derive(Clone, Debug, Serialize, Deserialize)]\npub struct MacroData {\n  pub id: MacroID,\n  pub specs: FnSpecs,\n  pub imported: bool,\n}\n\nfn make_tmp() -> io::Result<NamedTempFile> {\n  Builder::new()\n    .prefix(\"__gdlisp_macro\")\n    .suffix(\".gd\")\n    .rand_bytes(5)\n    .tempfile()\n}\n\nimpl MacroData {\n\n  pub fn to_imported(&self) -> MacroData {\n    let mut result = self.clone();\n    result.imported = true;\n    result\n  }\n\n}\n\npub fn create_macro_file(pipeline: &mut Pipeline, imports: Vec<ImportDecl>, src_table: &DeclarationTable, names: HashSet<Id>, existing_symbols: &SymbolTable, pos: SourceOffset, minimalist: bool) -> Result<NamedTempFile, PError> {\n  let mut table = existing_symbols.clone();\n  library::bind_builtins(&mut table, minimalist);\n\n  let current_filename = pipeline.current_filename();\n  let mut tmp_file = make_tmp().map_err(|err| IOError::new(err, pos))?;\n  let mut resolver = pipeline.make_preload_resolver();\n  // Replace the current file name with the macro file name.\n  resolver.insert(current_filename.into_path(), tmp_file.path().to_owned());\n\n  let mut compiler = Compiler::new(FreshNameGenerator::new(vec!()), Box::new(resolver), minimalist);\n  let decls = Vec::from(src_table.filter(|d| names.contains(&*d.id_like())));\n  let toplevel = {\n    let mut toplevel = TopLevel { imports, decls, minimalist_flag: minimalist };\n    // Strip main class qualifier; we don't need or want it during macro expansion.\n    for d in &mut toplevel.decls {\n      if let ir::decl::DeclF::ClassDecl(cdecl) = &mut d.value {\n        cdecl.main_class = false;\n      }\n    }\n    toplevel\n  };\n\n  let mut builder = CodeBuilder::new(ClassExtends::SimpleIdentifier(\"Node\".to_owned()));\n  compiler.frame(pipeline, &mut builder, &mut table, &mut OutsideOfClass).compile_toplevel(&toplevel)?;\n  let result = builder.build();\n\n  result.write_to_gd(&mut tmp_file).map_err(|err| IOError::new(err, pos))?;\n  tmp_file.flush().map_err(|err| IOError::new(err, pos))?;\n  Ok(tmp_file)\n\n}\n"
  },
  {
    "path": "src/ir/main_function.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Handlers for the main function in a GDLisp source file.\n\nuse super::incremental::IncCompiler;\nuse super::arglist::ordinary::ArgList;\nuse super::expr::Expr;\nuse super::decl::{Decl, DeclF, FnDecl};\nuse super::export::Visibility;\nuse crate::pipeline::source::SourceOffset;\nuse crate::compile::error::{GDError, GDErrorF};\n\n/// A [`MainFunctionHandler`] which errs out if there are any\n/// top-level expressions in the file. This handler performs no action\n/// if the expression slice is empty.\n#[derive(Clone, Debug, Copy)]\npub struct DisallowMainFunctionHandler;\n\n/// A [`MainFunctionHandler`] implementing the original behavior of\n/// GDLisp, accumulating all of the top-level expressions into a\n/// static function with the given name.\n///\n/// This handler is not used in GDLisp production but is used in the\n/// testing scaffolding in order to be able to run expressions\n/// alongside declarations. It may be added back into GDLisp proper in\n/// the future.\n#[derive(Clone, Debug)]\npub struct StaticMainFunctionHandler {\n  pub function_name: String,\n}\n\n/// When the incremental compiler compiles a source file, it accepts\n/// top-level expressions and declarations. The declarations are\n/// handled by the incremental compiler, and the expressions are\n/// passed on to the `MainFunctionHandler` to determine what to do\n/// with them.\n///\n/// The current default behavior in GDLisp is to err out if there are\n/// any expressions at the top-level, leaving the behavior available\n/// in future implementations. This implementation is provided by\n/// [`DisallowMainFunctionHandler`].\npub trait MainFunctionHandler {\n\n  fn handle_main(&self, icompiler: &mut IncCompiler, main: &[Expr]) -> Result<(), GDError>;\n\n}\n\nimpl MainFunctionHandler for DisallowMainFunctionHandler {\n\n  fn handle_main(&self, _icompiler: &mut IncCompiler, main: &[Expr]) -> Result<(), GDError> {\n    if main.is_empty() {\n      Ok(())\n    } else {\n      Err(GDError::new(GDErrorF::ExprAtTopLevel(main[0].clone()), main[0].pos))\n    }\n  }\n\n}\n\nimpl StaticMainFunctionHandler {\n\n  pub fn new(function_name: String) -> Self {\n    StaticMainFunctionHandler { function_name }\n  }\n\n}\n\nimpl MainFunctionHandler for StaticMainFunctionHandler {\n\n  fn handle_main(&self, icompiler: &mut IncCompiler, main: &[Expr]) -> Result<(), GDError> {\n    // Note: main_decl is synthesized from the file itself, so\n    // SourceOffset(0) isn't just a cop-out here; it's the actual\n    // right answer.\n    let pos = SourceOffset(0);\n    let main_decl = DeclF::FnDecl(FnDecl {\n      visibility: Visibility::FUNCTION,\n      call_magic: None,\n      name: self.function_name.to_owned(),\n      args: ArgList::empty(),\n      body: Expr::progn(main.to_vec(), pos),\n    });\n    let main_decl = Decl::new(main_decl, pos);\n    icompiler.declaration_table().add(main_decl);\n    Ok(())\n  }\n\n}\n"
  },
  {
    "path": "src/ir/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n// Intermediate representation used between the AST Lisp syntax and\n// the GDScript syntax. This representation shares all of the nice\n// expression semantics of Lisp but resolves any special forms to\n// something easily recognizable.\n//\n// NOTE: IR still exclusively uses the GDLisp names. Generating\n// GDScript names is the responsibility of the next compilation step.\n\npub mod expr;\npub mod decl;\npub mod arglist;\npub mod literal;\npub mod special_form;\npub mod declaration_table;\npub mod incremental;\npub mod depends;\npub mod macros;\npub mod quasiquote;\npub mod call_name;\npub mod import;\npub mod export;\npub mod identifier;\npub mod modifier;\npub mod access_type;\npub mod closure_names;\npub mod special_ref;\npub mod scope;\npub mod bootstrapping;\npub mod loops;\npub mod main_function;\npub mod classification;\n\nuse decl::Decl;\nuse macros::MacroData;\nuse identifier::Id;\nuse main_function::MainFunctionHandler;\nuse crate::sxp::ast::AST;\nuse crate::compile::error::GDError;\nuse crate::pipeline::error::PError;\nuse crate::pipeline::Pipeline;\n\nuse std::collections::HashMap;\n\npub fn compile_toplevel(pipeline: &mut Pipeline, body: &AST, main_function_handler: &impl MainFunctionHandler)\n                        -> Result<(decl::TopLevel, HashMap<Id, MacroData>), PError> {\n  let compiler = incremental::IncCompiler::new(body.all_symbols());\n  compiler.compile_toplevel(pipeline, body, main_function_handler)\n}\n\npub fn compile_and_check(pipeline: &mut Pipeline, body: &AST, main_function_handler: &impl MainFunctionHandler)\n                        -> Result<(decl::TopLevel, HashMap<Id, MacroData>), PError> {\n  let (ir, macros) = compile_toplevel(pipeline, body, main_function_handler)?;\n  check_ir(&ir)?;\n  Ok((ir, macros))\n}\n\npub fn check_ir(ir: &decl::TopLevel) -> Result<(), GDError> {\n  scope::check_scopes(ir)?;\n  loops::check_all_exprs(ir)?;\n  Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::ir::literal::Literal;\n  use crate::ir::expr::{Expr, ExprF, AssignTarget};\n  use crate::ir::decl::DeclF;\n  use crate::ir::arglist::ordinary::ArgList;\n  use crate::ir::export::Visibility;\n  use crate::pipeline::Pipeline;\n  use crate::pipeline::config::ProjectConfig;\n  use crate::pipeline::source::SourceOffset;\n  use crate::runner::version::VersionInfo;\n  use crate::sxp::ast::ASTF;\n\n  use std::path::PathBuf;\n\n  fn int(n: i32) -> AST {\n    AST::new(ASTF::int(n), SourceOffset::default())\n  }\n\n  fn symbol(s: &str) -> AST {\n    AST::new(ASTF::symbol(s), SourceOffset::default())\n  }\n\n  #[allow(dead_code)]\n  fn string(s: &str) -> AST {\n    AST::new(ASTF::string(s), SourceOffset::default())\n  }\n\n  fn nil() -> AST {\n    AST::nil(SourceOffset::default())\n  }\n\n  #[allow(dead_code)]\n  fn cons(a: AST, b: AST) -> AST {\n    AST::new(ASTF::cons(a, b), SourceOffset::default())\n  }\n\n  fn list(data: Vec<AST>) -> AST {\n    AST::dotted_list(data, nil())\n  }\n\n  fn call(name: &str, args: Vec<Expr>) -> Expr {\n    Expr::call(String::from(name), args, SourceOffset::default())\n  }\n\n  fn literal(literal: Literal) -> Expr {\n    Expr::literal(literal, SourceOffset::default())\n  }\n\n  fn progn(body: Vec<Expr>) -> Expr {\n    Expr::progn(body, SourceOffset::default())\n  }\n\n  fn compile_expr(pipeline: &mut Pipeline, expr: &AST)\n                  -> Result<Expr, PError> {\n    let mut compiler = incremental::IncCompiler::new(expr.all_symbols());\n    compiler.bind_builtin_macros(pipeline);\n    compiler.compile_expr(pipeline, expr)\n  }\n\n  fn compile_decl(pipeline: &mut Pipeline, decl: &AST)\n                  -> Result<Decl, PError> {\n    let mut vec: Vec<Decl> = Vec::new();\n    let mut compiler = incremental::IncCompiler::new(decl.all_symbols());\n    compiler.bind_builtin_macros(pipeline);\n    compiler.compile_decl(pipeline, &mut vec, decl)?;\n    assert_eq!(vec.len(), 1);\n    Ok(vec.remove(0))\n  }\n\n  fn do_compile_expr(expr: &AST) -> Result<Expr, PError> {\n    let mut pipeline = Pipeline::new(ProjectConfig { root_directory: PathBuf::from(\".\"), optimizations: false, godot_version: VersionInfo::default() });\n    compile_expr(&mut pipeline, &expr)\n  }\n\n  fn do_compile_decl(decl: &AST) -> Result<Decl, PError> {\n    let mut pipeline = Pipeline::new(ProjectConfig { root_directory: PathBuf::from(\".\"), optimizations: false, godot_version: VersionInfo::default() });\n    compile_decl(&mut pipeline, &decl)\n  }\n\n  #[test]\n  fn compile_call() {\n    let ast = list(vec!(symbol(\"foobar\"), int(10)));\n    let expected = call(\"foobar\", vec!(literal(Literal::Int(10))));\n    let actual = do_compile_expr(&ast).unwrap();\n    assert_eq!(actual, expected);\n  }\n\n  // These used to compile to different IR but they don't anymore.\n  // Test is still here because meh.\n  #[test]\n  fn compile_builtin() {\n    let ast = list(vec!(symbol(\"cons\"), int(10)));\n    let expected = call(\"cons\", vec!(literal(Literal::Int(10))));\n    let actual = do_compile_expr(&ast).unwrap();\n    assert_eq!(actual, expected);\n  }\n\n  #[test]\n  fn compile_int() {\n    assert_eq!(do_compile_expr(&int(99)).unwrap(), literal(Literal::Int(99)));\n    assert_eq!(do_compile_expr(&int(-10)).unwrap(), literal(Literal::Int(-10)));\n  }\n\n  #[test]\n  fn compile_nil() {\n    assert_eq!(do_compile_expr(&nil()).unwrap(), literal(Literal::Nil));\n  }\n\n  #[test]\n  fn compile_progn() {\n    assert_eq!(do_compile_expr(&list(vec!(symbol(\"progn\")))).unwrap(),\n               progn(vec!()));\n    assert_eq!(do_compile_expr(&list(vec!(symbol(\"progn\"), int(1)))).unwrap(),\n               progn(vec!(literal(Literal::Int(1)))));\n    assert_eq!(do_compile_expr(&list(vec!(symbol(\"progn\"), int(1), int(2)))).unwrap(),\n               progn(vec!(literal(Literal::Int(1)), literal(Literal::Int(2)))));\n  }\n\n  #[test]\n  fn compile_defn() {\n    assert_eq!(do_compile_decl(&list(vec!(symbol(\"defn\"),\n                                          symbol(\"foobar\"),\n                                          list(vec!(symbol(\"a\"),\n                                                    symbol(\"b\"))),\n                                          int(20)))).unwrap(),\n               Decl::new(DeclF::FnDecl(decl::FnDecl {\n                 visibility: Visibility::FUNCTION,\n                 call_magic: None,\n                 name: \"foobar\".to_owned(),\n                 args: ArgList::required(vec!(\"a\".to_owned(), \"b\".to_owned())),\n                 body: progn(vec!(literal(Literal::Int(20)))),\n               }), SourceOffset::default()));\n  }\n\n  #[test]\n  fn compile_defmacro() {\n    assert_eq!(do_compile_decl(&list(vec!(symbol(\"defmacro\"),\n                                          symbol(\"foobar\"),\n                                          list(vec!(symbol(\"a\"),\n                                                    symbol(\"b\"))),\n                                          int(20)))).unwrap(),\n               Decl::new(DeclF::MacroDecl(decl::MacroDecl {\n                 visibility: Visibility::MACRO,\n                 name: \"foobar\".to_owned(),\n                 args: ArgList::required(vec!(\"a\".to_owned(), \"b\".to_owned())),\n                 body: progn(vec!(literal(Literal::Int(20)))),\n               }), SourceOffset::default()));\n  }\n\n  #[test]\n  fn compile_set() {\n    assert_eq!(do_compile_expr(&list(vec!(symbol(\"set\"),\n                                          symbol(\"foobar\"),\n                                          int(1)))).unwrap(),\n               Expr::new(ExprF::Assign(AssignTarget::Variable(SourceOffset::default(), String::from(\"foobar\")), Box::new(literal(Literal::Int(1)))), SourceOffset::default()));\n  }\n\n}\n"
  },
  {
    "path": "src/ir/modifier/class.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parse rule for modifiers which apply to class declarations.\n\nuse crate::ir::decl::ClassDecl;\nuse crate::ir::export::Visibility;\nuse super::{ParseRule, Several, Constant};\nuse super::visibility;\n\n/// A modifier which can be applied to a [`ClassDecl`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum ClassMod {\n  /// The literal `main` symbol parses as `ClassMod::Main`, which\n  /// makes the class into the unique main class for the file.\n  Main,\n  /// A visibility modifier. See [`super::visibility`].\n  Visibility(Visibility),\n}\n\nimpl ClassMod {\n  /// Apply the modifier to `decl`.\n  pub fn apply(&self, decl: &mut ClassDecl) {\n    match self {\n      ClassMod::Main => {\n        decl.main_class = true;\n      }\n      ClassMod::Visibility(vis) => {\n        decl.visibility = *vis;\n      }\n    }\n  }\n}\n\n/// A parse rule for class declarations.\npub fn parser() -> impl ParseRule<Modifier=ClassMod> {\n  Several::new(vec!(\n    Box::new(Constant::new(\"main\", ClassMod::Main).unique()),\n    Box::new(visibility::parser().map(ClassMod::Visibility)),\n  ))\n}\n"
  },
  {
    "path": "src/ir/modifier/constant.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parse rule for modifiers that can be applied to constant declarations.\n\nuse crate::ir::decl::ConstDecl;\nuse crate::ir::export::Visibility;\nuse super::ParseRule;\nuse super::visibility;\n\n/// A modifier which applies to a [`ConstDecl`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum ConstMod {\n  /// A visibility modifier, as per [`super::visibility`].\n  Visibility(Visibility),\n}\n\nimpl ConstMod {\n  /// Apply the modifier to `decl`.\n  pub fn apply(&self, decl: &mut ConstDecl) {\n    match self {\n      ConstMod::Visibility(vis) => {\n        decl.visibility = *vis;\n      }\n    }\n  }\n}\n\n/// A parse rule for constant declarations.\npub fn parser() -> impl ParseRule<Modifier=ConstMod> {\n  visibility::parser().map(ConstMod::Visibility)\n}\n"
  },
  {
    "path": "src/ir/modifier/declare.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! A parse rule for modifiers to `sys/declare` declarations.\n\nuse crate::ir::decl::DeclareDecl;\nuse crate::ir::export::Visibility;\nuse super::ParseRule;\nuse super::visibility;\n\n/// Modifiers to compile-time declaration directives.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum DeclareMod {\n  /// A visibility modifier. See [`super::visibility`].\n  Visibility(Visibility),\n}\n\nimpl DeclareMod {\n  /// Apply the modifier to `decl`.\n  pub fn apply(&self, decl: &mut DeclareDecl) {\n    match self {\n      DeclareMod::Visibility(vis) => {\n        decl.visibility = *vis;\n      }\n    }\n  }\n}\n\n/// A parse rule for [`DeclareDecl`].\npub fn parser() -> impl ParseRule<Modifier=DeclareMod> {\n  visibility::parser().map(DeclareMod::Visibility)\n}\n"
  },
  {
    "path": "src/ir/modifier/enums.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parse rule for modifiers to `enum` declarations.\n\nuse crate::ir::decl::EnumDecl;\nuse crate::ir::export::Visibility;\nuse super::ParseRule;\nuse super::visibility;\n\n/// Modifiers which apply to [`EnumDecl`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum EnumMod {\n  /// A visibility modifier. See [`super::visibility`].\n  Visibility(Visibility),\n}\n\nimpl EnumMod {\n  /// Apply the modifier.\n  pub fn apply(&self, decl: &mut EnumDecl) {\n    match self {\n      EnumMod::Visibility(vis) => {\n        decl.visibility = *vis;\n      }\n    }\n  }\n}\n\n/// A parse rule for modifiers which apply to [`EnumDecl`].\npub fn parser() -> impl ParseRule<Modifier=EnumMod> {\n  visibility::parser().map(EnumMod::Visibility)\n}\n"
  },
  {
    "path": "src/ir/modifier/file.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Modifiers that have to do with an entire GDLisp file.\n\nuse crate::sxp::ast::AST;\nuse crate::sxp::dotted::DottedExpr;\nuse crate::ir::incremental::IncCompiler;\nuse super::{ParseRule, ParseError, ParseErrorF};\n\nuse std::convert::TryInto;\n\n/// Modifier type for file-local modifiers.\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum FileMod {\n  /// This modifier (`(sys/nostdlib)`) should be applied to the\n  /// standard library file to initiate the bootstrapping process. It\n  /// instructs the compiler to run a minimal compilation, which skips\n  /// over the usual steps of binding the standard library and also\n  /// disables macro resolution.\n  NoStdLib,\n}\n\n/// Custom parse rule which only successfully parses the literal value\n/// `(sys/nostdlib)` successfully.\n#[derive(Clone, Debug)]\npub struct NoStdLibParser;\n\nimpl FileMod {\n\n  /// Apply `self` to the `IncCompiler` supplied.\n  pub fn apply(&self, icompiler: &mut IncCompiler) {\n    match self {\n      FileMod::NoStdLib => {\n        icompiler.mark_as_minimalist()\n      }\n    }\n  }\n\n}\n\nimpl ParseRule for NoStdLibParser {\n  type Modifier = ();\n\n  fn name(&self) -> &str {\n    \"NoStdLibParser\"\n  }\n\n  fn parse_once(&mut self, ast: &AST) -> Result<(), ParseError> {\n    let vec: Vec<_> = DottedExpr::new(ast).try_into().map_err(|_| file_error(ast))?;\n    if vec.len() != 1 {\n      return Err(file_error(ast));\n    }\n    if let Some(sys_nostdlib) = vec[0].as_symbol_ref() {\n      if sys_nostdlib == \"sys/nostdlib\" {\n        return Ok(());\n      }\n    }\n    Err(file_error(ast))\n  }\n\n}\n\nfn file_error(ast: &AST) -> ParseError {\n  ParseError::new(ParseErrorF::Expecting(String::from(\"(file-level declaration)\"), ast.clone()), ast.pos)\n}\n\n/// Parse rule for modifiers to an entire file.\npub fn parser() -> impl ParseRule<Modifier=FileMod> {\n  NoStdLibParser.unique().map(|_| FileMod::NoStdLib)\n}\n"
  },
  {
    "path": "src/ir/modifier/function.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Modifiers which apply to function declarations.\n//!\n//! This modifier type applies to standalone functions. For the\n//! modifier type which applies to instance methods, see\n//! [`super::instance_method`].\n\nuse crate::ir::decl::FnDecl;\nuse crate::ir::export::Visibility;\nuse super::{ParseRule, Several};\nuse super::visibility;\nuse super::magic;\n\n/// Modifier type which applies to [`FnDecl`].\n#[derive(Clone)]\npub enum FnMod {\n  /// Visibility modifier, as per [`super::visibility`].\n  Visibility(Visibility),\n  /// A magic declaration, as per [`super::magic`].\n  Magic(String),\n}\n\nimpl FnMod {\n  /// Apply the modifier.\n  pub fn apply(&self, decl: &mut FnDecl) {\n    match self {\n      FnMod::Visibility(vis) => {\n        decl.visibility = *vis;\n      }\n      FnMod::Magic(m) => {\n        decl.call_magic = Some(m.clone());\n      }\n    }\n  }\n}\n\n/// A parse rule for function modifiers.\npub fn parser() -> impl ParseRule<Modifier=FnMod> {\n  Several::new(vec!(\n    Box::new(visibility::parser().map(FnMod::Visibility)),\n    Box::new(magic::parser().map(FnMod::Magic)),\n  ))\n}\n"
  },
  {
    "path": "src/ir/modifier/instance_method.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! A parse rule for modifiers which apply specifically to instance\n//! methods.\n\nuse crate::ir::decl::{ClassFnDecl, ConstructorDecl};\nuse crate::gdscript::decl::Static;\nuse crate::compile::error::{GDError, GDErrorF};\nuse super::{ParseRule, Several, Constant};\n\n/// Modifier for [`ClassFnDecl`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum MethodMod {\n  /// `static` declarations allow the instance method to be called\n  /// without an instance of the class available.\n  Static,\n  /// `sys/nullargs` declarations force all formal arguments to the\n  /// instance method to be given a default value of `null`.\n  Nullargs,\n}\n\nimpl MethodMod {\n\n  /// Apply the modifier to an instance function declaration.\n  pub fn apply(&self, decl: &mut ClassFnDecl) {\n    match self {\n      MethodMod::Static => {\n        decl.is_static = Static::IsStatic;\n      }\n      MethodMod::Nullargs => {\n        decl.is_nullargs = true;\n      }\n    }\n  }\n\n  /// Apply the modifier to a constructor. Some modifiers do not make\n  /// sense applied to constructors and will trigger an error if an\n  /// attempt is made to do so.\n  pub fn apply_to_constructor(&self, decl: &mut ConstructorDecl) -> Result<(), GDError> {\n    match self {\n      MethodMod::Static => {\n        Err(GDError::new(GDErrorF::StaticConstructor, decl.body.pos))\n      }\n      MethodMod::Nullargs => {\n        Err(GDError::new(GDErrorF::NullargsConstructor, decl.body.pos))\n      }\n    }\n  }\n}\n\n/// Parse rule for `MethodMod`.\npub fn parser() -> impl ParseRule<Modifier=MethodMod> {\n  Several::new(vec!(\n    Box::new(Constant::new(\"static\", MethodMod::Static).unique()),\n    Box::new(Constant::new(\"sys/nullargs\", MethodMod::Nullargs).unique()),\n  ))\n}\n"
  },
  {
    "path": "src/ir/modifier/macros.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parse rule for modifiers which apply to macros and symbol macros.\n\nuse crate::ir::decl::{MacroDecl, SymbolMacroDecl};\nuse crate::ir::export::Visibility;\nuse super::ParseRule;\nuse super::visibility;\n\n/// Modifier for [`MacroDecl`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum MacroMod {\n  Visibility(Visibility),\n}\n\nimpl MacroMod {\n  /// Apply the modifier.\n  pub fn apply(&self, decl: &mut MacroDecl) {\n    match self {\n      MacroMod::Visibility(vis) => {\n        decl.visibility = *vis;\n      }\n    }\n  }\n  /// Apply the modifier to a symbol macro.\n  pub fn apply_to_symbol_macro(&self, decl: &mut SymbolMacroDecl) {\n    match self {\n      MacroMod::Visibility(vis) => {\n        decl.visibility = *vis;\n      }\n    }\n  }\n}\n\n/// Parse rule for macro modifiers.\npub fn parser() -> impl ParseRule<Modifier=MacroMod> {\n  visibility::parser().map(MacroMod::Visibility)\n}\n"
  },
  {
    "path": "src/ir/modifier/magic.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parser for call magic declarations.\n//!\n//! [Call magic](crate::compile::symbol_table::call_magic) can be used\n//! to force a sort of inlining on standard library calls. This\n//! modifier is responsible for parsing declarations that a function\n//! would like to exhibit call magic behavior.\n\nuse crate::sxp::ast::AST;\nuse crate::sxp::dotted::DottedExpr;\nuse super::{ParseRule, ParseError, ParseErrorF};\n\nuse std::convert::TryInto;\n\n/// Parse rule for call magic. A `MagicParser` parses expressions of\n/// the form `(sys/call-magic name)` where `name` is an arbitrary\n/// symbol. On a successful parse, `name` is returned.\n#[derive(Clone, Debug)]\npub struct MagicParser;\n\nimpl ParseRule for MagicParser {\n  type Modifier = String;\n\n  fn name(&self) -> &str {\n    \"MagicParser\"\n  }\n\n  fn parse_once(&mut self, ast: &AST) -> Result<String, ParseError> {\n    let vec: Vec<_> = DottedExpr::new(ast).try_into().map_err(|_| magic_error(ast))?;\n    if vec.len() != 2 {\n      return Err(magic_error(ast));\n    }\n    if let Some(sys_call_magic) = vec[0].as_symbol_ref() {\n      if sys_call_magic == \"sys/call-magic\" {\n        if let Some(name) = vec[1].as_symbol_ref() {\n          return Ok(name.to_owned());\n        }\n      }\n    }\n    Err(magic_error(ast))\n  }\n\n}\n\nfn magic_error(ast: &AST) -> ParseError {\n  ParseError::new(ParseErrorF::Expecting(String::from(\"(magic declaration)\"), ast.clone()), ast.pos)\n}\n\n/// Parser for call magic.\npub fn parser() -> impl ParseRule<Modifier=String> {\n  MagicParser\n}\n\n"
  },
  {
    "path": "src/ir/modifier/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Framework for parsing and applying modifiers to various types.\n//!\n//! There are a few situations in the compiler where we want to read\n//! an optional sequence of modifiers from a list and then return the\n//! rest of the list. The [`ParseRule`] trait captures this pattern.\n\npub mod class;\npub mod instance_method;\npub mod function;\npub mod macros;\npub mod constant;\npub mod enums;\npub mod visibility;\npub mod magic;\npub mod file;\npub mod declare;\npub mod var;\n\nuse crate::sxp::ast::AST;\nuse crate::pipeline::source::{SourceOffset, Sourced};\n\nuse std::fmt;\nuse std::error::Error;\n\n// TODO Can we use this for defvar export statements too?\n\n/// `Constant` is a [`ParseRule`] which looks for a\n/// [`Literal::Symbol`](crate::sxp::literal::Literal::Symbol) with a\n/// specific string value. If it finds it, it returns a preset value.\n/// The `M` type must implement [`Clone`] to be usable as a\n/// `ParseRule`.\npub struct Constant<M> {\n  /// The symbol value to look for.\n  pub symbol_value: String,\n  /// The value to return on successful parse.\n  pub result: M,\n}\n\n/// `Several` is a [`ParseRule`] which will attempt to parse all of\n/// the modifiers in its values field in order, taking the first one\n/// which succeeds. The `Several` instance will only fail if all\n/// constituent parsers fail on the same input.\npub struct Several<'a, M> {\n  /// The `Several` instance's name, used to generate friendly error\n  /// messages.\n  pub name: String,\n  /// The parsers to try in order. All parse rules must have the same\n  /// `Modifier` type.\n  pub values: Vec<Box<dyn ParseRule<Modifier=M> + 'a>>,\n}\n\n/// `Map` is a [`ParseRule`] which will perform another parse rule and\n/// then apply a function to the result, effectively postprocessing\n/// the value. This rule is constructed using [`ParseRule::map`].\npub struct Map<R, F> {\n  rule: R,\n  function: F,\n}\n\n/// `Unique` is a [`ParseRule`] which keeps track of whether or not\n/// its inner parse rule has been tripped before. If it has and it\n/// would trigger a second time successfully, an appropriate error is\n/// signaled. This rule is constructed using ParseRule::unique.\npub struct Unique<R> {\n  triggered: bool,\n  rule: R,\n}\n\n/// The type of errors that can occur during parsing modifiers.\n///\n/// There are two broad categories of errors in parsing modifiers:\n/// fatal and non-fatal. A non-fatal error can be recovered by\n/// alternative cases, such as a [`Several`] instance with several\n/// options. However, if a fatal error occurs at any point during\n/// parsing, it is assumed to be critical and immediately fails the\n/// entire parse, regardless of alternatives.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub enum ParseErrorF {\n  /// A `UniquenessError` occurs when a parser wrapped in [`Unique`]\n  /// triggers successfully twice in the same parse. This error is\n  /// fatal.\n  UniquenessError(String),\n  /// An error in which the parser was expecting something, but some\n  /// non-matching `AST` was found instead.\n  Expecting(String, AST),\n  /// Generic error which is triggered when a [`Several`] exhausts its\n  /// options.\n  ExhaustedAlternatives,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct ParseError {\n  pub value: ParseErrorF,\n  pub pos: SourceOffset,\n}\n\n/// A [`ParseRule`] takes an [`AST`] and attempts to parse it as a\n/// particular modifier type. If successful, the modifier is returned.\n/// Otherwise, a [`ParseError`] is returned.\npub trait ParseRule {\n\n  /// The type of modifier to be returned. The interpretation of this\n  /// type is entirely dependent on context, and no explicit\n  /// requirements are placed on it.\n  type Modifier;\n\n  /// Attempts to parse the given AST using the parse rule.\n  fn parse_once(&mut self, ast: &AST) -> Result<Self::Modifier, ParseError>;\n\n  /// The name of a parse rule is used to generate more friendly error\n  /// messages and does not affect the act of parsing itself.\n  fn name(&self) -> &str;\n\n  /// `parse` takes a slice of AST's and attempts to parse them each\n  /// using the current parse rule. Whenever a parse fails, parsing\n  /// stops.\n  ///\n  /// If the parse failed with a non-fatal (continuable) error, then\n  /// the successfully parsed modifiers are returned, along with the\n  /// rest of the slice that was not parsed successfully. If the parse\n  /// failed with a fatal error, then that error is returned instead.\n  fn parse<'a, 'b>(&mut self, args: &'a [&'b AST]) -> Result<(Vec<Self::Modifier>, &'a [&'b AST]), ParseError> {\n    let mut modifiers = Vec::new();\n\n    let mut position = 0;\n    while position < args.len() {\n      let curr = args[position];\n      match self.parse_once(curr) {\n        Err(e) if e.is_fatal() => return Err(e),\n        Err(_) => break,\n        Ok(m) => modifiers.push(m),\n      }\n      position += 1;\n    }\n\n    Ok((modifiers, &args[position..]))\n  }\n\n  /// Produces a [`ParseRule`] which performs `self`, then applies `f`\n  /// to the result. Errors (fatal and non-fatal alike) will be\n  /// propagated through to the new parse rule.\n  fn map<N, F>(self, f: F) -> Map<Self, F>\n  where Self : Sized,\n        F : FnMut(Self::Modifier) -> N {\n    Map { rule: self, function: f }\n  }\n\n  /// Produces a [`Unique`] parser representing `self`. A `Unique`\n  /// parser will parse `self` successfully only once. If `self`\n  /// parses successfully twice or more, a fatal\n  /// [`ParseErrorF::UniquenessError`] will be signaled.\n  fn unique(self) -> Unique<Self>\n  where Self : Sized {\n    Unique { triggered: false, rule: self }\n  }\n\n}\n\nimpl ParseError {\n\n  pub fn new(value: ParseErrorF, pos: SourceOffset) -> ParseError {\n    ParseError { value, pos }\n  }\n\n  /// Fatal errors should abort the entire parse process, not allowing\n  /// any alternatives to run. Non-fatal errors allow alternative\n  /// parse attempts to be run.\n  pub fn is_fatal(&self) -> bool {\n    match &self.value {\n      ParseErrorF::UniquenessError(_) => true,\n      ParseErrorF::Expecting(_, _) => false,\n      ParseErrorF::ExhaustedAlternatives => false,\n    }\n  }\n\n}\n\nimpl Sourced for ParseError {\n  type Item = ParseErrorF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &ParseErrorF {\n    &self.value\n  }\n\n}\n\nimpl fmt::Display for ParseError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match &self.value {\n      ParseErrorF::UniquenessError(e) =>\n        write!(f, \"Got duplicate modifiers for {}, expecting at most one\", e),\n      ParseErrorF::Expecting(expected, actual) =>\n        write!(f, \"Expecting modifier '{}', found {}\", expected, actual),\n      ParseErrorF::ExhaustedAlternatives =>\n        write!(f, \"Invalid modifier, no alternatives matched\"),\n    }\n  }\n}\n\nimpl Error for ParseError {}\n\nimpl<M> Constant<M> {\n  pub fn new(symbol_value: &str, result: M) -> Constant<M> {\n    Constant {\n      symbol_value: String::from(symbol_value),\n      result\n    }\n  }\n}\n\nimpl<M> ParseRule for Constant<M> where M: Clone {\n  type Modifier = M;\n\n  fn parse_once(&mut self, ast: &AST) -> Result<M, ParseError> {\n    if ast.as_symbol_ref() == Some(&self.symbol_value) {\n      Ok(self.result.clone())\n    } else {\n      Err(ParseError::new(ParseErrorF::Expecting(self.symbol_value.clone(), ast.clone()), ast.pos))\n    }\n  }\n\n  fn name(&self) -> &str {\n    &self.symbol_value\n  }\n\n}\n\nimpl<'a, M> Several<'a, M> {\n\n  pub fn new(values: Vec<Box<dyn ParseRule<Modifier=M> + 'a>>) -> Several<'a, M> {\n    Several { name: String::from(\"(union parse rule)\"), values }\n  }\n\n  /// Assigns a given name to `self` and returns it.\n  pub fn named(mut self, name: &str) -> Self {\n    self.name = String::from(name);\n    self\n  }\n\n}\n\nimpl<'a, M> ParseRule for Several<'a, M> {\n  type Modifier = M;\n\n  fn parse_once(&mut self, ast: &AST) -> Result<M, ParseError> {\n    for value in &mut self.values {\n      match value.parse_once(ast) {\n        Ok(modifier) => {\n          return Ok(modifier);\n        }\n        Err(err) if err.is_fatal() => {\n          return Err(err);\n        }\n        Err(_) => {\n          // Continue with any other possible parses\n        }\n      }\n    }\n    Err(ParseError::new(ParseErrorF::ExhaustedAlternatives, ast.pos))\n  }\n\n  fn name(&self) -> &str {\n    &self.name\n  }\n\n}\n\nimpl<M, N, R, F> ParseRule for Map<R, F>\nwhere F : FnMut(M) -> N,\n      R : ParseRule<Modifier=M> {\n  type Modifier = N;\n\n  fn parse_once(&mut self, ast: &AST) -> Result<N, ParseError> {\n    let result = self.rule.parse_once(ast)?;\n    Ok((self.function)(result))\n  }\n\n  fn name(&self) -> &str {\n    self.rule.name()\n  }\n\n}\n\nimpl<R> ParseRule for Unique<R>\nwhere R : ParseRule {\n  type Modifier = R::Modifier;\n\n  fn parse_once(&mut self, ast: &AST) -> Result<Self::Modifier, ParseError> {\n    let result = self.rule.parse_once(ast)?;\n    if self.triggered {\n      Err(ParseError::new(ParseErrorF::UniquenessError(self.name().to_owned()), ast.pos))\n    } else {\n      self.triggered = true;\n      Ok(result)\n    }\n  }\n\n  fn name(&self) -> &str {\n    self.rule.name()\n  }\n\n}\n"
  },
  {
    "path": "src/ir/modifier/var.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parse rule for modifiers that can be applied to instance variable\n//! declarations.\n\nuse crate::ir::decl::ClassVarDecl;\nuse crate::compile::body::class_initializer::InitTime;\nuse super::{ParseRule, Constant};\n\n/// A modifier which applies to a [`ClassVarDecl`].\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum VarMod {\n  /// An \"onready\" modifier.\n  Onready,\n}\n\nimpl VarMod {\n  /// Apply the modifier to `decl`.\n  pub fn apply(&self, decl: &mut ClassVarDecl) {\n    match self {\n      VarMod::Onready => {\n        decl.init_time = InitTime::Ready;\n      }\n    }\n  }\n}\n\n/// A parse rule for constant declarations.\npub fn parser() -> impl ParseRule<Modifier=VarMod> {\n  Constant::new(\"onready\", VarMod::Onready).unique()\n}\n"
  },
  {
    "path": "src/ir/modifier/visibility.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parse rule for declaring [`Visibility`] of an identifier.\n\nuse crate::ir::export::Visibility;\nuse super::{ParseRule, Several, Constant};\n\n/// A parse rule which checks for the literal symbols `public` or\n/// `private`, to return the appropriate [`Visibility`] values. The\n/// entire parse rule is wrapped in [`ParseRule::unique`], hence if\n/// the user ever supplies two visibility modifiers (whether they are\n/// the same or different), a fatal error will be issued.\npub fn parser() -> impl ParseRule<Modifier=Visibility> {\n  Several::new(vec!(\n    Box::new(Constant::new(\"public\", Visibility::Public)),\n    Box::new(Constant::new(\"private\", Visibility::Private)),\n  )).named(\"visibility\").unique()\n}\n"
  },
  {
    "path": "src/ir/quasiquote.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::ir::incremental::IncCompiler;\nuse crate::compile::error::{GDError, GDErrorF};\nuse super::expr::Expr;\nuse crate::pipeline::error::PError;\nuse crate::pipeline::Pipeline;\n\n// TODO Where do I pick up PError in this file? Can we do all of this in GDError?\n\n/// A `QQSpliced` represents an expression within an `unquote` or\n/// `unquote-spliced` block. Specifically, a `QQSpliced` contains an\n/// expression that, when the quasiquote is evaluated, will have its\n/// value interpolated in some form or another into the surrounding\n/// expression.\n#[derive(Clone, Debug, PartialEq, Eq)]\nenum QQSpliced {\n  /// An expression that will be interpreted as a single atomic value.\n  /// The inside of an `unquote` block is parsed as this variant.\n  Single(Expr),\n  /// An expression that will be spliced into an array or list\n  /// expression. The inside of an `unquote-spliced` block parses as\n  /// this variant.\n  ///\n  /// It is an error if a `QQSpliced::Several` appears in a context\n  /// other than a list or array, as it cannot be spliced. For\n  /// example, it is an error if a `QQSpliced::Several` appears within\n  /// a dictionary literal.\n  Several(Expr),\n}\n\n/// A value within a quasiquoting expression. `UnquotedValue` always\n/// wraps a single [`AST`] and keeps track of what sort of expression\n/// it is, as far as the quasiquoting engine is concerned.\n#[derive(Clone, Debug, PartialEq, Eq)]\nenum UnquotedValue<'a> {\n  /// A simple, literal value which is not anything special as far as\n  /// the quasiquoting engine is concerned.\n  SimpleValue(&'a AST),\n  /// A nested `quasiquote` block. The [`AST`] is the *inside* of the\n  /// `quasiquote`, excluding the block itself.\n  Quasiquote(&'a AST),\n  /// An `unquote` block. The [`AST`] is the *inside* of the\n  /// `unquote`, excluding the block itself.\n  Unquote(&'a AST),\n  /// An `unquote-spliced` block. The [`AST`] is the *inside* of the\n  /// `unquote-spliced`, excluding the block itself.\n  UnquoteSpliced(&'a AST),\n}\n\nimpl QQSpliced {\n\n  /// Extracts the [`QQSpliced::Single`] value from `self`. If `self`\n  /// is not a [`QQSpliced::Single`] then an error is returned instead.\n  ///\n  /// The [`AST`] argument should be the original S-expression from\n  /// which the `QQSpliced` was produced. It will only be used in the\n  /// case of an error in order to provide a more detailed diagnostic.\n  fn into_single(self, ast: &AST) -> Result<Expr, GDError> {\n    match self {\n      QQSpliced::Single(e) => Ok(e),\n      QQSpliced::Several(_) => Err(GDError::new(GDErrorF::BadUnquoteSpliced(ast.clone()), ast.pos)),\n    }\n  }\n\n}\n\nimpl<'a> UnquotedValue<'a> {\n\n  /// Convenience constructor, delegates to\n  /// [`UnquotedValue::SimpleValue`].\n  fn verbatim(arg: &'a AST) -> UnquotedValue<'a> {\n    UnquotedValue::SimpleValue(arg)\n  }\n\n  /// Reads the top-level S-expressions and looks for any special\n  /// quasiquote keywords. Specifically, if the S-expression is a\n  /// proper list of two elements and the first element is one of the\n  /// following symbols, then the function returns an appropriate\n  /// value.\n  ///\n  /// * If the symbol is `quasiquote`, then an\n  /// [`UnquotedValue::Quasiquote`] containing the second element of\n  /// the list is returned.\n  ///\n  /// * If the symbol is `unquote`, then an [`UnquotedValue::Unquote`]\n  /// containing the second element of the list is returned.\n  ///\n  /// * If the symbol is `unquote-spliced`, then an\n  /// [`UnquotedValue::UnquoteSpliced`] containing the second element\n  /// of the list is returned.\n  ///\n  /// If the symbol is anything else, or if the AST has any other\n  /// shape, then this function returns an\n  /// [`UnquotedValue::SimpleValue`] containing the entire AST.\n  fn parse(arg: &'a AST) -> UnquotedValue<'a> {\n    if let ASTF::Cons(car, cdr) = &arg.value {\n      if let Some(name) = car.as_symbol_ref() {\n        if let ASTF::Cons(cadr, cddr) = &cdr.value {\n          if cddr.value == ASTF::NIL {\n            if name == \"quasiquote\" {\n              return UnquotedValue::Quasiquote(cadr);\n            } else if name == \"unquote\" {\n              return UnquotedValue::Unquote(cadr);\n            } else if name == \"unquote-spliced\" {\n              return UnquotedValue::UnquoteSpliced(cadr);\n            }\n          }\n        }\n      }\n    }\n    UnquotedValue::SimpleValue(arg)\n  }\n\n}\n\npub fn quasiquote(icompiler: &mut IncCompiler,\n                  pipeline: &mut Pipeline,\n                  arg: &AST)\n                  -> Result<Expr, PError> {\n  quasiquote_with_depth(icompiler, pipeline, arg, u32::MAX)\n}\n\npub fn quasiquote_with_depth(icompiler: &mut IncCompiler,\n                             pipeline: &mut Pipeline,\n                             arg: &AST,\n                             max_depth: u32)\n                  -> Result<Expr, PError> {\n  let mut engine = QuasiquoteEngine::new(icompiler, pipeline, max_depth);\n  engine.quasiquote_indexed(arg, 0, 0)\n}\n\n/// The quasiquoting engine is the internal class whose implementation\n/// recursively implements quasiquote parsing on an [`AST`].\n///\n/// A quasiquote engine requires mutable access to an [`IncCompiler`]\n/// and a [`Pipeline`], in order to call [`IncCompiler::compile_expr`]\n/// on unquoted values. Additionally, a quasiquote engine keeps track\n/// of how deep it is into a Godot expression. This is a workaround\n/// for a Godot parsing issue that manifests when an expression is\n/// nested too deeply.\nstruct QuasiquoteEngine<'a, 'b> {\n  icompiler: &'a mut IncCompiler,\n  pipeline: &'b mut Pipeline,\n  max_depth: u32,\n}\n\nimpl<'a, 'b> QuasiquoteEngine<'a, 'b> {\n\n  /// Given an incremental compiler and a pipeline, produce a\n  /// [`QuasiquoteEngine`].\n  ///\n  /// The `max_depth` argument indicates how deeply nested a generated\n  /// expression can be before the engine will split it into multiple\n  /// expressions with a temporary variable in between. For the\n  /// rationale behind this argument (as well as a reasonable default\n  /// value for it), see\n  /// [`MAX_QUOTE_REIFY_DEPTH`](crate::compile::frame::MAX_QUOTE_REIFY_DEPTH).\n  fn new(icompiler: &'a mut IncCompiler, pipeline: &'b mut Pipeline, max_depth: u32) -> Self {\n    QuasiquoteEngine { icompiler, pipeline, max_depth }\n  }\n\n  // Note: nesting_depth is the number of nested quasiquotes we're in.\n  // An unquote encountered when nesting_depth is positive simply\n  // decreases that value rather than performing an actual unquote\n  // operation. current_depth is how far down we are into our\n  // structure and is used to determine when to insert ExprF::Split\n  // calls to avoid Godot parsing issues.\n\n  fn quasiquote_indexed(&mut self,\n                        arg: &AST,\n                        nesting_depth: u32,\n                        current_depth: u32)\n                        -> Result<Expr, PError> {\n    let (needs_split_wrapper, current_depth) =\n      if current_depth > self.max_depth {\n        (true, 0)\n      } else {\n        (false, current_depth)\n      };\n\n    self.quasiquote_spliced(arg, nesting_depth, current_depth).and_then(|qq| {\n      let value = qq.into_single(arg)?;\n      if needs_split_wrapper {\n        let pos = value.pos;\n        Ok(value.named_split(\"_quasiquote\", pos))\n      } else {\n        Ok(value)\n      }\n    })\n\n  }\n\n  fn quasiquote_spliced(&mut self,\n                        arg: &AST,\n                        nesting_depth: u32,\n                        current_depth: u32)\n                        -> Result<QQSpliced, PError> {\n    let unquoted_value = UnquotedValue::parse(arg);\n\n    // Deal with nesting issues\n    let (unquoted_value, nesting_depth) = match unquoted_value {\n      UnquotedValue::SimpleValue(_) => {\n        (UnquotedValue::verbatim(arg), nesting_depth)\n      }\n      UnquotedValue::Quasiquote(_) => {\n        (UnquotedValue::verbatim(arg), nesting_depth + 1)\n      }\n      UnquotedValue::Unquote(_) | UnquotedValue::UnquoteSpliced(_) => {\n        if nesting_depth > 0 {\n          // We're inside a nested quasiquote, so do NOT unquote the value.\n          (UnquotedValue::verbatim(arg), nesting_depth - 1)\n        } else {\n          (unquoted_value, nesting_depth)\n        }\n      }\n    };\n\n    match unquoted_value {\n      UnquotedValue::Unquote(arg) => {\n        self.icompiler.compile_expr(self.pipeline, arg).map(QQSpliced::Single)\n      }\n      UnquotedValue::UnquoteSpliced(arg) => {\n        self.icompiler.compile_expr(self.pipeline, arg).map(QQSpliced::Several)\n      }\n      UnquotedValue::Quasiquote(_) => {\n        // The above nesting handler should always eliminate\n        // UnquotedValue::Quasiquote and convert it into\n        // UnquotedValue::SimpleValue, so this should never happen.\n        panic!(\"Internal error in quasiquote_spliced (impossible UnquotedValue::Quasiquote branch was reached)\")\n      }\n      UnquotedValue::SimpleValue(arg) => {\n        let body = match &arg.value {\n          ASTF::Atom(lit) => {\n            Expr::from_ast_literal(lit, arg.pos)\n          }\n          ASTF::Cons(car, cdr) => {\n            let car = self.quasiquote_spliced(car, nesting_depth, current_depth + 1)?;\n            let cdr = self.quasiquote_indexed(cdr, nesting_depth, current_depth + 1)?;\n            match car {\n              QQSpliced::Single(car) => {\n                Expr::call(String::from(\"cons\"), vec!(car, cdr), arg.pos)\n              }\n              QQSpliced::Several(car) => {\n                let converted_car = Expr::call(String::from(\"sys/qq-smart-list\"), vec!(car), arg.pos);\n                Expr::call(String::from(\"append\"), vec!(converted_car, cdr), arg.pos)\n              }\n            }\n          }\n        };\n        Ok(QQSpliced::Single(body))\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/ir/scope/decl.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Functions for determining scope at a high-level.\n//!\n//! These functions deal with high-level concepts such as classes and\n//! declarations and are specifically *not* concerned with lower-level\n//! concepts like `let` blocks and local variables.\n\nuse super::name_table::NameTable;\nuse super::name_table::builder::NameTableBuilder;\nuse super::error::ScopeError;\nuse crate::util::extract_err;\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir::identifier::{Namespace, ClassNamespace};\nuse crate::ir::decl::{Decl, DeclF, TopLevel, ClassDecl};\nuse crate::ir::expr::{Expr, ExprF, LambdaClass};\nuse crate::ir::literal::Literal;\nuse crate::gdscript::library;\nuse crate::optimize::ir::expr_walker::walk_exprs_in_decl;\n\nuse std::hash::Hash;\nuse std::convert::Infallible;\n\n/// Any type which implements [`DeclScope`] for the namespace\n/// [`Namespace`] can also correctly implement it for\n/// [`ClassNamespace`] via a simple embedding (`From::from`). The\n/// `ClassNamespaceAdaptor` type takes a value which implements\n/// `DeclScope<Namespace>` and provides a value which implements\n/// `DeclScope<ClassNamespace>`.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct ClassNamespaceAdaptor<'a, T>(pub &'a T);\n\n/// Trait for containers of declarations which can meaningfully\n/// enumerate the names declared in their scope. Implementors of this\n/// trait should only return a table of the names in the immediate\n/// scope and should specifically *not* recurse on inner declarations,\n/// such as the bodies of inner classes.\npub trait DeclScope<NS: Hash + Eq + Clone> {\n\n  /// Returns a table of all names, or an appropriate [`ScopeError`]\n  /// if a problem occurs during enumeration.\n  fn get_scope_names(&self) -> Result<NameTable<NS>, ScopeError<NS>>;\n\n}\n\nimpl<'a, T> DeclScope<ClassNamespace> for ClassNamespaceAdaptor<'a, T>\nwhere T: DeclScope<Namespace> {\n\n  fn get_scope_names(&self) -> Result<NameTable<ClassNamespace>, ScopeError<ClassNamespace>> {\n    self.0.get_scope_names()\n      .map_err(ScopeError::from)\n      .map(|table| table.map_ns(ClassNamespace::from))\n  }\n\n}\n\nimpl DeclScope<Namespace> for TopLevel {\n\n  /// Returns a table of all names, or an appropriate [`ScopeError`]\n  /// if a problem occurs during enumeration.\n  ///\n  /// This method does *not* enumerate imported names. It only\n  /// produces names for declarations which are actually defined in\n  /// this current file, not those imported into scope.\n  fn get_scope_names(&self) -> Result<NameTable<Namespace>, ScopeError<Namespace>> {\n    let mut builder = NameTableBuilder::new();\n    for decl in &self.decls {\n      let namespace = decl.namespace();\n      let name = decl.name().to_owned();\n      builder.add_name(namespace, name, decl.pos)?;\n    }\n    Ok(builder.build())\n  }\n\n}\n\nimpl DeclScope<ClassNamespace> for ClassDecl {\n\n  fn get_scope_names(&self) -> Result<NameTable<ClassNamespace>, ScopeError<ClassNamespace>> {\n    let mut builder = NameTableBuilder::new();\n\n    // Add the constructor as a special case\n    if let Some(constructor) = &self.constructor {\n      builder.add_name(ClassNamespace::Function, library::CONSTRUCTOR_NAME.to_owned(), constructor.body.pos)?;\n    }\n\n    for decl in &self.decls {\n      let namespace = decl.namespace();\n      let name = decl.name().into_owned();\n      builder.add_name(namespace, name, decl.pos)?;\n    }\n\n    Ok(builder.build())\n  }\n\n}\n\nimpl DeclScope<ClassNamespace> for LambdaClass {\n\n  fn get_scope_names(&self) -> Result<NameTable<ClassNamespace>, ScopeError<ClassNamespace>> {\n    let mut builder = NameTableBuilder::new();\n\n    // Add the constructor as a special case\n    if let Some(constructor) = &self.constructor {\n      builder.add_name(ClassNamespace::Function, library::CONSTRUCTOR_NAME.to_owned(), constructor.body.pos)?;\n    }\n\n    for decl in &self.decls {\n      let namespace = decl.namespace();\n      let name = decl.name().into_owned();\n      builder.add_name(namespace, name, decl.pos)?;\n    }\n\n    Ok(builder.build())\n  }\n\n}\n\nimpl<'a, T, NS: Hash + Eq + Clone> DeclScope<NS> for &'a T\nwhere T: DeclScope<NS> {\n  fn get_scope_names(&self) -> Result<NameTable<NS>, ScopeError<NS>> {\n    (*self).get_scope_names()\n  }\n}\n\n/// Return a vector of all of the declaration scopes in the given\n/// file, in an unspecified order.\n///\n/// This function returns all of the following.\n/// * The toplevel scope itself.\n/// * Any class scopes introduced in the file, including the main class, which\n///   is considered distinct from the toplevel.\n/// * Any anonymous classes defined in the file.\n#[allow(clippy::vec_init_then_push)] // For style consistency\npub fn get_all_decl_scopes<'a>(toplevel: &'a TopLevel) -> Vec<Box<dyn DeclScope<ClassNamespace> + 'a>> {\n  let mut acc: Vec<Box<dyn DeclScope<ClassNamespace> + 'a>> = Vec::new();\n\n  // The toplevel scope\n  acc.push(Box::new(ClassNamespaceAdaptor(toplevel)));\n\n  // Any classes defined in the toplevel\n  for decl in &toplevel.decls {\n    match &decl.value {\n      DeclF::ClassDecl(cls) => {\n        acc.push(Box::new(cls));\n      }\n      DeclF::FnDecl(_) | DeclF::MacroDecl(_) | DeclF::SymbolMacroDecl(_) | DeclF::ConstDecl(_) | DeclF::EnumDecl(_) | DeclF::DeclareDecl(_) => {}\n    }\n  }\n\n  // Any anonymous classes declared in the file\n  let err = on_each_lambda_class::<Infallible, _>(&toplevel.decls, |class| {\n    // *sigh* What an unfortunate copy. But I don't see a way to\n    // convince the borrow checker that this value isn't going to\n    // disappear. (TODO Yeah...)\n    acc.push(Box::new(class.clone()));\n    Ok(())\n  });\n  extract_err(err);\n\n  acc\n}\n\n/// Given a file, run the check for duplicate names, returning an\n/// error if any are found. If no errors occur, returns normally.\npub fn check_all_decl_scopes(toplevel: &TopLevel) -> Result<(), ScopeError<ClassNamespace>> {\n  for scope in get_all_decl_scopes(toplevel) {\n    // We don't need the resulting table; just any errors that occur\n    // while trying to produce it.\n    let _ = scope.get_scope_names()?;\n  }\n  Ok(())\n}\n\npub fn on_each_lambda_class<E, F>(decls: &[Decl], mut block: F) -> Result<(), E>\nwhere F : FnMut(&LambdaClass) -> Result<(), E> {\n  for decl in decls {\n    walk_exprs_in_decl(decl, |expr| {\n      if let ExprF::LambdaClass(cls) = &expr.value {\n        block(cls)?;\n      }\n      // Note: We don't use this value, so if this string ever appears\n      // in the output code, there's a problem.\n      Ok(Expr::literal(Literal::from(\"UNUSED STRING FROM on_each_lambda_class\"), SourceOffset(0)))\n    })?;\n  }\n  Ok(())\n}\n\n// TODO Tests\n"
  },
  {
    "path": "src/ir/scope/error.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`ScopeError`] type, which marks error during scope\n//! resolution.\n\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir::identifier::{Namespace, ClassNamespace};\n\n// TODO Encode the offset of both duplicate names in this error type.\n\n///// Display and error\n\n/// The type of errors during scope resolution.\n#[derive(PartialEq, Eq, Debug)]\npub enum ScopeError<NS> {\n  /// The same name was declared twice in the same namespace and\n  /// scope.\n  DuplicateName(NS, String, SourceOffset),\n  NameConflictWithMainClass(NS, String, SourceOffset),\n}\n\nimpl From<ScopeError<Namespace>> for ScopeError<ClassNamespace> {\n  fn from(error: ScopeError<Namespace>) -> ScopeError<ClassNamespace> {\n    match error {\n      ScopeError::DuplicateName(n, s, p) => ScopeError::DuplicateName(n.into(), s, p),\n      ScopeError::NameConflictWithMainClass(n, s, p) => ScopeError::NameConflictWithMainClass(n.into(), s, p),\n    }\n  }\n}\n"
  },
  {
    "path": "src/ir/scope/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Functions for identifying and detailing which names are in scope\n//! in the IR.\n\npub mod decl;\npub mod name_table;\npub mod error;\n\nuse crate::ir::decl::{TopLevel, Decl, DeclF};\nuse crate::compile::error::GDError;\nuse crate::ir::identifier::Namespace;\nuse decl::{DeclScope, check_all_decl_scopes, ClassNamespaceAdaptor};\nuse name_table::NameTable;\nuse name_table::builder::NameTableBuilder;\nuse error::ScopeError;\n\nstruct ConcreteNamesAdaptor<'a>(&'a TopLevel);\n\n/// Convenience method to perform all scope checking on an IR file\n/// representation.\npub fn check_scopes(toplevel: &TopLevel) -> Result<(),  GDError> {\n  check_all_decl_scopes(toplevel).map_err(GDError::from)?;\n  check_main_class_conflicts(toplevel)\n}\n\n/// Check for conflicts between names defined in the main class and\n/// names defined at the top level. There should be no overlap.\n/// Returns an appropriate error in case of overlap.\n///\n/// If there is no main class, then this function simply returns\n/// `Ok(())` without performing any further checks.\npub fn check_main_class_conflicts(toplevel: &TopLevel) -> Result<(), GDError> {\n  let main_class_option = toplevel.find_main_class()?;\n  if let Some(main_class) = main_class_option {\n\n    // Get all toplevel names *except* `sys/declare` names.\n    // `sys/declare` are specifically allowed to conflict with the\n    // main class under this rule (this is necessary in the standard\n    // library, where lots of supposedly module-level names are\n    // `sys/declare` and are actually implemented on the main class\n    // `GDLisp`).\n    let concrete_names_adaptor = ConcreteNamesAdaptor(toplevel);\n    let class_namespace_adaptor = ClassNamespaceAdaptor(&concrete_names_adaptor);\n    let toplevel_names = class_namespace_adaptor.get_scope_names()?;\n    let mut main_names = main_class.get_scope_names()?;\n    main_names.retain(|ns, name, _| toplevel_names.has_name(ns, name));\n\n    // If there are any conflicts, just report the first one.\n    let first_conflict = main_names.iter().next();\n    match first_conflict {\n      None => Ok(()),\n      Some((ns, name, pos)) => Err(GDError::from(ScopeError::NameConflictWithMainClass(ns, name.to_owned(), pos))),\n    }\n  } else {\n    // No main class is declared, so conflict is impossible.\n    Ok(())\n  }\n}\n\nimpl<'a> DeclScope<Namespace> for ConcreteNamesAdaptor<'a> {\n\n  /// As `DeclScope` on [`TopLevel`] but excludes `sys/declare`\n  /// declarations specifically. See the implementation of\n  /// [`check_main_class_conflicts`] for why we care to do this.\n  fn get_scope_names(&self) -> Result<NameTable<Namespace>, ScopeError<Namespace>> {\n    let mut builder = NameTableBuilder::new();\n    for decl in &self.0.decls {\n      if !is_declare_decl(decl) {\n        let namespace = decl.namespace();\n        let name = decl.name().to_owned();\n        builder.add_name(namespace, name, decl.pos)?;\n      }\n    }\n    Ok(builder.build())\n  }\n\n}\n\nfn is_declare_decl(decl: &Decl) -> bool {\n  matches!(decl.value, DeclF::DeclareDecl(_))\n}\n"
  },
  {
    "path": "src/ir/scope/name_table/builder.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Builder for a [`NameTable`](super::NameTable).\n\nuse super::NameTable;\nuse crate::ir::scope::error::ScopeError;\nuse crate::pipeline::source::SourceOffset;\n\nuse std::hash::Hash;\n\n/// A [`NameTableBuilder`] builds a [`NameTable`]. If it ever\n/// encounters a duplicate name or other error condition, it signals\n/// the error via [`ScopeError`].\n#[derive(Debug)]\npub struct NameTableBuilder<NS: Hash + Eq + Clone> {\n  table: NameTable<NS>,\n}\n\nimpl<NS: Hash + Eq + Clone> NameTableBuilder<NS> {\n\n  /// A new, empty builder.\n  pub fn new() -> NameTableBuilder<NS> {\n    NameTableBuilder::default()\n  }\n\n  /// Adds a name to the builder. If the name is already present, an\n  /// appropriate [`ScopeError::DuplicateName`] error is signaled.\n  ///\n  /// If this method signals an error, then the builder is left\n  /// unmodified.\n  pub fn add_name(&mut self, namespace: NS, name: String, pos: SourceOffset) -> Result<(), ScopeError<NS>> {\n\n    // Note: We do the get_name check in advance and assert the result\n    // of add_name to ensure that, if the name exists, then the\n    // builder is left in a consistent state. If we used the result of\n    // add_name to determine if an error had occurred, then we\n    // couldn't guarantee that the builder remains in its prior state\n    // in case of an error.\n    if self.table.get_name(namespace.clone(), &name).is_some() {\n      // Duplicate found\n      Err(ScopeError::DuplicateName(namespace, name, pos))\n    } else {\n      let result = self.table.add_name(namespace, name.clone(), pos);\n      assert!(result.is_none(), \"Internal error in add_name at {}\", name);\n      Ok(())\n    }\n\n  }\n\n  /// Build the [`NameTable`].\n  pub fn build(self) -> NameTable<NS> {\n    self.table\n  }\n\n}\n\nimpl<NS: Hash + Eq + Clone> Default for NameTableBuilder<NS> {\n  fn default() -> NameTableBuilder<NS> {\n    NameTableBuilder { table: NameTable::default() }\n  }\n}\n"
  },
  {
    "path": "src/ir/scope/name_table/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Defines the [`NameTable`] type, for keeping track of names in a\n//! given scope and namespace.\n\npub mod builder;\n\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir::identifier::IdLike;\n\nuse std::collections::HashMap;\nuse std::hash::Hash;\n\n/// A name table stores an unordered collection of names, tagged with\n/// the namespace they belong to and the place in the source code that\n/// they were declared.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct NameTable<NS: Hash + Eq + Clone> {\n  data: HashMap<(NS, String), SourceOffset>,\n}\n\nimpl<NS: Hash + Eq + Clone> NameTable<NS> {\n\n  /// A new, empty `NameTable`.\n  pub fn new() -> NameTable<NS> {\n    NameTable::default()\n  }\n\n  /// Given a namespace and a name, get the position in the source\n  /// code where the declaration for that name appears, if one exists.\n  pub fn get_name(&self, namespace: NS, name: &str) -> Option<SourceOffset> {\n    let key = (namespace, name);\n    self.data.get::<dyn IdLike<NS=NS>>((&key) as &(dyn IdLike<NS=NS>)).copied()\n  }\n\n  /// Returns whether or not this name table contains the given name\n  /// in the given namespace. Equivalent to `self.get_name(namespace,\n  /// name).is_some()`.\n  pub fn has_name(&self, namespace: NS, name: &str) -> bool {\n    self.get_name(namespace, name).is_some()\n  }\n\n  /// Adds a name to the name table, marking it as appearing at the\n  /// given source position. If that name already appears as a\n  /// declaration somewhere in the source code, this method overwrites\n  /// the existing one and returns it. Otherwise, this method inserts\n  /// the new name and returns `None`.\n  pub fn add_name(&mut self, namespace: NS, name: String, pos: SourceOffset) -> Option<SourceOffset> {\n    self.data.insert((namespace, name), pos)\n  }\n\n  /// The number of names which are known to this name table.\n  pub fn len(&self) -> usize {\n    self.data.len()\n  }\n\n  /// Whether the table is currently empty, i.e. contains no names.\n  pub fn is_empty(&self) -> bool {\n    self.data.is_empty()\n  }\n\n  /// Maps the namespace type of this name table, producing a new name\n  /// table where the namespace of every known name has been\n  /// transformed by the given function.\n  ///\n  /// This is mainly intended to be used with injective functions. If\n  /// the function argument is injective, then the new name table will\n  /// have no loss of information and will contain exactly as many\n  /// names as the original. If the function is not injective, then it\n  /// is unspecified which name will win out in the case of collisions\n  /// in the resulting name table.\n  pub fn map_ns<NS1: Hash + Eq + Clone>(self, mut function: impl FnMut(NS) -> NS1) -> NameTable<NS1> {\n    let mut result: NameTable<NS1> = NameTable::new();\n    for ((ns, name), pos) in self.data {\n      result.add_name(function(ns), name, pos);\n    }\n    result\n  }\n\n  pub fn retain<F>(&mut self, mut f: F)\n  where F: FnMut(NS, &str, SourceOffset) -> bool {\n    self.data.retain(|k, v| f(k.0.clone(), &k.1, *v));\n  }\n\n  pub fn iter(&self) -> impl Iterator<Item=(NS, &str, SourceOffset)> {\n    self.data.iter().map(|(k, v)| (k.0.clone(), &*k.1, *v))\n  }\n\n}\n\nimpl<NS: Hash + Eq + Clone> Default for NameTable<NS> {\n\n  fn default() -> NameTable<NS> {\n    NameTable { data: HashMap::default() }\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::ir::identifier::{Namespace, ClassNamespace};\n\n  #[test]\n  fn empty_table() {\n    let table: NameTable<Namespace> = NameTable::new();\n    assert_eq!(table.len(), 0);\n  }\n\n  #[test]\n  fn table_insert_names() {\n    let mut table: NameTable<Namespace> = NameTable::new();\n    assert_eq!(table.add_name(Namespace::Value, String::from(\"VALUE\"), SourceOffset(20)), None);\n    assert_eq!(table.add_name(Namespace::Function, String::from(\"FUNCTION\"), SourceOffset(10)), None);\n    assert_eq!(table.get_name(Namespace::Value, \"VALUE\"), Some(SourceOffset(20)));\n    assert_eq!(table.get_name(Namespace::Function, \"FUNCTION\"), Some(SourceOffset(10)));\n    assert_eq!(table.len(), 2);\n  }\n\n  #[test]\n  fn table_crossed_namespaces() {\n    let mut table: NameTable<Namespace> = NameTable::new();\n    assert_eq!(table.add_name(Namespace::Value, String::from(\"VALUE\"), SourceOffset(20)), None);\n    assert_eq!(table.add_name(Namespace::Function, String::from(\"FUNCTION\"), SourceOffset(10)), None);\n    assert_eq!(table.get_name(Namespace::Function, \"VALUE\"), None);\n    assert_eq!(table.get_name(Namespace::Value, \"FUNCTION\"), None);\n    assert_eq!(table.len(), 2);\n  }\n\n  #[test]\n  fn table_duplicate_name_different_namespace() {\n    let mut table: NameTable<Namespace> = NameTable::new();\n    assert_eq!(table.add_name(Namespace::Value, String::from(\"NAME\"), SourceOffset(20)), None);\n    assert_eq!(table.add_name(Namespace::Function, String::from(\"NAME\"), SourceOffset(10)), None);\n    assert_eq!(table.len(), 2);\n  }\n\n  #[test]\n  fn table_duplicate_name_same_namespace() {\n    let mut table: NameTable<Namespace> = NameTable::new();\n    assert_eq!(table.add_name(Namespace::Value, String::from(\"NAME\"), SourceOffset(20)), None);\n    assert_eq!(table.add_name(Namespace::Value, String::from(\"NAME\"), SourceOffset(10)), Some(SourceOffset(20)));\n    assert_eq!(table.len(), 1);\n  }\n\n  #[test]\n  fn table_map_ns() {\n\n    let mut original_table: NameTable<Namespace> = NameTable::new();\n    assert_eq!(original_table.add_name(Namespace::Value, String::from(\"VALUE\"), SourceOffset(20)), None);\n    assert_eq!(original_table.add_name(Namespace::Function, String::from(\"FUNCTION\"), SourceOffset(10)), None);\n\n    let mut new_table: NameTable<ClassNamespace> = NameTable::new();\n    assert_eq!(new_table.add_name(ClassNamespace::Value, String::from(\"VALUE\"), SourceOffset(20)), None);\n    assert_eq!(new_table.add_name(ClassNamespace::Function, String::from(\"FUNCTION\"), SourceOffset(10)), None);\n\n    let mapped_table = original_table.map_ns(ClassNamespace::from);\n    assert_eq!(mapped_table, new_table);\n\n  }\n\n  #[test]\n  fn table_map_ns_collision() {\n\n    let mut original_table: NameTable<Namespace> = NameTable::new();\n    assert_eq!(original_table.add_name(Namespace::Value, String::from(\"A\"), SourceOffset(20)), None);\n    assert_eq!(original_table.add_name(Namespace::Function, String::from(\"A\"), SourceOffset(10)), None);\n\n    let mapped_table = original_table.map_ns(|_| Namespace::Value); // Cause the two names to collide.\n\n    // It is unspecified which name will win out, but there should\n    // only be one name in the resulting table.\n    assert_eq!(mapped_table.len(), 1);\n\n  }\n\n}\n"
  },
  {
    "path": "src/ir/special_form/access_slot.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::sxp::ast::AST;\nuse crate::sxp::dotted::DottedExpr;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::compile::args::{Expecting, ExpectedShape};\nuse crate::pipeline::source::SourceOffset;\n\nuse std::convert::TryInto;\n\n#[derive(Clone, Debug)]\npub struct AccessSlotSyntax<'a> {\n  pub object: &'a AST,\n  pub slot_name: String,\n}\n\npub const ACCESS_SLOT_FORM_NAME: &str = \"access-slot\";\n\nimpl<'a> AccessSlotSyntax<'a> {\n\n  pub fn parse_ast(ast: &'a AST) -> Result<Self, GDError> {\n    let args: Vec<_> = DottedExpr::new(ast).try_into()?;\n    if args.is_empty() {\n      return Err(GDError::new(GDErrorF::InvalidArg(String::from(ACCESS_SLOT_FORM_NAME), ast.clone(), ExpectedShape::NonemptyList), ast.pos));\n    }\n    let head = ExpectedShape::extract_symbol(ACCESS_SLOT_FORM_NAME, args[0].clone())?;\n    if head != ACCESS_SLOT_FORM_NAME {\n      return Err(GDError::new(GDErrorF::InvalidArg(String::from(ACCESS_SLOT_FORM_NAME), ast.clone(), ExpectedShape::AccessSlotName), ast.pos));\n    }\n    Self::parse_from_tail(&args[1..], ast.pos)\n  }\n\n  pub fn parse_from_tail(tail: &[&'a AST], pos: SourceOffset) -> Result<Self, GDError> {\n    Expecting::exactly(2).validate(ACCESS_SLOT_FORM_NAME, pos, tail)?;\n    let object = tail[0];\n    let slot_name = ExpectedShape::extract_symbol(ACCESS_SLOT_FORM_NAME, tail[1].clone())?;\n    Ok(AccessSlotSyntax { object, slot_name })\n  }\n\n}\n"
  },
  {
    "path": "src/ir/special_form/assignment.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::ir::expr::{Expr, ExprF, AssignTarget};\nuse crate::pipeline::source::SourceOffset;\n\n// This enum represents the different ways a (set ...) assignment form\n// can expand.\n#[derive(Clone, Debug)]\npub enum AssignmentForm {\n  Simple(AssignTarget),\n  SetterCall(String, Vec<Expr>),\n}\n\nimpl AssignmentForm {\n\n  pub fn str_to_setter_prefix(name: &str) -> String {\n    format!(\"set-{}\", name)\n  }\n\n  pub fn into_expr(self, rhs: Expr, pos: SourceOffset) -> Expr {\n    match self {\n      AssignmentForm::Simple(target) => {\n        Expr::new(ExprF::Assign(target, Box::new(rhs)), pos)\n      }\n      AssignmentForm::SetterCall(f, mut args) => {\n        args.insert(0, rhs);\n        Expr::call(f, args, pos)\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/ir/special_form/local_binding.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::ir::expr::{Expr, ExprF, LocalFnClause, FunctionBindingType};\nuse crate::pipeline::source::SourceOffset;\n\n// flet and labels use a lot of common code and only really differ in\n// the way in which bindings work. We factor out those differences\n// here so that all of the common code can be written only once over\n// in ir::special_form.\n//\n// TODO I think FunctionBindingType makes this irrelevant. These\n// differences can just go over there.\n\npub struct FLetLocalBinding;\npub struct LabelsLocalBinding;\n\npub trait LocalBinding {\n  fn function_binding_type(&self) -> FunctionBindingType;\n  fn has_recursive_bindings(&self) -> bool;\n\n  fn wrap_in_expr(&self, clauses: Vec<LocalFnClause>, body: Box<Expr>, pos: SourceOffset) -> Expr {\n    Expr::new(ExprF::FunctionLet(self.function_binding_type(), clauses, body), pos)\n  }\n\n}\n\nimpl LocalBinding for FLetLocalBinding {\n  fn function_binding_type(&self) -> FunctionBindingType {\n    FunctionBindingType::OuterScoped\n  }\n  fn has_recursive_bindings(&self) -> bool {\n    false\n  }\n}\n\nimpl LocalBinding for LabelsLocalBinding {\n  fn function_binding_type(&self) -> FunctionBindingType {\n    FunctionBindingType::Recursive\n  }\n  fn has_recursive_bindings(&self) -> bool {\n    true\n  }\n}\n"
  },
  {
    "path": "src/ir/special_form/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod access_slot;\npub mod assignment;\npub mod local_binding;\n\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::literal::Literal;\nuse crate::sxp::dotted::DottedExpr;\nuse super::expr::{ExprF, Expr, FuncRefTarget, AssignTarget, LambdaClass, LocalVarClause, LocalFnClause};\nuse super::special_ref::SpecialRef;\nuse super::decl::{self, Decl, DeclF};\nuse super::arglist::ordinary::ArgList;\nuse super::quasiquote::quasiquote_with_depth;\nuse super::bootstrapping::compile_bootstrapping_expr;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::compile::args::{Expecting, ExpectedShape};\nuse crate::compile::frame::MAX_QUOTE_REIFY_DEPTH;\nuse crate::ir::incremental::IncCompiler;\nuse crate::ir::identifier::{Id, IdLike, Namespace};\nuse crate::ir::export::Visibility;\nuse crate::ir::import::{ImportDecl, ImportDeclParseError};\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::source::SourceOffset;\nuse crate::pipeline::error::PError;\nuse local_binding::{FLetLocalBinding, LabelsLocalBinding, LocalBinding};\nuse assignment::AssignmentForm;\nuse access_slot::AccessSlotSyntax;\n\nuse std::convert::{TryFrom, TryInto};\n\npub fn dispatch_form(icompiler: &mut IncCompiler,\n                     pipeline: &mut Pipeline,\n                     head: &str,\n                     tail: &[&AST],\n                     pos: SourceOffset)\n                     -> Result<Option<Expr>, PError> {\n  match head {\n    \"progn\" => progn_form(icompiler, pipeline, tail, pos).map(Some),\n    \"cond\" => cond_form(icompiler, pipeline, tail, pos).map(Some),\n    \"while\" => while_form(icompiler, pipeline, tail, pos).map(Some),\n    \"for\" => for_form(icompiler, pipeline, tail, pos).map(Some),\n    \"let\" => let_form(icompiler, pipeline, tail, pos).map(Some),\n    \"flet\" => flet_form(icompiler, pipeline, tail, pos, FLetLocalBinding).map(Some),\n    \"labels\" => flet_form(icompiler, pipeline, tail, pos, LabelsLocalBinding).map(Some),\n    \"lambda\" => lambda_form(icompiler, pipeline, tail, pos).map(Some),\n    \"function\" => function_form(tail, pos).map(Some),\n    \"set\" => assign_form(icompiler, pipeline, tail, pos).map(Some),\n    \"quote\" => quote_form(tail, pos).map(Some),\n    \"quasiquote\" => quasiquote_form(icompiler, pipeline, tail, pos).map(Some),\n    \"unquote\" => Err(PError::from(GDError::new(GDErrorF::UnquoteOutsideQuasiquote, pos))),\n    \"unquote-spliced\" => Err(PError::from(GDError::new(GDErrorF::UnquoteSplicedOutsideQuasiquote, pos))),\n    \"access-slot\" => access_slot_form(icompiler, pipeline, tail, pos).map(Some),\n    \"new\" => new_form(icompiler, pipeline, tail, pos).map(Some),\n    \"yield\" => yield_form(icompiler, pipeline, tail, pos).map(Some),\n    \"assert\" => assert_form(icompiler, pipeline, tail, pos).map(Some),\n    \"return\" => return_form(icompiler, pipeline, tail, pos).map(Some),\n    \"macrolet\" => macrolet_form(icompiler, pipeline, tail, pos).map(Some),\n    \"symbol-macrolet\" => symbol_macrolet_form(icompiler, pipeline, tail, pos).map(Some),\n    \"sys/special-ref\" => special_ref_form(icompiler, pipeline, tail, pos).map(Some),\n    \"sys/context-filename\" => context_filename_form(icompiler, pipeline, tail, pos).map(Some),\n    \"literally\" => literally_form(tail, pos).map(Some),\n    \"sys/split\" => split_form(icompiler, pipeline, tail, pos).map(Some),\n    \"preload\" => preload_form(icompiler, pipeline, tail, pos).map(Some),\n    \"sys/bootstrap\" => bootstrap_form(icompiler, pipeline, tail, pos).map(Some),\n    \"break\" => break_form(icompiler, pipeline, tail, pos).map(Some),\n    \"continue\" => continue_form(icompiler, pipeline, tail, pos).map(Some),\n    _ => Ok(None),\n  }\n}\n\npub fn progn_form(icompiler: &mut IncCompiler,\n                  pipeline: &mut Pipeline,\n                  tail: &[&AST],\n                  pos: SourceOffset)\n                  -> Result<Expr, PError> {\n  let body = tail.iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n  Ok(Expr::progn(body, pos))\n}\n\npub fn cond_form(icompiler: &mut IncCompiler,\n                 pipeline: &mut Pipeline,\n                 tail: &[&AST],\n                 pos: SourceOffset)\n                 -> Result<Expr, PError> {\n  let body = tail.iter().map(|clause| {\n    let vec: Vec<&AST> = DottedExpr::new(clause).try_into().map_err(|x| GDError::from_value(x, pos))?;\n    match vec.len() {\n      0 => {\n        Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"cond\"), (*clause).clone(), ExpectedShape::NonemptyList), pos)))\n      }\n      1 => {\n        let cond = icompiler.compile_expr(pipeline, vec[0])?;\n        Ok((cond, None))\n      }\n      _ => {\n        let cond = icompiler.compile_expr(pipeline, vec[0])?;\n        let inner = vec[1..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n        // In this branch, vec.len() > 1, so vec[1] is safe\n        Ok((cond, Some(Expr::progn(inner, vec[1].pos))))\n      }\n    }\n  }).collect::<Result<Vec<_>, _>>()?;\n  Ok(Expr::new(ExprF::CondStmt(body), pos))\n}\n\npub fn while_form(icompiler: &mut IncCompiler,\n                  pipeline: &mut Pipeline,\n                  tail: &[&AST],\n                  pos: SourceOffset)\n                  -> Result<Expr, PError> {\n  Expecting::at_least(1).validate(\"while\", pos, tail)?;\n  let cond = icompiler.compile_expr(pipeline, tail[0])?;\n  let body = tail[1..].iter().map(|x| icompiler.compile_expr(pipeline, x)).collect::<Result<Vec<_>, _>>()?;\n  Ok(Expr::while_stmt(cond, Expr::progn(body, pos), pos))\n}\n\npub fn for_form(icompiler: &mut IncCompiler,\n                pipeline: &mut Pipeline,\n                tail: &[&AST],\n                pos: SourceOffset)\n                -> Result<Expr, PError> {\n  Expecting::at_least(2).validate(\"for\", pos, tail)?;\n  let name = ExpectedShape::extract_symbol(\"for\", tail[0].clone())?;\n  let iter = icompiler.compile_expr(pipeline, tail[1])?;\n  let body = tail[2..].iter().map(|x| icompiler.compile_expr(pipeline, x)).collect::<Result<Vec<_>, _>>()?;\n  Ok(Expr::for_stmt(name, iter, Expr::progn(body, pos), pos))\n}\n\npub fn let_form(icompiler: &mut IncCompiler,\n                pipeline: &mut Pipeline,\n                tail: &[&AST],\n                pos: SourceOffset)\n                -> Result<Expr, PError> {\n  // TODO Clean this up :(\n  Expecting::at_least(1).validate(\"let\", pos, tail)?;\n  let vars: Vec<_> = DottedExpr::new(tail[0]).try_into()?;\n  let var_clauses = vars.into_iter().map(|clause| {\n    let var: Vec<_> = match DottedExpr::new(clause) {\n      DottedExpr { elements, terminal: AST { value: ASTF::NIL, pos: _ } } if !elements.is_empty() => elements,\n      DottedExpr { elements, terminal: tail@AST { value: ASTF::Atom(Literal::Symbol(_)), pos: _ } } if elements.is_empty() => vec!(tail),\n      _ => return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"let\"), (*clause).clone(), ExpectedShape::VarDecl), pos)))\n    };\n    let result_value = var[1..].iter().map(|e| icompiler.compile_expr(pipeline, e)).collect::<Result<Vec<_>, _>>()?;\n    let name = match &var[0].value {\n      ASTF::Atom(Literal::Symbol(s)) => Ok(s.clone()),\n      _ => Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"let\"), (*clause).clone(), ExpectedShape::VarDecl), pos))),\n    }?;\n    Ok(LocalVarClause { name, value: Expr::progn(result_value, clause.pos) })\n  }).collect::<Result<Vec<_>, _>>()?;\n  let var_names: Vec<_> = var_clauses.iter().map(|x| x.name.clone()).collect();\n  macrolet_unbind_macros(icompiler, pipeline, &mut var_names.iter().map(|x| (Namespace::Value, &**x)), |icompiler, pipeline| {\n    let body = tail[1..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n    Ok(Expr::new(ExprF::Let(var_clauses, Box::new(Expr::progn(body, pos))), pos))\n  })\n}\n\npub fn lambda_form(icompiler: &mut IncCompiler,\n                   pipeline: &mut Pipeline,\n                   tail: &[&AST],\n                   pos: SourceOffset)\n                   -> Result<Expr, PError> {\n  Expecting::at_least(1).validate(\"lambda\", pos, tail)?;\n  let args: Vec<_> = DottedExpr::new(tail[0]).try_into()?;\n  let args = ArgList::parse(args, pos)?;\n  let body = tail[1..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n  Ok(Expr::new(ExprF::Lambda(args, Box::new(Expr::progn(body, pos))), pos))\n}\n\npub fn function_form(tail: &[&AST],\n                     pos: SourceOffset)\n                     -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"function\", pos, tail)?;\n  let s = ExpectedShape::extract_symbol(\"function\", tail[0].clone())?;\n  Ok(Expr::new(ExprF::FuncRef(FuncRefTarget::SimpleName(s)), pos))\n}\n\npub fn assign_form(icompiler: &mut IncCompiler,\n                   pipeline: &mut Pipeline,\n                   tail: &[&AST],\n                   pos: SourceOffset)\n                   -> Result<Expr, PError> {\n  Expecting::exactly(2).validate(\"set\", pos, tail)?;\n  let assign_target = match &tail[0].value {\n    ASTF::Atom(Literal::Symbol(s)) => {\n      AssignmentForm::Simple(AssignTarget::Variable(tail[0].pos, s.to_owned()))\n    }\n    _ => {\n      let x = tail[0];\n      let inner: Vec<_> = DottedExpr::new(x).try_into()?;\n      if inner[0].value == ASTF::symbol(String::from(\"access-slot\")) {\n        let AccessSlotSyntax { object, slot_name } = AccessSlotSyntax::parse_from_tail(&inner[1..], pos)?;\n        let object = icompiler.compile_expr(pipeline, object)?;\n        AssignmentForm::Simple(AssignTarget::InstanceField(inner[0].pos, Box::new(object), slot_name))\n      } else {\n        let s = ExpectedShape::extract_symbol(\"set\", inner[0].clone())?;\n        let head = AssignmentForm::str_to_setter_prefix(&s);\n        let args = inner[1..].iter().map(|x| icompiler.compile_expr(pipeline, x)).collect::<Result<_, _>>()?;\n        AssignmentForm::SetterCall(head, args)\n      }\n    }\n  };\n  let value = icompiler.compile_expr(pipeline, tail[1])?;\n  Ok(assign_target.into_expr(value, pos))\n}\n\npub fn flet_form(icompiler: &mut IncCompiler,\n                 pipeline: &mut Pipeline,\n                 tail: &[&AST],\n                 pos: SourceOffset,\n                 binding_rule: impl LocalBinding)\n                 -> Result<Expr, PError> {\n  // TODO This function is used for flet and labels, so using \"flet\"\n  // in all of the errors is not strictly correct.\n  Expecting::at_least(1).validate(\"flet\", pos, tail)?;\n  let fns: Vec<_> = DottedExpr::new(tail[0]).try_into()?;\n\n  // Get all of the names of the declared functions\n  let fn_names: Vec<_> = fns.iter().map(|clause| {\n    let func: Vec<_> = DottedExpr::new(clause).try_into()?;\n    if func.len() < 2 {\n      return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"flet\"), (*clause).clone(), ExpectedShape::FnDecl), pos)));\n    }\n    let result = ExpectedShape::extract_symbol(\"flet\", func[0].clone())?;\n    Ok(result)\n  }).collect::<Result<_, _>>()?;\n\n  // If the recursive binding rule is in effect (i.e. for labels),\n  // then we want to bind all of the names before compiling anything.\n  // If the recursive binding rule is not in effect (i.e. for flet),\n  // then we want to bind the names after compiling the clauses but\n  // before compiling the body.\n  let (pre_names, post_names) = if binding_rule.has_recursive_bindings() {\n    (fn_names, vec!())\n  } else {\n    (vec!(), fn_names)\n  };\n\n  macrolet_unbind_macros(icompiler, pipeline, &mut pre_names.iter().map(|x| (Namespace::Function, &**x)), |icompiler, pipeline| {\n    let fn_clauses = fns.into_iter().map(|clause| {\n      let func: Vec<_> = DottedExpr::new(clause).try_into()?;\n      if func.len() < 2 {\n        return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"flet\"), clause.clone(), ExpectedShape::FnDecl), pos)));\n      }\n      let name = match &func[0].value {\n        ASTF::Atom(Literal::Symbol(s)) => Ok(s.clone()),\n        _ => Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"flet\"), (*clause).clone(), ExpectedShape::FnDecl), pos))),\n      }?;\n      let args: Vec<_> = DottedExpr::new(func[1]).try_into()?;\n      let args = ArgList::parse(args, pos)?;\n      let body = func[2..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n      Ok(LocalFnClause { name, args, body: Expr::progn(body, clause.pos) })\n    }).collect::<Result<Vec<_>, _>>()?;\n    macrolet_unbind_macros(icompiler, pipeline, &mut post_names.iter().map(|x| (Namespace::Function, &**x)), |icompiler, pipeline| {\n      let body = tail[1..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n      Ok(binding_rule.wrap_in_expr(fn_clauses, Box::new(Expr::progn(body, pos)), pos))\n    })\n  })\n}\n\npub fn quote_form(tail: &[&AST], pos: SourceOffset) -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"quote\", pos, tail)?;\n  Ok(Expr::new(ExprF::Quote(tail[0].clone()), pos))\n}\n\npub fn quasiquote_form(icompiler: &mut IncCompiler,\n                       pipeline: &mut Pipeline,\n                       tail: &[&AST],\n                       pos: SourceOffset)\n                       -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"quasiquote\", pos, tail)?;\n  quasiquote_with_depth(icompiler, pipeline, tail[0], MAX_QUOTE_REIFY_DEPTH)\n}\n\npub fn access_slot_form(icompiler: &mut IncCompiler,\n                        pipeline: &mut Pipeline,\n                        tail: &[&AST],\n                        pos: SourceOffset)\n                        -> Result<Expr, PError> {\n  let AccessSlotSyntax { object, slot_name } = AccessSlotSyntax::parse_from_tail(tail, pos)?;\n  let object = icompiler.compile_expr(pipeline, object)?;\n  Ok(Expr::new(ExprF::FieldAccess(Box::new(object), slot_name), pos))\n}\n\npub fn new_form(icompiler: &mut IncCompiler,\n                pipeline: &mut Pipeline,\n                tail: &[&AST],\n                pos: SourceOffset)\n                -> Result<Expr, PError> {\n  Expecting::at_least(1).validate(\"new\", pos, tail)?;\n  let super_call = match &tail[0].value {\n    ASTF::Atom(Literal::Symbol(_)) => AST::dotted_list(vec!((*tail[0]).clone()), AST::nil(tail[0].pos)),\n    _ => tail[0].clone(),\n  };\n  let super_call = Vec::try_from(DottedExpr::new(&super_call))?;\n  if super_call.is_empty() {\n    return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"new\"), tail[0].clone(), ExpectedShape::SuperclassDecl), pos)));\n  }\n  let superclass = match &super_call[0].value {\n    ASTF::Atom(Literal::Symbol(superclass_name)) => superclass_name.to_owned(),\n    _ => return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"new\"), tail[0].clone(), ExpectedShape::SuperclassDecl), pos))),\n  };\n  let super_args = super_call[1..].iter().map(|arg| icompiler.compile_expr(pipeline, arg)).collect::<Result<Vec<_>, _>>()?;\n  let mut cls = decl::ClassDecl::new(String::from(\"(local anonymous class)\"), superclass);\n  for decl in &tail[1..] {\n    icompiler.compile_class_inner_decl(pipeline, &mut cls, decl)?;\n  }\n  let lambda_class = LambdaClass::from((cls, super_args));\n  Ok(Expr::new(ExprF::LambdaClass(Box::new(lambda_class)), pos))\n}\n\npub fn yield_form(icompiler: &mut IncCompiler,\n                  pipeline: &mut Pipeline,\n                  tail: &[&AST],\n                  pos: SourceOffset)\n                  -> Result<Expr, PError> {\n  Expecting::between(0, 2).validate(\"yield\", pos, tail)?;\n  match tail.len() {\n    0 => {\n      Ok(Expr::yield_none(pos))\n    }\n    1 => {\n      // TODO yield() is weird in that it's the only thing in GDLisp\n      // right now that takes a non-interval number of arguments.\n      // Every other function or special form can be broadly described\n      // as taking [a, b] or [a, infinity) arguments, where a and b\n      // are integers. yield() takes {0, 2}.\n      //\n      // It would be nice to *define* some semantics for yield(x), for\n      // consistency, but I can't think of what that would mean. OTOH\n      // how do we correctly report errors for this relatively bizarre\n      // special form, since WrongNumberArgs assumes an interval as\n      // expected argument count?\n      Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"yield\"), AST::nil(SourceOffset(0)), ExpectedShape::YieldArg), pos)))\n    }\n    2 => {\n      let lhs = icompiler.compile_expr(pipeline, tail[0])?;\n      let rhs = icompiler.compile_expr(pipeline, tail[1])?;\n      Ok(Expr::yield_some(lhs, rhs, pos))\n    }\n    _ => {\n      unreachable!()\n    }\n  }\n}\n\npub fn assert_form(icompiler: &mut IncCompiler,\n                   pipeline: &mut Pipeline,\n                   tail: &[&AST],\n                   pos: SourceOffset)\n                   -> Result<Expr, PError> {\n  Expecting::between(1, 2).validate(\"assert\", pos, tail)?;\n  match tail.len() {\n    1 => {\n      let cond = icompiler.compile_expr(pipeline, tail[0])?;\n      Ok(Expr::assert_expr(cond, None, pos))\n    }\n    2 => {\n      let cond = icompiler.compile_expr(pipeline, tail[0])?;\n      let message = icompiler.compile_expr(pipeline, tail[1])?;\n      Ok(Expr::assert_expr(cond, Some(message), pos))\n    }\n    _ => {\n      unreachable!()\n    }\n  }\n}\n\npub fn return_form(icompiler: &mut IncCompiler,\n                   pipeline: &mut Pipeline,\n                   tail: &[&AST],\n                   pos: SourceOffset)\n                   -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"return\", pos, tail)?;\n  let expr = icompiler.compile_expr(pipeline, tail[0])?;\n  Ok(Expr::new(ExprF::Return(Box::new(expr)), pos))\n}\n\npub fn macrolet_form(icompiler: &mut IncCompiler,\n                     pipeline: &mut Pipeline,\n                     tail: &[&AST],\n                     pos: SourceOffset)\n                     -> Result<Expr, PError> {\n  Expecting::at_least(1).validate(\"macrolet\", pos, tail)?;\n  let fns: Vec<_> = DottedExpr::new(tail[0]).try_into()?;\n  let fn_clauses = fns.into_iter().map(|clause| {\n    let func: Vec<_> = DottedExpr::new(clause).try_into()?;\n    if func.len() < 2 {\n      return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"macrolet\"), clause.clone(), ExpectedShape::MacroDecl), pos)));\n    }\n    let name = match &func[0].value {\n      ASTF::Atom(Literal::Symbol(s)) => Ok(s.clone()),\n      _ => Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"macrolet\"), (*clause).clone(), ExpectedShape::MacroDecl), pos))),\n    }?;\n    let args: Vec<_> = DottedExpr::new(func[1]).try_into()?;\n    let args = ArgList::parse(args, pos)?;\n    let body = func[2..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n    Ok(decl::MacroDecl { visibility: Visibility::MACRO, name, args, body: Expr::progn(body, clause.pos) })\n  }).collect::<Result<Vec<_>, _>>()?;\n\n  let mut fn_clauses = fn_clauses.into_iter().map(|x| (Namespace::Function, x));\n  macrolet_bind_locals(icompiler, pipeline, &mut fn_clauses, pos, |icompiler, pipeline| {\n    let body = tail[1..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n    Ok(Expr::progn(body, pos))\n  })\n\n}\n\npub fn symbol_macrolet_form(icompiler: &mut IncCompiler,\n                            pipeline: &mut Pipeline,\n                            tail: &[&AST],\n                            pos: SourceOffset)\n                            -> Result<Expr, PError> {\n  Expecting::at_least(1).validate(\"symbol-macrolet\", pos, tail)?;\n  let vars: Vec<_> = DottedExpr::new(tail[0]).try_into()?;\n  let var_clauses = vars.into_iter().map(|clause| {\n    let var: Vec<_> = DottedExpr::new(clause).try_into()?;\n    if var.len() != 2 {\n      return Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"symbol-macrolet\"), clause.clone(), ExpectedShape::MacroDecl), pos)));\n    }\n    let name = match &var[0].value {\n      ASTF::Atom(Literal::Symbol(s)) => Ok(s.clone()),\n      _ => Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"symbol-macrolet\"), (*clause).clone(), ExpectedShape::MacroDecl), pos))),\n    }?;\n    let body = icompiler.compile_expr(pipeline, var[1])?;\n    Ok(decl::MacroDecl { visibility: Visibility::MACRO, name, args: ArgList::empty(), body: body })\n  }).collect::<Result<Vec<_>, _>>()?;\n\n  let mut var_clauses = var_clauses.into_iter().map(|x| (Namespace::Value, x));\n  macrolet_bind_locals(icompiler, pipeline, &mut var_clauses, pos, |icompiler, pipeline| {\n    let body = tail[1..].iter().map(|expr| icompiler.compile_expr(pipeline, expr)).collect::<Result<Vec<_>, _>>()?;\n    Ok(Expr::progn(body, pos))\n  })\n\n}\n\npub fn special_ref_form(_icompiler: &mut IncCompiler,\n                        _pipeline: &mut Pipeline,\n                        tail: &[&AST],\n                        pos: SourceOffset)\n                        -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"sys/special-ref\", pos, tail)?;\n  if let Some(sym) = tail[0].as_symbol_ref() {\n    match sym {\n      \"this-file\" => Ok(Expr::from_value(SpecialRef::ThisFile, pos)),\n      \"this-filename\" => Ok(Expr::from_value(SpecialRef::ThisFileName, pos)),\n      \"this-true-filename\" => Ok(Expr::from_value(SpecialRef::ThisTrueFileName, pos)),\n      \"godot-version\" => Ok(Expr::from_value(SpecialRef::GodotVersion, pos)),\n      _ => Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"sys/special-ref\"), tail[0].clone(), ExpectedShape::SpecialRefValue), pos))),\n    }\n  } else {\n    Err(PError::from(GDError::new(GDErrorF::InvalidArg(String::from(\"sys/special-ref\"), tail[0].clone(), ExpectedShape::SpecialRefValue), pos)))\n  }\n}\n\n// This one requires a bit of explanation. (sys/context-filename path)\n// is a compiler hook designed to assist with macro expansion. It's\n// much easier to explain *how* it works than to explain when you'd\n// need it. For the latter, see the expansion for `deflazy`, which\n// uses this trick.\n//\n// path must be a literal string. (sys/context-filename path) plugs\n// path into the *current* preload resolver during the compilation\n// stage and produces a new literal string consisting of the result.\n// Obviously, path must be a literal string which parses as a resource\n// path (i.e. \"res://*\"). This is basically the same transformation\n// (use ...) directives undergo, but I'm exposing it as an\n// expression-level special form so that macros can exploit it.\n//\n// Note that (sys/context-filename ...) is *not* involved in\n// dependency resolution, so macros won't detect it as a dependency\n// like they will a (use ...) directive. Thus, it's basically only\n// safe to use this directive in situations where the relevant file is\n// guaranteed to *already* be loaded by a prior (use ...) in the same\n// file.\npub fn context_filename_form(_icompiler: &mut IncCompiler,\n                             _pipeline: &mut Pipeline,\n                             tail: &[&AST],\n                             pos: SourceOffset)\n                             -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"sys/context-filename\", pos, tail)?;\n  let s = ExpectedShape::extract_string(\"sys/context-filename\", tail[0].clone())?;\n  let path = ImportDecl::parse_path_param(&s).ok_or_else(|| {\n    let err = ImportDeclParseError::InvalidPath(s.clone());\n    PError::from(GDError::from_value(err, pos))\n  })?;\n  Ok(Expr::new(ExprF::ContextualFilename(path), pos))\n}\n\npub fn literally_form(tail: &[&AST], pos: SourceOffset) -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"literally\", pos, tail)?;\n  let name = ExpectedShape::extract_symbol(\"literally\", tail[0].clone())?;\n  Ok(Expr::atomic_var(name, pos))\n}\n\npub fn split_form(icompiler: &mut IncCompiler,\n                  pipeline: &mut Pipeline,\n                  tail: &[&AST],\n                  pos: SourceOffset)\n                  -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"sys/split\", pos, tail)?;\n  let expr = icompiler.compile_expr(pipeline, tail[0])?;\n  Ok(expr.split(pos))\n}\n\nfn macrolet_bind_locals<B, E, F, I>(icompiler: &mut IncCompiler,\n                                    pipeline: &mut Pipeline,\n                                    macros: &mut I,\n                                    pos: SourceOffset,\n                                    func: F)\n                                    -> Result<B, E>\nwhere E : From<PError>,\n      F : FnOnce(&mut IncCompiler, &mut Pipeline) -> Result<B, E>,\n      I : Iterator<Item=(Namespace, decl::MacroDecl)> {\n  match macros.next() {\n    None => func(icompiler, pipeline),\n    #[allow(clippy::redundant_clone)] // Clippy thinks this clone is\n                                      // redundant but it doesn't\n                                      // compile without it.\n    Some((namespace, m)) => icompiler.locally_save_macro(&*Id::build(namespace, &m.name), |icompiler| {\n      let name = m.name.to_string();\n      icompiler.bind_macro(pipeline, m.to_owned(), pos, true, namespace)?;\n      let old_symbol_value = {\n        let table = icompiler.declaration_table();\n        table.get(&*Id::build(namespace, &name)).cloned()\n      };\n      match namespace {\n        Namespace::Function => {\n          icompiler.declaration_table().add(Decl::new(DeclF::MacroDecl(m.clone()), pos));\n        }\n        Namespace::Value => {\n          let symbol_macro = decl::SymbolMacroDecl { visibility: m.visibility, name: m.name.to_owned(), body: m.body.clone() };\n          icompiler.declaration_table().add(Decl::new(DeclF::SymbolMacroDecl(symbol_macro), pos));\n        }\n      };\n      let result = macrolet_bind_locals(icompiler, pipeline, macros, pos, func);\n      if let Some(old_symbol_value) = old_symbol_value {\n        let table = icompiler.declaration_table();\n        table.add(old_symbol_value);\n      } else {\n        let table = icompiler.declaration_table();\n        table.del(&*Id::build(namespace, &name));\n      }\n      icompiler.unbind_macro(&*Id::build(namespace, &name));\n      result\n    }),\n  }\n}\n\nfn macrolet_unbind_macros<'a, B, E, F, I>(icompiler: &mut IncCompiler,\n                                          pipeline: &mut Pipeline,\n                                          macros: &mut I,\n                                          func: F)\n                                          -> Result<B, E>\nwhere E : From<PError>,\n      F : FnOnce(&mut IncCompiler, &mut Pipeline) -> Result<B, E>,\n      I : Iterator<Item=(Namespace, &'a str)> {\n  match macros.next() {\n    None => func(icompiler, pipeline),\n    Some(name) => {\n      let name: &(dyn IdLike<NS=Namespace> + 'a) = &name;\n      if icompiler.has_macro(name) {\n        icompiler.locally_save_macro(name, |icompiler| {\n          let old_symbol_value = {\n            let table = icompiler.declaration_table();\n            table.get(name).cloned()\n          };\n          icompiler.unbind_macro(name);\n          let result = macrolet_unbind_macros(icompiler, pipeline, macros, func);\n          if let Some(old_symbol_value) = old_symbol_value {\n            let table = icompiler.declaration_table();\n            table.add(old_symbol_value);\n          } else {\n            let table = icompiler.declaration_table();\n            table.del(name);\n          }\n          result\n        })\n      } else {\n        macrolet_unbind_macros(icompiler, pipeline, macros, func)\n      }\n    }\n  }\n}\n\npub fn preload_form(_icompiler: &mut IncCompiler,\n                    _pipeline: &mut Pipeline,\n                    tail: &[&AST],\n                    pos: SourceOffset)\n                    -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"preload\", pos, tail)?;\n  let s = ExpectedShape::extract_string(\"preload\", tail[0].clone())?;\n  Ok(Expr::new(ExprF::Preload(s), pos))\n}\n\npub fn bootstrap_form(icompiler: &mut IncCompiler,\n                      pipeline: &mut Pipeline,\n                      tail: &[&AST],\n                      pos: SourceOffset)\n                    -> Result<Expr, PError> {\n  Expecting::exactly(1).validate(\"sys/bootstrap\", pos, tail)?;\n  let s = ExpectedShape::extract_symbol(\"sys/bootstrap\", tail[0].clone())?;\n  compile_bootstrapping_expr(icompiler, pipeline, &s, pos).map_err(PError::from)\n}\n\npub fn break_form(_icompiler: &mut IncCompiler,\n                  _pipeline: &mut Pipeline,\n                  tail: &[&AST],\n                  pos: SourceOffset)\n                  -> Result<Expr, PError> {\n  Expecting::NONE.validate(\"break\", pos, tail)?;\n  Ok(Expr::new(ExprF::Break, pos))\n}\n\npub fn continue_form(_icompiler: &mut IncCompiler,\n                     _pipeline: &mut Pipeline,\n                     tail: &[&AST],\n                     pos: SourceOffset)\n                     -> Result<Expr, PError> {\n  Expecting::NONE.validate(\"continue\", pos, tail)?;\n  Ok(Expr::new(ExprF::Continue, pos))\n}\n"
  },
  {
    "path": "src/ir/special_ref.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`SpecialRef`] type and associated values.\n\nuse super::expr::ExprF;\n\n/// A `SpecialRef` is a type of [`Expr`](super::expr::Expr) which is\n/// superficially a literal form but which acquires some state from\n/// its surrounding environment or context. That is, a special\n/// reference can be thought of as a 0-ary function which is not, in\n/// the mathematical sense, a pure function.\n///\n/// Special references are converted to [`Expr`](super::expr::Expr)\n/// either via the explicit constructor\n/// [`ExprF::SpecialRef`](super::expr::ExprF::SpecialRef) or via the\n/// general-purpose helper\n/// [`Expr::from_value`](super::expr::Expr::from_value).\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub enum SpecialRef {\n  /// `ThisFile` will compile into a `load` expression which loads and\n  /// returns a reference to the current file, as a GDScript script\n  /// object.\n  ThisFile,\n  /// `ThisFileName` will compile into a string literal expression\n  /// which refers to the current (GDScript) filename. If called\n  /// during a macro expansion, this will expand to the virtual name\n  /// of the macro file.\n  ThisFileName,\n  /// `ThisTrueFileName` will compile into a string literal expression\n  /// which refers to the current (GDScript) filename. If called\n  /// during macro expansion, this will expand into the *true* final\n  /// name of the file, not the name of the virtual macro file.\n  ThisTrueFileName,\n  /// `GodotVersion` returns the current Godot version, as an integer\n  /// such that later Godot versions compare greater than earlier\n  /// ones.\n  GodotVersion,\n}\n\nimpl From<SpecialRef> for ExprF {\n  fn from(special_ref: SpecialRef) -> ExprF {\n    ExprF::SpecialRef(special_ref)\n  }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n#![allow(clippy::redundant_field_names)]\n#![allow(non_shorthand_field_patterns)]\n\n#[macro_use] extern crate lalrpop_util;\n#[macro_use] extern crate lazy_static;\n\npub mod sxp;\npub mod compile;\npub mod ir;\npub mod gdscript;\npub mod runner;\npub mod graph;\npub mod util;\npub mod command_line;\npub mod pipeline;\npub mod optimize;\npub mod repl;\nmod parser_test;\n\nlalrpop_mod!(#[allow(clippy::all)] pub parser);\n\nlazy_static! {\n\n  pub static ref AST_PARSER: parser::ASTParser =\n    parser::ASTParser::new();\n\n  pub static ref SOME_AST_PARSER: parser::SomeASTParser =\n    parser::SomeASTParser::new();\n\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate lalrpop_util;\nextern crate walkdir;\nextern crate gdlisp;\n\n/*\npub mod sxp;\npub mod compile;\npub mod gdscript;\nmod parser_test;\n\nlalrpop_mod!(pub parser);\n*/\n\nuse gdlisp::gdscript::{decl, library};\nuse gdlisp::command_line::{parse_args, show_help_message};\nuse gdlisp::repl::Repl;\nuse gdlisp::pipeline::Pipeline;\nuse gdlisp::pipeline::config::ProjectConfig;\nuse gdlisp::pipeline::source::{SourcedValue, SourceOffset};\nuse gdlisp::runner::version::{VersionInfo, get_godot_version_as_string};\n\nuse walkdir::WalkDir;\n\nuse std::io::{self, BufRead, Write};\nuse std::env;\nuse std::path::{PathBuf, Path};\nuse std::str::FromStr;\nuse std::thread::sleep;\nuse std::time::Duration;\n\n// Legacy REPL\nfn run_pseudo_repl(godot_version: VersionInfo) {\n  let stdin = io::stdin();\n  let config = ProjectConfig {\n    root_directory: PathBuf::from_str(\".\").unwrap(), // Infallible\n    optimizations: true,\n    godot_version,\n  };\n  let mut pipeline = Pipeline::new(config);\n\n  for line in stdin.lock().lines() {\n    let line = line.unwrap();\n    match pipeline.compile_code(\"./REPL.lisp\", &line) {\n      Err(err) => {\n        let err = SourcedValue::new(&err, &line);\n        println!(\"Error: {}\", err);\n      }\n      Ok(trans) => {\n        let cls = decl::TopLevelClass::from(trans);\n        print!(\"{}\", cls.to_gd());\n      }\n    }\n  }\n\n}\n\nfn run_repl(godot_version: VersionInfo) {\n\n  fn prompt() -> Option<String> {\n    let mut line = String::new();\n    let stdin = io::stdin();\n    let mut stdout = io::stdout();\n    print!(\"> \");\n    stdout.flush().expect(\"Failed to flush stdout\");\n    let bytes = stdin.read_line(&mut line).expect(\"Could not read from stdin; broken pipe?\");\n    if bytes == 0 {\n      None\n    } else {\n      Some(line)\n    }\n  }\n\n  let config = ProjectConfig {\n    root_directory: PathBuf::from_str(\".\").unwrap(), // Infallible\n    optimizations: true,\n    godot_version,\n  };\n  let mut repl = Repl::new(config);\n  repl.force_load();\n\n  while let Some(line) = prompt() {\n    let result = repl.parse_and_run_code(&line);\n    match result {\n      Err(err) => {\n        let err = SourcedValue::new(&err, &line);\n        println!(\"Error: {}\", err);\n      }\n      Ok(result) => {\n        println!(\"{}\", result);\n      }\n    }\n\n    // Check and see if the Godot process has terminated. If the user\n    // calls `get_tree().quit()`, then the process will terminate at\n    // the end of the current frame. Godot's default FPS is 60, so\n    // this should take 1/60th of a second, or 16 milliseconds. Out of\n    // an abundance of caution, we pause for 100 milliseconds here.\n    sleep(Duration::from_millis(100));\n    if !repl.is_running() {\n      break;\n    }\n  }\n\n}\n\nfn compile_file<P : AsRef<Path> + ?Sized>(input: &P, godot_version: VersionInfo) {\n  let input = input.as_ref();\n  let mut pipeline = Pipeline::new(ProjectConfig { root_directory: input.parent().unwrap_or(input).to_owned(), optimizations: true, godot_version });\n  match pipeline.load_file(input.file_name().unwrap(), SourceOffset(0)) {\n    Err(err) => {\n      let err = SourcedValue::from_file(&err, input).unwrap();\n      println!(\"Error: {}\", err);\n    }\n    Ok(_unit) => {}\n  }\n}\n\nfn compile_all_files<P : AsRef<Path> + ?Sized>(input: &P, godot_version: VersionInfo) {\n  let input = input.as_ref();\n  let mut pipeline = Pipeline::new(ProjectConfig { root_directory: input.to_owned(), optimizations: true, godot_version });\n  for entry in WalkDir::new(input).into_iter().filter_map(|e| e.ok()) {\n    if entry.path().is_file() && entry.path().extension() == Some(\"lisp\".as_ref()) {\n      println!(\"Compiling {} ...\", entry.path().to_string_lossy());\n      let path = entry.path().strip_prefix(&pipeline.config().root_directory).expect(\"Non-local file load detected\");\n      match pipeline.load_file(path, SourceOffset(0)) {\n        Err(err) => {\n          let err = SourcedValue::from_file(&err, entry.path()).unwrap();\n          println!(\"Error in {}: {}\", entry.path().to_string_lossy(), err);\n        }\n        Ok(_unit) => {}\n      }\n    }\n  }\n}\n\nfn main() {\n  let args: Vec<_> = env::args().collect();\n  let program_name = &args[0];\n  let parsed_args = parse_args(&args[1..]);\n  if parsed_args.help_message {\n    show_help_message(program_name);\n  } else {\n\n    let version = get_godot_version_as_string();\n    match &version {\n      Ok(version) => {\n        println!(\"GDLisp v1.0.0\");\n        println!(\"Running under Godot {}\", version);\n      }\n      Err(err) => {\n        eprintln!(\"Warning: `godot` is not on your path. A significant portion of the GDLisp compiler depends on the Godot engine. It is strongly recommended that you add `godot` to your path before continuing.\");\n        eprintln!(\"Warning: While looking for Godot, the error I got was: {}\", err);\n      }\n    }\n    // If Godot isn't on the path, then any version checks will return\n    // 0.0.0 since we can't get that information accurately.\n    let version = version.unwrap_or_default();\n\n    if parsed_args.compile_stdlib_flag {\n      library::load_stdlib_to_file();\n      println!(\"Stdlib compiled successfully.\")\n    } else if let Some(input) = parsed_args.input_file {\n      let input: &Path = input.as_ref();\n      if input.is_dir() {\n        compile_all_files(input, VersionInfo::parse(&version));\n      } else {\n        compile_file(input, VersionInfo::parse(&version));\n      }\n    } else if parsed_args.legacy_repl_flag {\n      run_pseudo_repl(VersionInfo::parse(&version));\n    } else {\n      run_repl(VersionInfo::parse(&version));\n    }\n\n  }\n}\n"
  },
  {
    "path": "src/optimize/gdscript/assignment.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::op;\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse crate::gdscript::expr::{Expr, ExprF};\n\n#[derive(Clone, Debug)]\npub struct AssignmentStmt<'a> {\n  pub assign_type: AssignType,\n  pub var_name: &'a str,\n  pub expr: &'a Expr,\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum AssignType {\n  VarDecl,\n  Assignment(op::AssignOp),\n}\n\nimpl<'a> AssignmentStmt<'a> {\n\n  pub fn match_stmt(stmt: &'a Stmt) -> Option<AssignmentStmt<'a>> {\n    match &stmt.value {\n      StmtF::VarDecl(var_name, expr) => {\n        Some(AssignmentStmt { assign_type: AssignType::VarDecl, var_name, expr })\n      }\n      StmtF::Assign(var_name, op, expr) => {\n        if let ExprF::Var(var_name) = &var_name.value {\n          Some(AssignmentStmt { assign_type: AssignType::Assignment(*op), var_name, expr })\n        } else {\n           None\n        }\n      }\n      _ => {\n        None\n      }\n    }\n  }\n\n}\n\nimpl AssignType {\n\n  pub fn ensure_eq(self) -> Self {\n    match self {\n      AssignType::VarDecl => AssignType::VarDecl,\n      AssignType::Assignment(_) => AssignType::Assignment(op::AssignOp::Eq),\n    }\n  }\n\n}\n\nimpl<'a> From<AssignmentStmt<'a>> for Stmt {\n\n  fn from(stmt: AssignmentStmt) -> Stmt {\n    let AssignmentStmt { assign_type, var_name, expr } = stmt;\n    let var_name = var_name.to_owned();\n    let expr = expr.clone();\n    let pos = expr.pos;\n    match assign_type {\n      AssignType::VarDecl =>\n        Stmt::var_decl(var_name, expr, pos),\n      AssignType::Assignment(op) =>\n        Stmt::new(StmtF::Assign(Box::new(Expr::var(&var_name, expr.pos)), op, Box::new(expr)), pos),\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/basic_math_ops.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::ExpressionLevelPass;\nuse crate::gdscript::expr::{self, Expr, ExprF};\nuse crate::gdscript::op;\nuse crate::compile::error::GDError;\nuse super::constant;\n\npub struct BasicMathOps;\n\nimpl ExpressionLevelPass for BasicMathOps {\n  fn run_on_expr(&self, expr: &Expr) -> Result<Expr, GDError> {\n    let mut expr = expr.clone();\n    // (TODO Expand this)\n\n    // Negation on constant\n    if let ExprF::Unary(op::UnaryOp::Not, inner) = &expr.value {\n      if let Some(b) = constant::deduce_bool(inner) {\n        expr = Expr::from_value(!b, expr.pos);\n      }\n    }\n\n    // Ternary if on constant\n    if let ExprF::TernaryIf(if_expr) = &expr.value {\n      let expr::TernaryIf { true_case, cond, false_case } = if_expr.clone();\n      if let Some(b) = constant::deduce_bool(&cond) {\n        expr = if b { *true_case } else { *false_case };\n      }\n    }\n\n    // Double negation elimination (TODO Maybe only in if statements;\n    // it does technically Booleanize at least)\n    if let ExprF::Unary(op::UnaryOp::Not, outer) = &expr.value {\n      if let ExprF::Unary(op::UnaryOp::Not, inner) = &outer.value {\n        expr = (**inner).clone();\n      }\n    }\n\n    Ok(expr)\n  }\n}\n"
  },
  {
    "path": "src/optimize/gdscript/constant.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::expr::{self, Expr, ExprF};\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse crate::gdscript::literal::Literal;\n\n// Attempt to determine whether a value is true or false by looking at\n// the expression that produces it. If we can't tell or if the\n// expression is stateful, then we return None.\npub fn deduce_bool(expr: &Expr) -> Option<bool> {\n  match &expr.value {\n    ExprF::Literal(lit) => {\n      match lit {\n        Literal::Int(n) => Some(*n != 0),\n        Literal::Float(f) => Some(**f != 0.0),\n        Literal::String(s) => Some(!s.is_empty()),\n        Literal::NodeLiteral(_) => None, // Truthy iff the node exists; can't determine at compile-time\n        Literal::NodePathLiteral(s) => Some(!s.is_empty()),\n        Literal::Null => Some(false),\n        Literal::Bool(b) => Some(*b),\n      }\n    }\n    _ => None,\n  }\n}\n\n// Attempt to discern whether or not a statement or expression has any\n// side effects. If in doubt, assume it does.\n\npub fn stmt_has_side_effects(stmt: &Stmt) -> bool {\n  match &stmt.value {\n    StmtF::Expr(e) => {\n      expr_has_side_effects(e)\n    }\n    StmtF::PassStmt => false,\n    StmtF::IfStmt(_) | StmtF::ForLoop(_) | StmtF::WhileLoop(_) |\n      StmtF::MatchStmt(_, _) => true, // TODO These\n    StmtF::BreakStmt | StmtF::ContinueStmt | StmtF::VarDecl(_, _) |\n      StmtF::ReturnStmt(_) | StmtF::Assign(_, _, _) => true,\n  }\n}\n\npub fn expr_has_side_effects(expr: &Expr) -> bool {\n  match &expr.value {\n    ExprF::Var(_) => false,\n    ExprF::Literal(_) => false,\n    // Subscript is a bit questionable, because it can't call\n    // arbitrary methods (only works in a predefined, simple way), but\n    // it can err out if out of bounds.\n    ExprF::Subscript(a, b) => expr_has_side_effects(a) || expr_has_side_effects(b),\n    // Attribute is questionable as well because setget can do method\n    // calls.\n    ExprF::Attribute(a, _) => expr_has_side_effects(a),\n    ExprF::Call(_, _, _) => true,\n    ExprF::SuperCall(_, _) => true,\n    ExprF::Unary(_, e) => expr_has_side_effects(e),\n    ExprF::Binary(a, _, b) => expr_has_side_effects(a) || expr_has_side_effects(b),\n    ExprF::TernaryIf(expr::TernaryIf { true_case: a, cond: b, false_case: c }) =>\n      expr_has_side_effects(a) || expr_has_side_effects(b) || expr_has_side_effects(c),\n    ExprF::ArrayLit(es) => es.iter().any(expr_has_side_effects),\n    ExprF::DictionaryLit(es) => es.iter().any(|(k, v)| expr_has_side_effects(k) || expr_has_side_effects(v)),\n  }\n}\n\n// A constant expression neither reads nor writes to any local or\n// global state. If a variable has a constant value, any references to\n// that variable can be safely replaced with the value without\n// changing the behavior of the code.\npub fn expr_is_constant(expr: &Expr) -> bool {\n  match &expr.value {\n    ExprF::Var(_) => false,\n    ExprF::Literal(_) => true,\n    ExprF::Subscript(_, _) => false,\n    ExprF::Attribute(_, _) => false,\n    ExprF::Call(_, _, _) => false,\n    ExprF::SuperCall(_, _) => false,\n    ExprF::Unary(_, e) => expr_is_constant(e),\n    ExprF::Binary(a, _, b) => expr_is_constant(a) && expr_is_constant(b),\n    ExprF::TernaryIf(expr::TernaryIf { true_case: a, cond: b, false_case: c }) =>\n      expr_is_constant(a) && expr_is_constant(b) && expr_is_constant(c),\n    // These two produce mutable values that cannot be blindly substituted\n    ExprF::ArrayLit(_) => false,\n    ExprF::DictionaryLit(_) => false,\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::source::SourceOffset;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  #[test]\n  fn literals_as_bool_test() {\n    assert_eq!(deduce_bool(&e(ExprF::from(0))), Some(false));\n    assert_eq!(deduce_bool(&e(ExprF::from(1))), Some(true));\n    assert_eq!(deduce_bool(&e(ExprF::Literal(Literal::Null))), Some(false));\n    assert_eq!(deduce_bool(&e(ExprF::from(true))), Some(true));\n    assert_eq!(deduce_bool(&e(ExprF::from(false))), Some(false));\n    assert_eq!(deduce_bool(&e(ExprF::from(\"A\"))), Some(true));\n    assert_eq!(deduce_bool(&e(ExprF::from(\"\"))), Some(false));\n    assert_eq!(deduce_bool(&e(ExprF::Literal(Literal::NodeLiteral(String::from(\"foobar\"))))), None);\n    assert_eq!(deduce_bool(&e(ExprF::Literal(Literal::NodePathLiteral(String::from(\"A\"))))), Some(true));\n    assert_eq!(deduce_bool(&e(ExprF::Literal(Literal::NodePathLiteral(String::from(\"\"))))), Some(false));\n  }\n\n  #[test]\n  fn arbitrary_expr_as_bool_test() {\n    assert_eq!(deduce_bool(&Expr::var(\"some-variable\", SourceOffset::default())), None);\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/constant_conditional_branch.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::stmt::{self, Stmt, StmtF};\nuse crate::compile::error::GDError;\nuse crate::pipeline::source::SourceOffset;\nuse super::StatementLevelPass;\nuse super::constant;\n\npub struct ConstantConditionalBranch;\n\n// Eliminate the \"if\" part of the conditional\nfn kill_if_branch(if_stmt: &stmt::IfStmt, pos: SourceOffset) -> Vec<Stmt> {\n  if if_stmt.elif_clauses.is_empty() {\n    // No elif, so the else clause becomes the whole conditional\n    if_stmt.else_clause.clone().unwrap_or_default()\n  } else {\n    // Use the first elif\n    let new_if_stmt = stmt::IfStmt {\n      if_clause: if_stmt.elif_clauses[0].clone(),\n      elif_clauses: if_stmt.elif_clauses[1..].to_vec(),\n      else_clause: if_stmt.else_clause.clone(),\n    };\n    vec!(Stmt::new(StmtF::IfStmt(new_if_stmt), pos))\n  }\n}\n\nimpl ConstantConditionalBranch {\n\n  pub fn run_on_stmt_acc(&self, stmts: &[Stmt]) -> Result<Vec<Stmt>, GDError> {\n    let mut result = Vec::new();\n    for stmt in stmts {\n      result.extend(self.run_on_stmt(stmt)?);\n    }\n    Ok(result)\n  }\n\n}\n\nimpl StatementLevelPass for ConstantConditionalBranch {\n\n  fn run_on_stmt(&self, stmt: &Stmt) -> Result<Vec<Stmt>, GDError> {\n    // Check for obviously true or false cases\n    if let StmtF::IfStmt(if_stmt) = &stmt.value {\n\n      // If branch\n      if let Some(b) = constant::deduce_bool(&if_stmt.if_clause.0) {\n        if b {\n          // Definitely true case\n          return self.run_on_stmt_acc(&if_stmt.if_clause.1);\n        } else {\n          // Definitely false case\n          let rest_of_stmt = kill_if_branch(if_stmt, stmt.pos);\n          return self.run_on_stmt_acc(&rest_of_stmt);\n        }\n      }\n\n      // Elif branches\n\n      // If any are definitely false, we can run_on_stmt them\n      let mut v = if_stmt.elif_clauses.clone();\n      v.retain(|(e, _)| constant::deduce_bool(e) != Some(false));\n      if v != if_stmt.elif_clauses.clone() {\n        let mut new_if_stmt = if_stmt.clone();\n        new_if_stmt.elif_clauses = v;\n        return self.run_on_stmt(&Stmt::new(StmtF::IfStmt(new_if_stmt), stmt.pos));\n      }\n\n      // If any are definitely true, then they become the else branch\n      let match_index = if_stmt.elif_clauses.iter().position(|(e, _)| constant::deduce_bool(e) == Some(true));\n      if let Some(match_index) = match_index {\n        let mut new_if_stmt = if_stmt.clone();\n        new_if_stmt.elif_clauses = if_stmt.elif_clauses[0..match_index].to_vec();\n        new_if_stmt.else_clause = Some(if_stmt.elif_clauses[match_index].1.clone());\n        return self.run_on_stmt(&Stmt::new(StmtF::IfStmt(new_if_stmt), stmt.pos));\n      }\n\n    }\n    Ok(vec!(stmt.clone()))\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/dead_code_elimination.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse crate::compile::error::GDError;\nuse super::StatementLevelPass;\nuse super::noop;\n\npub struct DeadCodeElimination;\n\n// TODO Apply to code after a return\nimpl StatementLevelPass for DeadCodeElimination {\n\n  fn run_on_stmt(&self, stmt: &Stmt) -> Result<Vec<Stmt>, GDError> {\n    let mut stmt = stmt.clone();\n\n    // If the statement itself has no effect, then omit it entirely\n    if noop::is_code_noop(&stmt) {\n      return Ok(vec!());\n    }\n\n    // Check for empty else clause\n    if let StmtF::IfStmt(if_stmt) = &mut stmt.value {\n      if let Some(else_clause) = &if_stmt.else_clause {\n        if noop::is_code_seq_noop(else_clause) {\n          if_stmt.else_clause = None;\n        }\n      }\n    }\n\n    Ok(vec!(stmt))\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::optimize::gdscript::FunctionOptimization;\n  use crate::gdscript::stmt;\n  use crate::gdscript::expr::{Expr, ExprF};\n  use crate::gdscript::decl;\n  use crate::gdscript::arglist::ArgList;\n  use crate::pipeline::source::SourceOffset;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  #[test]\n  fn else_do_not_run_on_stmt() {\n    /* (Change nothing)\n     * if 0:\n     *   return 1\n     * else:\n     *   return 2\n     */\n    let stmt = stmt::if_else(e(ExprF::from(0)), vec!(s(StmtF::ReturnStmt(e(ExprF::from(1))))), vec!(s(StmtF::ReturnStmt(e(ExprF::from(2))))), SourceOffset::default());\n    let decl = decl::FnDecl { name: String::from(\"example\"), args: ArgList::empty(), body: vec!(stmt) };\n    let mut transformed_decl = decl.clone();\n    DeadCodeElimination.run_on_function(&mut transformed_decl).unwrap();\n    assert_eq!(decl, transformed_decl);\n  }\n\n  #[test]\n  fn else_run_on_stmt() {\n    /* (Eliminate spurious else)\n     * if 0:\n     *   return 1\n     * else:\n     *   pass\n     */\n\n    let stmt0 = stmt::if_else(e(ExprF::from(0)), vec!(s(StmtF::ReturnStmt(e(ExprF::from(1))))), vec!(s(StmtF::PassStmt)), SourceOffset::default());\n    let decl0 = decl::FnDecl { name: String::from(\"example\"), args: ArgList::empty(), body: vec!(stmt0) };\n\n    let stmt1 = stmt::if_then(e(ExprF::from(0)), vec!(s(StmtF::ReturnStmt(e(ExprF::from(1))))), SourceOffset::default());\n    let decl1 = decl::FnDecl { name: String::from(\"example\"), args: ArgList::empty(), body: vec!(stmt1) };\n\n    let mut transformed_decl = decl0.clone();\n    DeadCodeElimination.run_on_function(&mut transformed_decl).unwrap();\n    assert_eq!(decl1, transformed_decl);\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/dead_decl_elimination.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::decl::{self, DeclF};\nuse crate::compile::error::GDError;\nuse super::{FileOptimization, on_each_decl};\n\n/// `DeadDeclElimination` simply removes all \"pass\" declarations.\n/// These can be produced as a compilation artifact of other\n/// optimization passes and of certain types of declarations in the\n/// language. They're unnecessary in the resulting output and can\n/// always be safely removed.\npub struct DeadDeclElimination;\n\n// TODO In the future, this will expand to remove private declarations\n// that are unused (such as lambda classes which have been optimized\n// out by other passes). For now, it just runs on \"pass\" decls.\nimpl FileOptimization for DeadDeclElimination {\n\n  fn run_on_file(&self, file: &mut decl::TopLevelClass) -> Result<(), GDError> {\n    file.body = on_each_decl(&file.body, |d| {\n      if let DeclF::PassDecl = &d.value {\n        Ok(vec!())\n      } else {\n        Ok(vec!(d.clone()))\n      }\n    })?;\n    Ok(())\n  }\n\n}\n\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::decl::{self, Decl, DeclF};\n  use crate::gdscript::class_extends::ClassExtends;\n  use crate::pipeline::source::SourceOffset;\n\n  fn d(decl: DeclF) -> Decl {\n    Decl::new(decl, SourceOffset::default())\n  }\n\n  #[test]\n  fn eliminate_pass() {\n    /* (Eliminate the pass decl)\n     * pass\n     */\n    let decls = vec!(\n      d(DeclF::PassDecl),\n    );\n    let mut toplevel = decl::TopLevelClass {\n      name: None,\n      extends: ClassExtends::SimpleIdentifier(String::from(\"Reference\")),\n      body: decls,\n    };\n    DeadDeclElimination.run_on_file(&mut toplevel).unwrap();\n    assert_eq!(toplevel.name, None);\n    assert_eq!(toplevel.extends, ClassExtends::SimpleIdentifier(String::from(\"Reference\")));\n    assert_eq!(toplevel.body, vec!());\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/dead_var_elimination.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse crate::gdscript::decl;\nuse crate::compile::error::GDError;\nuse super::FunctionOptimization;\nuse super::stmt_walker;\nuse super::variables::get_variable_info;\n\npub struct DeadVarElimination;\n\nimpl DeadVarElimination {\n  fn run_on_body(&self, stmts: &mut Vec<Stmt>) {\n    let vars = get_variable_info(stmts);\n    *stmts = stmt_walker::walk_stmts_ok(stmts, stmt_walker::on_each_stmt_ok(|stmt| {\n      if let StmtF::VarDecl(var_name, expr) = &stmt.value {\n        if let Some(info) = vars.get(var_name) {\n          if !info.is_ever_used() {\n            return vec!(Stmt::expr(expr.clone()));\n          }\n        }\n      }\n      vec!(stmt.clone())\n    }));\n  }\n}\n\n/*\n * If a variable is never used, then we can omit it entirely.\n */\nimpl FunctionOptimization for DeadVarElimination {\n  fn run_on_function(&self, function: &mut decl::FnDecl) -> Result<(), GDError> {\n    self.run_on_body(&mut function.body);\n    Ok(())\n  }\n  fn run_on_init_function(&self, function: &mut decl::InitFnDecl) -> Result<(), GDError> {\n    self.run_on_body(&mut function.body);\n    Ok(())\n  }\n}\n"
  },
  {
    "path": "src/optimize/gdscript/direct_var_substitute.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::expr::ExprF;\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::decl;\nuse crate::compile::error::GDError;\nuse super::FunctionOptimization;\nuse super::constant;\nuse super::expr_walker;\nuse super::variables::get_variable_info;\n\npub struct DirectVarSubstitute;\n\n// NOTE: This optimization assumes that a local variable is only\n// declared once in a given function. I don't know Godot's exact\n// scoping rules, but I know the GDLisp compiler ensures uniqueness of\n// names in local variables. If we ever stop doing so, we need to\n// alter this a bit.\n\nimpl DirectVarSubstitute {\n  fn run_on_body(&self, stmts: &mut Vec<Stmt>) {\n    let vars = get_variable_info(stmts);\n    *stmts = expr_walker::walk_exprs_ok(stmts, |var_expr| {\n      if let ExprF::Var(var_name) = &var_expr.value {\n        if let Some(info) = vars.get(var_name) {\n          if info.is_read_only() && constant::expr_is_constant(&info.value) {\n            return info.value.clone();\n          }\n        }\n      }\n      var_expr.clone()\n    });\n  }\n}\n\n/*\n * If a variable is declared and assigned a constant value, and if the\n * variable is never written to again, then we can simply use the\n * constant value anywhere we would use the variable.\n */\nimpl FunctionOptimization for DirectVarSubstitute {\n  fn run_on_function(&self, function: &mut decl::FnDecl) -> Result<(), GDError> {\n    self.run_on_body(&mut function.body);\n    Ok(())\n  }\n  fn run_on_init_function(&self, function: &mut decl::InitFnDecl) -> Result<(), GDError> {\n    self.run_on_body(&mut function.body);\n    Ok(())\n  }\n}\n"
  },
  {
    "path": "src/optimize/gdscript/else_then_if_fold.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse crate::compile::error::GDError;\nuse super::StatementLevelPass;\n\npub struct ElseThenIfFold;\n\nimpl StatementLevelPass for ElseThenIfFold {\n  fn run_on_stmt(&self, stmt: &Stmt) -> Result<Vec<Stmt>, GDError> {\n    // If we have an else whose body is an if, we can flatten it.\n    // This comes up when compiling cond sometimes.\n    if let StmtF::IfStmt(if_stmt) = &stmt.value {\n      if let Some(else_stmt) = &if_stmt.else_clause {\n        if let [Stmt { value: StmtF::IfStmt(inner_if_stmt), pos: _ }] = &else_stmt[..] {\n          let mut new_if_stmt = if_stmt.clone();\n          new_if_stmt.elif_clauses.push(inner_if_stmt.if_clause.clone());\n          for elif in &inner_if_stmt.elif_clauses {\n            new_if_stmt.elif_clauses.push(elif.clone());\n          }\n          new_if_stmt.else_clause = inner_if_stmt.else_clause.clone();\n          return Ok(vec!(Stmt::new(StmtF::IfStmt(new_if_stmt), stmt.pos)));\n        }\n      }\n    }\n    Ok(vec!(stmt.clone()))\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::expr::{Expr, ExprF};\n  use crate::gdscript::stmt;\n  use crate::gdscript::decl;\n  use crate::gdscript::arglist::ArgList;\n  use crate::optimize::gdscript::FunctionOptimization;\n  use crate::pipeline::source::SourceOffset;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  #[test]\n  fn else_then_if_optimize_test() {\n    /* (Merge else and if)\n     * if 1:\n     *   return 1\n     * else:\n     *   if 2:\n     *     return 2\n     */\n    let inner_stmt = stmt::if_then(e(ExprF::from(2)), vec!(s(StmtF::ReturnStmt(e(ExprF::from(2))))), SourceOffset::default());\n    let stmt = stmt::if_else(e(ExprF::from(1)), vec!(s(StmtF::ReturnStmt(e(ExprF::from(1))))), vec!(inner_stmt), SourceOffset::default());\n    let mut decl = decl::FnDecl { name: String::from(\"example\"), args: ArgList::empty(), body: vec!(stmt) };\n\n    let desired_stmt = s(StmtF::IfStmt(stmt::IfStmt {\n      if_clause: (e(ExprF::from(1)), vec!(s(StmtF::ReturnStmt(e(ExprF::from(1)))))),\n      elif_clauses: vec!((e(ExprF::from(2)), vec!(s(StmtF::ReturnStmt(e(ExprF::from(2))))))),\n      else_clause: None,\n    }));\n    let desired_decl = decl::FnDecl { name: String::from(\"example\"), args: ArgList::empty(), body: vec!(desired_stmt) };\n\n    ElseThenIfFold.run_on_function(&mut decl).unwrap();\n    assert_eq!(decl, desired_decl);\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/expr_walker.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::stmt_walker;\nuse crate::gdscript::stmt::{self, Stmt, StmtF};\nuse crate::gdscript::expr::{self, Expr, ExprF};\nuse crate::util::extract_err;\n\n// Post-order traversal\n\npub fn walk_expr<'a, E>(stmt: &Stmt, mut walker: impl FnMut(&Expr) -> Result<Expr, E> + 'a)\n                        -> Result<Vec<Stmt>, E> {\n  stmt_walker::walk_stmt(stmt, stmt_walker::on_each_stmt(|s| walk_impl(&mut walker, s)))\n}\n\npub fn walk_exprs<'a, E>(stmts: &[Stmt], mut walker: impl FnMut(&Expr) -> Result<Expr, E> + 'a)\n                         -> Result<Vec<Stmt>, E> {\n  stmt_walker::walk_stmts(stmts, stmt_walker::on_each_stmt(|s| walk_impl(&mut walker, s)))\n}\n\npub fn walk_expr_ok<'a>(stmt: &Stmt, mut walker: impl FnMut(&Expr) -> Expr + 'a)\n                        -> Vec<Stmt> {\n  let result = walk_expr(stmt, move |x| Ok(walker(x)));\n  extract_err(result)\n}\n\npub fn walk_exprs_ok<'a>(stmts: &[Stmt], mut walker: impl FnMut(&Expr) -> Expr + 'a)\n                         -> Vec<Stmt> {\n  let result = walk_exprs(stmts, move |x| Ok(walker(x)));\n  extract_err(result)\n}\n\nfn walk_impl_expr<'a, E>(walker: &mut (impl FnMut(&Expr) -> Result<Expr, E> + 'a), expr: &Expr)\n                         -> Result<Expr, E> {\n  let mut expr = expr.clone();\n  match &mut expr.value {\n    ExprF::Subscript(a, b) => {\n      **a = walk_impl_expr(walker, a)?;\n      **b = walk_impl_expr(walker, b)?;\n    }\n    ExprF::Attribute(a, _) => {\n      **a = walk_impl_expr(walker, a)?;\n    }\n    ExprF::Call(lhs, _, args) => {\n      if let Some(lhs) = lhs.as_mut() {\n        **lhs = walk_impl_expr(walker, lhs)?;\n      }\n      for arg in args.iter_mut() {\n        *arg = walk_impl_expr(walker, arg)?;\n      }\n    }\n    ExprF::SuperCall(_, args) => {\n      for arg in args.iter_mut() {\n        *arg = walk_impl_expr(walker, arg)?;\n      }\n    }\n    ExprF::Unary(_, a) => {\n      **a = walk_impl_expr(walker, a)?;\n    }\n    ExprF::Binary(a, _, b) => {\n      **a = walk_impl_expr(walker, a)?;\n      **b = walk_impl_expr(walker, b)?;\n    }\n    ExprF::TernaryIf(expr::TernaryIf { true_case: a, cond: b, false_case: c }) => {\n      **a = walk_impl_expr(walker, a)?;\n      **b = walk_impl_expr(walker, b)?;\n      **c = walk_impl_expr(walker, c)?;\n    }\n    ExprF::ArrayLit(args) => {\n      for arg in args.iter_mut() {\n        *arg = walk_impl_expr(walker, arg)?;\n      }\n    }\n    ExprF::DictionaryLit(args) => {\n      for (k, v) in args.iter_mut() {\n        *k = walk_impl_expr(walker, k)?;\n        *v = walk_impl_expr(walker, v)?;\n      }\n    }\n    ExprF::Var(_) | ExprF::Literal(_) => {}\n  }\n  walker(&expr)\n}\n\nfn walk_impl<'a, E>(walker: &mut (impl FnMut(&Expr) -> Result<Expr, E> + 'a), stmt: &Stmt)\n                    -> Result<Vec<Stmt>, E> {\n  let new_stmt = match &stmt.value {\n    StmtF::Expr(e) => {\n      StmtF::Expr(walk_impl_expr(walker, e)?)\n    }\n    StmtF::IfStmt(stmt::IfStmt { if_clause, elif_clauses, else_clause }) => {\n      let new_if_stmt = stmt::IfStmt {\n        if_clause: (walk_impl_expr(walker, &if_clause.0)?, if_clause.1.clone()),\n        elif_clauses: (elif_clauses.iter().map(|(e, s)| Ok((walk_impl_expr(walker, e)?, s.clone()))).collect::<Result<_, E>>()?),\n        else_clause: else_clause.clone(),\n      };\n      StmtF::IfStmt(new_if_stmt)\n    }\n    StmtF::ForLoop(stmt::ForLoop { iter_var, collection, body }) => {\n      let new_for_loop = stmt::ForLoop {\n        iter_var: iter_var.clone(),\n        collection: walk_impl_expr(walker, collection)?,\n        body: body.clone(),\n      };\n      StmtF::ForLoop(new_for_loop)\n    }\n    StmtF::WhileLoop(stmt::WhileLoop { condition, body }) => {\n      let new_while_loop = stmt::WhileLoop {\n        condition: walk_impl_expr(walker, condition)?,\n        body: body.clone(),\n      };\n      StmtF::WhileLoop(new_while_loop)\n    }\n    StmtF::MatchStmt(expr, clauses) => {\n      StmtF::MatchStmt(walk_impl_expr(walker, expr)?, clauses.clone())\n    }\n    StmtF::VarDecl(name, expr) => {\n      StmtF::VarDecl(name.clone(), walk_impl_expr(walker, expr)?)\n    }\n    StmtF::ReturnStmt(expr) => {\n      StmtF::ReturnStmt(walk_impl_expr(walker, expr)?)\n    }\n    StmtF::Assign(lhs, op, rhs) => {\n      let lhs = walk_impl_expr(walker, lhs)?;\n      let rhs = walk_impl_expr(walker, rhs)?;\n      StmtF::Assign(Box::new(lhs), *op, Box::new(rhs))\n    }\n    StmtF::PassStmt | StmtF::BreakStmt | StmtF::ContinueStmt => {\n      stmt.value.clone()\n    }\n  };\n  Ok(vec!(Stmt::new(new_stmt, stmt.pos)))\n}\n\n// TODO Test me :)\n"
  },
  {
    "path": "src/optimize/gdscript/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod noop;\npub mod stmt_walker;\npub mod expr_walker;\npub mod constant;\npub mod variables;\npub mod assignment;\npub mod dead_code_elimination;\npub mod dead_decl_elimination;\npub mod constant_conditional_branch;\npub mod else_then_if_fold;\npub mod basic_math_ops;\npub mod redundant_assignment_elimination;\npub mod direct_var_substitute;\npub mod dead_var_elimination;\npub mod ternary_if_fold;\n\nuse crate::gdscript::decl::{self, Decl, DeclF};\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse crate::compile::error::GDError;\n\n// Note: If optimization results in an error, the code is guaranteed\n// to be in a valid, correct state. It may or may not be rolled back\n// to the way it started, but it should perform equivalently at\n// runtime.\n\npub trait ExpressionLevelPass {\n  fn run_on_expr(&self, expr: &Expr) -> Result<Expr, GDError>;\n}\n\npub trait StatementLevelPass {\n  fn run_on_stmt(&self, stmt: &Stmt) -> Result<Vec<Stmt>, GDError>;\n}\n\npub trait FunctionOptimization {\n  fn run_on_function(&self, function: &mut decl::FnDecl) -> Result<(), GDError>;\n  fn run_on_init_function(&self, function: &mut decl::InitFnDecl) -> Result<(), GDError>;\n}\n\npub trait FileOptimization {\n  fn run_on_file(&self, file: &mut decl::TopLevelClass) -> Result<(), GDError>;\n}\n\n// TODO Note that expression-level optimizations won't run on\n// ConstDecl, VarDecl, or EnumDecl expressions right now. Also on\n// super args in InitFnDecl.\nfn on_each_decl_impl(decls: &[Decl], acc: &mut Vec<Decl>, block: &mut impl FnMut(&Decl) -> Result<Vec<Decl>, GDError>) -> Result<(), GDError> {\n  for decl in decls {\n    let mut new_decls = block(decl)?;\n    for new_decl in &mut new_decls {\n      match &mut new_decl.value {\n        DeclF::FnDecl(_, _) | DeclF::InitFnDecl(_) | DeclF::VarDecl(_) | DeclF::ConstDecl(_, _) | DeclF::SignalDecl(_, _) | DeclF::EnumDecl(_) | DeclF::PassDecl => {\n          // Nothing to recurse on, so pass.\n        }\n        DeclF::ClassDecl(cdecl) => {\n          let mut inner_acc: Vec<Decl> = Vec::new();\n          on_each_decl_impl(&cdecl.body, &mut inner_acc, block)?;\n          cdecl.body = inner_acc;\n        }\n      }\n    }\n    acc.append(&mut new_decls);\n  }\n  Ok(())\n}\n\n/// Runs the block for each declaration, including those nested inside\n/// of classes. This is a pre-order traversal, so the block will be\n/// run on the outer declaration first and then on any inner\n/// declarations in the result.\n///\n/// For each declaration, the block is free to return zero or more\n/// declarations in its place. The resulting declarations will be\n/// concatenated together, with the original order preserved. Any\n/// errors during the block invocation are propagated to the caller of\n/// `on_each_decl`.\npub fn on_each_decl(decls: &[Decl], mut block: impl FnMut(&Decl) -> Result<Vec<Decl>, GDError>) -> Result<Vec<Decl>, GDError> {\n  let mut acc: Vec<Decl> = Vec::new();\n  on_each_decl_impl(decls, &mut acc, &mut block)?;\n  Ok(acc)\n}\n\nfn on_decl(opt: &impl FunctionOptimization, decl: &Decl) -> Result<Vec<Decl>, GDError> {\n  match &decl.value {\n    DeclF::FnDecl(s, fndecl) => {\n      let mut fndecl = fndecl.clone();\n      opt.run_on_function(&mut fndecl)?;\n      Ok(vec!(Decl::new(DeclF::FnDecl(*s, fndecl), decl.pos)))\n    }\n    DeclF::InitFnDecl(fndecl) => {\n      let mut fndecl = fndecl.clone();\n      opt.run_on_init_function(&mut fndecl)?;\n      Ok(vec!(Decl::new(DeclF::InitFnDecl(fndecl), decl.pos)))\n    }\n    DeclF::ClassDecl(_) | DeclF::VarDecl(_) | DeclF::ConstDecl(_, _) | DeclF::SignalDecl(_, _) | DeclF::EnumDecl(_) | DeclF::PassDecl => {\n      Ok(vec!(decl.clone()))\n    }\n  }\n}\n\n// Every FunctionOptimization is a FileOptimization by applying it to\n// each function in the file.\nimpl<T> FileOptimization for T where T : FunctionOptimization {\n  fn run_on_file(&self, file: &mut decl::TopLevelClass) -> Result<(), GDError> {\n    file.body = on_each_decl(&file.body, |d| on_decl(self, d))?;\n    Ok(())\n  }\n}\n\n// A StatementLevelPass is just a local FunctionOptimization\nimpl<T> FunctionOptimization for T where T : StatementLevelPass {\n  fn run_on_function(&self, function: &mut decl::FnDecl) -> Result<(), GDError> {\n    function.body = stmt_walker::walk_stmts(&function.body, stmt_walker::on_each_stmt(|x| self.run_on_stmt(x)))?;\n    Ok(())\n  }\n  fn run_on_init_function(&self, function: &mut decl::InitFnDecl) -> Result<(), GDError> {\n    function.body = stmt_walker::walk_stmts(&function.body, stmt_walker::on_each_stmt(|x| self.run_on_stmt(x)))?;\n    Ok(())\n  }\n}\n\n// An ExpressionLevelPass can easily be realized as a StatementLevelPass\nimpl<T> StatementLevelPass for T where T : ExpressionLevelPass {\n  fn run_on_stmt(&self, stmt: &Stmt) -> Result<Vec<Stmt>, GDError> {\n    expr_walker::walk_expr(stmt, |e| self.run_on_expr(e))\n  }\n}\n\n// TODO We'll refine this a lot. Right now, it's hard coded.\npub fn run_standard_passes(file: &mut decl::TopLevelClass) -> Result<(), GDError> {\n\n  // Run thrice, for good measure :)\n  for _ in 0..3 {\n\n    // Fold trivial if statements containing assignments into the variable\n    ternary_if_fold::TernaryIfFold.run_on_file(file)?;\n    redundant_assignment_elimination::RedundantAssignmentElimination.run_on_file(file)?;\n    dead_var_elimination::DeadVarElimination.run_on_file(file)?;\n    dead_code_elimination::DeadCodeElimination.run_on_file(file)?;\n\n    // Simplify anything we can at the expression level.\n    basic_math_ops::BasicMathOps.run_on_file(file)?;\n\n    // Eliminate and fold conditionals\n    else_then_if_fold::ElseThenIfFold.run_on_file(file)?;\n    constant_conditional_branch::ConstantConditionalBranch.run_on_file(file)?;\n    dead_code_elimination::DeadCodeElimination.run_on_file(file)?;\n\n    // Get rid of unnecessary assignments\n    redundant_assignment_elimination::RedundantAssignmentElimination.run_on_file(file)?;\n\n    // Eliminate constant variables\n    direct_var_substitute::DirectVarSubstitute.run_on_file(file)?;\n    dead_var_elimination::DeadVarElimination.run_on_file(file)?;\n\n    // Another conditional and dead code pass\n    else_then_if_fold::ElseThenIfFold.run_on_file(file)?;\n    constant_conditional_branch::ConstantConditionalBranch.run_on_file(file)?;\n    dead_code_elimination::DeadCodeElimination.run_on_file(file)?;\n\n    // Eliminate dead variables\n    dead_var_elimination::DeadVarElimination.run_on_file(file)?;\n    dead_code_elimination::DeadCodeElimination.run_on_file(file)?;\n    dead_decl_elimination::DeadDeclElimination.run_on_file(file)?;\n\n  }\n\n  Ok(())\n}\n"
  },
  {
    "path": "src/optimize/gdscript/noop.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n// Helper functions for checking whether code is a noop\n\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse super::constant;\n\npub fn is_code_seq_noop(stmts: &[Stmt]) -> bool {\n  stmts.iter().all(is_code_noop)\n}\n\npub fn is_code_noop(stmt: &Stmt) -> bool {\n  !constant::stmt_has_side_effects(stmt)\n}\n\npub fn is_expr_noop(expr: &Expr) -> bool {\n  !constant::expr_has_side_effects(expr)\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::op;\n  use crate::gdscript::expr::ExprF;\n  use crate::gdscript::stmt::StmtF;\n  use crate::pipeline::source::SourceOffset;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  #[test]\n  fn expr_noop() {\n    // True\n    assert!(is_expr_noop(&e(ExprF::Var(String::from(\"example_variable\")))));\n    assert!(is_expr_noop(&e(ExprF::from(1))));\n    assert!(is_expr_noop(&e(ExprF::Unary(op::UnaryOp::Negate, Box::new(e(ExprF::from(1)))))));\n    assert!(is_expr_noop(&e(ExprF::ArrayLit(vec!()))));\n    assert!(is_expr_noop(&e(ExprF::ArrayLit(vec!(e(ExprF::from(1)), e(ExprF::from(2)), e(ExprF::from(3)))))));\n    assert!(is_expr_noop(&e(ExprF::Subscript(Box::new(e(ExprF::from(1))), Box::new(e(ExprF::from(2)))))));\n    assert!(is_expr_noop(&e(ExprF::Attribute(Box::new(e(ExprF::from(1))), String::from(\"attribute_name\")))));\n\n    // False\n    let call = e(ExprF::Call(None, String::from(\"function_name\"), vec!()));\n    assert!(!is_expr_noop(&call));\n    assert!(!is_expr_noop(&e(ExprF::Unary(op::UnaryOp::Negate, Box::new(call.clone())))));\n    assert!(!is_expr_noop(&e(ExprF::Binary(Box::new(e(ExprF::from(2))), op::BinaryOp::Add, Box::new(call.clone())))));\n  }\n\n  #[test]\n  fn stmt_noop() {\n    // True\n    assert!(is_code_noop(&Stmt::expr(e(ExprF::from(1)))));\n    assert!(is_code_noop(&s(StmtF::PassStmt)));\n    // False\n    let call = e(ExprF::Call(None, String::from(\"function_name\"), vec!()));\n    assert!(!is_code_noop(&Stmt::expr(call)));\n    assert!(!is_code_noop(&s(StmtF::BreakStmt)));\n    assert!(!is_code_noop(&s(StmtF::ContinueStmt)));\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/redundant_assignment_elimination.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::decl;\nuse crate::gdscript::op;\nuse crate::compile::error::GDError;\nuse super::FunctionOptimization;\nuse super::constant;\nuse super::stmt_walker;\nuse super::assignment::{AssignmentStmt, AssignType};\n\npub struct RedundantAssignmentElimination;\n\n/*\n * This catches situations like\n *\n *     var example = 0\n *     example = 10\n *\n * and replaces them with\n *\n *     var example = 10\n *\n * Specifically, any variable declaration or (possibly compound)\n * assignment (where the expression is non-stateful), followed by zero\n * or more non-stateful statements, followed by another assignment, is\n * considered redundant and can be culled down to one declaration /\n * assignment.\n */\nimpl RedundantAssignmentElimination {\n\n  fn match_first_assign<'a>(&self, stmt: &'a Stmt) -> Option<AssignmentStmt<'a>> {\n    AssignmentStmt::match_stmt(stmt)\n  }\n\n  // The second assignment (the one that makes the first redundant)\n  // must be a Stmt::Assign. If it's a Stmt::VarDecl, then it's a\n  // Godot error (I think? Might be a shadowed variable? Either way,\n  // not our problem)\n  fn match_second_assign<'a, 'b, 'c>(&'a self, name: &'b str, stmt: &'c Stmt) -> Option<&'c Expr> {\n    if let Some(AssignmentStmt { assign_type, var_name, expr }) = AssignmentStmt::match_stmt(stmt) {\n      if assign_type == AssignType::Assignment(op::AssignOp::Eq) && name == var_name {\n        return Some(expr);\n      }\n    }\n    None\n  }\n\n  fn rebuild_assignment(&self, assign_type: AssignType, var_name: &str, expr: &Expr) -> Stmt {\n    AssignmentStmt { assign_type, var_name, expr }.into()\n  }\n\n  pub fn run_on_stmts(&self, stmts: &[Stmt]) -> Result<Vec<Stmt>, GDError> {\n    // Look for something to eliminate. If we find it, do the\n    // elimination and call the function again. If not, we're done.\n    for (index, stmt1) in stmts.iter().enumerate() {\n      if let Some(AssignmentStmt { assign_type, var_name: name, expr: expr1 }) = self.match_first_assign(stmt1) {\n        if !constant::expr_has_side_effects(expr1) {\n          // Found a match. Keep going.\n          for jndex in index+1..stmts.len() {\n            let stmt2 = &stmts[jndex];\n            if let Some(expr2) = self.match_second_assign(name, stmt2) {\n              // Redundant assignment; cull\n              let new_stmt = self.rebuild_assignment(assign_type.ensure_eq(), name, expr2);\n              let mut new_stmts = stmts.to_vec();\n              new_stmts.splice(index..=jndex, vec!(new_stmt).into_iter());\n              return self.run_on_stmts(&new_stmts);\n            } else if constant::stmt_has_side_effects(stmt2) {\n              // If it's stateful, then we can't do anything\n              break;\n            }\n          }\n        }\n      }\n    }\n    // Found nothing to do, so just pass through\n    Ok(stmts.to_vec())\n  }\n\n}\n\nimpl FunctionOptimization for RedundantAssignmentElimination {\n  fn run_on_function(&self, function: &mut decl::FnDecl) -> Result<(), GDError> {\n    function.body = stmt_walker::walk_stmts(&function.body, |x| self.run_on_stmts(x))?;\n    Ok(())\n  }\n  fn run_on_init_function(&self, function: &mut decl::InitFnDecl) -> Result<(), GDError> {\n    function.body = stmt_walker::walk_stmts(&function.body, |x| self.run_on_stmts(x))?;\n    Ok(())\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::arglist::ArgList;\n  use crate::gdscript::expr::ExprF;\n  use crate::gdscript::stmt::StmtF;\n  use crate::pipeline::source::SourceOffset;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  #[test]\n  fn redundant_assign_test_1() {\n    /* (Eliminate after decl)\n     * var foo = 1\n     * foo = 2\n     * foo = 3\n     */\n\n    let body0 = vec!(\n      s(StmtF::VarDecl(String::from(\"foo\"), e(ExprF::from(1)))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(3))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0,\n    };\n\n    let body1 = vec!(\n      s(StmtF::VarDecl(String::from(\"foo\"), e(ExprF::from(3)))),\n    );\n    let func1 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body1,\n    };\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n  #[test]\n  fn redundant_assign_test_2() {\n    /* (Eliminate after decl)\n     * var foo = 1\n     * \"string\"\n     * null\n     * foo = 2\n     */\n\n    let body0 = vec!(\n      s(StmtF::VarDecl(String::from(\"foo\"), e(ExprF::from(1)))),\n      s(StmtF::Expr(Expr::from_value(\"string\", SourceOffset::default()))),\n      s(StmtF::Expr(Expr::null(SourceOffset::default()))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0,\n    };\n\n    let body1 = vec!(\n      s(StmtF::VarDecl(String::from(\"foo\"), e(ExprF::from(2)))),\n    );\n    let func1 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body1,\n    };\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n  #[test]\n  fn redundant_assign_test_3() {\n    /* (Eliminate after assign)\n     * foo = 1\n     * foo = 2\n     */\n\n    let body0 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0,\n    };\n\n    let body1 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n    );\n    let func1 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body1,\n    };\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n  #[test]\n  fn redundant_assign_test_4() {\n    /* (Eliminate after assign (compound))\n     * foo += 1\n     * foo = 2\n     */\n\n    let body0 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Add, Box::new(e(ExprF::from(1))))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0,\n    };\n\n    let body1 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(Expr::from_value(2, SourceOffset::default())))),\n    );\n    let func1 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body1,\n    };\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n  #[test]\n  fn redundant_assign_test_no_trigger_1() {\n    /* (Do not eliminate if stateful in between)\n     * foo = 1\n     * fn()\n     * foo = 2\n     */\n\n    let body0 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))),\n      s(StmtF::Expr(e(ExprF::Call(None, String::from(\"fn\"), vec!())))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0.clone(),\n    };\n    let func1 = func0.clone();\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n  #[test]\n  fn redundant_assign_test_no_trigger_2() {\n    /* (Do not eliminate if stateful on assign)\n     * foo = fn()\n     * foo = 2\n     */\n\n    let body0 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::Call(None, String::from(\"fn\"), vec!()))))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0.clone(),\n    };\n    let func1 = func0.clone();\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n  #[test]\n  fn redundant_assign_test_no_trigger_3() {\n    /* (Do not eliminate if different variables)\n     * foo = 1\n     * bar = 2\n     */\n\n    let body0 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))),\n      s(StmtF::Assign(Box::new(Expr::var(\"bar\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0.clone(),\n    };\n    let func1 = func0.clone();\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n  #[test]\n  fn redundant_assign_test_no_trigger_4() {\n    /* (Do not eliminate if second assignment is compound)\n     * foo = 1\n     * foo += 2\n     */\n\n    let body0 = vec!(\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))),\n      s(StmtF::Assign(Box::new(Expr::var(\"foo\", SourceOffset::default())), op::AssignOp::Add, Box::new(e(ExprF::from(2))))),\n    );\n    let mut func0 = decl::FnDecl {\n      name: String::from(\"example\"),\n      args: ArgList::empty(),\n      body: body0.clone(),\n    };\n    let func1 = func0.clone();\n\n    RedundantAssignmentElimination.run_on_function(&mut func0).unwrap();\n    assert_eq!(func0, func1);\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/stmt_walker.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides mechanisms for walking GDScript\n//! [`Stmt`](crate::gdscript::stmt::Stmt) statements or sequences\n//! thereof.\n//!\n//! The primary endpoints of this module are [`walk_stmt`] and\n//! [`walk_stmts`] for traversing, respectively, a single statement or\n//! a slice of statements.\n\nuse crate::gdscript::stmt::{self, Stmt, StmtF};\nuse crate::util::extract_err;\n\nstruct StmtWalker<'a, E> {\n  imp: Box<WalkFn<'a, E>>,\n}\n\ntype WalkFn<'a, E> = dyn FnMut(&[Stmt]) -> Result<Vec<Stmt>, E> + 'a;\n\nimpl<'a, E> StmtWalker<'a, E> {\n\n  fn new(function: impl FnMut(&[Stmt]) -> Result<Vec<Stmt>, E> + 'a) -> StmtWalker<'a, E> {\n    StmtWalker { imp: Box::new(function) }\n  }\n\n  fn walk_stmts(&mut self, stmts: &[Stmt]) -> Result<Vec<Stmt>, E> {\n    let mut result = Vec::new();\n    for stmt in stmts {\n      result.extend(self.walk_stmt(stmt)?);\n    }\n    (self.imp)(&result)\n  }\n\n  fn walk_stmt(&mut self, stmt: &Stmt) -> Result<Vec<Stmt>, E> {\n    let new_stmt = match &stmt.value {\n      StmtF::Expr(_) | StmtF::PassStmt | StmtF::BreakStmt | StmtF::ContinueStmt\n        | StmtF::VarDecl(_, _) | StmtF::ReturnStmt(_) | StmtF::Assign(_, _, _) => stmt.value.clone(),\n      StmtF::IfStmt(stmt::IfStmt { if_clause, elif_clauses, else_clause }) => {\n        let if_clause = (if_clause.0.clone(), self.walk_stmts(&if_clause.1)?);\n        let elif_clauses = elif_clauses.iter().map(|(e, s)| self.walk_stmts(s).map(|s| (e.clone(), s))).collect::<Result<_, _>>()?;\n        let else_clause = else_clause.as_ref().map(|s| self.walk_stmts(s)).transpose()?;\n        StmtF::IfStmt(stmt::IfStmt { if_clause, elif_clauses, else_clause })\n      }\n      StmtF::ForLoop(stmt::ForLoop { iter_var, collection, body }) => {\n        let body = self.walk_stmts(body)?;\n        StmtF::ForLoop(stmt::ForLoop {\n          iter_var: iter_var.clone(),\n          collection: collection.clone(),\n          body: body,\n        })\n      }\n      StmtF::WhileLoop(stmt::WhileLoop { condition, body }) => {\n        let body = self.walk_stmts(body)?;\n        StmtF::WhileLoop(stmt::WhileLoop {\n          condition: condition.clone(),\n          body: body,\n        })\n      }\n      StmtF::MatchStmt(expr, clauses) => {\n        let clauses = clauses.iter().map(|(p, s)| self.walk_stmts(s).map(|s| (p.clone(), s))).collect::<Result<_, _>>()?;\n        StmtF::MatchStmt(expr.clone(), clauses)\n      }\n    };\n    Ok(vec!(Stmt::new(new_stmt, stmt.pos)))\n  }\n\n}\n\n/// Takes a callable designed to operate on one `&Stmt` and returns a\n/// callable which operates on each of a slice `&[Stmt]` of statements\n/// individually, effectively mapping the callable over the slice.\n///\n/// The resulting vectors from each call to `walker` are concatenated\n/// together into the final vector result.\npub fn on_each_stmt<'a, E>(mut walker: impl FnMut(&Stmt) -> Result<Vec<Stmt>, E> + 'a)\n                           -> impl FnMut(&[Stmt]) -> Result<Vec<Stmt>, E> + 'a {\n  move |stmts| {\n    let mut result = Vec::new();\n    for stmt in stmts {\n      result.extend(walker(stmt)?);\n    }\n    Ok(result)\n  }\n}\n\n/// As [`on_each_stmt`] but with no error type.\npub fn on_each_stmt_ok<'a>(mut walker: impl FnMut(&Stmt) -> Vec<Stmt> + 'a)\n                           -> impl FnMut(&[Stmt]) -> Vec<Stmt> + 'a {\n  let mut new_walker = on_each_stmt(move |x| Ok(walker(x)));\n  move |stmts| {\n    let result = new_walker(stmts);\n    extract_err(result)\n  }\n}\n\n/// Walks the statement `stmt`, calling `walker` on each sequence of\n/// statements in `stmt` (including the sequence consisting of `stmt`\n/// itself). The calls are done in postorder, so changes made on the\n/// inner structure of the statement during the walk will be reflected\n/// in the later calls.\npub fn walk_stmt<'a, E>(stmt: &Stmt, walker: impl FnMut(&[Stmt]) -> Result<Vec<Stmt>, E> + 'a)\n                        -> Result<Vec<Stmt>, E> {\n  let stmts = vec!(stmt.clone());\n  walk_stmts(&stmts[..], walker)\n}\n\n/// Walks the sequence of statements `stmts`, calling `walker` on each\n/// sequence in `stmts` (including `stmts` itself). The calls are done\n/// in postorder, so changes made on the inner structure during the\n/// walk will be reflected in later calls.\npub fn walk_stmts<'a, E>(stmts: &[Stmt], walker: impl FnMut(&[Stmt]) -> Result<Vec<Stmt>, E> + 'a)\n                         -> Result<Vec<Stmt>, E> {\n  let mut walker = StmtWalker::new(walker);\n  walker.walk_stmts(stmts)\n}\n\n/// As [`walk_stmt`] but with no error type.\npub fn walk_stmt_ok<'a>(stmt: &Stmt, mut walker: impl FnMut(&[Stmt]) -> Vec<Stmt> + 'a)\n                        -> Vec<Stmt> {\n  let result = walk_stmt(stmt, move |x| Ok(walker(x)));\n  extract_err(result)\n}\n\n/// As [`walk_stmts`] but with no error type.\npub fn walk_stmts_ok<'a>(stmts: &[Stmt], mut walker: impl FnMut(&[Stmt]) -> Vec<Stmt> + 'a)\n                         -> Vec<Stmt> {\n  let result = walk_stmts(stmts, move |x| Ok(walker(x)));\n  extract_err(result)\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::gdscript::expr::{Expr, ExprF};\n  use crate::pipeline::source::SourceOffset;\n\n  // TODO More test coverage\n\n  #[derive(Copy, Clone, Debug, Eq, PartialEq)]\n  struct SampleError;\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  #[test]\n  fn test_walk_simple_stmt() {\n    let stmt = s(StmtF::PassStmt);\n    let mut call_result = Vec::new();\n    walk_stmt_ok(&stmt, |x| { call_result.push(x.to_vec()); x.to_vec() });\n    assert_eq!(call_result, vec!(vec!(s(StmtF::PassStmt))));\n  }\n\n  #[test]\n  fn test_walk_simple_stmt_seq() {\n    let stmts = vec!(s(StmtF::PassStmt), s(StmtF::BreakStmt));\n    let mut call_result = Vec::new();\n    walk_stmts_ok(&stmts, |x| { call_result.push(x.to_vec()); x.to_vec() });\n    assert_eq!(call_result, vec!(vec!(s(StmtF::PassStmt), s(StmtF::BreakStmt))));\n  }\n\n  #[test]\n  fn test_walk_complex_stmt() {\n    let stmt = stmt::if_else(e(ExprF::from(1)), vec!(s(StmtF::PassStmt)), vec!(s(StmtF::BreakStmt)), SourceOffset::default());\n    let mut call_result = Vec::new();\n    walk_stmt_ok(&stmt, |x| { call_result.push(x.to_vec()); x.to_vec() });\n    assert_eq!(call_result, vec!(\n      vec!(s(StmtF::PassStmt)),\n      vec!(s(StmtF::BreakStmt)),\n      vec!(stmt),\n    ));\n  }\n\n  #[test]\n  fn test_walk_complex_stmt_seq() {\n    let stmt = stmt::if_else(e(ExprF::from(1)), vec!(s(StmtF::PassStmt)), vec!(s(StmtF::BreakStmt)), SourceOffset::default());\n    let stmts = vec!(stmt.clone(), s(StmtF::PassStmt));\n    let mut call_result = Vec::new();\n    walk_stmts_ok(&stmts, |x| { call_result.push(x.to_vec()); x.to_vec() });\n    assert_eq!(call_result, vec!(\n      vec!(s(StmtF::PassStmt)),\n      vec!(s(StmtF::BreakStmt)),\n      vec!(stmt, s(StmtF::PassStmt)),\n    ));\n  }\n\n  #[test]\n  fn test_walk_simple_error_1() {\n    let stmts = vec!(s(StmtF::PassStmt));\n    let result = walk_stmts(&stmts, on_each_stmt(|_| Err(SampleError)));\n    assert_eq!(result, Err(SampleError));\n  }\n\n  #[test]\n  fn test_walk_simple_error_2() {\n    let stmts = vec!();\n    let result = walk_stmts(&stmts, on_each_stmt(|_| Err(SampleError)));\n    assert_eq!(result, Ok(vec!()));\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/ternary_if_fold.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::gdscript::stmt::{self, Stmt, StmtF};\nuse crate::gdscript::expr::{self, Expr, ExprF};\nuse crate::gdscript::op;\nuse crate::compile::error::GDError;\nuse super::StatementLevelPass;\n\npub struct TernaryIfFold;\n\n/*\n * Factor out common assignment in an if-statement. Specifically, if\n * we have a statement of the form\n *\n * if a:\n *     example = b\n * elif c:\n *     example = c\n * ...\n * else:\n *     example = z\n *\n * Where every branch is a simple assignment to a common variable and\n * none of the conditions depend on that variable in any way.\n */\nimpl TernaryIfFold {\n\n  fn fold_into_ternary(acc: Expr, next: (Expr, Expr)) -> Expr {\n    let pos = acc.pos;\n    Expr::from_value(\n      expr::TernaryIf {\n        true_case: Box::new(next.1),\n        cond: Box::new(next.0),\n        false_case: Box::new(acc),\n      },\n      pos,\n    )\n  }\n\n  fn match_simple_assign<'a>(&self, stmts: &'a [Stmt]) -> Option<(&'a str, &'a Expr)> {\n    if let [Stmt { value: StmtF::Assign(lhs, op::AssignOp::Eq, rhs), pos: _ }] = stmts {\n      if let ExprF::Var(var_name) = &lhs.value {\n        return Some((var_name, rhs));\n      }\n    }\n    None\n  }\n\n  fn try_to_run(&self, if_stmt: &stmt::IfStmt) -> Option<Stmt> {\n    let stmt::IfStmt { if_clause, elif_clauses, else_clause } = if_stmt;\n\n    // Check if\n    if let Some((var_name, if_expr)) = self.match_simple_assign(&if_clause.1) {\n      let mut clauses = vec!((if_clause.0.clone(), if_expr.clone()));\n\n      // Check elif\n      for (elif_cond, elif_stmts) in elif_clauses {\n        if let Some((var_name_elif, elif_expr)) = self.match_simple_assign(elif_stmts) {\n          if var_name_elif == var_name {\n            clauses.push((elif_cond.clone(), elif_expr.clone()));\n            continue;\n          }\n        }\n        return None;\n      }\n\n      // Check else (Note: Don't fire if we're missing the else clause)\n      let (else_var_name, else_body) = self.match_simple_assign(else_clause.as_ref()?)?;\n      if else_var_name != var_name {\n        return None;\n      }\n      let else_body = else_body.clone();\n\n      let final_expr = clauses.into_iter().rev().fold(else_body, TernaryIfFold::fold_into_ternary);\n      let final_expr_pos = final_expr.pos;\n      return Some(Stmt::simple_assign(Expr::var(var_name, final_expr.pos), final_expr, final_expr_pos));\n    }\n\n    None\n  }\n\n}\n\nimpl StatementLevelPass for TernaryIfFold {\n  fn run_on_stmt(&self, stmt: &Stmt) -> Result<Vec<Stmt>, GDError> {\n    if let StmtF::IfStmt(if_stmt) = &stmt.value {\n      if let Some(stmt) = self.try_to_run(if_stmt) {\n        return Ok(vec!(stmt));\n      }\n    }\n    Ok(vec!(stmt.clone()))\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::source::SourceOffset;\n\n  fn e(expr: ExprF) -> Expr {\n    Expr::new(expr, SourceOffset::default())\n  }\n\n  fn s(stmt: StmtF) -> Stmt {\n    Stmt::new(stmt, SourceOffset::default())\n  }\n\n  #[test]\n  fn ternary_if_fold_test_1() {\n    /*\n     * if a:\n     *   x = 1\n     * else:\n     *   x = 2\n     */\n    let stmt1 = s(StmtF::IfStmt(stmt::IfStmt {\n      if_clause: (Expr::var(\"a\", SourceOffset::default()), vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))))),\n      elif_clauses: vec!(),\n      else_clause: Some(vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2))))))),\n    }));\n    let stmt2 = s(StmtF::Assign(\n      Box::new(Expr::var(\"x\", SourceOffset::default())),\n      op::AssignOp::Eq,\n      Box::new(e(ExprF::TernaryIf(expr::TernaryIf {\n        true_case: Box::new(e(ExprF::from(1))),\n        cond: Box::new(Expr::var(\"a\", SourceOffset::default())),\n        false_case: Box::new(e(ExprF::from(2))),\n      }))),\n    ));\n    assert_eq!(TernaryIfFold.run_on_stmt(&stmt1).unwrap(), vec!(stmt2));\n  }\n\n  #[test]\n  fn ternary_if_fold_test_2() {\n    /*\n     * if a:\n     *   x = 1\n     * elif b:\n     *   x = 2\n     * else:\n     *   x = 3\n     */\n    let stmt1 = s(StmtF::IfStmt(stmt::IfStmt {\n      if_clause: (Expr::var(\"a\", SourceOffset::default()), vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))))),\n      elif_clauses: vec!((Expr::var(\"b\", SourceOffset::default()), vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2)))))))),\n      else_clause: Some(vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(3))))))),\n    }));\n    let stmt2 = s(StmtF::Assign(\n      Box::new(Expr::var(\"x\", SourceOffset::default())),\n      op::AssignOp::Eq,\n      Box::new(e(ExprF::TernaryIf(expr::TernaryIf {\n        true_case: Box::new(e(ExprF::from(1))),\n        cond: Box::new(Expr::var(\"a\", SourceOffset::default())),\n        false_case: Box::new(e(ExprF::TernaryIf(expr::TernaryIf {\n          true_case: Box::new(e(ExprF::from(2))),\n          cond: Box::new(Expr::var(\"b\", SourceOffset::default())),\n          false_case: Box::new(e(ExprF::from(3))),\n        }))),\n      }))),\n    ));\n    assert_eq!(TernaryIfFold.run_on_stmt(&stmt1).unwrap(), vec!(stmt2));\n  }\n\n  #[test]\n  fn ternary_if_fold_test_no_trigger_1() {\n    /* (Different variables)\n     * if a:\n     *   x = 1\n     * elif b:\n     *   y = 2\n     * else:\n     *   x = 3\n     */\n    let stmt1 = s(StmtF::IfStmt(stmt::IfStmt {\n      if_clause: (Expr::var(\"a\", SourceOffset::default()), vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))))),\n      elif_clauses: vec!((Expr::var(\"b\", SourceOffset::default()), vec!(s(StmtF::Assign(Box::new(Expr::var(\"y\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2)))))))),\n      else_clause: Some(vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(3))))))),\n    }));\n    assert_eq!(TernaryIfFold.run_on_stmt(&stmt1).unwrap(), vec!(stmt1));\n  }\n\n  #[test]\n  fn ternary_if_fold_test_no_trigger_2() {\n    /* (No else)\n     * if a:\n     *   x = 1\n     * elif b:\n     *   x = 2\n     */\n    let stmt1 = s(StmtF::IfStmt(stmt::IfStmt {\n      if_clause: (Expr::var(\"a\", SourceOffset::default()), vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(1))))))),\n      elif_clauses: vec!((Expr::var(\"b\", SourceOffset::default()), vec!(s(StmtF::Assign(Box::new(Expr::var(\"x\", SourceOffset::default())), op::AssignOp::Eq, Box::new(e(ExprF::from(2)))))))),\n      else_clause: None,\n    }));\n    assert_eq!(TernaryIfFold.run_on_stmt(&stmt1).unwrap(), vec!(stmt1));\n  }\n\n}\n"
  },
  {
    "path": "src/optimize/gdscript/variables.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n// Helper for getting information about variable usage in a block of code\n\nuse crate::gdscript::expr::{Expr, ExprF};\nuse crate::gdscript::stmt::{Stmt, StmtF};\nuse super::stmt_walker;\nuse super::expr_walker;\n\nuse std::collections::HashMap;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct VarInfo {\n  pub value: Expr,\n  pub read_count: u32,\n  pub write_count: u32,\n}\n\nimpl VarInfo {\n\n  pub fn new(expr: Expr) -> VarInfo {\n    VarInfo { value: expr, read_count: 0, write_count: 0 }\n  }\n\n  pub fn is_ever_used(&self) -> bool {\n    self.read_count > 0 || self.write_count > 0\n  }\n\n  pub fn is_read_only(&self) -> bool {\n    self.write_count == 0\n  }\n\n}\n\npub fn get_variable_info(stmts: &[Stmt]) -> HashMap<String, VarInfo> {\n  let mut map = HashMap::new();\n\n  // Read declarations\n  stmt_walker::walk_stmts_ok(stmts, stmt_walker::on_each_stmt_ok(|stmt| {\n    if let StmtF::VarDecl(s, e) = &stmt.value {\n      map.insert(s.to_owned(), VarInfo::new(e.clone()));\n    }\n    vec!(stmt.clone()) // Pass through\n  }));\n\n  // Read modifications\n  stmt_walker::walk_stmts_ok(stmts, stmt_walker::on_each_stmt_ok(|stmt| {\n    if let StmtF::Assign(s, _, _) = &stmt.value {\n      if let ExprF::Var(s) = &s.value {\n        map.entry(s.to_owned()).and_modify(|v| v.write_count += 1);\n      }\n    }\n    vec!(stmt.clone()) // Pass through\n  }));\n\n  // Read accesses\n  expr_walker::walk_exprs_ok(stmts, |expr| {\n    if let ExprF::Var(s) = &expr.value {\n      map.entry(s.to_owned()).and_modify(|v| v.read_count += 1);\n    }\n    expr.clone() // Pass through\n  });\n\n  map\n}\n"
  },
  {
    "path": "src/optimize/ir/expr_walker.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::ir::expr::{self, Expr, ExprF};\nuse crate::ir::decl::{self, Decl, DeclF};\nuse crate::util::extract_err;\n\n// Post-order traversal\n\nstruct ExprWalker<'a, E> {\n  imp: Box<WalkFn<'a, E>>,\n}\n\ntype WalkFn<'a, E> = dyn (FnMut(&Expr) -> Result<Expr, E>) + 'a;\n\nimpl<'a, E> ExprWalker<'a, E> {\n\n  fn new(function: impl FnMut(&Expr) -> Result<Expr, E> + 'a) -> ExprWalker<'a, E> {\n    ExprWalker { imp: Box::new(function) }\n  }\n\n  fn walk_exprs(&mut self, exprs: &[Expr]) -> Result<Vec<Expr>, E> {\n    exprs.iter().map(|e| {\n      self.walk_expr(e)\n    }).collect()\n  }\n\n  fn walk_expr(&mut self, expr: &Expr) -> Result<Expr, E> {\n    let new_expr = match &expr.value {\n      ExprF::BareName(_) => {\n        expr.value.clone()\n      }\n      ExprF::Literal(_) => {\n        expr.value.clone()\n      }\n      ExprF::Quote(_) => {\n        expr.value.clone()\n      }\n      ExprF::SpecialRef(_) => {\n        expr.value.clone()\n      }\n      ExprF::ContextualFilename(_) => {\n        expr.value.clone()\n      }\n      ExprF::Progn(body) => {\n        ExprF::Progn(self.walk_exprs(body)?)\n      }\n      ExprF::CondStmt(options) => {\n        ExprF::CondStmt(options.iter().map(|(cond, body)| {\n          Ok((self.walk_expr(cond)?, body.as_ref().map(|b| self.walk_expr(b)).transpose()?))\n        }).collect::<Result<Vec<_>, _>>()?)\n      }\n      ExprF::WhileStmt(cond, body) => {\n        ExprF::WhileStmt(\n          Box::new(self.walk_expr(cond)?),\n          Box::new(self.walk_expr(body)?),\n        )\n      }\n      ExprF::ForStmt(name, iter, body) => {\n        ExprF::ForStmt(\n          name.clone(),\n          Box::new(self.walk_expr(iter)?),\n          Box::new(self.walk_expr(body)?),\n        )\n      }\n      ExprF::Call(object, name, body) => {\n        let object = match object {\n          expr::CallTarget::Scoped => expr::CallTarget::Scoped,\n          expr::CallTarget::Super => expr::CallTarget::Super,\n          expr::CallTarget::Atomic => expr::CallTarget::Atomic,\n          expr::CallTarget::Object(inner) => expr::CallTarget::Object(Box::new(self.walk_expr(inner)?)),\n        };\n        ExprF::Call(\n          object,\n          name.clone(),\n          self.walk_exprs(body)?,\n        )\n      }\n      ExprF::Let(clauses, body) => {\n        let clauses = clauses.iter().map(|clause| {\n          Ok(expr::LocalVarClause {\n            name: clause.name.clone(),\n            value: self.walk_expr(&clause.value)?,\n          })\n        }).collect::<Result<Vec<_>, _>>()?;\n        ExprF::Let(\n          clauses,\n          Box::new(self.walk_expr(body)?),\n        )\n      }\n      ExprF::FunctionLet(binding_type, clauses, body) => {\n        let clauses = clauses.iter().map(|clause| {\n          Ok(expr::LocalFnClause {\n            name: clause.name.clone(),\n            args: clause.args.clone(),\n            body: self.walk_expr(&clause.body)?,\n          })\n        }).collect::<Result<Vec<_>, _>>()?;\n        ExprF::FunctionLet(\n          *binding_type,\n          clauses,\n          Box::new(self.walk_expr(body)?),\n        )\n      }\n      ExprF::Lambda(args, body) => {\n        ExprF::Lambda(\n          args.clone(),\n          Box::new(self.walk_expr(body)?),\n        )\n      }\n      ExprF::FuncRef(expr::FuncRefTarget::SimpleName(s)) => {\n        ExprF::FuncRef(\n          expr::FuncRefTarget::SimpleName(s.clone()),\n        )\n      }\n      ExprF::Assign(target, rhs) => {\n        let target = match target {\n          expr::AssignTarget::Variable(_, _) => {\n            target.clone()\n          }\n          expr::AssignTarget::InstanceField(pos, inner, name) => {\n            expr::AssignTarget::InstanceField(\n              *pos,\n              Box::new(self.walk_expr(inner)?),\n              name.clone(),\n            )\n          }\n        };\n        ExprF::Assign(\n          target,\n          Box::new(self.walk_expr(rhs)?),\n        )\n      }\n      ExprF::FieldAccess(lhs, name) => {\n        ExprF::FieldAccess(\n          Box::new(self.walk_expr(lhs)?),\n          name.clone(),\n        )\n      }\n      ExprF::LambdaClass(cls) => {\n        ExprF::LambdaClass(\n          Box::new(self.walk_lambda_class(cls)?),\n        )\n      }\n      ExprF::Yield(None) => {\n        ExprF::Yield(None)\n      }\n      ExprF::Yield(Some((a, b))) => {\n        ExprF::Yield(Some((\n          Box::new(self.walk_expr(a)?),\n          Box::new(self.walk_expr(b)?),\n        )))\n      }\n      ExprF::Assert(cond, message) => {\n        let cond = self.walk_expr(cond)?;\n        let message = message.as_ref().map(|x| self.walk_expr(x)).transpose()?;\n        ExprF::Assert(Box::new(cond), message.map(Box::new))\n      }\n      ExprF::Return(v) => {\n        ExprF::Return(Box::new(self.walk_expr(v)?))\n      }\n      ExprF::Break => {\n        ExprF::Break\n      }\n      ExprF::Continue => {\n        ExprF::Continue\n      }\n      ExprF::Split(name, body) => {\n        ExprF::Split(\n          name.clone(),\n          Box::new(self.walk_expr(body)?),\n        )\n      }\n      ExprF::Preload(name) => {\n        ExprF::Preload(name.to_owned())\n      }\n    };\n    let new_expr = Expr::new(new_expr, expr.pos);\n    (self.imp)(&new_expr)\n  }\n\n  fn walk_lambda_class(&mut self, cls: &expr::LambdaClass) -> Result<expr::LambdaClass, E> {\n    // TODO Once we can walk declarations, unify parts of this with\n    // that implementation.\n    let extends = cls.extends.clone();\n    let args = self.walk_exprs(&cls.args)?;\n    let constructor = cls.constructor.as_ref().map(|c| {\n      let super_call = decl::SuperCall { call: self.walk_exprs(&c.super_call.call)?, pos: c.super_call.pos };\n      Ok(decl::ConstructorDecl {\n        args: c.args.clone(),\n        super_call: super_call,\n        body: self.walk_expr(&c.body)?,\n      })\n    }).transpose()?;\n    let decls = cls.decls.iter().map(|d| {\n      // TODO Technically ClassConstDecl and ClassVarDecl contain\n      // expressions. We should walk those too.\n      let new_decl = match &d.value {\n        decl::ClassInnerDeclF::ClassSignalDecl(_) => d.value.clone(),\n        decl::ClassInnerDeclF::ClassConstDecl(_) => d.value.clone(),\n        decl::ClassInnerDeclF::ClassVarDecl(_) => d.value.clone(),\n        decl::ClassInnerDeclF::ClassFnDecl(inner) => {\n          decl::ClassInnerDeclF::ClassFnDecl(decl::ClassFnDecl {\n            is_static: inner.is_static,\n            is_nullargs: inner.is_nullargs,\n            name: inner.name.clone(),\n            args: inner.args.clone(),\n            body: self.walk_expr(&inner.body)?,\n          })\n        }\n      };\n      Ok(decl::ClassInnerDecl { value: new_decl, pos: d.pos })\n    }).collect::<Result<Vec<_>, _>>()?;\n    Ok(expr::LambdaClass { extends, args, constructor, decls })\n  }\n\n}\n\npub fn walk_expr<'a, E>(expr: &Expr, walker: impl FnMut(&Expr) -> Result<Expr, E> + 'a)\n                        -> Result<Expr, E> {\n  let mut walker = ExprWalker::new(walker);\n  walker.walk_expr(expr)\n}\n\npub fn walk_expr_ok<'a>(expr: &Expr, mut walker: impl FnMut(&Expr) -> Expr + 'a)\n                        -> Expr {\n  let result = walk_expr(expr, move |x| Ok(walker(x)));\n  extract_err(result)\n}\n\npub fn walk_exprs_in_decl<'a, E>(decl: &Decl, walker: impl FnMut(&Expr) -> Result<Expr, E> + 'a)\n                                 -> Result<Decl, E> {\n  let new_decl = match &decl.value {\n    DeclF::ConstDecl(d) => {\n      DeclF::ConstDecl(decl::ConstDecl {\n        visibility: d.visibility,\n        name: d.name.clone(),\n        value: walk_expr(&d.value, walker)?,\n      })\n    }\n    DeclF::FnDecl(d) => {\n      DeclF::FnDecl(decl::FnDecl {\n        visibility: d.visibility,\n        call_magic: d.call_magic.clone(),\n        name: d.name.clone(),\n        args: d.args.clone(),\n        body: walk_expr(&d.body, walker)?,\n      })\n    }\n    DeclF::MacroDecl(d) => {\n      DeclF::MacroDecl(decl::MacroDecl {\n        visibility: d.visibility,\n        name: d.name.clone(),\n        args: d.args.clone(),\n        body: walk_expr(&d.body, walker)?,\n      })\n    }\n    DeclF::SymbolMacroDecl(d) => {\n      DeclF::SymbolMacroDecl(decl::SymbolMacroDecl {\n        visibility: d.visibility,\n        name: d.name.clone(),\n        body: walk_expr(&d.body, walker)?,\n      })\n    }\n    DeclF::EnumDecl(d) => {\n      let mut walker = walker;\n      let clauses = d.clauses.iter().map(|(s, e)| {\n        Ok((s.clone(), e.as_ref().map(|e1| walk_expr(e1, |x| walker(x))).transpose()?))\n      }).collect::<Result<Vec<_>, _>>()?;\n      DeclF::EnumDecl(decl::EnumDecl {\n        visibility: d.visibility,\n        name: d.name.clone(),\n        clauses: clauses,\n      })\n    }\n    DeclF::DeclareDecl(d) => {\n      DeclF::DeclareDecl(d.clone())\n    }\n    DeclF::ClassDecl(d) => {\n      let mut walker = walker;\n      let mut walker = ExprWalker::new(|x| walker(x));\n      let constructor = d.constructor.as_ref().map(|c| {\n        let super_call = decl::SuperCall { call: walker.walk_exprs(&c.super_call.call)?, pos: c.super_call.pos };\n        Ok(decl::ConstructorDecl {\n          args: c.args.clone(),\n          super_call: super_call,\n          body: walker.walk_expr(&c.body)?,\n        })\n      }).transpose()?;\n      let decls = d.decls.iter().map(|d| {\n        // TODO Technically ClassConstDecl and ClassVarDecl contain\n        // expressions. We should walk those too.\n        let new_decl = match &d.value {\n          decl::ClassInnerDeclF::ClassSignalDecl(_) => d.value.clone(),\n          decl::ClassInnerDeclF::ClassConstDecl(_) => d.value.clone(),\n          decl::ClassInnerDeclF::ClassVarDecl(_) => d.value.clone(),\n          decl::ClassInnerDeclF::ClassFnDecl(inner) => {\n            decl::ClassInnerDeclF::ClassFnDecl(decl::ClassFnDecl {\n              is_static: inner.is_static,\n              is_nullargs: inner.is_nullargs,\n              name: inner.name.clone(),\n              args: inner.args.clone(),\n              body: walker.walk_expr(&inner.body)?,\n            })\n          }\n        };\n        Ok(decl::ClassInnerDecl { value: new_decl, pos: d.pos })\n      }).collect::<Result<Vec<_>, _>>()?;\n      DeclF::ClassDecl(decl::ClassDecl {\n        visibility: d.visibility,\n        name: d.name.clone(),\n        extends: d.extends.clone(),\n        main_class: d.main_class,\n        constructor: constructor,\n        decls: decls,\n      })\n    }\n  };\n  Ok(Decl::new(new_decl, decl.pos))\n}\n\npub fn walk_exprs_in_toplevel<'a, E>(decl: &decl::TopLevel, mut walker: impl FnMut(&Expr) -> Result<Expr, E> + 'a)\n                                     -> Result<decl::TopLevel, E> {\n  Ok(decl::TopLevel {\n    imports: decl.imports.clone(),\n    decls: decl.decls.iter().map(|d| walk_exprs_in_decl(d, |x| walker(x))).collect::<Result<Vec<_>, _>>()?,\n    minimalist_flag: decl.minimalist_flag,\n  })\n}\n\npub fn walk_exprs_in_toplevel_ok<'a>(decl: &decl::TopLevel, mut walker: impl FnMut(&Expr) -> Expr + 'a)\n                                     -> decl::TopLevel {\n  let result = walk_exprs_in_toplevel(decl, move |x| Ok(walker(x)));\n  extract_err(result)\n}\n\n// TODO Test me :)\n"
  },
  {
    "path": "src/optimize/ir/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod expr_walker;\n\nuse crate::ir::decl::TopLevel;\nuse crate::compile::error::GDError;\n\npub trait FileOptimization {\n  fn run_on_file(&self, file: &mut TopLevel) -> Result<(), GDError>;\n}\n"
  },
  {
    "path": "src/optimize/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod gdscript;\npub mod ir;\n"
  },
  {
    "path": "src/parser.lalrpop",
    "content": "// -*- Rust -*-\n\n// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse std::str::FromStr;\nuse crate::sxp::ast::AST;\nuse crate::sxp::string;\nuse crate::sxp::syntax;\nuse crate::pipeline::source::SourceOffset;\n\ngrammar;\n\nInt: i32 = <s:INT> => i32::from_str(s).unwrap();\nFloat: f32 = <s:FLOAT> => f32::from_str(s).unwrap();\nString: String = <s:STRING> => {\n  let t = &s[1..s.bytes().count()-1];\n  string::parse_escapes(t).unwrap()\n};\nSymbol: String = <s:SYMBOL> => String::from(s);\nNodePath: String = {\n  <s:PATH> => {\n    String::from(&s[1..])\n  },\n  <s:QUOTED_PATH> => {\n    let t = &s[2..s.bytes().count()-1];\n    string::parse_escapes(t).unwrap()\n  },\n};\n\nmatch {\n  r\"\\s*\" => {},\n  r\";[^\\n\\r]*[\\n\\r]?\" => {}, // Line comments (starting with semicolon)\n  r\"#\\|([^|]|\\|[^#])*\\|#\" => {}, // Block comments (nesting is not currently allowed)\n  \"#t\", \"#f\",\n  r\"[+-]?[0-9]+\" => INT,\n  r\"[+-]?[0-9]+((\\.[0-9]+)([eE][+-]?[0-9]+)?|(\\.[0-9]+)?([eE][+-]?[0-9]+))\" => FLOAT,\n  r#\"\"([^\\\\\"]|\\\\.)*\"\"# => STRING,\n  r\"\\$[A-Za-z0-9_~+=\\-\\\\!$%^&*<>/?]+\" => PATH,\n  r#\"\\$\"([^\\\\\"]|\\\\.)*\"\"# => QUOTED_PATH,\n  \"(\", \")\", \".\", \"[\", \"]\", \"'\", \"#'\", \"`\", \",\", \":\", \"V{\", \"{\", \"}\", \",.\", \"@\",\n} else {\n  // A symbol consists of the following, in order:\n  //\n  // 1. A starting character\n  //\n  // 2. An initial segment of zero or more subsequent characters.\n  //\n  // 3. A sequence of zero or more qualifiers, where a qualifier is a\n  // dot followed by one or more subsequent characters.\n  //\n  // The starting character can be an ASCII letter, underscore, tilde,\n  // plus, equal sign, minus, backslash, forward slash, exclamation\n  // mark, percent, caret, ampersand, star, less than, greater than,\n  // question mark, or any Unicode character in the categories L, Mn,\n  // Nl, No, S, Pc, Pd, Po.\n  //\n  // The subsequent characters can be any starting character, an ASCII\n  // number, or any Unicode character in the category N.\n  r\"[A-Za-z_~+=\\-\\\\!%^&*<>/?[\\p{L}\\p{Mn}\\p{No}\\p{Nl}\\p{S}\\p{Pc}\\p{Pd}\\p{Po}--\\p{Ascii}]][A-Za-z0-9_~+=\\-\\\\!$%^&*<>/?[\\p{L}\\p{Mn}\\p{N}\\p{S}\\p{Pc}\\p{Pd}\\p{Po}--\\p{Ascii}]]*(\\.[A-Za-z0-9_~+=\\-\\\\!$%^&*<>/?[\\p{L}\\p{Mn}\\p{N}\\p{S}\\p{Pc}\\p{Pd}\\p{Po}--\\p{Ascii}]]+)*\" => SYMBOL,\n}\n\npub AST: AST = {\n  <ast: LiteralOrNestedAST> => ast,\n  <pos:@L> \"'\" <body: AST> => syntax::quote(body, SourceOffset(pos)),\n  <pos:@L> \"#'\" <body: AST> => syntax::function(body, SourceOffset(pos)),\n  <pos:@L> \"`\" <body: AST> => syntax::quasiquote(body, SourceOffset(pos)),\n  <pos:@L> \",\" <body: AST> => syntax::unquote(body, SourceOffset(pos)),\n  <pos:@L> \",.\" <body: AST> => syntax::unquote_spliced(body, SourceOffset(pos)),\n}\n\npub SomeAST: AST = {\n  <pos:@L> <one: AST> => AST::cons(one, AST::nil(SourceOffset(pos)), SourceOffset(pos)),\n  <pos:@L> <first: AST> <rest: SomeAST> => AST::cons(first, rest, SourceOffset(pos)),\n}\n\n// This is here to fix an ambiguity with the grammar. Specifically, I\n// want ' a : b to parse as (quote (access-slot a b)), not\n// (access-slot (quote a) b). There is never a use case where the\n// latter would be helpful, as (quote a) has no GDScript-only slots on\n// it. The same applies to function and quasiquote. unquote is a bit\n// more nebulous, as there are use cases for either possible parse of\n// ,a:b, but I'm treating it the same as the other three for now.\npub LiteralOrNestedAST: AST = {\n  <pos:@L> \"#t\" => AST::from_value(true, SourceOffset(pos)),\n  <pos:@L> \"#f\" => AST::from_value(false, SourceOffset(pos)),\n  <pos:@L> <i: Int> => AST::from_value(i, SourceOffset(pos)),\n  <pos:@L> <f: Float> => AST::from_value(f, SourceOffset(pos)),\n  <pos:@L> <s: String> => AST::string(s, SourceOffset(pos)), // TODO Escaping\n  <pos:@L> <s: Symbol> => AST::symbol(s, SourceOffset(pos)),\n  <pos:@L> \"(\" \")\" => AST::nil(SourceOffset(pos)),\n  <pos:@L> \"(\" <car: AST> <cdr: Cdr> \")\" => AST::cons(car, cdr, SourceOffset(pos)),\n  <pos:@L> \"[\" <contents: ArrayContents> \"]\" => syntax::array(contents, SourceOffset(pos)),\n  <pos:@L> \"{\" <contents: DictContents> \"}\" => syntax::dict(contents, SourceOffset(pos)),\n  <pos:@L> \"V{\" <x: AST> <y: AST> \"}\" => syntax::vector2(x, y, SourceOffset(pos)),\n  <pos:@L> \"V{\" <x: AST> <y: AST> <z: AST> \"}\" => syntax::vector3(x, y, z, SourceOffset(pos)),\n  <pos:@L> <body: LiteralOrNestedAST> \":\" <rpos:@L> <name: Symbol> => syntax::access_slot(body, AST::symbol(name, SourceOffset(rpos)), SourceOffset(pos)),\n  <pos:@L> <body: LiteralOrNestedAST> \":\" <rpos:@L> <path: NodePath> => syntax::get_node_on(body, AST::string(path, SourceOffset(pos)), SourceOffset(pos)),\n  <pos:@L> <path: NodePath> => syntax::get_node_on(AST::symbol(\"self\", SourceOffset(pos)), AST::string(path, SourceOffset(pos)), SourceOffset(pos)),\n  <pos:@L> \"@\" <rpos:@L> <name: Symbol> => syntax::access_slot(AST::symbol(\"self\", SourceOffset(pos)), AST::symbol(name, SourceOffset(rpos)), SourceOffset(pos)),\n}\n\nCdr: AST = {\n  <pos:@L> => AST::nil(SourceOffset(pos)),\n  \".\" <cdr: AST> => cdr,\n  <pos:@L> <cadr: AST> <cddr: Cdr> => {\n    AST::cons(cadr, cddr, SourceOffset(pos))\n  }\n}\n\nArrayContents: AST = {\n  <pos:@L> => AST::nil(SourceOffset(pos)),\n  <pos:@L> <first: AST> <rest: ArrayContents> => {\n    AST::cons(first, rest, SourceOffset(pos))\n  }\n}\n\nDictContents: AST = {\n  <pos:@L> => AST::nil(SourceOffset(pos)),\n  <pos:@L> <first_key: AST> <mpos:@L> <first_value: AST> <rest: DictContents> => {\n    AST::cons(first_key, AST::cons(first_value, rest, SourceOffset(mpos)), SourceOffset(pos))\n  }\n}\n"
  },
  {
    "path": "src/parser_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n// These are the tests for parser.lalrpop\n\n#[cfg(test)]\nmod tests {\n  use crate::AST_PARSER;\n  use crate::sxp::ast::{AST, ASTF};\n  use crate::pipeline::source::SourceOffset;\n\n  fn so(x: usize) -> SourceOffset {\n    // I'm tired of writing it >.<\n    SourceOffset(x)\n  }\n\n  #[test]\n  fn parser_simple() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"12\").unwrap(), AST::new(ASTF::int(12), so(0)));\n    assert_eq!(p.parse(\"12.0\").unwrap(), AST::new(ASTF::float(12.0), so(0)));\n    assert_eq!(p.parse(\"abc\").unwrap(), AST::symbol(\"abc\", so(0)));\n    assert_eq!(p.parse(\"abc.def\").unwrap(), AST::symbol(\"abc.def\", so(0)));\n    assert_eq!(p.parse(\"\\\"abc\\\"\").unwrap(), AST::string(\"abc\", so(0)));\n  }\n\n  #[test]\n  fn parser_simple_unicode() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"αβγ\").unwrap(), AST::symbol(\"αβγ\", so(0))); // Category Ll\n    assert_eq!(p.parse(\"ΓΔ.ῼῼ\").unwrap(), AST::symbol(\"ΓΔ.ῼῼ\", so(0))); // Categories Lu and Lt\n    assert_eq!(p.parse(\"aʳ\").unwrap(), AST::symbol(\"aʳ\", so(0))); // Category Lm\n    assert_eq!(p.parse(\"aª\").unwrap(), AST::symbol(\"aª\", so(0))); // Category Lo\n    assert_eq!(p.parse(\"a͚\").unwrap(), AST::symbol(\"a͚\", so(0))); // Category Mn\n    assert_eq!(p.parse(\"Ⅸ³\").unwrap(), AST::symbol(\"Ⅸ³\", so(0))); // Categories Nl and No\n    assert_eq!(p.parse(\"£3÷£2\").unwrap(), AST::symbol(\"£3÷£2\", so(0))); // Categories Sm and Sc\n    assert_eq!(p.parse(\"©©©˄\").unwrap(), AST::symbol(\"©©©˄\", so(0))); // Categories Sk and So\n    assert_eq!(p.parse(\"⁀־־٭\").unwrap(), AST::symbol(\"⁀־־٭\", so(0))); // Categories Pc, Pd, and Po\n    assert_eq!(p.parse(\"value༣༣༣\").unwrap(), AST::symbol(\"value༣༣༣\", so(0))); // Category Nd (not allowed at beginning)\n  }\n\n  #[test]\n  fn parser_string() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"\\\"abcdef\\\"\").unwrap(), AST::string(\"abcdef\", so(0)));\n    assert_eq!(p.parse(r#\"\"abc\\\"def\"\"#).unwrap(), AST::string(\"abc\\\"def\", so(0)));\n    assert_eq!(p.parse(r#\"\"abc\\\\def\\\\\"\"#).unwrap(), AST::string(\"abc\\\\def\\\\\", so(0)));\n  }\n\n  #[test]\n  fn parser_list() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"()\").unwrap(), AST::nil(so(0)));\n    assert_eq!(p.parse(\"(1)\").unwrap(), AST::cons(AST::int(1, so(1)), AST::nil(so(2)), so(0)));\n    assert_eq!(p.parse(\"(1 . 2)\").unwrap(), AST::cons(AST::int(1, so(1)), AST::int(2, so(5)), so(0)));\n    assert_eq!(p.parse(\"(1 2)\").unwrap(), AST::cons(AST::int(1, so(1)), AST::cons(AST::int(2, so(3)), AST::nil(so(4)), so(3)), so(0)));\n  }\n\n  #[test]\n  fn parser_quoting() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"'a\").unwrap(), AST::list(vec!(AST::symbol(\"quote\", so(0)), AST::symbol(\"a\", so(1))), so(0)));\n    assert_eq!(p.parse(\"`a\").unwrap(), AST::list(vec!(AST::symbol(\"quasiquote\", so(0)), AST::symbol(\"a\", so(1))), so(0)));\n    assert_eq!(p.parse(\",a\").unwrap(), AST::list(vec!(AST::symbol(\"unquote\", so(0)), AST::symbol(\"a\", so(1))), so(0)));\n  }\n\n  #[test]\n  fn parser_colon() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"a:b\").unwrap(), AST::list(vec!(AST::symbol(\"access-slot\", so(0)), AST::symbol(\"a\", so(0)), AST::symbol(\"b\", so(2))), so(0)));\n    assert_eq!(p.parse(\"(1 . 2):b\").unwrap(), AST::list(vec!(AST::symbol(\"access-slot\", so(0)), AST::cons(AST::int(1, so(1)), AST::int(2, so(5)), so(0)), AST::symbol(\"b\", so(8))), so(0)));\n    assert_eq!(p.parse(\"'a:b\").unwrap(), AST::list(vec!(AST::symbol(\"quote\", so(0)), AST::list(vec!(AST::symbol(\"access-slot\", so(1)), AST::symbol(\"a\", so(1)), AST::symbol(\"b\", so(3))), so(1))), so(0)));\n    assert_eq!(p.parse(\"a:b:c\").unwrap(), AST::list(vec!(AST::symbol(\"access-slot\", so(0)), AST::list(vec!(AST::symbol(\"access-slot\", so(0)), AST::symbol(\"a\", so(0)), AST::symbol(\"b\", so(2))), so(0)), AST::symbol(\"c\", so(4))), so(0)));\n  }\n\n  #[test]\n  fn parser_at_self() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"@b\").unwrap(), AST::list(vec!(AST::symbol(\"access-slot\", so(0)), AST::symbol(\"self\", so(0)), AST::symbol(\"b\", so(1))), so(0)));\n    assert_eq!(p.parse(\"@b:c\").unwrap(), AST::list(vec!(AST::symbol(\"access-slot\", so(0)), AST::list(vec!(AST::symbol(\"access-slot\", so(0)), AST::symbol(\"self\", so(0)), AST::symbol(\"b\", so(1))), so(0)), AST::symbol(\"c\", so(3))), so(0)));\n  }\n\n  #[test]\n  fn parser_comments() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"\\\"abcdef\\\" ;; test comment\").unwrap(), AST::string(\"abcdef\", so(0)));\n    assert_eq!(p.parse(\"\\\"abc ;; def\\\"\").unwrap(), AST::string(\"abc ;; def\", so(0))); // Note: Not a comment\n    assert_eq!(p.parse(\"(a ;; b \\n\\n\\n c)\").unwrap(), AST::cons(AST::symbol(\"a\", so(1)), AST::cons(AST::symbol(\"c\", so(12)), AST::nil(so(13)), so(12)), so(0)));\n    assert_eq!(p.parse(\"\\\"abcdef\\\" #| test comment |#\").unwrap(), AST::string(\"abcdef\", so(0)));\n    assert_eq!(p.parse(\"\\\"abc #| |# def\\\"\").unwrap(), AST::string(\"abc #| |# def\", so(0))); // Note: Not a comment\n    assert_eq!(p.parse(\"(a #| b |# c)\").unwrap(), AST::cons(AST::symbol(\"a\", so(1)), AST::cons(AST::symbol(\"c\", so(11)), AST::nil(so(12)), so(11)), so(0)));\n  }\n\n  #[test]\n  fn parser_array() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"[]\").unwrap(), AST::list(vec!(AST::symbol(\"array\", so(0))), so(1)));\n    assert_eq!(p.parse(\"[1]\").unwrap(), AST::list(vec!(AST::symbol(\"array\", so(0)), AST::int(1, so(1))), so(2)));\n    assert_eq!(p.parse(\"[1 2]\").unwrap(), AST::list(vec!(AST::symbol(\"array\", so(0)), AST::int(1, so(1)), AST::int(2, so(3))), so(4)));\n  }\n\n  #[test]\n  fn parser_dict() {\n    let p = &AST_PARSER;\n    assert_eq!(p.parse(\"{}\").unwrap(), AST::list(vec!(AST::symbol(\"dict\", so(0))), so(1)));\n    assert_eq!(p.parse(\"{1 2}\").unwrap(), AST::list(vec!(AST::symbol(\"dict\", so(0)), AST::int(1, so(1)), AST::int(2, so(3))), so(4)));\n    assert_eq!(p.parse(\"{1 2 3 4}\").unwrap(), AST::list(vec!(AST::symbol(\"dict\", so(0)), AST::int(1, so(1)), AST::int(2, so(3)), AST::int(3, so(5)), AST::int(4, so(7))), so(8)));\n  }\n\n  #[test]\n  fn parser_failures() {\n    let p = &AST_PARSER;\n    assert!(p.parse(\"(\").is_err());\n    assert!(p.parse(\"\\\"foo\\\\\\\"\").is_err());\n    assert!(p.parse(\")\").is_err());\n    assert!(p.parse(\"(()\").is_err());\n    assert!(p.parse(\"1.\").is_err());\n    assert!(p.parse(\"()(\").is_err());\n    assert!(p.parse(\"(1 . )\").is_err());\n    assert!(p.parse(\"a:\").is_err());\n    assert!(p.parse(\":b\").is_err());\n    assert!(p.parse(\"a:(1 . 2)\").is_err());\n    assert!(p.parse(\"abc.\").is_err());\n    assert!(p.parse(\".def\").is_err());\n    assert!(p.parse(\"[a\").is_err());\n    assert!(p.parse(\"{a\").is_err());\n    assert!(p.parse(\"{a}\").is_err()); // Not an even number of entries\n    assert!(p.parse(\"༣value\").is_err()); // Category Nd (not allowed at beginning)\n    assert!(p.parse(\"valueऻ\").is_err()); // Category Mc\n    assert!(p.parse(\"value⃝value\").is_err()); // Category Me\n    assert!(p.parse(\"value❰\").is_err()); // Category Ps\n    assert!(p.parse(\"value❱\").is_err()); // Category Pe\n    assert!(p.parse(\"value«\").is_err()); // Category Pi\n    assert!(p.parse(\"value»\").is_err()); // Category Pf\n  }\n\n}\n"
  },
  {
    "path": "src/pipeline/can_load.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`CanLoad`] trait.\n\nuse crate::runner::path::RPathBuf;\nuse super::Pipeline;\n\n/// Trait for things that have a reasonable implementation of \"load\n/// the current file\" as an expression. The typical example is\n/// [`Pipeline`], and this trait is mainly provided as a way for the\n/// type checker to say \"I only need the load expression\" as opposed\n/// to requiring the whole `Pipeline` in general.\n///\n/// That is, there are many functions which need to take a `Pipeline`\n/// but only do so in order to get the current file name. Those\n/// functions can opt to take `impl CanLoad` instead, to demonstrate\n/// to the viewer and the type checker that they only need a limited\n/// part of the `Pipeline` functionality.\npub trait CanLoad {\n\n  /// Gets the current filename, or `None` if there is no current\n  /// file.\n  fn current_filename_option(&self) -> Option<RPathBuf>;\n\n  /// As\n  /// [`current_filename_option`](CanLoad::current_filename_option),\n  /// but panics in case of `None`. Generally, when we call the\n  /// functions on this trait, we fully expect our pipeline (or\n  /// equivalent object) to be in a fully-initialized state. If this\n  /// precondition is violated, we should fail fast.\n  ///\n  /// # Panics\n  ///\n  /// Panics if `current_filename_option` returns `None`.\n  fn current_filename(&self) -> RPathBuf {\n    self.current_filename_option().expect(\"Could not identify current filename\")\n  }\n\n}\n\nimpl CanLoad for Pipeline {\n  fn current_filename_option(&self) -> Option<RPathBuf> {\n    let mut filename = self.currently_loading_file()?.to_owned();\n    filename.path_mut().set_extension(\"gd\");\n    Some(filename)\n  }\n}\n"
  },
  {
    "path": "src/pipeline/config.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Project-specific configuration data.\n\nuse crate::runner::version::VersionInfo;\n\nuse std::path::PathBuf;\n\n/// This structure describes any project-specific settings and\n/// information that is necessary throughout the project compilation\n/// process.\n#[derive(Debug, Clone)]\npub struct ProjectConfig {\n  /// The path to the root directory of the Godot project. If we're\n  /// compiling a full project, this directory should contain a\n  /// `project.godot`. If we're compiling an individual file, this\n  /// directory should contain that file.\n  pub root_directory: PathBuf,\n  /// Whether or not optimizations are turned on. Generally, this\n  /// setting defaults to `true`, except for integration testing,\n  /// where all optimizations are turned off.\n  pub optimizations: bool,\n  /// The Godot version being used to compile this project.\n  pub godot_version: VersionInfo,\n}\n"
  },
  {
    "path": "src/pipeline/error.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::compile::error::GDError;\nuse crate::pipeline::source::{SourceOffset, Sourced};\nuse crate::sxp::dotted::TryFromDottedExprError;\nuse crate::ir::loops::error::LoopPrimitiveError;\nuse crate::ir::arglist::error::ArgListParseError;\nuse crate::ir::modifier::{ParseError as ModifierParseError};\nuse crate::ir::decl::DuplicateMainClassError;\nuse crate::ir::scope::error::ScopeError;\nuse crate::ir::depends::DependencyError;\n\nuse lalrpop_util::ParseError;\n\nuse std::io;\nuse std::fmt;\nuse std::error::Error;\n\n#[derive(Debug)]\npub enum PError {\n  ParseError(ParseError<SourceOffset, String, String>),\n  IOError(IOError),\n  GDError(GDError),\n}\n\n/// An [`io::Error`] which has a [`SourceOffset`] attached to it.\n#[derive(Debug)]\npub struct IOError {\n  pub error: io::Error,\n  pub pos: SourceOffset,\n}\n\n/// [`PError`] is [`Sourced`] in a somewhat trivial way. It's not a\n/// recursive data type, so it doesn't \"contain\" a separate value in\n/// the same sense as other implementors like [`GDError`] (which\n/// contains [`GDErrorF`](crate::compile::error::GDErrorF)). But a\n/// `PError` always has its `SourceOffset` nonetheless.\nimpl Sourced for PError {\n  type Item = PError;\n\n  fn get_source(&self) -> SourceOffset {\n    match self {\n      PError::ParseError(err) => get_source_from_parse_error(err),\n      PError::IOError(err) => err.pos,\n      PError::GDError(err) => err.get_source(),\n    }\n  }\n\n  fn get_value(&self) -> &PError {\n    self\n  }\n\n}\n\nimpl Error for PError {\n\n  fn source(&self) -> Option<&(dyn Error + 'static)> {\n    match self {\n      PError::ParseError(err) => Some(err),\n      PError::IOError(err) => Some(&err.error),\n      PError::GDError(err) => Some(err),\n    }\n  }\n\n}\n\nfn get_source_from_parse_error<T, E>(err: &ParseError<SourceOffset, T, E>) -> SourceOffset {\n  match err {\n    ParseError::InvalidToken { location } => *location,\n    ParseError::UnrecognizedEOF { location, .. } => *location,\n    ParseError::UnrecognizedToken { token: (location, _, _), .. } => *location,\n    ParseError::ExtraToken { token: (location, _, _), .. } => *location,\n    ParseError::User { .. } => SourceOffset::default(), // TODO There's no error location information for this one. Can we get the user error type E to contain that information somehow?\n  }\n}\n\nimpl fmt::Display for PError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      PError::ParseError(err) => write!(f, \"{}\", err),\n      PError::IOError(err) => write!(f, \"{}\", err),\n      PError::GDError(err) => write!(f, \"{}\", err),\n    }\n  }\n}\n\nimpl fmt::Display for IOError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"{}\", self.error)\n  }\n}\n\nimpl IOError {\n\n  pub fn new(error: io::Error, pos: SourceOffset) -> IOError {\n    IOError { error, pos }\n  }\n\n}\n\nimpl From<IOError> for io::Error {\n  fn from(err: IOError) -> io::Error {\n    err.error\n  }\n}\n\nfn _impl_partial_eq_warning(err: PError) {\n  // If this function produces exhaustive match warnings, then\n  // PartialEq is missing a case.\n  match err {\n    PError::ParseError(_) => (),\n    PError::IOError(_) => (),\n    PError::GDError(_) => (),\n  }\n}\n\nimpl PartialEq<PError> for PError {\n  fn eq(&self, other: &PError) -> bool {\n    match (self, other) {\n      (PError::ParseError(a), PError::ParseError(b)) => a == b,\n      (PError::IOError(_), PError::IOError(_)) => true, // Best we can do\n      (PError::GDError(a), PError::GDError(b)) => a == b,\n      (_, _) => false,\n    }\n  }\n}\n\nimpl Eq for PError {}\n\nimpl<L, T : fmt::Display, E : fmt::Display> From<ParseError<L, T, E>> for PError\nwhere SourceOffset : From<L> {\n  fn from(e: ParseError<L, T, E>) -> PError {\n    PError::ParseError(e.map_location(SourceOffset::from).map_token(|x| x.to_string()).map_error(|x| x.to_string()))\n  }\n}\n\nimpl From<IOError> for PError {\n  fn from(e: IOError) -> PError {\n    PError::IOError(e)\n  }\n}\n\nimpl From<GDError> for PError {\n  fn from(e: GDError) -> PError {\n    PError::GDError(e)\n  }\n}\n\nimpl From<TryFromDottedExprError> for PError {\n  fn from(e: TryFromDottedExprError) -> PError {\n    PError::from(GDError::from(e))\n  }\n}\n\nimpl From<ArgListParseError> for PError {\n  fn from(e: ArgListParseError) -> PError {\n    PError::from(GDError::from(e))\n  }\n}\n\nimpl From<ModifierParseError> for PError {\n  fn from(e: ModifierParseError) -> PError {\n    PError::from(GDError::from(e))\n  }\n}\n\nimpl From<DuplicateMainClassError> for PError {\n  fn from(e: DuplicateMainClassError) -> PError {\n    PError::from(GDError::from(e))\n  }\n}\n\nimpl<NS> From<ScopeError<NS>> for PError\nwhere GDError: From<ScopeError<NS>> {\n  fn from(e: ScopeError<NS>) -> PError {\n    PError::from(GDError::from(e))\n  }\n}\n\nimpl From<LoopPrimitiveError> for PError {\n  fn from(e: LoopPrimitiveError) -> PError {\n    PError::from(GDError::from(e))\n  }\n}\n\nimpl From<DependencyError> for PError {\n  fn from(e: DependencyError) -> PError {\n    PError::from(GDError::from(e))\n  }\n}\n"
  },
  {
    "path": "src/pipeline/loader.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n#![deprecated]\n\nuse super::TranslationUnit;\n\nuse std::path::Path;\n\npub struct NullFileLoader;\n\npub struct NullFileLoaderError;\n\npub trait FileLoader {\n  type Error;\n\n  fn load_file<'a, 'b, P>(&'a mut self, input_path: &'b P)\n                          -> Result<&'a TranslationUnit, Self::Error>\n  where P : AsRef<Path> + ?Sized;\n\n  fn get_file<'a, 'b, P>(&'a self, input_path: &'b P) -> Option<&'a TranslationUnit>\n  where P :AsRef<Path> + ?Sized;\n\n}\n\n// Minimal implementation; always results in an error.\nimpl FileLoader for NullFileLoader {\n  type Error = NullFileLoaderError;\n\n  fn load_file<'a, 'b, P>(&'a mut self, _input_path: &'b P)\n                          -> Result<&'a TranslationUnit, Self::Error>\n  where P : AsRef<Path> + ?Sized {\n    Err(NullFileLoaderError)\n  }\n\n  fn get_file<'a, 'b, P>(&'a self, _input_path: &'b P) -> Option<&'a TranslationUnit>\n  where P :AsRef<Path> + ?Sized {\n    None\n  }\n\n}\n"
  },
  {
    "path": "src/pipeline/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod error;\npub mod translation_unit;\npub mod stdlib_unit;\npub mod config;\npub mod loader;\npub mod resolver;\npub mod can_load;\npub mod source;\n\nuse translation_unit::TranslationUnit;\nuse config::ProjectConfig;\nuse error::{PError, IOError};\nuse source::SourceOffset;\nuse resolver::{NameResolver, DefaultNameResolver};\nuse crate::SOME_AST_PARSER;\nuse crate::ir;\nuse crate::ir::main_function::DisallowMainFunctionHandler;\nuse crate::compile::Compiler;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::compile::body::builder::CodeBuilder;\nuse crate::compile::body::class_scope::OutsideOfClass;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::compile::preload_resolver::{DefaultPreloadResolver, LookupPreloadResolver};\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::gdscript::library;\nuse crate::gdscript::library::gdnative::NativeClasses;\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::util;\nuse crate::util::lazy::Lazy;\nuse crate::runner::macro_server::named_file_server::NamedFileServer;\nuse crate::runner::path::{RPathBuf, PathSrc};\nuse crate::optimize::gdscript::run_standard_passes;\n\nuse tempfile::Builder;\n\nuse std::io::Write;\nuse std::path::{Path, PathBuf};\nuse std::collections::HashMap;\nuse std::mem;\n\npub struct Pipeline {\n  config: ProjectConfig,\n  resolver: Box<dyn NameResolver>,\n  known_files: HashMap<PathBuf, KnownFile>,\n  known_files_paths: HashMap<PathBuf, PathBuf>,\n  server: NamedFileServer,\n  native_classes: Lazy<NativeClasses, fn() -> NativeClasses>,\n  current_file_path: Option<RPathBuf>,\n}\n\n/// The state of a file known to the [`Pipeline`].\nenum KnownFile {\n  /// A partially loaded file. An attempt to access a file in this\n  /// state results in a [`GDError::CyclicImport`] error.\n  PartiallyLoaded,\n  /// A fully loaded file, accessible to other files for import.\n  FullyLoaded(Box<TranslationUnit>),\n}\n\nimpl Pipeline {\n\n  pub fn with_resolver(config: ProjectConfig, resolver: Box<dyn NameResolver>) -> Pipeline {\n    Pipeline {\n      config: config,\n      resolver: resolver,\n      known_files: HashMap::new(),\n      known_files_paths: HashMap::new(),\n      server: NamedFileServer::new(),\n      native_classes: Lazy::new(Pipeline::get_native_classes_impl),\n      current_file_path: None,\n    }\n  }\n\n  pub fn new(config: ProjectConfig) -> Pipeline {\n    Pipeline::with_resolver(config, Box::new(DefaultNameResolver))\n  }\n\n  fn get_native_classes_impl() -> NativeClasses {\n    let result = NativeClasses::get_api_from_godot();\n    result.expect(\"Could not read GDNative API from Godot binary\")\n  }\n\n  pub fn compile_code<P : AsRef<Path> + ?Sized>(&mut self, filename: &P, input: &str)\n                                                -> Result<TranslationUnit, PError> {\n\n    let file_path = filename.as_ref().strip_prefix(&self.config.root_directory).expect(\"Non-local file load detected\").to_owned();\n    let mut old_file_path = Some(RPathBuf::new(PathSrc::Res, file_path).expect(\"Non-local file load detected\"));\n    mem::swap(&mut old_file_path, &mut self.current_file_path);\n\n    let ast = SOME_AST_PARSER.parse(input)?;\n\n    let (ir, macros) = ir::compile_and_check(self, &ast, &DisallowMainFunctionHandler)?;\n\n    let mut compiler = Compiler::new(FreshNameGenerator::new(ast.all_symbols()), Box::new(DefaultPreloadResolver), ir.minimalist_flag);\n\n\n    let mut table = SymbolTable::new();\n    library::bind_builtins(&mut table, ir.minimalist_flag);\n\n    let mut builder = CodeBuilder::new(ClassExtends::SimpleIdentifier(\"Node\".to_owned()));\n    compiler.frame(self, &mut builder, &mut table, &mut OutsideOfClass).compile_toplevel(&ir)?;\n    let mut result = builder.build();\n    if self.config.optimizations {\n      run_standard_passes(&mut result)?;\n    }\n\n    mem::swap(&mut old_file_path, &mut self.current_file_path);\n\n    let exports = ir::export::get_export_list(&ir.decls);\n    Ok(TranslationUnit::new(filename.as_ref().to_owned(), table, ir, result, exports, macros))\n  }\n\n  fn load_file_unconditionally<'a, 'b, P>(&'a mut self, input_path: &'b P)\n                                          -> Result<&'a TranslationUnit, PError>\n  where P : AsRef<Path> + ?Sized {\n    let input_path = input_path.as_ref();\n    let output_path = input_to_output_filename(input_path);\n    let mut input_file = self.resolver.resolve_input_path(input_path).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n    let mut output_file = self.resolver.resolve_output_path(&output_path).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n\n    self.known_files.insert(input_path.to_owned(), KnownFile::PartiallyLoaded);\n\n    let contents = util::read_to_end(&mut input_file).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n    let unit = self.compile_code(&input_path, &contents)?;\n    write!(output_file, \"{}\", unit.gdscript.to_gd()).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n\n    let file_path = input_path.strip_prefix(&self.config.root_directory).expect(\"Non-local file load detected\").to_owned();\n    let mut old_file_path = Some(RPathBuf::new(PathSrc::Res, file_path).expect(\"Non-local file load detected\"));\n    mem::swap(&mut old_file_path, &mut self.current_file_path);\n\n    // Also output to a temporary file\n    let mut tmpfile = Builder::new()\n      .prefix(\"__gdlisp_file\")\n      .suffix(\".gd\")\n      .rand_bytes(5)\n      .tempfile()\n      .map_err(|err| IOError::new(err, SourceOffset(0)))?;\n\n    let mut input_path_store_name = input_path.strip_prefix(&self.config.root_directory).expect(\"Non-local file load detected\").to_owned();\n    input_path_store_name.set_extension(\"gd\");\n    self.known_files_paths.insert(input_path_store_name, tmpfile.path().to_owned());\n\n    let ast = SOME_AST_PARSER.parse(&contents)?;\n    let resolver = self.make_preload_resolver();\n    let mut compiler = Compiler::new(FreshNameGenerator::new(ast.all_symbols()), Box::new(resolver), false);\n    let mut table = SymbolTable::new();\n\n    library::bind_builtins(&mut table, unit.ir.minimalist_flag);\n\n    let mut builder = CodeBuilder::new(ClassExtends::SimpleIdentifier(\"Node\".to_owned()));\n    compiler.frame(self, &mut builder, &mut table, &mut OutsideOfClass).compile_toplevel(&unit.ir)?;\n    let mut tmpresult = builder.build();\n    if self.config.optimizations {\n      run_standard_passes(&mut tmpresult)?;\n    }\n\n    if !unit.ir.minimalist_flag {\n      write!(tmpfile, \"{}\", tmpresult.to_gd()).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n      tmpfile.flush().map_err(|err| IOError::new(err, SourceOffset(0)))?;\n      self.server.stand_up_file(tmpfile).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n    }\n\n    mem::swap(&mut old_file_path, &mut self.current_file_path);\n\n    self.known_files.insert(input_path.to_owned(), KnownFile::FullyLoaded(Box::new(unit)));\n    Ok(self.get_loaded_file(input_path, SourceOffset(0)).expect(\"Path not present in load_file\"))\n  }\n\n  pub fn get_loaded_file<'a, 'b, P>(&'a self, input_path: &'b P, pos: SourceOffset)\n                                    -> Result<&'a TranslationUnit, PError>\n  where P : AsRef<Path> + ?Sized {\n    match self.known_files.get(input_path.as_ref()) {\n      None => Err(PError::from(GDError::new(GDErrorF::NoSuchFile(input_path.as_ref().to_string_lossy().into_owned()), pos))),\n      Some(KnownFile::PartiallyLoaded) => Err(PError::from(GDError::new(GDErrorF::CyclicImport(input_path.as_ref().to_string_lossy().into_owned()), pos))),\n      Some(KnownFile::FullyLoaded(unit)) => Ok(unit),\n    }\n  }\n\n  fn to_absolute_path<P>(&self, input_path: &P) -> PathBuf\n  where P : AsRef<Path> + ?Sized {\n    let input_path = input_path.as_ref();\n    if input_path.is_absolute() {\n      input_path.to_owned()\n    } else {\n      self.config.root_directory.join(input_path)\n    }\n  }\n\n  pub fn load_file<'a, 'b, P>(&'a mut self, input_path: &'b P, pos: SourceOffset)\n                              -> Result<&'a TranslationUnit, PError>\n  where P : AsRef<Path> + ?Sized {\n    let input_path = self.to_absolute_path(input_path);\n    // if-let here causes Rust to complain due to lifetime rules, so\n    // we use contains_key instead.\n    if self.known_files.contains_key(&input_path) {\n      self.get_loaded_file(&input_path, pos)\n    } else {\n      self.load_file_unconditionally(&input_path)\n    }\n  }\n\n  // TODO This is redundant with get_loaded_file and is only here for\n  // legacy reasons.\n  pub fn get_file<'a, 'b, P>(&'a self, input_path: &'b P, pos: SourceOffset) -> Result<&'a TranslationUnit, PError>\n  where P :AsRef<Path> + ?Sized {\n    self.get_loaded_file(input_path, pos)\n  }\n\n  pub fn get_server(&self) -> &NamedFileServer {\n    &self.server\n  }\n\n  pub fn get_server_mut(&mut self) -> &mut NamedFileServer {\n    &mut self.server\n  }\n\n  pub fn make_preload_resolver(&self) -> LookupPreloadResolver {\n    LookupPreloadResolver(self.known_files_paths.clone())\n  }\n\n  pub fn currently_loading_file(&self) -> Option<&RPathBuf> {\n    self.current_file_path.as_ref()\n  }\n\n  // This is done automatically if you use any of the built-in compile\n  // functions above. You can do it yourself manually with this\n  // function for testing purposes.\n  pub fn set_currently_loading_file(&mut self, path: RPathBuf) {\n    self.current_file_path = Some(path);\n  }\n\n  pub fn config(&self) -> &ProjectConfig {\n    &self.config\n  }\n\n  pub fn get_native_classes(&mut self) -> &NativeClasses {\n    self.native_classes.force_mut()\n  }\n\n}\n\npub fn input_to_output_filename<P : AsRef<Path> + ?Sized>(input: &P) -> PathBuf {\n  let mut output_path = input.as_ref().to_owned();\n  let renamed = output_path.set_extension(\"gd\");\n  assert!(renamed); // Make sure the path was actually to a file\n  output_path\n}\n"
  },
  {
    "path": "src/pipeline/resolver.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Defines the [`NameResolver`] trait and a suitable [default\n//! implementation](DefaultNameResolver).\n\nuse std::path::Path;\nuse std::io::{self, Read, BufReader, Write, BufWriter};\nuse std::fs::File;\n\n/// A [`NameResolver`] which simply opens the file it is given, for\n/// reading or writing as requested.\n///\n/// `DefaultNameResolver` should be good for 99% of actual use cases.\n/// The primary use case for having a non-standard name resolver is\n/// during testing, when we want to mock a virtual filesystem rather\n/// than importing from the real one where feasible.\n#[derive(Clone, Debug)]\npub struct DefaultNameResolver;\n\n/// A [`NameResolver`] which always fails. The resolver methods will\n/// unconditionally panic if called on this object.\n///\n/// This is mainly provided for testing purposes, to assert that a\n/// given test case should *not* invoke the name resolver.\n#[derive(Clone, Debug)]\npub struct PanickingNameResolver;\n\n/// A `NameResolver` specifies to a [`Pipeline`](super::Pipeline) how\n/// it should read and write data before and after the compilation\n/// process.\n///\n/// A `Pipeline` is always given an input pathname of a GDLisp source\n/// file to load. From that pathname, a predetermined process\n/// translates the name into the output pathname for GDScript code.\n/// Then the pipeline's `NameResolver` is queried, to actually open\n/// those files.\n///\n/// For most practical use cases, [`DefaultNameResolver`] is the\n/// appropriate name resolver. `DefaultNameResolver` simply opens the\n/// files for input and output respectively, without performing any\n/// additional transformations. This is provided as a trait to allow\n/// dependency injection, primarily for testing purposes. A testing\n/// framework can replace the name resolver in order to load files\n/// from a virtual testing environment, rather than from the actual\n/// hard drive.\npub trait NameResolver {\n  /// Open the file with the given filename for reading. An\n  /// [`io::Error`] should be signaled if the file does not exist.\n  fn resolve_input_path(&self, filename: &Path) -> io::Result<Box<dyn Read>>;\n  /// Open the file with the given filename for writing, clearing the\n  /// file if it already exists.\n  fn resolve_output_path(&self, filename: &Path) -> io::Result<Box<dyn Write>>;\n}\n\nimpl NameResolver for DefaultNameResolver {\n\n  fn resolve_input_path(&self, filename: &Path) -> io::Result<Box<dyn Read>> {\n    let input_file = BufReader::new(File::open(filename)?);\n    Ok(Box::new(input_file))\n  }\n\n  fn resolve_output_path(&self, filename: &Path) -> io::Result<Box<dyn Write>> {\n    let output_file = BufWriter::new(File::create(filename)?);\n    Ok(Box::new(output_file))\n  }\n\n}\n\nimpl NameResolver for PanickingNameResolver {\n\n  fn resolve_input_path(&self, _filename: &Path) -> io::Result<Box<dyn Read>> {\n    panic!(\"PanickingNameResolver.resolve_input_path\")\n  }\n\n  fn resolve_output_path(&self, _filename: &Path) -> io::Result<Box<dyn Write>> {\n    panic!(\"PanickingNameResolver.resolve_output_path\")\n  }\n\n}\n"
  },
  {
    "path": "src/pipeline/source.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Wrapper struct and helpers for keeping track of positions in the\n//! source code.\n//!\n//! All source positions always refer to the original GDLisp source\n//! file, never to the position in an intermediate representation.\n\nuse crate::util;\n\nuse std::fmt;\nuse std::io::{self, BufReader};\nuse std::path::Path;\nuse std::fs::File;\n\n/// A `SourceOffset` is really just a [`usize`] representing a\n/// 0-indexed byte offset into the original file.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]\n#[repr(transparent)]\npub struct SourceOffset(pub usize);\n\n/// A `SourcePos` indicates a line and column number in a file, where\n/// all offsets are indicated in *characters*. Source position lines\n/// and columns are always 1-indexed.\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct SourcePos {\n  pub line: usize,\n  pub column: usize,\n}\n\n/// A `SourcedValue` is a view of a [`Sourced`] instance, with its\n/// [`SourceOffset`] converted to a [`SourcePos`] by looking at the\n/// original material from which the offset was constructed.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct SourcedValue<'a, T: Sourced> {\n  source: &'a T,\n  error_pos: SourcePos,\n}\n\n/// Trait for types which contain [source offset](SourceOffset)\n/// information.\n///\n/// Several data structures in GDLisp take the following form. There\n/// is one type `FooF` which represents some abstract syntax tree.\n/// `FooF` values contain `Foo` values, while `Foo` is a struct that\n/// is roughly isomorphic to `(FooF, SourceOffset)`. For any such\n/// `Foo` type, this trait applies and provides the mechanism to get\n/// the underlying data structure and the source offset.\npub trait Sourced {\n  /// The type of value underlying this data structure. In the `Foo`\n  /// example in the trait description, this would be `FooF`.\n  type Item;\n\n  /// Gets the [`SourceOffset`] for the position `self` starts at.\n  fn get_source(&self) -> SourceOffset;\n\n  /// Gets the value underlying `self`.\n  fn get_value(&self) -> &Self::Item;\n\n}\n\nimpl<'a, T: Sourced> SourcedValue<'a, T> {\n\n  /// Constructs a `SourcedValue` view of `value`, assuming\n  /// `value.get_source()` is a reference to a position in the string\n  /// `source_code`.\n  pub fn new(value: &'a T, source_code: &str) -> SourcedValue<'a, T> {\n    let error_offset = value.get_source();\n    let error_pos = SourcePos::from_offset(error_offset, source_code);\n    SourcedValue { source: value, error_pos }\n  }\n\n  /// Constructs a `SourcedValue` view of `value`, assuming\n  /// `value.get_source()` is a reference to a position in the file\n  /// indicated. Returns an IO error in case of error reading the\n  /// file.\n  pub fn from_file<P>(value: &'a T, source_file: &P) -> io::Result<SourcedValue<'a, T>>\n  where P: AsRef<Path> + ?Sized {\n    let mut input_file = BufReader::new(File::open(source_file)?);\n    let file_contents = util::read_to_end(&mut input_file)?;\n    Ok(SourcedValue::new(value, &file_contents))\n  }\n\n  pub fn get_source_pos(&self) -> SourcePos {\n    self.error_pos\n  }\n\n}\n\nimpl<'a, T: Sourced> Sourced for SourcedValue<'a, T> {\n  type Item = T::Item;\n\n  fn get_source(&self) -> SourceOffset {\n    self.source.get_source()\n  }\n\n  fn get_value(&self) -> &Self::Item {\n    self.source.get_value()\n  }\n\n}\n\n/// Returns the position of all of the newlines in the source text.\n/// For the purposes of this function, CARRIAGE RETURN (U+001D) and\n/// NEWLINE (U+001A) are considered to be newlines. Additionally, a\n/// CRLF (U+001D U+001A) sequence is considered to be only a single\n/// newline.\npub fn find_newlines(source: &str) -> Vec<SourceOffset> {\n  let mut result = Vec::new();\n\n  let mut prev = None;\n  for (idx, ch) in source.bytes().enumerate() {\n    if ch == 13 {\n      result.push(SourceOffset(idx));\n    } else if ch == 10 {\n      // An LF after a CR doesn't count\n      if prev != Some(13) {\n        result.push(SourceOffset(idx));\n      }\n    }\n    prev = Some(ch)\n  }\n\n  result\n}\n\nimpl SourcePos {\n\n  /// Constructs a SourcePos representing the given line and column\n  /// number.\n  pub fn new(line: usize, column: usize) -> SourcePos {\n    SourcePos { line, column }\n  }\n\n  /// Converts `offset` into a [`SourcePos`] representing a position\n  /// in the given string of text `source`.\n  pub fn from_offset(offset: SourceOffset, source: &str) -> SourcePos {\n    let lines = {\n      let mut lines = find_newlines(source);\n      lines.push(SourceOffset(usize::MAX)); // Implicitly assume there's a final newline at the end of time.\n      lines\n    };\n\n    let mut line_number = 1;\n    let mut column_number = 1;\n\n    for (idx, ch) in source.char_indices() {\n      if idx >= offset.0 {\n        break\n      }\n      if lines[line_number - 1].0 <= idx {\n        line_number += 1;\n        column_number = 1;\n      } else if ch != '\\n' { // Windows compatibility (don't advance on CRLF)\n        column_number += 1;\n      }\n    }\n\n    SourcePos::new(line_number, column_number)\n  }\n\n}\n\nimpl From<usize> for SourceOffset {\n  fn from(x: usize) -> SourceOffset {\n    SourceOffset(x)\n  }\n}\n\nimpl From<SourceOffset> for usize {\n  fn from(x: SourceOffset) -> usize {\n    x.0\n  }\n}\n\nimpl fmt::Display for SourceOffset {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"{}\", self.0)\n  }\n}\n\nimpl fmt::Display for SourcePos {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"line {} column {}\", self.line, self.column)\n  }\n}\n\nimpl<'a, T: Sourced + fmt::Display> fmt::Display for SourcedValue<'a, T> {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"On {}: {}\", self.error_pos, self.source)\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn test_newlines() {\n    let s1 = \"abcdef\";\n    assert_eq!(find_newlines(s1), vec!());\n\n    let s2 = \"abcdef\\nghi\";\n    assert_eq!(find_newlines(s2), vec!(SourceOffset(6)));\n\n    let s3 = \"abcdef\\nghi\\njkl\";\n    assert_eq!(find_newlines(s3), vec!(SourceOffset(6), SourceOffset(10)));\n\n    let s4 = \"abcdef\\nghi\\rjkl\";\n    assert_eq!(find_newlines(s4), vec!(SourceOffset(6), SourceOffset(10)));\n\n    let s5 = \"abcdef\\nghi\\r\\njkl\";\n    assert_eq!(find_newlines(s5), vec!(SourceOffset(6), SourceOffset(10)));\n\n    let s6 = \"abcdef\\n\\nghi\\r\\njkl\";\n    assert_eq!(find_newlines(s6), vec!(SourceOffset(6), SourceOffset(7), SourceOffset(11)));\n\n    let s7 = \"abcdef\\n\\nghi\\r\\n\\rjkl\";\n    assert_eq!(find_newlines(s7), vec!(SourceOffset(6), SourceOffset(7), SourceOffset(11), SourceOffset(13)));\n\n    let s8 = \"abcdef\\n\\nghi\\r\\n\\r\\njkl\";\n    assert_eq!(find_newlines(s8), vec!(SourceOffset(6), SourceOffset(7), SourceOffset(11), SourceOffset(13)));\n  }\n\n  #[test]\n  fn test_offset() {\n\n    assert_eq!(SourcePos::from_offset(SourceOffset(0), \"abc\"), SourcePos::new(1, 1));\n    assert_eq!(SourcePos::from_offset(SourceOffset(1), \"abc\"), SourcePos::new(1, 2));\n    assert_eq!(SourcePos::from_offset(SourceOffset(2), \"abc\"), SourcePos::new(1, 3));\n    assert_eq!(SourcePos::from_offset(SourceOffset(3), \"abc\"), SourcePos::new(1, 4));\n\n    assert_eq!(SourcePos::from_offset(SourceOffset(0), \"abc\\ndef\"), SourcePos::new(1, 1));\n    assert_eq!(SourcePos::from_offset(SourceOffset(1), \"abc\\ndef\"), SourcePos::new(1, 2));\n    assert_eq!(SourcePos::from_offset(SourceOffset(2), \"abc\\ndef\"), SourcePos::new(1, 3));\n    assert_eq!(SourcePos::from_offset(SourceOffset(3), \"abc\\ndef\"), SourcePos::new(1, 4));\n    assert_eq!(SourcePos::from_offset(SourceOffset(4), \"abc\\ndef\"), SourcePos::new(2, 1));\n    assert_eq!(SourcePos::from_offset(SourceOffset(5), \"abc\\ndef\"), SourcePos::new(2, 2));\n    assert_eq!(SourcePos::from_offset(SourceOffset(6), \"abc\\ndef\"), SourcePos::new(2, 3));\n    assert_eq!(SourcePos::from_offset(SourceOffset(7), \"abc\\ndef\"), SourcePos::new(2, 4));\n\n    assert_eq!(SourcePos::from_offset(SourceOffset(0), \"abc\\r\\ndef\"), SourcePos::new(1, 1));\n    assert_eq!(SourcePos::from_offset(SourceOffset(1), \"abc\\r\\ndef\"), SourcePos::new(1, 2));\n    assert_eq!(SourcePos::from_offset(SourceOffset(2), \"abc\\r\\ndef\"), SourcePos::new(1, 3));\n    assert_eq!(SourcePos::from_offset(SourceOffset(3), \"abc\\r\\ndef\"), SourcePos::new(1, 4));\n    assert_eq!(SourcePos::from_offset(SourceOffset(4), \"abc\\r\\ndef\"), SourcePos::new(2, 1));\n    assert_eq!(SourcePos::from_offset(SourceOffset(5), \"abc\\r\\ndef\"), SourcePos::new(2, 1));\n    assert_eq!(SourcePos::from_offset(SourceOffset(6), \"abc\\r\\ndef\"), SourcePos::new(2, 2));\n    assert_eq!(SourcePos::from_offset(SourceOffset(7), \"abc\\r\\ndef\"), SourcePos::new(2, 3));\n    assert_eq!(SourcePos::from_offset(SourceOffset(8), \"abc\\r\\ndef\"), SourcePos::new(2, 4));\n\n  }\n\n}\n"
  },
  {
    "path": "src/pipeline/stdlib_unit.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`StdlibUnit`] structure.\n\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::ir::identifier::Id;\nuse crate::ir::macros::MacroData;\nuse super::translation_unit::TranslationUnit;\n\nuse serde::{Serialize, Deserialize};\n\nuse std::collections::HashMap;\n\n/// A stripped-down version of [`TranslationUnit`] containing the\n/// parts of the unit that are necessary to understand the standard\n/// library.\n#[derive(Serialize, Deserialize)]\npub struct StdlibUnit {\n  /// See [`TranslationUnit::table`].\n  pub table: SymbolTable,\n  /// See [`TranslationUnit::exports`].\n  pub exports: Vec<Id>,\n  /// See [`TranslationUnit::macros`].\n  pub macros: HashMap<Id, MacroData>,\n}\n\nimpl From<TranslationUnit> for StdlibUnit {\n\n  fn from(unit: TranslationUnit) -> StdlibUnit {\n    let TranslationUnit { table, exports, macros, .. } = unit;\n    StdlibUnit { table, exports, macros }\n  }\n\n}\n"
  },
  {
    "path": "src/pipeline/translation_unit.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`TranslationUnit`] structure.\n\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::ir::decl::TopLevel;\nuse crate::ir::identifier::Id;\nuse crate::ir::macros::MacroData;\nuse crate::gdscript::decl::TopLevelClass;\n\nuse std::path::PathBuf;\nuse std::collections::HashMap;\n\n/// A translation unit is the result of compiling a single file of\n/// GDLisp code. As we compile a Godot project, the\n/// [`Pipeline`](super::Pipeline) keeps an index of loaded files in\n/// the form of translation units. If the same file is required\n/// multiple times, the latter loads can simply query the cached file.\n/// As such, this structure necessarily keeps all of the information\n/// needed to reconstruct the entire relevant compilation process for\n/// import purposes.\n#[derive(Debug)]\npub struct TranslationUnit {\n  /// The path to the file this translation unit references.\n  pub filename: PathBuf,\n  /// The compiler symbol table for the top-level of this translation\n  /// unit. This includes all top-level declarations which are\n  /// available at runtime.\n  ///\n  /// Note that, at present, this symbol table includes the names of\n  /// macros defined in the file. This may change in the future, if\n  /// macros become unavailable at runtime.\n  pub table: SymbolTable,\n  /// The intermediate representation used during compilation of the\n  /// translation unit.\n  pub ir: TopLevel,\n  /// The compiled GDScript code, the final result of compilation.\n  pub gdscript: TopLevelClass,\n  /// A vector of exported identifiers. This includes any names which\n  /// should be available from imports and explicitly *excludes* those\n  /// names marked private or those generated for local use, such as\n  /// lambda closure classes.\n  pub exports: Vec<Id>,\n  /// A map of all of the macros defined in the translation unit.\n  ///\n  /// This map should exclude macros which were imported into the\n  /// unit's scope but were not defined there, so\n  /// [`MacroData::imported`] should be false for every value in this\n  /// map.\n  pub macros: HashMap<Id, MacroData>,\n}\n\nimpl TranslationUnit {\n\n  /// Convenience function for constructing translation units.\n  pub fn new(filename: PathBuf,\n             table: SymbolTable,\n             ir: TopLevel,\n             gdscript: TopLevelClass,\n             exports: Vec<Id>,\n             macros: HashMap<Id, MacroData>)\n             -> TranslationUnit {\n    TranslationUnit { filename, table, ir, gdscript, exports, macros }\n  }\n\n  /// Clone the [`TranslationUnit`].\n  ///\n  /// `TranslationUnit` does not directly implement [`Clone`], and for\n  /// good reason. The identifiers in a given translation unit (for\n  /// example, those in [`TranslationUnit::macros`]) apply to a\n  /// particular [`Pipeline`](super::Pipeline). Associating a\n  /// translation unit with a different pipeline can result in\n  /// unintended consequences. Thus, we provide this explicit\n  /// mechanism for cloning a translation unit. This method should\n  /// only be called if you *definitely* know what you're doing.\n  ///\n  /// In particular, `clone_detached` is safe on translation units\n  /// which do not define any macros, or which define only macros\n  /// using reserved identifiers. That means that `clone_detached` is\n  /// safe to use on the translation unit describing the GDLisp\n  /// standard library.\n  pub fn clone_detached(&self) -> TranslationUnit {\n    TranslationUnit {\n      filename: self.filename.clone(),\n      table: self.table.clone(),\n      ir: self.ir.clone(),\n      gdscript: self.gdscript.clone(),\n      exports: self.exports.clone(),\n      macros: self.macros.clone(),\n    }\n  }\n\n}\n\nimpl From<TranslationUnit> for TopLevelClass {\n\n  fn from(trans: TranslationUnit) -> TopLevelClass {\n    trans.gdscript\n  }\n\n}\n"
  },
  {
    "path": "src/repl.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse crate::pipeline::Pipeline;\nuse crate::pipeline::source::SourceOffset;\nuse crate::pipeline::error::{PError, IOError};\nuse crate::pipeline::config::ProjectConfig;\nuse crate::runner::path::RPathBuf;\nuse crate::sxp::ast::AST;\nuse crate::ir;\nuse crate::ir::incremental::IncCompiler;\nuse crate::ir::macros::MacroData;\nuse crate::ir::identifier::{Namespace, Id};\nuse crate::ir::main_function::StaticMainFunctionHandler;\nuse crate::compile::Compiler;\nuse crate::compile::body::builder::CodeBuilder;\nuse crate::compile::body::class_scope::OutsideOfClass;\nuse crate::compile::symbol_table::SymbolTable;\nuse crate::compile::symbol_table::local_var::VarName;\nuse crate::compile::symbol_table::function_call::FnSpecs;\nuse crate::compile::symbol_table::call_magic::CallMagic;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::compile::preload_resolver::DefaultPreloadResolver;\nuse crate::compile::error::{GDError, GDErrorF};\nuse crate::gdscript::library;\nuse crate::gdscript::class_extends::ClassExtends;\nuse crate::gdscript::metadata::REPL_FUNCTION_NAME;\nuse crate::optimize::gdscript::run_standard_passes;\nuse crate::SOME_AST_PARSER;\n\nuse tempfile::Builder;\n\nuse std::convert::TryFrom;\nuse std::io::Write;\nuse std::path::Path;\nuse std::collections::HashMap;\n\n/// A `Repl` instance maintains the state of a read-eval-print loop.\n/// Specifically, it maintains a running Godot instance\n/// (lazily-initialized, as always), as well as a table of all known\n/// symbols from *previous* REPL commands.\n///\n/// Commands are sent to a `Repl` via [`Repl::run_code`] or\n/// [`Repl::parse_and_run_code`].\npub struct Repl {\n  pipeline: Pipeline,\n  full_symbol_table: SymbolTable,\n  full_macro_table: HashMap<Id, MacroData>,\n  /// Indicates whether the REPL has had its REPL-specific\n  /// initialization code run yet.\n  initialized: bool,\n}\n\nimpl Repl {\n\n  const REPL_FILENAME: &'static str =\n    if cfg!(windows) { \"C:/tmp/REPL.lisp\" } else { \"/tmp/REPL.lisp\" };\n\n  pub fn new(config: ProjectConfig) -> Repl {\n    Repl::with_pipeline(Pipeline::new(config))\n  }\n\n  pub fn with_pipeline(pipeline: Pipeline) -> Repl {\n    Repl {\n      pipeline,\n      full_symbol_table: SymbolTable::new(),\n      full_macro_table: HashMap::new(),\n      initialized: false,\n    }\n  }\n\n  fn initialize(&mut self) {\n    // Do this first, or else the parse_and_run_code implementation\n    // below will end us up in an infinite loop.\n    self.initialized = true;\n\n    // Re-route print to printerr, since we suppress stdout output.\n    // Fix for https://github.com/Mercerenies/gdlisp/issues/119.\n    self.parse_and_run_code(\"(defn print (&rest args) (apply #'printerr args))\")\n      .expect(\"Internal error in Repl::initialize\");\n  }\n\n  /// Normally, the macro server and all of the subsystems are lazy\n  /// and only load when requested. However, this creates the awkward\n  /// effect where the REPL loads fast but then the first command (and\n  /// only the first command) takes a long time to run. This function\n  /// can be called to force the subsystems to load immediately.\n  pub fn force_load(&mut self) {\n    self.parse_and_run_code(\"()\").expect(\"Internal error in force_load\");\n  }\n\n  /// Runs the code given by the AST as GDLisp source. The result is\n  /// returned. In case of success, the result shall be the string\n  /// representation of the value produced by the expression. On\n  /// failure, the result shall be the error message.\n  ///\n  /// The top-level AST shall be a list, or a GDLisp error will be\n  /// returned. The list can consist of a mix of expressions and\n  /// declarations freely. The declarations will be put into scope\n  /// first (in the order they are declared), and then (and *only*\n  /// then) the expressions will be evaluated in order. If there are\n  /// no expressions and the declarations are successfully compiled,\n  /// then a string representation of the nil list will be returned as\n  /// a default value.\n  ///\n  /// `run_code` exhibits a strong exception guarantee. If this\n  /// function returns an error, then the [`Repl`] object it was\n  /// called on has not been modified. On the other hand, if this\n  /// function returns an `Ok` value, then the `Repl` object's\n  /// internal symbol table will have any new declarations from the\n  /// given code added to it.\n  ///\n  /// For a version that parses the code and *then* runs it, see\n  /// [`parse_and_run_code`](Repl::parse_and_run_code).\n  pub fn run_code(&mut self, code: &AST) -> Result<String, PError> {\n\n    if !self.initialized {\n      self.initialize();\n    }\n\n    self.pipeline.set_currently_loading_file(RPathBuf::try_from(String::from(Repl::REPL_FILENAME)).unwrap());\n\n    let mut compiler = Compiler::new(FreshNameGenerator::new(code.all_symbols()), Box::new(DefaultPreloadResolver), false);\n\n    let mut icompiler = IncCompiler::with_ambient_symbols(code.all_symbols(), self.full_symbol_table.clone()); // TODO Don't like this clone here, symbol tables are big and cloning is expensive.\n    icompiler.bind_macros_from(self.full_macro_table.iter().map(|(id, data)| (id.clone(), data.clone())));\n    let (ir, macros) = icompiler.compile_toplevel(&mut self.pipeline, code, &Repl::main_function_handler())?;\n    ir::check_ir(&ir)?;\n\n    if ir.minimalist_flag {\n      return Err(PError::from(GDError::new(GDErrorF::MinimalistAtRepl, SourceOffset(0))));\n    }\n\n    let mut table = SymbolTable::new();\n    library::bind_builtins(&mut table, false);\n    self.bind_existing_names(&mut table);\n\n    let mut builder = CodeBuilder::new(ClassExtends::SimpleIdentifier(\"Node\".to_owned()));\n    compiler.frame(&mut self.pipeline, &mut builder, &mut table, &mut OutsideOfClass).compile_toplevel(&ir)?;\n    let mut result = builder.build();\n    if self.pipeline.config().optimizations {\n      run_standard_passes(&mut result)?;\n    }\n\n    let mut tmpfile = Builder::new()\n      .prefix(\"__gdlisp_replfile\")\n      .suffix(\".gd\")\n      .rand_bytes(5)\n      .tempfile()\n      .map_err(|err| IOError::new(err, SourceOffset(0)))?;\n    let tmpfile_name = tmpfile.path().to_owned();\n\n    write!(tmpfile, \"{}\", result.to_gd()).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n    tmpfile.flush().map_err(|err| IOError::new(err, SourceOffset(0)))?;\n\n    let server = self.pipeline.get_server_mut();\n    let macro_id =\n      server.stand_up_macro(String::from(REPL_FUNCTION_NAME), tmpfile)\n      .map_err(|err| IOError::new(err, SourceOffset(0)))?;\n\n    let final_result = server.run_server_file_str(macro_id, vec!(), FnSpecs::EMPTY, vec!(), SourceOffset(0))?;\n\n    // If everything happened correctly, then save the symbols we got\n    // from this line.\n    self.remember_symbols(&tmpfile_name, &mut table, &ir::export::get_export_list(&ir.decls));\n    self.full_macro_table.extend(macros.into_iter());\n\n    Ok(final_result)\n  }\n\n  /// Parse the code as one or more GDLisp S-expressions and then run\n  /// the result in the REPL.\n  ///\n  /// See [`Repl::run_code`] for more details on the effects running\n  /// code has on the [`Repl`] object itself.\n  pub fn parse_and_run_code(&mut self, code: &str) -> Result<String, PError> {\n    let ast = SOME_AST_PARSER.parse(code)?;\n    self.run_code(&ast)\n  }\n\n  /// Returns true if the child process backing the REPL is still\n  /// running.\n  ///\n  /// Note that there is a frame of lag between a Godot command that\n  /// terminates the REPL and the actual termination, so if a call to\n  /// [`Repl::run_code`] or [`Repl::parse_and_run_code`] terminates\n  /// the REPL, then it will take approximately 16 milliseconds before\n  /// `Repl::is_running` begins to return false.\n  pub fn is_running(&mut self) -> bool {\n    self.pipeline.get_server_mut().is_process_healthy()\n  }\n\n  fn remember_symbols(&mut self, filename: &Path, table: &mut SymbolTable, exports: &[Id]) {\n    let direct_load_import = VarName::DirectLoad(filename.to_string_lossy().into_owned());\n\n    for name in exports {\n      match name.namespace {\n        Namespace::Value => {\n          let mut var = table.get_var(&name.name).unwrap().to_owned();\n          var.name = var.name.into_imported_var(direct_load_import.clone());\n          self.full_symbol_table.set_var(name.name.to_owned(), var);\n        }\n        Namespace::Function => {\n          let mut func = table.get_fn(&name.name).unwrap().0.to_owned();\n          func.object = func.object.into_imported_var(direct_load_import.clone());\n          self.full_symbol_table.set_fn(name.name.to_owned(), func, CallMagic::DefaultCall);\n        }\n      }\n    }\n  }\n\n  fn bind_existing_names(&self, table: &mut SymbolTable) {\n    table.assign_from(&self.full_symbol_table);\n  }\n\n  fn main_function_handler() -> StaticMainFunctionHandler {\n    StaticMainFunctionHandler::new(String::from(REPL_FUNCTION_NAME))\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::runner::version::VersionInfo;\n  use std::path::PathBuf;\n  use std::thread::sleep;\n  use std::time::Duration;\n\n  fn dummy_config() -> ProjectConfig {\n    ProjectConfig {\n      root_directory: PathBuf::from(\".\"),\n      optimizations: false,\n      godot_version: VersionInfo::default(),\n    }\n  }\n\n  #[test]\n  fn basic_repl_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"(+ 1 1)\"), Ok(String::from(\"2\")));\n  }\n\n  #[test]\n  fn quoted_repl_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"'(1 2 . (3 . 4))\"), Ok(String::from(\"(1 2 3 . 4)\")));\n  }\n\n  #[test]\n  fn array_repl_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"[1 2 3 4]\"), Ok(String::from(\"[1 2 3 4]\")));\n  }\n\n  #[test]\n  fn dict_repl_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"{1 2 3 4}\"), Ok(String::from(\"{1 2 3 4}\")));\n  }\n\n  #[test]\n  fn decl_then_expr_in_one_repl_command_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"(defn foo (x) (+ x 1)) (foo 100)\"), Ok(String::from(\"101\")));\n  }\n\n  #[test]\n  fn force_load_in_repl_test() {\n    let mut repl = Repl::new(dummy_config());\n    // This returns nothing, so there's nothing to assert. But we do\n    // expect this function to run without panicking.\n    repl.force_load();\n  }\n\n  #[test]\n  fn decl_then_expr_in_two_repl_commands_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"(defn foo (x) (+ x 1))\"), Ok(String::from(\"()\")));\n    assert_eq!(repl.parse_and_run_code(\"(foo 100)\"), Ok(String::from(\"101\")));\n    assert!(repl.is_running());\n  }\n\n  #[test]\n  fn failed_decl_then_expr_repl_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"(defn foo (x) (+ x 1)) nonexistent-variable\"),\n               Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"nonexistent-variable\")), SourceOffset(23)))));\n    assert_eq!(repl.parse_and_run_code(\"(foo 100)\"),\n               Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"foo\")), SourceOffset(0)))));\n\n  }\n\n  #[test]\n  fn failed_repl_continues_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"(defn foo (x) (+ x 1))\"), Ok(String::from(\"()\")));\n    assert_eq!(repl.parse_and_run_code(\"nonexistent-variable\"),\n               Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"nonexistent-variable\")), SourceOffset(0)))));\n    assert_eq!(repl.parse_and_run_code(\"(foo 100)\"), Ok(String::from(\"101\")));\n\n  }\n\n  #[test]\n  fn minimalist_at_repl_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"(sys/nostdlib)\"),\n               Err(PError::from(GDError::new(GDErrorF::MinimalistAtRepl, SourceOffset(0)))));\n  }\n\n  #[test]\n  #[ignore = \"Race condition (see issue #127)\"]\n  fn terminate_repl_via_tree_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"((GDLisp:get-tree):quit)\"), Ok(String::from(\"()\")));\n    sleep(Duration::from_millis(200));\n    assert!(!repl.is_running());\n  }\n\n  #[test]\n  #[ignore = \"Race condition (see issue #127)\"]\n  fn terminate_repl_via_quit_test() {\n    let mut repl = Repl::new(dummy_config());\n    assert_eq!(repl.parse_and_run_code(\"(quit)\"), Ok(String::from(\"()\")));\n    sleep(Duration::from_millis(200));\n    assert!(!repl.is_running());\n  }\n\n}\n"
  },
  {
    "path": "src/runner/godot.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides a DSL for executing arbitrary Godot subprocesses.\n\nuse std::process::{Command, Stdio, Output, Child, ExitStatus};\nuse std::path::Path;\nuse std::ffi::OsStr;\nuse std::io;\n\n/// `GodotCommand` is a wrapper around [`Command`] specifically\n/// designed for running Godot processes. This structure implements\n/// much of the same interface as `Command`, as well as adding some\n/// Godot-specific functions.\n///\n/// `GodotCommand` instances are created either using\n/// [`raw`](GodotCommand::raw) or [`base`](GodotCommand::base).\n#[derive(Debug)]\npub struct GodotCommand {\n  command: Command,\n}\n\nimpl GodotCommand {\n\n  /// A raw [`GodotCommand`]. This is the simplest way to create a\n  /// Godot command. It provides *no* command line arguments or\n  /// special stream handling, leaving everything at the [`Command`]\n  /// defaults.\n  ///\n  /// Equivalent to `Command::new(\"godot\")`, except wrapped as a\n  /// `GodotCommand`.\n  ///\n  /// For most GDLisp use cases, [`base`](GodotCommand::base) should\n  /// be preferred, as it implements common sense defaults on top of\n  /// `raw`.\n  pub fn raw() -> Self {\n    GodotCommand {\n      command: Command::new(\"godot\"),\n    }\n  }\n\n  /// Constructs a [`GodotCommand`] with sensible defaults. This\n  /// includes passing the `--no-window` argument to Godot, as well as\n  /// setting stderr to [`Stdio::inherit`] and stdout to\n  /// [`Stdio::piped`].\n  ///\n  /// For a version of [`GodotCommand`] with no defaults set, see\n  /// [`raw`](GodotCommand::raw).\n  pub fn base() -> Self {\n    let mut instance = Self::raw();\n    instance\n      .no_window()\n      .stderr(Stdio::inherit())\n      .stdout(Stdio::piped());\n    instance\n  }\n\n  /// Adds an argument to pass to Godot.\n  ///\n  /// See [`Command::arg`].\n  pub fn arg(&mut self, arg_text: impl AsRef<OsStr>) -> &mut Self {\n    self.command.arg(arg_text);\n    self\n  }\n\n  /// Adds the `--no-window` option to the command line arguments.\n  ///\n  /// This is already included if `self` was created with\n  /// [`GodotCommand::base`], so it's only necessary for commands\n  /// created with [`GodotCommand::raw`].\n  pub fn no_window(&mut self) -> &mut Self {\n    self.arg(\"--no-window\")\n  }\n\n  /// Adds a command line argument indicating to the Godot process\n  /// that the script at the given file path should be run.\n  pub fn script_file(&mut self, filename: &Path) -> &mut Self {\n    self.arg(\"-s\").arg(filename)\n  }\n\n  /// Adds a command line argument indicating to the Godot process\n  /// that the Godot project in the given directory should be run.\n  /// There must be a `project.godot` file in the directory.\n  ///\n  /// Note that the path argument to `project_dir` should be a\n  /// *directory*, not the `project.godot` file itself.\n  pub fn project_dir(&mut self, directory_name: &Path) -> &mut Self {\n    self.arg(\"--path\").arg(directory_name)\n  }\n\n  /// Adds the `--quit` option to the command line arguments,\n  /// indicating that the process should quit after one iteration.\n  pub fn quit_after_one(&mut self) -> &mut Self {\n    self.arg(\"--quit\")\n  }\n\n  /// Adds the `--quiet` option to the command line arguments.\n  pub fn quiet(&mut self) -> &mut Self {\n    self.arg(\"--quiet\")\n  }\n\n  /// Adds a single environment variable that will be visible to the\n  /// Godot process.\n  ///\n  /// See [`Command::env`].\n  pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self\n  where K: AsRef<OsStr>,\n        V: AsRef<OsStr> {\n    self.command.env(key, val);\n    self\n  }\n\n  /// Adds environment variables that will be visible to the Godot\n  /// process.\n  ///\n  /// See [`Command::envs`].\n  pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self\n  where I: IntoIterator<Item = (K, V)>,\n        K: AsRef<OsStr>,\n        V: AsRef<OsStr> {\n    self.command.envs(vars);\n    self\n  }\n\n  /// Sets a handler for the standard error stream.\n  ///\n  /// See [`Command::stderr`].\n  pub fn stderr(&mut self, cfg: impl Into<Stdio>) -> &mut Self {\n    self.command.stderr(cfg);\n    self\n  }\n\n  /// Sets a handler for the standard output stream.\n  ///\n  /// See [`Command::stdout`].\n  pub fn stdout(&mut self, cfg: impl Into<Stdio>) -> &mut Self {\n    self.command.stdout(cfg);\n    self\n  }\n\n  /// Sets a handler for the standard input stream.\n  ///\n  /// See [`Command::stdin`].\n  pub fn stdin(&mut self, cfg: impl Into<Stdio>) -> &mut Self {\n    self.command.stdin(cfg);\n    self\n  }\n\n  /// Executes the command, waits on it to terminate, and then\n  /// collects all of its output.\n  ///\n  /// See [`Command::output`].\n  pub fn output(&mut self) -> io::Result<Output> {\n    self.command.output()\n  }\n\n  /// Executes the command, waits on it to terminate, and then\n  /// returns its exit status.\n  ///\n  /// See [`Command::status`].\n  pub fn status(&mut self) -> io::Result<ExitStatus> {\n    self.command.status()\n  }\n\n  /// Executes the command, returning a [`Child`] instance\n  /// immediately.\n  ///\n  /// See [`Command::spawn`].\n  pub fn spawn(&mut self) -> io::Result<Child> {\n    self.command.spawn()\n  }\n\n}\n\nimpl From<GodotCommand> for Command {\n  fn from(godot_command: GodotCommand) -> Command {\n    godot_command.command\n  }\n}\n"
  },
  {
    "path": "src/runner/into_gd_file.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! This module defines [`IntoGDFile`], a trait for objects which can\n//! be written to disk as GDScript source files.\n//!\n//! The primary implementor is [`TopLevelClass`], which represents\n//! GDScript source code as a Rust-side AST.\n\nuse crate::gdscript::decl::TopLevelClass;\n\nuse std::io::{self, Write};\nuse std::borrow::Borrow;\n\n/// A trait for objects which can be reasonably written to a file as\n/// GDScript source code.\npub trait IntoGDFile {\n  /// Writes the contents of `self` to `file` as GDScript source code.\n  fn write_to_gd(&self, file: &mut impl Write) -> io::Result<()>;\n}\n\n/// A [`TopLevelClass`] can be written to a file using\n/// [`to_gd`](TopLevelClass::to_gd()).\nimpl IntoGDFile for TopLevelClass {\n  fn write_to_gd(&self, file: &mut impl Write) -> io::Result<()> {\n    write!(file, \"{}\", self.to_gd())\n  }\n}\n\n/// A string of GDScript source code can be written to a file\n/// verbatim.\nimpl<T : Borrow<str> + ?Sized> IntoGDFile for T {\n  fn write_to_gd(&self, file: &mut impl Write) -> io::Result<()> {\n    write!(file, \"{}\", self.borrow())\n  }\n}\n"
  },
  {
    "path": "src/runner/macro_server/command.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! The types representing commands which can be sent to a\n//! [`MacroServer`](super::MacroServer).\n//!\n//! [`ServerCommand`] represents the various commands which can be\n//! sent to the macro server. Commands are eventually encoded as JSON\n//! and passed onto the server. Eventually, the server will receive a\n//! [`ServerResponse`](super::response::ServerResponse) back as reply.\n//!\n//! Note that, while it is *highly* unlikely that this will ever come\n//! into play, the total length of a command, including the JSON\n//! formatting and command name, cannot exceed 2^32 characters. Thus,\n//! any string arguments to `ServerCommand` should not approach that\n//! length.\n\nuse json::JsonValue;\n\n/// A server command.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum ServerCommand {\n  Quit,\n  Ping,\n  Eval(String),\n  Exec(String),\n  Load(String),\n}\n\nimpl ServerCommand {\n\n  /// The name of the command, as a string.\n  pub fn name(&self) -> &'static str {\n    match self {\n      ServerCommand::Quit => \"quit\",\n      ServerCommand::Ping => \"ping\",\n      ServerCommand::Eval(_) => \"eval\",\n      ServerCommand::Exec(_) => \"exec\",\n      ServerCommand::Load(_) => \"load\",\n    }\n  }\n\n  /// The arguments to the command, as a sequence of strings.\n  pub fn arguments(&self) -> Vec<&str> {\n    match self {\n      ServerCommand::Quit => vec!(),\n      ServerCommand::Ping => vec!(),\n      ServerCommand::Eval(s) => vec!(s),\n      ServerCommand::Exec(s) => vec!(s),\n      ServerCommand::Load(s) => vec!(s),\n    }\n  }\n\n  /// Convert the command to JSON.\n  ///\n  /// Commands are sent as JSON objects which have the following keys.\n  ///\n  /// * command (required) - The name of the command.\n  ///\n  /// * args (required) - An array of values, usually strings. Its\n  /// interpretation depends on the command being run.\n  pub fn to_json(&self) -> JsonValue {\n    let command = String::from(self.name());\n    let args = self.arguments().into_iter().map(JsonValue::from).collect();\n    json::object!{\n      \"command\" => command,\n      \"args\" => JsonValue::Array(args),\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/runner/macro_server/lazy.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides [`LazyServer`] for lazily constructing\n//! [`MacroServer`](super::MacroServer) instances.\n\nuse super::MacroServer;\n\nuse std::io;\nuse std::process::ExitStatus;\n\n/// A macro server that may or may not have been initialized yet.\n///\n/// A `LazyServer` is similar to a\n/// [`Lazy<MacroServer>`](https://doc.rust-lang.org/std/lazy/struct.Lazy.html),\n/// in that it doesn't actually construct a macro server until the\n/// value is actually forced. This provides the key benefit that the\n/// server will not be forced as soon as the compiler starts up,\n/// instead waiting until it's actually required for macro resolution,\n/// macro definition, or for storage of a completed file.\n///\n/// Note that, unlike `Lazy`, this type does *not* provide\n/// [`Deref`](std::ops::Deref). Macro servers must be explicitly\n/// queried with [`LazyServer::get_mut`]. This is because the macro server\n/// construction is a nontrivial operation, so we require the user to\n/// clearly assert that they understand the cost.\npub struct LazyServer(Option<MacroServer>);\n\n// **Note:** [`Default`] doesn't make as much sense for this type, as\n// it's meant to behave like a server, not a simple datatype. The fact\n// that it *can* be default-allocated is an implementation detail.\n#[allow(clippy::new_without_default)]\nimpl LazyServer {\n\n  /// Construct a new, empty `LazyServer`. No additional processes\n  /// will be spawned.\n  pub fn new() -> LazyServer {\n    LazyServer(None)\n  }\n\n  /// Query whether or not the server has been started.\n  pub fn is_running(&self) -> bool {\n    self.0.is_some()\n  }\n\n  /// Get the macro server, starting a new server process if one has\n  /// not been started yet.\n  ///\n  /// If the server has already been started\n  /// ([`is_running`](LazyServer::is_running) is true), then this\n  /// method is guaranteed to succeed. If the server has not been\n  /// started yet, then IO errors while spawning the process will be\n  /// propagated to the return value of this method. If an error\n  /// occurs, the `LazyServer` is guaranteed to be left in an\n  /// uninitialized state, and future calls to `get_mut` will attempt\n  /// to spawn the process once again.\n  pub fn get_mut(&mut self) -> io::Result<&mut MacroServer> {\n    if self.0.is_none() {\n      self.0 = Some(MacroServer::new()?);\n    }\n    Ok(self.0.as_mut().unwrap())\n  }\n\n  /// Returns the macro server, but only if it has already been\n  /// started. If the macro server has not yet been started, then this\n  /// will return `None`.\n  ///\n  /// For a function that lazily initializes the server if it hasn't\n  /// been initialized, see [`LazyServer::get_mut`].\n  pub fn get_mut_if_initialized(&mut self) -> Option<&mut MacroServer> {\n    self.0.as_mut()\n  }\n\n  /// Consume the `LazyServer` and shut down the server, if it's\n  /// running. This is equivalent to simply dropping the `LazyServer`\n  /// instance but allows custom error handling to be implemented.\n  ///\n  /// If the lazy server is uninitialized, then `Ok(None)` will always\n  /// be returned. Otherwise, the server will be shut down via\n  /// [`MacroServer::shutdown`] and any errors will be propagated out.\n  /// Assuming there are no IO errors during shutdown, the result will\n  /// be `Ok(Some(exit_status))`, where `exit_status` is the exit\n  /// status of the macro server.\n  pub fn shutdown(self) -> io::Result<Option<ExitStatus>> {\n    match self.0 {\n      None => Ok(None),\n      Some(x) => x.shutdown().map(Some),\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/runner/macro_server/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Functionality for constructing and interacting with a Godot server\n//! process.\n//!\n//! This module provides [`MacroServer`], for spawning Godot server\n//! processes. A `MacroServer` can be sent [`ServerCommand`] commands\n//! and will receive responses in the form of [`ServerResponse`].\n//!\n//! In addition to the primitive interface, this module also provides\n//! submodules for richer server interaction. [`lazy`] provides a\n//! means of lazily constructing a macro server, delaying construction\n//! of the child process until absolutely necessary.\n//! [`named_file_server`] provides a high-level API for providing\n//! macros and other resources to a server and for calling macros on a\n//! server.\n\npub mod command;\npub mod response;\npub mod lazy;\npub mod named_file_server;\n\nuse super::godot::GodotCommand;\nuse crate::gdscript::library;\n\nuse command::ServerCommand;\nuse response::ServerResponse;\n\nuse std::io::{self, Write, Read, ErrorKind};\nuse std::process::{Child, Stdio, ExitStatus};\nuse std::net::{TcpListener, TcpStream};\nuse std::convert::{TryFrom, TryInto};\nuse std::mem::ManuallyDrop;\nuse std::sync::{Mutex, MutexGuard};\nuse std::env::current_exe;\n\n/// The TCP port used for communicating with the macro server.\npub const DEFAULT_PORT_NUMBER: u16 = 61992;\n\nlazy_static! {\n  static ref MACRO_SERVER_LOCK: Mutex<()> = Mutex::new(());\n}\n\n/// A `MacroServer` instance manages a child Godot process and a TCP\n/// connection to that process. Using this instance, callers can send\n/// commands and receive responses via\n/// [`issue_command`](MacroServer::issue_command).\npub struct MacroServer {\n  tcp_server: TcpStream,\n  godot_server: Child,\n}\n\nimpl MacroServer {\n\n  /// Construct a new `MacroServer`. This function immediately spawns\n  /// a child process. For a lazily-constructed server that only\n  /// spawns once necessary, consider using [`lazy::LazyServer`]\n  /// instead.\n  pub fn new() -> io::Result<MacroServer> {\n    Self::new_on_ports(DEFAULT_PORT_NUMBER, u16::MAX)\n  }\n\n  pub fn new_on_ports(min_port: u16, max_port: u16) -> io::Result<MacroServer> {\n\n    // This mutex protects our access to GDLisp.gd. Especially when\n    // testing (but also just in general), multiple threads can easily\n    // try to run the fs::copy command below at the same time,\n    // resulting in an inconsistent state of the GDLisp.gd file. With\n    // this mutex, the following steps are atomic:\n    //\n    // 1. Initialize stdlib (if uninitialized)\n    // 2. Copy the GDLisp.gd file into the subdirectory\n    // 3. Load Godot and allow it to read GDLisp.gd\n    //\n    // No other thread can start copying GDLisp.gd until our current\n    // thread allows its subprocess to parse it fully.\n    let _lock_guard = MacroServer::lock_macro_server_init()?;\n\n    library::ensure_stdlib_loaded();\n    let (tcp_listener, port) = MacroServer::try_to_bind_port(min_port, max_port)?;\n    let gd_server = run_godot_child_process(port)?;\n    let (tcp_server, _) = tcp_listener.accept()?;\n    Ok(MacroServer {\n      tcp_server: tcp_server,\n      godot_server: gd_server,\n    })\n  }\n\n  fn lock_macro_server_init() -> io::Result<MutexGuard<'static, ()>> {\n    MACRO_SERVER_LOCK.lock().map_err(|_| {\n      io::Error::new(io::ErrorKind::Other, \"MACRO_SERVER_LOCK was poisoned\")\n    })\n  }\n\n  fn try_to_bind_port(start: u16, end: u16) -> io::Result<(TcpListener, u16)> {\n    for port in start..=end {\n      match TcpListener::bind((\"127.0.0.1\", port)) {\n        Ok(listener) => {\n          return Ok((listener, port));\n        }\n        Err(err) if err.kind() == ErrorKind::AddrInUse => {\n          // Continue.\n        }\n        Err(err) => {\n          // Unhandled error\n          return Err(err);\n        }\n      }\n    }\n    Err(io::Error::new(ErrorKind::AddrInUse, \"Could not find TCP port to bind\"))\n  }\n\n  fn send_string(&mut self, string: &str) -> io::Result<()> {\n    let mut buf = Vec::new();\n    let len: u32 = string.len().try_into().expect(\"String too long to send to Godot TCP server\");\n    buf.extend(len.to_be_bytes());\n    buf.extend(string.bytes());\n    self.tcp_server.write_all(&buf)?;\n    Ok(())\n  }\n\n  fn receive_string(&mut self) -> io::Result<String> {\n    let mut len_buf = [0; 4];\n    self.tcp_server.read_exact(&mut len_buf)?;\n    let len: usize = u32::from_be_bytes(len_buf).try_into().expect(\"String too long to receive from Godot TCP server\");\n    let mut buf = vec![0; len];\n    self.tcp_server.read_exact(&mut buf)?;\n    String::from_utf8(buf).map_err(|_| io::Error::new(io::ErrorKind::InvalidData, \"Error in UTF8 conversion\"))\n  }\n\n  /// Issues the given command to the macro server, waits on a\n  /// response, and returns a [`ServerResponse`] indicating success or\n  /// failure.\n  pub fn issue_command(&mut self, command: &ServerCommand) -> io::Result<ServerResponse> {\n    let json = command.to_json();\n    self.send_string(&json.to_string())?;\n\n    let result = self.receive_string()?;\n    let result = json::parse(&result).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err.to_string()))?;\n    ServerResponse::try_from(result).map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err.to_string()))\n  }\n\n  // Unsafe to use the server afterward, so we only expose a public\n  // version that takes ownership.\n  fn _shutdown(&mut self) -> io::Result<ExitStatus> {\n    self.issue_command(&ServerCommand::Quit)?;\n    self.godot_server.wait()\n  }\n\n  /// Shuts down the server process. This is equivalent to simply\n  /// dropping `self` except that this method allows the caller to\n  /// handle any error conditions that arise from shutting down the\n  /// server.\n  pub fn shutdown(self) -> io::Result<ExitStatus> {\n    let mut server = ManuallyDrop::new(self);\n    server._shutdown()\n  }\n\n  /// Returns true if the Godot subprocess is running and is healthy.\n  /// If the process has terminated for any reason or the GDLisp\n  /// process cannot communicate with it, then this function returns false.\n  pub fn is_process_healthy(&mut self) -> bool {\n    match self.godot_server.try_wait() {\n      Ok(Some(_)) => {\n        // Process has terminated.\n        false\n      }\n      Ok(None) => {\n        // Process has not terminated.\n        true\n      }\n      Err(_) => {\n        // Error occurred trying to get process status; assume\n        // unhealthy.\n        false\n      }\n    }\n  }\n\n}\n\n/// Dropping a `MacroServer` kills the child process, suppressing any\n/// errors that result from doing so.\nimpl Drop for MacroServer {\n\n  fn drop(&mut self) {\n    let _r = self._shutdown(); // Ignore io::Result (we're in Drop so we can't handle it)\n  }\n\n}\n\nfn run_godot_child_process(port: u16) -> io::Result<Child> {\n  let exe_path = current_exe()?;\n  let exe_dir = exe_path.parent().ok_or_else(|| io::Error::new(io::ErrorKind::Other, \"Could not locate executable path\"))?;\n  GodotCommand::base()\n    .project_dir(&exe_dir.join(\"MacroServer\"))\n    .env(\"GDLISP_PORT_NUMBER\", port.to_string())\n    .stdout(Stdio::null())\n    .spawn()\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::sxp::reify::Reify;\n  use crate::AST_PARSER;\n  use json::object;\n  use std::convert::TryFrom;\n  use std::thread::sleep;\n  use std::time::Duration;\n\n  fn issue_command_and_unwrap(server: &mut MacroServer, value: &ServerCommand) -> String {\n    let result = server.issue_command(value).unwrap();\n    String::try_from(result).unwrap()\n  }\n\n  fn roundtrip_value(server: &mut MacroServer, value: &str) {\n    let ast = AST_PARSER.parse(value).unwrap();\n    let result = issue_command_and_unwrap(server, &ServerCommand::Eval(ast.reify().to_gd()));\n    assert_eq!(value, &result);\n  }\n\n  #[test]\n  fn spawn_server_simple_test() {\n    MacroServer::new().unwrap().shutdown().unwrap();\n  }\n\n  #[test]\n  fn spawn_server_ping_pong_test() {\n    let mut server = MacroServer::new().unwrap();\n    let response = issue_command_and_unwrap(&mut server, &ServerCommand::Ping);\n    assert_eq!(response, \"pong\");\n    server.shutdown().unwrap();\n  }\n\n  #[test]\n  fn spawn_server_eval_test() {\n    let mut server = MacroServer::new().unwrap();\n    let response = issue_command_and_unwrap(&mut server, &ServerCommand::Eval(String::from(\"1 + 1\")));\n    assert_eq!(response, \"2\");\n  }\n\n  #[test]\n  fn spawn_server_roundtrip_test_1() {\n    let mut server = MacroServer::new().unwrap();\n    roundtrip_value(&mut server, \"(1)\");\n    roundtrip_value(&mut server, \"(1 2 . 3)\");\n    roundtrip_value(&mut server, \"(1 . 2)\");\n    roundtrip_value(&mut server, \"(array #t #f abc def)\");\n    roundtrip_value(&mut server, \"(array 10 20 (30 40) \\\"ABC\\\")\");\n    roundtrip_value(&mut server, \"(array 10 20 (30 40 . 50) \\\"ABC\\\")\");\n    roundtrip_value(&mut server, \"(array 10 20 (30 40 50 (60 70)) \\\"ABC\\\")\");\n  }\n\n  #[test]\n  fn spawn_server_roundtrip_test_2() {\n    let mut server = MacroServer::new().unwrap();\n    roundtrip_value(&mut server, \"\\\"ABC\\\"\");\n    roundtrip_value(&mut server, \"\\\"αβγ ⊕\\\"\");\n    roundtrip_value(&mut server, \"\\\"😁😁😁😁😁😁\\\"\");\n    roundtrip_value(&mut server, r#\"\"abc\\\"def\"\"#);\n    roundtrip_value(&mut server, r#\"\"''\\\"'\\\"'\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\\\\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\\"\\\\\\\"\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\\\\\\\\\\\\"\"#);\n  }\n\n  #[test]\n  fn spawn_server_roundtrip_test_3() {\n    let mut server = MacroServer::new().unwrap();\n    roundtrip_value(&mut server, r#\"\"\\n\\t\\t\\n\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\r\\r\\r\\r\"\"#);\n    roundtrip_value(&mut server, r#\"\"abc \\a def\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\t\\v\\f\\f\"\"#);\n    roundtrip_value(&mut server, r#\"\"xxx\\b\\b\\b\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\v\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\\\v\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\\\\\v\"\"#);\n    roundtrip_value(&mut server, r#\"\"\\\\\\\\v\"\"#);\n  }\n\n  #[test]\n  fn spawn_server_load_test() {\n    let mut server = MacroServer::new().unwrap();\n    let load_response = issue_command_and_unwrap(&mut server, &ServerCommand::Load(String::from(\"res://TestLoadedFile.gd\")));\n    assert_eq!(load_response, \"0\");\n    let eval_response = issue_command_and_unwrap(&mut server, &ServerCommand::Eval(String::from(r#\"MAIN.loaded_files[0].example()\"#)));\n    assert_eq!(eval_response, \"\\\"Test succeeded\\\"\");\n    server.shutdown().unwrap();\n  }\n\n  #[test]\n  fn spawn_server_exec_test() {\n    let mut server = MacroServer::new().unwrap();\n    let command = ServerCommand::Exec(String::from(\"    var tmp_var = 1 + 1\\n    return tmp_var\"));\n    let response = issue_command_and_unwrap(&mut server, &command);\n    assert_eq!(response, \"2\");\n  }\n\n  #[test]\n  fn spawn_server_bad_json_test() {\n    let mut server = MacroServer::new().unwrap();\n    let command = \"[{INVALID_JSON\";\n    server.send_string(command).unwrap();\n    let response = server.receive_string().unwrap();\n    let response = json::parse(&response).unwrap();\n    let expected_response =\n      object!{\n        \"error_code\" => 30, // ERR_INVALID_DATA\n        \"error_string\" => \"Invalid JSON Expected key\",\n        \"response_string\" => \"\",\n      };\n    assert_eq!(response, expected_response);\n  }\n\n  #[test]\n  fn healthy_server_test() {\n    let mut server = MacroServer::new().unwrap();\n    assert!(server.is_process_healthy());\n  }\n\n  #[test]\n  #[ignore = \"Race condition (see issue #127)\"]\n  fn unhealthy_server_test() {\n    let mut server = MacroServer::new().unwrap();\n    let command = ServerCommand::Eval(String::from(\"GDLisp.get_tree().quit()\"));\n    issue_command_and_unwrap(&mut server, &command);\n\n    // The quit command will terminate the Godot subprocess within\n    // 1/60th of a second (Godot will terminate at the end of the\n    // current frame, and the default framerate is 60 frames per\n    // second). So we'll wait 300 milliseconds to make sure it has\n    // time to terminate.\n    sleep(Duration::from_millis(300));\n\n    assert!(!server.is_process_healthy());\n  }\n\n}\n"
  },
  {
    "path": "src/runner/macro_server/named_file_server.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! A server which manages macro resolution and can run Godot code.\n//!\n//! A [`NamedFileServer`] is a more powerful form of\n//! [`MacroServer`](super::MacroServer). Whereas the latter manages\n//! the primitive operations of sending commands to and from a Godot\n//! process, a `NamedFileServer` manages the higher-level operations\n//! of declaring and calling macros.\n\nuse crate::sxp::ast::AST;\nuse crate::runner::macro_server::lazy::LazyServer;\nuse crate::compile::names;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse crate::compile::symbol_table::local_var::VarName;\nuse crate::compile::symbol_table::function_call::{FnCall, FnScope, FnSpecs, FnName};\nuse crate::compile::symbol_table::call_magic::compile_default_call;\nuse crate::compile::stmt_wrapper::{self, StmtWrapper};\nuse crate::gdscript::expr::{Expr as GDExpr};\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::library;\nuse crate::pipeline::error::{PError, IOError};\nuse crate::pipeline::source::SourceOffset;\nuse crate::AST_PARSER;\nuse super::command::ServerCommand;\nuse super::response;\n\nuse tempfile::NamedTempFile;\nuse serde::{Serialize, Deserialize};\n\nuse std::collections::HashMap;\nuse std::path::Path;\nuse std::io;\nuse std::convert::TryFrom;\n\n/// A `NamedFileServer` maintains a [`LazyServer`], as well as a\n/// registry of the macros which have been uploaded to the server.\n///\n/// Macros are normally uploaded to the file server via\n/// [`stand_up_macro`](NamedFileServer::stand_up_macro), which also\n/// adds the macro to the server's registry for later access.\n/// Alternatively, files which do *not* contain macros can be uploaded\n/// with [`stand_up_file`](NamedFileServer::stand_up_file). The latter\n/// method loads a file onto the macro server without adding it to the\n/// registry. Hence, the file is available for other macros to use,\n/// provided they know its name, but it is unavailable for direct\n/// calling, since it is not a macro. Finally,\n/// [`add_reserved_macro`](NamedFileServer::add_reserved_macro) is\n/// used to add data to the registry without standing up any files.\n/// This is used to add standard library macros (which are side-loaded\n/// using another mechanism before `NamedFileServer` is even\n/// constructed) to the registry.\n///\n/// Macros are indexed by [`MacroID`], a wrapper struct around `u32`.\npub struct NamedFileServer {\n  server: LazyServer,\n  macro_files: HashMap<MacroID, MacroCall>,\n  next_id: MacroID,\n  next_reserved_id: MacroID,\n}\n\n/// A simple identifier type which is used as the key type in\n/// [`NamedFileServer`].\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]\n#[repr(transparent)]\npub struct MacroID(u32);\n\n#[derive(Debug)]\nstruct MacroCall {\n  index: u32, // Index in the lookup table on the GDScript side.\n  #[allow(dead_code)]\n  original_name: String, // Probably not needed, but we have it so we may as well keep track of it.\n  name: String,\n  #[allow(dead_code)]\n  file: Option<NamedTempFile>, // Macros can optionally retain a file resource, which will be deleted when the macro is discarded from scope.\n}\n\nconst RESERVED_MACRO_INDEX: u32 = u32::MAX;\n\n// TODO Make this return GDError like it probably should.\nfn response_to_string(response: response::ServerResponse) -> io::Result<String> {\n  String::try_from(response).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))\n}\n\n#[allow(clippy::new_without_default)]\nimpl NamedFileServer {\n\n  /// Constructs a new `NamedFileServer`. The server does not\n  /// initially spawn off a Godot process and will only do so once\n  /// required to (similar to [`LazyServer`]).\n  pub fn new() -> NamedFileServer {\n    NamedFileServer {\n      server: LazyServer::new(),\n      macro_files: HashMap::new(),\n      next_id: MacroID::smallest_unreserved(),\n      next_reserved_id: MacroID::smallest_stdlib(),\n    }\n  }\n\n  /// Adds a reserved standard library macro to this file server's\n  /// known macros list.\n  ///\n  /// A reserved macro is a special kind of macro that does not have\n  /// its own file. Instead, a reserved macro is a macro that is\n  /// side-loaded onto the macro server via some other means (usually,\n  /// by being present in the standard library file `GDLisp.lisp`,\n  /// which gets preloaded into the macro server at process start\n  /// time). This function does *not* instruct the Godot process to\n  /// load any new files or to do anything at all. Instead, this\n  /// function simply makes the `NamedFileServer` struct aware that\n  /// there is a macro with the given name in the standard library\n  /// file. It is the caller's responsibility to ensure that the\n  /// information is actually correct.\n  pub fn add_reserved_macro(&mut self, name: String) -> MacroID {\n    let id = self.next_reserved_id;\n    self.next_reserved_id = self.next_reserved_id.next();\n    self.macro_files.insert(id, MacroCall {\n      index: RESERVED_MACRO_INDEX,\n      original_name: name.clone(),\n      name,\n      file: None,\n    });\n    id\n  }\n\n  fn load_file_on_server(&mut self, path: &Path) -> io::Result<u32> {\n    let server = self.server.get_mut()?;\n    let cmd = ServerCommand::Load((*path.to_string_lossy()).to_owned());\n    let result: String = response_to_string(server.issue_command(&cmd)?)?;\n    result.parse().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))\n  }\n\n  /// Stand up a file on the `NamedFileServer`.\n  ///\n  /// This should be used instead of\n  /// [`stand_up_macro`](NamedFileServer::stand_up_macro) in cases\n  /// where the resulting file will never be called directly.\n  /// Specifically, this should be used to supply the macro server\n  /// with resources that are not directly macros but which may be\n  /// needed indirectly by other macros in the future. The file will\n  /// simply be loaded into the server using [`ServerCommand::Load`]\n  /// blindly, and it is the caller's responsibility to keep track of\n  /// the filename and provide some way to access the loaded file.\n  pub fn stand_up_file(&mut self, file: NamedTempFile) -> io::Result<()> {\n    self.load_file_on_server(file.path())?;\n    Ok(())\n  }\n\n  /// Stand up a file as a macro file on the `NamedFileServer`.\n  ///\n  /// This is the more powerful form of `stand_up_file`. In addition\n  /// to physically loading the file via [`ServerCommand::Load`], this\n  /// method also stores the macro, with the given name and argument\n  /// list, in the `NamedFileServer`'s macro registry, which can later\n  /// be accessed via\n  /// [`run_server_file`](NamedFileServer::run_server_file).\n  ///\n  /// This method returns a [`MacroID`] which can be used later to\n  /// call the macro.\n  pub fn stand_up_macro(&mut self, name: String, file: NamedTempFile) -> io::Result<MacroID> {\n    let idx = self.load_file_on_server(file.path())?;\n    let gdname = names::lisp_to_gd(&name);\n    let call = MacroCall { index: idx, original_name: name, name: gdname, file: Some(file) };\n    let id = self.next_id;\n    self.next_id = self.next_id.next();\n    self.macro_files.insert(id, call);\n    Ok(id)\n  }\n\n  fn get_file(&self, id: MacroID) -> Option<&MacroCall> {\n    self.macro_files.get(&id)\n  }\n\n  /// Run a macro with the given arguments. `id` should be a\n  /// [`MacroID`] returned from a prior call to\n  /// [`stand_up_macro`](NamedFileServer::stand_up_macro) or similar,\n  /// and `args` should be a list of Godot expressions to provide as\n  /// the arguments. In case of Godot errors, including but not\n  /// limited to parsing errors, IO communication errors, or semantic\n  /// errors in macro evaluation, an `Err` value is returned.\n  ///\n  /// `prelude` is a vector of statements which will be run before the\n  /// actual macro call and can be used to set up the environment or\n  /// provide local variables to the macro arguments.\n  ///\n  /// # Panics\n  ///\n  /// This method will panic if given an invalid `id` value. The `id`\n  /// must be the macro ID from a prior invocation of `stand_up_macro`\n  /// or [`add_reserved_macro`](NamedFileServer::add_reserved_macro)\n  /// on the same `NamedFileServer` instance.\n  pub fn run_server_file(&mut self, id: MacroID, prelude: Vec<Stmt>, specs: FnSpecs, args: Vec<GDExpr>, pos: SourceOffset)\n                         -> Result<AST, PError> {\n    let result = self.run_server_file_str(id, prelude, specs, args, pos)?;\n    let parsed = AST_PARSER.parse(&result)?;\n    //println!(\"{}\", parsed);\n    Ok(parsed)\n  }\n\n  pub fn run_server_file_str(&mut self, id: MacroID, prelude: Vec<Stmt>, specs: FnSpecs, args: Vec<GDExpr>, pos: SourceOffset)\n                             -> Result<String, PError> {\n    let call = self.get_file(id).expect(\"Invalid MacroID in run_server_file\");\n    let call_object =\n      if id.is_reserved() {\n        let gdlisp_root = library::gdlisp_root_var_name();\n        FnName::OnLocalVar(Box::new(gdlisp_root))\n      } else {\n\n        // A non-reserved macro should never end up at this position.\n        // (This would get caught by the below try_from, but we can\n        // supply a better error message in this specific case)\n        if call.index == RESERVED_MACRO_INDEX {\n          panic!(\"Non-reserved macro at reserved index {}\", call.index);\n        }\n\n        let index_error_message = format!(\"Macro reference indices exceeded i32 range, got {}\", call.index);\n        let call_index = i32::try_from(call.index).expect(&index_error_message);\n\n        let target = VarName::SubscriptedConstant(\n          Box::new(VarName::ImportedConstant(\n            Box::new(VarName::FileConstant(String::from(\"MAIN\"))),\n            String::from(\"loaded_files\"),\n          )),\n          call_index,\n        );\n        FnName::OnLocalVar(Box::new(target))\n      };\n    let call = FnCall {\n      scope: FnScope::Global,\n      object: call_object,\n      function: call.name.to_owned(),\n      specs: specs,\n      is_macro: true,\n    };\n    self.do_macro_call(prelude, call, args, pos)\n  }\n\n  fn do_macro_call(&mut self,\n                   prelude: Vec<Stmt>,\n                   call: FnCall,\n                   args: Vec<GDExpr>,\n                   pos: SourceOffset) -> Result<String, PError> {\n    let server = self.server.get_mut().map_err(|err| IOError::new(err, pos))?;\n    let expr = compile_default_call(call, args, pos)?;\n    let mut stmts = prelude;\n    stmts.push(stmt_wrapper::Return.wrap_expr(expr));\n    let mut exec_str = String::new();\n    Stmt::write_gd_stmts(stmts.iter(), &mut exec_str, 4).expect(\"Could not write to string in do_macro_call\");\n    let result = server.issue_command(&ServerCommand::Exec(exec_str)).map_err(|err| IOError::new(err, pos))?;\n    let result = response_to_string(result).map_err(|err| IOError::new(err, pos))?;\n    Ok(result)\n  }\n\n  /// Issues a command to the server setting its global name generator\n  /// to `gen`.\n  pub fn set_global_name_generator(&mut self, gen: &FreshNameGenerator) -> io::Result<()> {\n    let server = self.server.get_mut()?;\n    let json = gen.to_json();\n    let exec_str = format!(\"    GDLisp.__gdlisp_Global_name_generator = GDLisp.sys_DIV_FreshNameGenerator.from_json({})\", json);\n    let cmd = ServerCommand::Exec(exec_str);\n    let _result = response_to_string(server.issue_command(&cmd)?)?;\n    Ok(())\n  }\n\n  /// Issues a command to the server setting the global name generator\n  /// to a newly-constructed name generator.\n  pub fn reset_global_name_generator(&mut self) -> io::Result<()> {\n    let server = self.server.get_mut()?;\n    let exec_str = String::from(r#\"    GDLisp.__gdlisp_Global_name_generator = GDLisp.sys_DIV_FreshNameGenerator.new([], \"\")\"#);\n    let cmd = ServerCommand::Exec(exec_str);\n    let _result = response_to_string(server.issue_command(&cmd)?)?;\n    Ok(())\n  }\n\n  /// Returns true if the Godot subprocess is running and is healthy.\n  /// If the child process has started, then this delegates to\n  /// [`MacroServer::is_process_healthy`](super::MacroServer::is_process_healthy).\n  /// If not, then this returns false, as the process is not running.\n  pub fn is_process_healthy(&mut self) -> bool {\n    match self.server.get_mut_if_initialized() {\n      None => false,\n      Some(server) => server.is_process_healthy(),\n    }\n  }\n\n}\n\nimpl MacroID {\n\n  // Current partitioning scheme:\n  //\n  // 0-63: Unused (future use)\n  // 64-511: Built-in (stdlib) macros\n  // 512-1023: Unused (future use)\n  // 1024-max: User-defined macros\n\n  const RESERVED: u32 = 1024;\n\n  fn smallest_stdlib() -> MacroID {\n    MacroID(64)\n  }\n\n  fn smallest_unreserved() -> MacroID {\n    MacroID(MacroID::RESERVED)\n  }\n\n  fn is_reserved(self) -> bool {\n    self.0 < MacroID::RESERVED\n  }\n\n  fn next(self) -> MacroID {\n    MacroID(self.0 + 1)\n  }\n\n}\n"
  },
  {
    "path": "src/runner/macro_server/response.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Types for representing the success or failure of a\n//! [`ServerCommand`](super::command::ServerCommand).\n\nuse json::JsonValue;\n\nuse std::convert::TryFrom;\nuse std::fmt;\nuse std::error::Error;\n\n/// A response received from the server as the result of a command.\n///\n/// `ServerResponse` objects are typically constructed using\n/// [`TryFrom::<JsonValue>`] on the actual JSON data received from the\n/// server.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub enum ServerResponse {\n  Failure(Failure),\n  Success(Success),\n}\n\n/// A negative response, indicating that something went wrong during\n/// command evaluation.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct Failure {\n  /// A [Godot error\n  /// code](https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#class-globalscope-constant-ok)\n  /// indicating what went wrong.\n  pub error_code: u32,\n  /// A user-friendly string detailing the error.\n  pub error_string: String,\n}\n\n/// A positive response, indicating successful completion of the\n/// command.\n#[derive(Clone, Debug, Eq, PartialEq)]\npub struct Success {\n  /// A string detailing the response. The exact format and use of\n  /// this string is entirely dependent on the command issued.\n  pub response_string: String,\n}\n\n/// An error during parsing of a [`JsonValue`] as a\n/// [`ServerResponse`].\n#[derive(Clone, Debug)]\npub enum ParseError {\n  MalformedResponse(String),\n}\n\nfn fail(json: &JsonValue) -> ParseError {\n  ParseError::MalformedResponse(json.to_string())\n}\n\nimpl TryFrom<ServerResponse> for Success {\n  type Error = Failure;\n\n  fn try_from(resp: ServerResponse) -> Result<Success, Failure> {\n    match resp {\n      ServerResponse::Failure(err) => Err(err),\n      ServerResponse::Success(val) => Ok(val),\n    }\n  }\n\n}\n\nimpl TryFrom<ServerResponse> for String {\n  type Error = Failure;\n\n  fn try_from(resp: ServerResponse) -> Result<String, Failure> {\n    let Success { response_string } = Success::try_from(resp)?;\n    Ok(response_string)\n  }\n\n}\n\n/// Responses are received as JSON objects which have the following keys.\n///\n/// * error_code (required) - 0 if successful, or a Godot error code\n/// if failed.\n///\n/// * error_string (required) - A string of text, possibly empty,\n/// specifying more details about the error.\n///\n/// response_string (required) - A string of text specifying the\n/// response. This string must be empty if an error occurred.\nimpl TryFrom<JsonValue> for ServerResponse {\n  type Error = ParseError;\n\n  fn try_from(json: JsonValue) -> Result<ServerResponse, ParseError> {\n    let obj = match &json {\n      JsonValue::Object(obj) => obj,\n      _ => return Err(fail(&json)),\n    };\n    let error_code = obj.get(\"error_code\")\n      .and_then(|err| err.as_u32())\n      .ok_or_else(|| fail(&json))?;\n    if error_code == 0 {\n      // Success case\n      let response_string = obj.get(\"response_string\").and_then(|r| r.as_str()).ok_or_else(|| fail(&json))?;\n      let response_string = response_string.to_owned();\n      Ok(ServerResponse::Success(Success { response_string }))\n    } else {\n      // Failure case\n      let error_string = obj.get(\"error_string\").and_then(|r| r.as_str()).ok_or_else(|| fail(&json))?;\n      let error_string = error_string.to_owned();\n      Ok(ServerResponse::Failure(Failure { error_code, error_string }))\n    }\n  }\n\n}\n\nimpl fmt::Display for ParseError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      ParseError::MalformedResponse(s) => {\n        writeln!(f, \"{}\", s)\n      }\n    }\n  }\n}\n\nimpl fmt::Display for Failure {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    writeln!(f, \"{} {}\", self.error_code, self.error_string)\n  }\n}\n\nimpl Error for Failure {}\n"
  },
  {
    "path": "src/runner/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Mechanisms for running Godot code in child processes.\n//!\n//! The functions defined in this module are general-purpose functions\n//! for running arbitrary Godot code in child processes. The submodule\n//! [`macro_server`] provides the more specific use-case of starting\n//! up a Godot process as a server and communicating with it via TCP.\n\npub mod godot;\npub mod into_gd_file;\npub mod macro_server;\npub mod named_file;\npub mod path;\npub mod version;\n\nuse godot::GodotCommand;\n\nuse tempfile::{Builder, NamedTempFile};\n\nuse std::io::{self, Seek, SeekFrom};\nuse std::fmt;\nuse std::error::Error;\n\n/// An error in the exit status of the process spawned by\n/// `dump_json_api`.\n#[derive(Debug, Clone)]\npub struct JsonApiError;\n\nimpl fmt::Display for JsonApiError {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"error reading JSON API from Godot binary\")\n  }\n}\n\nimpl Error for JsonApiError {}\n\n/// Runs a Godot process to dump the JSON API for GDNative to a\n/// (newly-created) temporary file. This method returns the temporary\n/// file object. The corresponding file will be deleted when the\n/// returned value is dropped.\npub fn dump_json_api() -> io::Result<NamedTempFile> {\n  let mut file = make_tmp_file()?;\n  let status = GodotCommand::base()\n    .quiet()\n    .arg(\"--gdnative-generate-json-api\")\n    .arg(file.path())\n    .status()?;\n  if !status.success() {\n    return Err(io::Error::new(io::ErrorKind::Other, JsonApiError));\n  }\n  // Not sure if this is necessary, but better safe than sorry. Ensure\n  // that the seek position of the opened file handle is at 0.\n  file.seek(SeekFrom::Start(0))?;\n  Ok(file)\n}\n\nfn make_tmp_file() -> io::Result<NamedTempFile> {\n  Builder::new()\n    .prefix(\"__gdlisp_test\")\n    .suffix(\".gd\")\n    .rand_bytes(5)\n    .tempfile()\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::runner::into_gd_file::IntoGDFile;\n  use std::io::Write;\n\n  fn run_with_temporary(data: &str) -> io::Result<String> {\n    let mut tmp = make_tmp_file()?;\n    data.write_to_gd(&mut tmp)?;\n    tmp.flush()?;\n\n    let out =\n      GodotCommand::base()\n      .script_file(tmp.path())\n      .quit_after_one()\n      .output()?;\n    let text = String::from_utf8_lossy(&out.stdout);\n    Ok(text.into())\n  }\n\n  #[test]\n  fn run_minimal_test() {\n    run_with_temporary(r#\"\nextends SceneTree\n\nfunc _init():\n    pass\n\"#).unwrap();\n  }\n\n  #[test]\n  fn run_printing_test() {\n    let out = run_with_temporary(r#\"\nextends SceneTree\n\nfunc _init():\n    print(\"999_SAMPLE_OUTPUT_STRING_999\")\n\"#).unwrap();\n    assert!(out.contains(\"999_SAMPLE_OUTPUT_STRING_999\"));\n  }\n\n  #[test]\n  fn dump_json_api_test() {\n    // Checks that dump_json_api completes successfully and produces\n    // output. It does *not* check the contents of the output. That is\n    // tested over in `crate::gdscript::library::gdnative`.\n    let mut tempfile = dump_json_api().unwrap();\n    let file_len = tempfile.seek(SeekFrom::End(0)).unwrap();\n    assert!(file_len > 0);\n  }\n\n}\n"
  },
  {
    "path": "src/runner/named_file.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Defines the [`NamedFile`] trait and some trivial implementations.\n\nuse tempfile::NamedTempFile;\n\nuse std::path::{Path, PathBuf};\nuse std::io;\nuse std::fs::File;\nuse std::borrow::ToOwned;\n\n/// Instances of `NamedFile` are file-like objects which have a path.\n/// Any object which has a reasonable notion of \"file path\" can\n/// implement this trait.\npub trait NamedFile : io::Write {\n\n  /// Returns the path of the file represented by this object.\n  fn path(&self) -> &Path;\n\n}\n\n/// A `SimpleNamedFile` is a [`NamedFile`] which simply contains a\n/// path and the file with no additional behavior. This struct is most\n/// useful in situations where [`NamedTempFile`]-like semantics are\n/// desired, but we wish to persist the relevant file.\npub struct SimpleNamedFile(PathBuf, File);\n\nimpl SimpleNamedFile {\n\n  /// Construct a `SimpleNamedFile`. This will attempt to open the\n  /// file for writing, and in case of failure will return the\n  /// appropriate IO error.\n  pub fn create<P : AsRef<Path>>(path: P) -> io::Result<SimpleNamedFile> {\n    let buf = path.as_ref().to_owned();\n    File::create(path).map(|f| SimpleNamedFile(buf, f))\n  }\n\n}\n\nimpl io::Write for SimpleNamedFile {\n\n  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {\n    self.1.write(buf)\n  }\n\n  fn flush(&mut self) -> io::Result<()> {\n    self.1.flush()\n  }\n\n}\n\nimpl NamedFile for SimpleNamedFile {\n  fn path(&self) -> &Path {\n    self.0.as_ref()\n  }\n}\n\nimpl NamedFile for NamedTempFile {\n  fn path(&self) -> &Path {\n    NamedTempFile::path(self)\n  }\n}\n"
  },
  {
    "path": "src/runner/path.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Structure for representing a Godot resource path.\n//!\n//! Godot resource paths are represented using a special syntax. There\n//! are three types of paths that can be represented in this way.\n//!\n//! 1. **Absolute paths** have no prefix and are written as-is. These\n//! paths start from the file-system root and refer to a specific\n//! location.\n//!\n//! 2. **Resource paths** begin with `res://` and represent a path\n//! relative to the project directory.\n//!\n//! 3. **User paths** begin with `user://` and represent a path\n//! relative to some user-local storage directory, usually used for\n//! save files or the like.\n//!\n//! This module defines [`RPathBuf`], a [`PathBuf`]-like structure\n//! which represents the above file path formats.\n\n// An RPathBuf consists of a PathBuf together with a specifier\n\nuse std::path::{PathBuf, Path, Components};\nuse std::convert::TryFrom;\nuse std::str::FromStr;\nuse std::fmt;\nuse std::ffi::OsStr;\n\n/// An `RPathBuf` represents a file path using Godot's format.\n///\n/// See the [module-level documentation](crate::runner::path) for more\n/// details.\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub struct RPathBuf {\n  source: PathSrc,\n  path: PathBuf,\n}\n\n/// An [`RPathBuf`] can refer to an absolute path, a resource path, or\n/// a user path.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\npub enum PathSrc {\n  /// A user path, relative to some user-local storage directory.\n  User,\n  /// A resource path, relative to the project direction.\n  Res,\n  /// An absolute path, from the file system root directory.\n  Absolute,\n}\n\n/// Errors which can occur when constructing an [`RPathBuf`] object.\n#[derive(Clone, Copy, Debug)]\npub enum TryFromRPathBufError {\n  /// A relative path was expected (based on the [`PathSrc`]) but an\n  /// absolute one was provided.\n  ExpectedRelative,\n  /// An absolute path was expected (based on the [`PathSrc`]) but a\n  /// relative one was provided.\n  ExpectedAbsolute,\n}\n\nimpl RPathBuf {\n\n  /// Construct an `RPathBuf` from a source and a file path.\n  ///\n  /// Note that `path` should be a relative path if and only if\n  /// [`source.should_be_relative_path()`](PathSrc::should_be_relative_path)\n  /// is true. If this condition is violated, then an error will be\n  /// returned from this function.\n  pub fn new(source: PathSrc, path: PathBuf) -> Result<RPathBuf, TryFromRPathBufError> {\n    if source.should_be_relative_path() == path.is_relative() {\n      Ok(RPathBuf { source, path })\n    } else if source.should_be_relative_path() {\n      Err(TryFromRPathBufError::ExpectedRelative)\n    } else {\n      Err(TryFromRPathBufError::ExpectedAbsolute)\n    }\n  }\n\n  /// Gets the path's source type.\n  pub fn source(&self) -> PathSrc {\n    self.source\n  }\n\n  /// Gets the file path.\n  pub fn path(&self) -> &Path {\n    &self.path\n  }\n\n  /// Gets the file path, mutably.\n  ///\n  /// Callers of this function are expected to maintain the\n  /// constructor's precondition, namely that the path is relative if\n  /// and only if [`PathSrc::should_be_relative_path`] is true.\n  pub fn path_mut(&mut self) -> &mut PathBuf {\n    &mut self.path\n  }\n\n  /// Consumes `self` and produce its file path as a [`PathBuf`].\n  pub fn into_path(self) -> PathBuf {\n    self.path\n  }\n\n  /// Returns the path's components. Equivalent to\n  /// `self.path().components()`.\n  pub fn components(&self) -> Components<'_> {\n    self.path().components()\n  }\n\n  /// Returns the path's extension. Equivalent to\n  /// `self.path().extension()`.\n  pub fn extension(&self) -> Option<&OsStr> {\n    self.path().extension()\n  }\n\n  /// Returns the path's components, like [`RPathBuf::components`].\n  /// However, if the path is an absolute path, then the root `/` will\n  /// be stripped from the components.\n  pub fn components_no_root(&self) -> Components<'_> {\n    let mut comp = self.components();\n    if self.path().has_root() {\n      let _ignore_root = comp.next();\n    }\n    comp\n  }\n\n  /// Return the path as a string.\n  ///\n  /// Forward slashes will always be used as the path delimiter, even\n  /// on Windows. This is consistent with Godot's behavior.\n  pub fn path_to_string<P : AsRef<Path> + ?Sized>(path: &P) -> String {\n    path.as_ref().to_string_lossy().replace('\\\\', \"/\")\n  }\n\n}\n\n/// Displays the path as a string, using Godot's `user://` and\n/// `res://` syntax as appropriate. Absolute paths will be shown\n/// verbatim with no prefix.\nimpl fmt::Display for RPathBuf {\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    let path = RPathBuf::path_to_string(self.path());\n    write!(f, \"{}{}\", self.source().prefix(), path)\n  }\n}\n\nimpl PathSrc {\n\n  /// Returns whether a path with this source type should be a\n  /// relative path.\n  ///\n  /// All source types except [`PathSrc::Absolute`] should refer to\n  /// relative paths.\n  pub fn should_be_relative_path(self) -> bool {\n    self != PathSrc::Absolute\n  }\n\n  /// Returns the Godot-style prefix (`user://` or `res://`) for the\n  /// path type.\n  ///\n  /// In the case of [`PathSrc::Absolute`], an empty string is\n  /// returned, as no prefix is necessary in this case.\n  pub fn prefix(self) -> &'static str {\n    match self {\n      PathSrc::User => \"user://\",\n      PathSrc::Res => \"res://\",\n      PathSrc::Absolute => \"\",\n    }\n  }\n\n}\n\n/// Converts a Godot-style import string (potentially beginning with\n/// `res://` or `user://`) into the appropriate type of [`RPathBuf`]\n/// object.\nimpl TryFrom<String> for RPathBuf {\n  type Error = TryFromRPathBufError;\n\n  fn try_from(path: String) -> Result<RPathBuf, TryFromRPathBufError> {\n    if let Some(s) = path.strip_prefix(\"res://\") {\n      RPathBuf::new(PathSrc::Res, PathBuf::from_str(s).unwrap()) // Infallible\n    } else if let Some(s) = path.strip_prefix(\"user://\") {\n      RPathBuf::new(PathSrc::User, PathBuf::from_str(s).unwrap()) // Infallible\n    } else {\n      // Assume it's an absolute path\n      RPathBuf::new(PathSrc::Absolute, PathBuf::from(path))\n    }\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn test_rpathbuf_new_relative() {\n    assert!(RPathBuf::new(PathSrc::Res, PathBuf::from_str(\"a/b\").unwrap()).is_ok());\n    assert!(RPathBuf::new(PathSrc::User, PathBuf::from_str(\"a/b\").unwrap()).is_ok());\n    assert!(RPathBuf::new(PathSrc::Absolute, PathBuf::from_str(\"a/b\").unwrap()).is_err());\n  }\n\n  #[test]\n  #[cfg(target_family = \"windows\")]\n  fn test_rpathbuf_new_absolute_windows() {\n    assert!(RPathBuf::new(PathSrc::Absolute, PathBuf::from_str(\"C:/a/b\").unwrap()).is_ok());\n    assert!(RPathBuf::new(PathSrc::Res, PathBuf::from_str(\"C:/a/b\").unwrap()).is_err());\n    assert!(RPathBuf::new(PathSrc::User, PathBuf::from_str(\"C:/a/b\").unwrap()).is_err());\n  }\n\n  #[test]\n  #[cfg(target_family = \"unix\")]\n  fn test_rpathbuf_new_absolute_unix() {\n    assert!(RPathBuf::new(PathSrc::Absolute, PathBuf::from_str(\"/a/b\").unwrap()).is_ok());\n    assert!(RPathBuf::new(PathSrc::Res, PathBuf::from_str(\"/a/b\").unwrap()).is_err());\n    assert!(RPathBuf::new(PathSrc::User, PathBuf::from_str(\"/a/b\").unwrap()).is_err());\n  }\n\n  #[test]\n  fn test_rpathbuf_parse() {\n\n    let res = RPathBuf::try_from(String::from(\"res://foo/bar\"));\n    assert!(res.is_ok());\n    let res = res.unwrap();\n    assert_eq!(res.source(), PathSrc::Res);\n    assert_eq!(res.path(), PathBuf::from_str(\"foo/bar\").unwrap());\n\n    let user = RPathBuf::try_from(String::from(\"user://foo/bar\"));\n    assert!(user.is_ok());\n    let user = user.unwrap();\n    assert_eq!(user.source(), PathSrc::User);\n    assert_eq!(user.path(), PathBuf::from_str(\"foo/bar\").unwrap());\n\n  }\n\n  #[test]\n  #[cfg(target_family = \"windows\")]\n  fn test_rpath_parse_windows() {\n\n    let abs = RPathBuf::try_from(String::from(\"C:/foo/bar\"));\n    assert!(abs.is_ok());\n    let abs = abs.unwrap();\n    assert_eq!(abs.source(), PathSrc::Absolute);\n    assert_eq!(abs.path(), PathBuf::from_str(\"C:/foo/bar\").unwrap());\n  }\n\n  #[test]\n  #[cfg(target_family = \"unix\")]\n  fn test_rpath_parse_unix() {\n\n    let abs = RPathBuf::try_from(String::from(\"/foo/bar\"));\n    assert!(abs.is_ok());\n    let abs = abs.unwrap();\n    assert_eq!(abs.source(), PathSrc::Absolute);\n    assert_eq!(abs.path(), PathBuf::from_str(\"/foo/bar\").unwrap());\n  }\n\n  #[test]\n  fn test_rpathbuf_display() {\n    assert_eq!(RPathBuf::try_from(String::from(\"res://foo/bar\")).unwrap().to_string(), \"res://foo/bar\");\n    assert_eq!(RPathBuf::try_from(String::from(\"user://foo/bar\")).unwrap().to_string(), \"user://foo/bar\");\n  }\n\n  #[test]\n  #[cfg(target_family = \"windows\")]\n  fn test_rpathbuf_display_windows() {\n    assert_eq!(RPathBuf::try_from(String::from(\"C:/foo/bar\")).unwrap().to_string(), \"C:/foo/bar\");\n  }\n\n  #[test]\n  #[cfg(target_family = \"unix\")]\n  fn test_rpathbuf_display_unix() {\n    assert_eq!(RPathBuf::try_from(String::from(\"/foo/bar\")).unwrap().to_string(), \"/foo/bar\");\n  }\n\n}\n"
  },
  {
    "path": "src/runner/version.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helper to get the Godot version.\n\nuse std::process::{Command, Stdio};\nuse std::io;\nuse std::collections::VecDeque;\nuse std::str::FromStr;\n\n/// A version for a piece of software, in Semantic Versioning.\n#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]\npub struct Version {\n  pub major: i32,\n  pub minor: i32,\n  pub patch: i32,\n}\n\n/// Version information, together with modifiers applied after the\n/// base Semantic Versioning information.\n#[derive(Clone, Default, Debug, PartialEq, Eq)]\npub struct VersionInfo {\n  pub version: Version,\n  pub modifiers: Vec<String>,\n}\n\nimpl Version {\n\n  /// Constructs a version object representing a version of the\n  /// software.\n  pub fn new(major: i32, minor: i32, patch: i32) -> Self {\n    Version { major, minor, patch }\n  }\n\n  /// Converts the version number into a single integer value which\n  /// represents the version information. This is *not* a one-to-one\n  /// mapping. It is intended to be a human-readable number\n  /// representing the same version, which preserves the [`Version`]\n  /// order for reasonable values.\n  pub fn into_i32(self) -> i32 {\n    self.major * 1000000 + self.minor * 10000 + self.patch * 100\n  }\n\n}\n\nimpl VersionInfo {\n\n  /// An empty version info object, corresponding to the version\n  /// string `\"0.0.0\"`.\n  pub fn new() -> Self {\n    VersionInfo::default()\n  }\n\n  /// Given a Godot version string, parse it as a [`VersionInfo`]\n  /// object. This method will not fail. Any missing fields will be\n  /// initialized to an appropriate default (zero in the case of\n  /// version numbers).\n  pub fn parse(version_string: &str) -> VersionInfo {\n    if version_string.is_empty() {\n      // Corner case, since .split() will give us a single empty\n      // string (rather than no values) in the case of an empty string\n      // as input.\n      return VersionInfo::default();\n    }\n\n    let mut atoms: VecDeque<&str> = version_string.split('.').collect();\n\n    // Try to identify major, minor, patch version\n    let major = pop_version_number(&mut atoms).unwrap_or(0);\n    let minor = pop_version_number(&mut atoms).unwrap_or(0);\n    let patch = pop_version_number(&mut atoms).unwrap_or(0);\n\n    let version = Version { major, minor, patch };\n    VersionInfo { version, modifiers: atoms.into_iter().map(str::to_owned).collect() }\n  }\n\n}\n\n/// Get the version of Godot that is present on the system path.\n///\n/// The format of the resulting string is dependent on Godot and\n/// should generally be used as an informative message to the user,\n/// not compared to implement a feature check.\n///\n/// In the case of IO error (including the situation where no\n/// executable named `godot` is on the system path), returns an\n/// appropriate error.\npub fn get_godot_version_as_string() -> io::Result<String> {\n  let output =\n    Command::new(\"godot\")\n    .arg(\"--version\")\n    .stderr(Stdio::null())\n    .stdout(Stdio::piped())\n    .output()?;\n  let mut s = String::from_utf8_lossy(&output.stdout).into_owned();\n  s.pop(); // Remove newline from end\n  Ok(s)\n}\n\n/// Get the version of Godot that is present on the system path,\n/// parsing it as a version string.\npub fn get_godot_version() -> io::Result<VersionInfo> {\n  let version_string = get_godot_version_as_string()?;\n  Ok(VersionInfo::parse(&version_string))\n}\n\nfn pop_version_number(atoms: &mut VecDeque<&str>) -> Option<i32> {\n  // Only pop the first element if it can actually be parsed.\n  let v = atoms.front().and_then(|atom| i32::from_str(atom).ok())?;\n  atoms.pop_front();\n  Some(v)\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn test_parse_basic_version_string() {\n\n    assert_eq!(\n      VersionInfo::parse(\"1.2.3\"),\n      VersionInfo {\n        version: Version::new(1, 2, 3),\n        modifiers: vec!(),\n      },\n    );\n\n    assert_eq!(\n      VersionInfo::parse(\"1.2\"),\n      VersionInfo {\n        version: Version::new(1, 2, 0),\n        modifiers: vec!(),\n      },\n    );\n\n    assert_eq!(\n      VersionInfo::parse(\"1\"),\n      VersionInfo {\n        version: Version::new(1, 0, 0),\n        modifiers: vec!(),\n      },\n    );\n\n    assert_eq!(\n      VersionInfo::parse(\"\"),\n      VersionInfo {\n        version: Version::new(0, 0, 0),\n        modifiers: vec!(),\n      },\n    );\n  }\n\n  #[test]\n  fn test_parse_nonsense_version_string() {\n    // Parsing version strings does not fail. It may just produce\n    // minimal information.\n    assert_eq!(\n      VersionInfo::parse(\"e\"),\n      VersionInfo {\n        version: Version::new(0, 0, 0),\n        modifiers: vec!(String::from(\"e\")),\n      },\n    );\n  }\n\n  #[test]\n  fn test_parse_full_version_string() {\n    assert_eq!(\n      VersionInfo::parse(\"10.9.0.a.b.foo\"),\n      VersionInfo {\n        version: Version::new(10, 9, 0),\n        modifiers: vec!(String::from(\"a\"), String::from(\"b\"), String::from(\"foo\")),\n      },\n    );\n    assert_eq!(\n      VersionInfo::parse(\"10.9.1.a.b.foo\"),\n      VersionInfo {\n        version: Version::new(10, 9, 1),\n        modifiers: vec!(String::from(\"a\"), String::from(\"b\"), String::from(\"foo\")),\n      },\n    );\n  }\n\n  #[test]\n  fn test_parse_partial_version_string() {\n    assert_eq!(\n      VersionInfo::parse(\"10.9.a.b.foo\"),\n      VersionInfo {\n        version: Version::new(10, 9, 0),\n        modifiers: vec!(String::from(\"a\"), String::from(\"b\"), String::from(\"foo\")),\n      },\n    );\n    assert_eq!(\n      VersionInfo::parse(\"10.a.b.foo\"),\n      VersionInfo {\n        version: Version::new(10, 0, 0),\n        modifiers: vec!(String::from(\"a\"), String::from(\"b\"), String::from(\"foo\")),\n      },\n    );\n  }\n\n}\n"
  },
  {
    "path": "src/sxp/ast.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Defines the basic [`AST`] type.\n\nuse crate::pipeline::source::{SourceOffset, Sourced};\nuse crate::util::extract_err;\nuse crate::util::recursive::Recursive;\nuse super::literal::Literal;\n\nuse std::fmt;\nuse std::convert::Infallible;\nuse std::cmp::max;\n\n/// The basic type used for representing Lisp S-expressions.\n#[derive(PartialEq, Eq, Hash, Clone, Debug)]\npub enum ASTF {\n  /// An atomic AST that does not contain any further AST values\n  /// inside of it.\n  Atom(Literal),\n  /// A pair of values. The first value is referred to as the car and\n  /// the second as the cdr. All *proper* Lisp lists are made up of\n  /// cons cells and [`Literal::Nil`]. Displays as `(car . cdr)`.\n  Cons(Box<AST>, Box<AST>),\n}\n\n/// An `AST` is an [`ASTF`] together with information about the offset\n/// in the source code of the S-expression.\n#[derive(PartialEq, Eq, Hash, Clone, Debug)]\npub struct AST {\n  pub value: ASTF,\n  pub pos: SourceOffset,\n}\n\nfn fmt_list(a: &AST, b: &AST, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n  match &b.value {\n    ASTF::Atom(Literal::Nil) =>\n      // End of list; just print the known value\n      write!(f, \"{}\", a),\n    ASTF::Cons(b1, c1) => {\n      // Another cons cell in cdr; continue printing list\n      write!(f, \"{} \", a)?;\n      fmt_list(b1, c1, f)\n    },\n    _ =>\n      // Dotted list; print with dot\n      write!(f, \"{} . {}\", a, b)\n  }\n}\n\nimpl ASTF {\n\n  /// The literal nil AST value. Up to equality, there is only one\n  /// such value.\n  ///\n  /// Note that, in some Lisps, the nil value is considered to be a\n  /// symbol. In GDLisp, this is *not* the case: the nil value is a\n  /// special value distinct from all symbols.\n  pub const NIL: ASTF = ASTF::Atom(Literal::Nil);\n\n  /// An [`ASTF::Cons`] cell. This is more convenient than calling the\n  /// constructor directly, as you needn't explicitly box the values.\n  pub fn cons(car: AST, cdr: AST) -> ASTF {\n    ASTF::Cons(Box::new(car), Box::new(cdr))\n  }\n\n  /// A [`Literal::String`]. Clones the string argument into a new\n  /// [`ASTF`] value.\n  pub fn string<S>(s: S) -> ASTF\n  where String : From<S> {\n    ASTF::Atom(Literal::String(String::from(s)))\n  }\n\n  /// A [`Literal::Symbol`]. Clones the string argument into a new\n  /// [`ASTF`] value.\n  pub fn symbol<S>(s: S) -> ASTF\n  where String : From<S> {\n    ASTF::Atom(Literal::Symbol(String::from(s)))\n  }\n\n  /// A literal integer, as an [`ASTF`].\n  pub fn int(value: i32) -> ASTF {\n    ASTF::Atom(Literal::Int(value))\n  }\n\n  /// A literal float, as an [`ASTF`].\n  pub fn float(value: f32) -> ASTF {\n    ASTF::Atom(Literal::Float(value.into()))\n  }\n\n}\n\nimpl AST {\n\n  /// A new `AST` with the given value and position. Equivalent to\n  /// `AST { value, pos }`.\n  pub fn new(value: ASTF, pos: SourceOffset) -> AST {\n    AST { value, pos }\n  }\n\n  /// A [`Literal::Nil`] wrapped in `AST` with the given source\n  /// offset. Equivalent to `AST::new(ASTF::Nil, pos)`.\n  pub fn nil(pos: SourceOffset) -> AST {\n    AST::new(ASTF::Atom(Literal::Nil), pos)\n  }\n\n  /// A [`Literal::Symbol`] with the given value.\n  pub fn symbol<S>(name: S, pos: SourceOffset) -> AST\n  where String : From<S> {\n    AST::new(ASTF::symbol(name), pos)\n  }\n\n  /// A [`Literal::String`] with the given value.\n  pub fn string<S>(name: S, pos: SourceOffset) -> AST\n  where String : From<S> {\n    AST::new(ASTF::string(name), pos)\n  }\n\n  /// A [`Literal::Int`] with the given value.\n  pub fn int(value: i32, pos: SourceOffset) -> AST {\n    AST::new(ASTF::int(value), pos)\n  }\n\n  /// A [`Literal::Float`] with the given value.\n  pub fn float(value: f32, pos: SourceOffset) -> AST {\n    AST::new(ASTF::float(value), pos)\n  }\n\n  /// An [`ASTF::Cons`] with the given value.\n  pub fn cons(car: AST, cdr: AST, pos: SourceOffset) -> AST {\n    AST::new(ASTF::cons(car, cdr), pos)\n  }\n\n  fn _recurse<'a, 'b, F1, F2, E>(&'a self, func: &mut F1, default: &mut F2) -> Result<(), E>\n  where F1 : FnMut(&'b AST) -> Result<(), E>,\n        F2 : FnMut() -> Result<(), E>,\n        'a : 'b {\n    match &self.value {\n      ASTF::Cons(car, cdr) => {\n        func(car)?;\n        func(cdr)?;\n      }\n      ASTF::Atom(_) => {\n        default()?;\n      }\n    }\n    Ok(())\n  }\n\n  fn _recurse_mut<'a, 'b, F1, F2, E>(&'a mut self, func: &mut F1, default: &mut F2) -> Result<(), E>\n  where F1 : FnMut(&'b mut AST) -> Result<(), E>,\n        F2 : FnMut() -> Result<(), E>,\n        'a : 'b {\n    match &mut self.value {\n      ASTF::Cons(car, cdr) => {\n        func(&mut *car)?;\n        func(&mut *cdr)?;\n      }\n      ASTF::Atom(_) => {\n        default()?;\n      }\n    }\n    Ok(())\n  }\n\n  fn _walk_preorder<'a, 'b, F, E>(&'a self, func: &mut F) -> Result<(), E>\n  where F: FnMut(&'b AST) -> Result<(), E>,\n        'a: 'b {\n    func(self)?;\n    self._recurse(&mut |x| x._walk_preorder(func), &mut || Ok(()))\n  }\n\n  fn _walk_preorder_mut<'a, F, E>(&'a mut self, func: &mut F) -> Result<(), E>\n  where F: for<'b> FnMut(&'b mut AST) -> Result<(), E> {\n    func(self)?;\n    self._recurse_mut(&mut |x| x._walk_preorder_mut(func), &mut || Ok(()))\n  }\n\n  fn _walk_postorder<'a, 'b, F, E>(&'a self, func: &mut F) -> Result<(), E>\n  where F: FnMut(&'b AST) -> Result<(), E>,\n        'a: 'b {\n    self._recurse(&mut |x| x._walk_postorder(func), &mut || Ok(()))?;\n    func(self)\n  }\n\n  fn _walk_postorder_mut<'a, F, E>(&'a mut self, func: &mut F) -> Result<(), E>\n  where F: for<'b> FnMut(&'b mut AST) -> Result<(), E> {\n    self._recurse_mut(&mut |x| x._walk_postorder_mut(func), &mut || Ok(()))?;\n    func(self)\n  }\n\n  /// Walk the `AST`, calling a function on the node itself and every\n  /// child recursively. That includes both elements of an\n  /// [`ASTF::Cons`] recursively. The function will be called on the\n  /// current node *before* recursing on its children.\n  ///\n  /// Any error that occurs during walking will be propagated to the\n  /// caller.\n  pub fn walk_preorder<'a, 'b, F, E>(&'a self, mut func: F) -> Result<(), E>\n  where F: FnMut(&'b AST) -> Result<(), E>,\n        'a: 'b {\n    self._walk_preorder(&mut func)\n  }\n\n  /// As [`AST::walk_preorder`], but with a mutable `self`.\n  pub fn walk_preorder_mut<'a, F, E>(&'a mut self, mut func: F) -> Result<(), E>\n  where F: for<'b> FnMut(&'b mut AST) -> Result<(), E> {\n    self._walk_preorder_mut(&mut func)\n  }\n\n  /// Walk the `AST`, calling a function on the node itself and every\n  /// child recursively. That includes both elements of an\n  /// [`ASTF::Cons`] recursively. The function will be called on the\n  /// current node only *after* recursing on its children.\n  ///\n  /// Any error that occurs during walking will be propagated to the\n  /// caller.\n  pub fn walk_postorder<'a, 'b, F, E>(&'a self, mut func: F) -> Result<(), E>\n  where F: FnMut(&'b AST) -> Result<(), E>,\n        'a: 'b {\n    self._walk_postorder(&mut func)\n  }\n\n  /// As [`AST::walk_postorder`], but with a mutable `self`.\n  pub fn walk_postorder_mut<'a, F, E>(&'a mut self, mut func: F) -> Result<(), E>\n  where F: for<'b> FnMut(&'b mut AST) -> Result<(), E> {\n    self._walk_postorder_mut(&mut func)\n  }\n\n  /// Walk the `AST`, transforming all of the [`SourceOffset`] tags\n  /// using the given function. The walk is performed using\n  /// [`AST::walk_preorder_mut`].\n  pub fn each_source_mut<F>(&mut self, mut func: F)\n  where F: FnMut(SourceOffset) -> SourceOffset {\n    let result = self.walk_preorder_mut(|ast| {\n      ast.pos = func(ast.pos);\n      Ok(())\n    });\n    extract_err(result)\n  }\n\n  /// As [`AST::each_source_mut`], but returns a new object.\n  pub fn each_source<F>(&self, func: F) -> AST\n  where F: FnMut(SourceOffset) -> SourceOffset {\n    let mut ast = self.clone();\n    ast.each_source_mut(func);\n    ast\n  }\n\n  /// Walk the `AST`, producing a list of all symbols that appear (as\n  /// [`Literal::Symbol`]) anywhere in the tree. The symbols will\n  /// appear in the resulting list in the order they appear in the\n  /// `AST`, and any duplicates will be represented multiple times,\n  /// once for each appearance.\n  pub fn all_symbols<'a>(&'a self) -> Vec<&'a str> {\n    let mut result: Vec<&'a str> = Vec::new();\n    let err = self.walk_preorder::<_, Infallible>(|x| {\n      if let ASTF::Atom(Literal::Symbol(x)) = &x.value {\n        result.push(x);\n      }\n      Ok(())\n    });\n    extract_err(err);\n    result\n  }\n\n  /// In Lisp, we generally think of a *dotted list* as a sequence of\n  /// zero or more cons cells, where the cdr of each cell is the next\n  /// cons cell, eventually terminated by some non-cons value. For\n  /// instance, `(1 . (2 . (3 . 4)))` would be a dotted list where the\n  /// values in the \"list\" portion are `1`, `2`, and `3`, and the\n  /// terminator is `4`.\n  ///\n  /// We call a dotted list which terminates in `()` (i.e.\n  /// [`Literal::Nil`]) a *proper list*. Some sources explicitly\n  /// define a dotted list to *not* be a proper list, but this\n  /// documentation does not make that distinction.\n  ///\n  /// This function constructs an [`ASTF`] value from a sequence of\n  /// values `vec` and a terminator `terminal`. For each value in the\n  /// sequence, a [`ASTF::Cons`] cell will be constructed, and the\n  /// final cdr will be `terminal`.\n  ///\n  /// For the inverse operation of converted an [`ASTF`] *back* into a\n  /// sequence and terminator, see [`super::dotted::DottedExpr`].\n  pub fn dotted_list(vec: Vec<AST>, terminal: AST) -> AST {\n    vec.into_iter().rev().fold(terminal, AST::dotted_list_fold) // NOTE: Arguments reversed\n  }\n\n  fn dotted_list_fold(cdr: AST, car: AST) -> AST { // NOTE: Arguments reversed from the usual order\n    let pos = car.pos;\n    AST::new(ASTF::cons(car, cdr), pos)\n  }\n\n  /// A dotted list terminated by nil at the given source position.\n  pub fn list(vec: Vec<AST>, nil_pos: SourceOffset) -> AST {\n    AST::dotted_list(vec, AST::nil(nil_pos))\n  }\n\n  /// Uses a [`From`] instance of [`ASTF`] to construct an `AST`.\n  pub fn from_value<T>(value: T, pos: SourceOffset) -> AST\n  where ASTF : From<T> {\n    AST::new(ASTF::from(value), pos)\n  }\n\n  /// If `self` stores a [`Literal::Symbol`], then a reference to the\n  /// inside of that symbol is returned. Otherwise, `None` is\n  /// returned.\n  pub fn as_symbol_ref(&self) -> Option<&str> {\n    if let ASTF::Atom(Literal::Symbol(s)) = &self.value {\n      Some(s)\n    } else {\n      None\n    }\n  }\n\n}\n\n/// Pretty-print an AST, using a format compatible with [`parser`](crate::parser).\n/// Cons cells whose cdr is a cons cell will be pretty-printed as list\n/// prefixes.\nimpl fmt::Display for AST {\n\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match &self.value {\n      ASTF::Atom(lit) => write!(f, \"{}\", lit),\n      ASTF::Cons(a, b) => {\n        write!(f, \"(\")?;\n        fmt_list(a, b, f)?;\n        write!(f, \")\")\n      }\n    }\n  }\n\n}\n\nimpl Sourced for AST {\n  type Item = ASTF;\n\n  fn get_source(&self) -> SourceOffset {\n    self.pos\n  }\n\n  fn get_value(&self) -> &ASTF {\n    &self.value\n  }\n\n}\n\nimpl Recursive for AST {\n\n  fn depth(&self) -> u32 {\n    match &self.value {\n      ASTF::Atom(_) => 1,\n      ASTF::Cons(a, b) => 1 + max(a.depth(), b.depth()),\n    }\n  }\n\n}\n\nimpl From<()> for ASTF {\n  fn from(_: ()) -> ASTF {\n    ASTF::Atom(Literal::Nil)\n  }\n}\n\nimpl From<i32> for ASTF {\n  fn from(n: i32) -> ASTF {\n    ASTF::Atom(Literal::from(n))\n  }\n}\n\nimpl From<bool> for ASTF {\n  fn from(b: bool) -> ASTF {\n    ASTF::Atom(Literal::from(b))\n  }\n}\n\nimpl From<f32> for ASTF {\n  fn from(f: f32) -> ASTF {\n    ASTF::Atom(Literal::from(f))\n  }\n}\n\nimpl From<String> for ASTF {\n  fn from(s: String) -> ASTF {\n    ASTF::Atom(Literal::String(s))\n  }\n}\n\nimpl<'a> From<&'a str> for ASTF {\n  fn from(s: &'a str) -> ASTF {\n    ASTF::Atom(Literal::String(String::from(s)))\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use std::string::ToString;\n\n  // A handful of helpers for the tests that don't care about\n  // SourceOffset and are only testing the structure. These just fill\n  // in SourceOffset::default() wherever necessary.\n\n  fn int(n: i32) -> AST {\n    AST::new(ASTF::int(n), SourceOffset::default())\n  }\n\n  fn nil() -> AST {\n    AST::nil(SourceOffset::default())\n  }\n\n  fn cons(a: AST, b: AST) -> AST {\n    AST::new(ASTF::cons(a, b), SourceOffset::default())\n  }\n\n  #[test]\n  fn runtime_repr_numerical() {\n    assert_eq!(int(150).to_string(), 150.to_string());\n    assert_eq!(int(-99).to_string(), (-99).to_string());\n    assert_eq!(AST::new(ASTF::float(0.83), SourceOffset::default()).to_string(), (0.83).to_string());\n    assert_eq!(AST::new(ASTF::float(-1.2), SourceOffset::default()).to_string(), (-1.2).to_string());\n  }\n\n  #[test]\n  fn runtime_repr_nil() {\n    assert_eq!(AST::new(ASTF::Atom(Literal::Nil), SourceOffset::default()).to_string(), \"()\");\n  }\n\n  #[test]\n  fn runtime_repr_string() {\n    assert_eq!(AST::string(\"abc\", SourceOffset::default()).to_string(), r#\"\"abc\"\"#);\n    assert_eq!(AST::string(\"abc\\\"d\", SourceOffset::default()).to_string(), r#\"\"abc\\\"d\"\"#);\n    assert_eq!(AST::string(\"\\\\foo\\\"bar\\\\\", SourceOffset::default()).to_string(), r#\"\"\\\\foo\\\"bar\\\\\"\"#);\n  }\n\n  #[test]\n  fn runtime_repr_symbol() {\n    assert_eq!(AST::symbol(\"foo\", SourceOffset::default()).to_string(), \"foo\");\n    assert_eq!(AST::symbol(\"bar\", SourceOffset::default()).to_string(), \"bar\");\n  }\n\n  #[test]\n  fn runtime_repr_cons() {\n    assert_eq!(cons(int(1), int(2)).to_string(), \"(1 . 2)\");\n    assert_eq!(cons(int(1), cons(int(2), int(3))).to_string(), \"(1 2 . 3)\");\n    assert_eq!(cons(int(1), cons(int(2), cons(int(3), nil()))).to_string(), \"(1 2 3)\");\n  }\n\n  #[test]\n  fn runtime_repr_list() {\n    assert_eq!(AST::dotted_list(vec!(int(1), int(2), int(3)), nil()).to_string(), \"(1 2 3)\");\n    assert_eq!(AST::dotted_list(vec!(int(1), int(2), int(3)), int(4)).to_string(), \"(1 2 3 . 4)\");\n  }\n\n  #[test]\n  fn get_all_symbols() {\n    assert_eq!(nil().all_symbols(), Vec::<&str>::new());\n    assert_eq!(int(3).all_symbols(), Vec::<&str>::new());\n    assert_eq!(AST::symbol(\"abc\", SourceOffset::default()).all_symbols(), vec!(\"abc\"));\n\n    let foo = AST::symbol(\"foo\", SourceOffset::default());\n    let bar = AST::symbol(\"bar\", SourceOffset::default());\n    assert_eq!(cons(foo.clone(), bar.clone()).all_symbols(), vec!(\"foo\", \"bar\"));\n    assert_eq!(AST::dotted_list(vec!(foo.clone(), bar.clone()), nil()).all_symbols(), vec!(\"foo\", \"bar\"));\n  }\n\n  #[test]\n  fn each_source() {\n    let example1 = AST::symbol(\"foo\", SourceOffset(3));\n    let example2 = AST::symbol(\"foo\", SourceOffset(13));\n    assert_eq!(example1.each_source(add10), example2);\n  }\n\n  fn add10(x: SourceOffset) -> SourceOffset {\n    (usize::from(x) + 10).into()\n  }\n\n  #[test]\n  fn test_depth_atomic() {\n    assert_eq!(int(3).depth(), 1);\n    assert_eq!(nil().depth(), 1);\n    assert_eq!(AST::symbol(\"my-symbol\", SourceOffset(0)).depth(), 1);\n    assert_eq!(AST::string(\"my-string\", SourceOffset(0)).depth(), 1);\n    assert_eq!(AST::float(0.0, SourceOffset(0)).depth(), 1);\n  }\n\n  #[test]\n  fn test_depth_nested() {\n    assert_eq!(cons(int(1), int(2)).depth(), 2);\n    assert_eq!(cons(int(1), cons(int(2), int(3))).depth(), 3);\n    assert_eq!(cons(cons(int(1), int(2)), cons(int(3), int(4))).depth(), 3);\n    assert_eq!(cons(cons(int(1), int(2)), int(3)).depth(), 3);\n    assert_eq!(cons(cons(int(1), int(2)), cons(int(3), nil())).depth(), 3);\n  }\n\n}\n"
  },
  {
    "path": "src/sxp/dotted.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Parsing of [`AST`](super::ast::AST) values as dotted S-expressions.\n//!\n//! This module provides the inverse operation to\n//! [`AST::dotted_list`](super::ast::AST::dotted_list). That function\n//! takes a vector and a terminator and produces an `AST` value, while\n//! [`DottedExpr::new`] takes an `AST` value and interprets it as a\n//! vector and a terminator.\n\nuse super::ast::{AST, ASTF};\nuse crate::pipeline::source::SourceOffset;\n\nuse std::convert::TryFrom;\n\n/// A dotted list expression consists of a sequence of elements and a\n/// final terminator.\n///\n/// Note that a `DottedExpr` does not *own* any data itself. Instead,\n/// it acts as a view on an existing [`AST`](super::ast::AST) owned by\n/// someone else.\n#[derive(Clone, Debug, Eq, PartialEq, Hash)]\npub struct DottedExpr<'a> {\n  /// The sequence of elements leading up to the terminator.\n  pub elements: Vec<&'a AST>,\n  /// The terminator element.\n  pub terminal: &'a AST,\n}\n\n/// The type of errors produced by [`TryFrom::<DottedExpr>::try_from`].\n#[derive(Clone, Copy, Eq, PartialEq, Debug)]\npub struct TryFromDottedExprError {\n  pub pos: SourceOffset,\n}\n\nfn accumulate_ast<'a>(vec: &mut Vec<&'a AST>, ast: &'a AST) -> &'a AST {\n  match &ast.value {\n    ASTF::Cons(car, cdr) => {\n      vec.push(car);\n      accumulate_ast(vec, cdr)\n    }\n    _ => ast\n  }\n}\n\nimpl<'a> DottedExpr<'a> {\n\n  /// Given an `AST`, construct a `DottedExpr` view representing it.\n  ///\n  /// A dotted list is, here, defined to be a collection of zero or\n  /// more cons cells, linked together by their cdr, and terminated by\n  /// an arbitrary element. Note that, under this definition, *every*\n  /// `AST` can be meaningfully interpreted as a dotted list, which is\n  /// why this function cannot fail.\n  ///\n  /// If `ast` is not a cons cell, then it is a dotted list of zero\n  /// elements where the terminator is `ast`. If `ast` is a cons cell,\n  /// then the first element of the dotted list is its car, and the\n  /// rest of the dotted list, as well as its terminator, is\n  /// calculated recursively on the cdr of `ast`.\n  pub fn new(ast: &'a AST) -> DottedExpr<'a> {\n    let mut elements = Vec::new();\n    let terminal = accumulate_ast(&mut elements, ast);\n    DottedExpr { elements, terminal }\n  }\n\n}\n\n/// A dotted list whose terminator is\n/// [`Literal::Nil`](super::literal::Literal::Nil) is called a proper\n/// list. It is reasonable to interpret a proper list as a simple\n/// vector, since the nil terminator is merely a placeholder for the\n/// end of the list. [`TryFrom::<DottedExpr>::try_from`] performs this\n/// conversion, returning [`DottedExpr::elements`] if\n/// [`DottedExpr::terminal`] is nil and producing an error otherwise.\nimpl<'a> TryFrom<DottedExpr<'a>> for Vec<&'a AST> {\n  type Error = TryFromDottedExprError;\n\n  // TODO This should really be an inherent method on DottedExpr,\n  // rather than relying on type inference and TryFrom to figure out\n  // what we really mean.\n  fn try_from(expr: DottedExpr<'a>) -> Result<Vec<&'a AST>, TryFromDottedExprError> {\n    if expr.terminal.value == ASTF::NIL {\n      Ok(expr.elements)\n    } else {\n      Err(TryFromDottedExprError { pos: expr.terminal.pos })\n    }\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::source::SourceOffset;\n\n  fn int(n: i32) -> AST {\n    AST::new(ASTF::int(n), SourceOffset::default())\n  }\n\n  fn nil() -> AST {\n    AST::nil(SourceOffset::default())\n  }\n\n  fn list(data: Vec<AST>) -> AST {\n    AST::dotted_list(data, nil())\n  }\n\n  #[test]\n  fn simple_dot() {\n    let ast = int(33);\n    let dot = DottedExpr::new(&ast);\n\n    assert!(dot.elements.is_empty());\n    assert_eq!(*dot.terminal, ast);\n    assert!(Vec::try_from(dot).is_err());\n\n  }\n\n  #[test]\n  fn proper_list() {\n    let vec = vec!(int(1), int(2), int(3));\n    let ast = list(vec.clone());\n    let dot = DottedExpr::new(&ast);\n\n    assert_eq!(dot.elements, vec.iter().collect::<Vec<_>>());\n    assert_eq!(*dot.terminal, nil());\n\n    let vec1 = Vec::try_from(dot);\n    assert_eq!(vec1, Ok(vec.iter().collect()));\n\n  }\n\n  #[test]\n  fn improper_list() {\n    let vec = vec!(int(1), int(2), int(3));\n    let end = int(4);\n    let ast = AST::dotted_list(vec.clone(), end.clone());\n    let dot = DottedExpr::new(&ast);\n\n    assert_eq!(dot.elements, vec.iter().collect::<Vec<_>>());\n    assert_eq!(*dot.terminal, end);\n\n    assert!(Vec::try_from(dot).is_err());\n\n  }\n\n}\n"
  },
  {
    "path": "src/sxp/literal.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Literal values at the abstract syntax tree level.\n\nuse super::string::insert_escapes;\n\nuse ordered_float::OrderedFloat;\n\nuse std::fmt;\n\n/// A GDLisp literal AST. A literal is an AST that is not recursive,\n/// i.e. does not contain any more ASTs inside of it.\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\npub enum Literal {\n  /// A nil value, or `()`. This is comparable to `null` in some\n  /// languages but also functions as the empty list.\n  Nil,\n  /// A literal 32-bit integer value.\n  Int(i32),\n  /// A literal floating-point value. For AST purposes, we do not use\n  /// standard IEEE comparison semantics and instead use\n  /// [`OrderedFloat`], whose ordering and equality relations satisfy\n  /// convenient abstract mathematical properties.\n  Float(OrderedFloat<f32>),\n  /// A literal string.\n  String(String),\n  /// A literal symbol.\n  Symbol(String),\n  /// A literal Boolean value.\n  Bool(bool),\n}\n\n/// Pretty-print the atomic AST value, using a format compatible with\n/// [`parser`](crate::parser).\nimpl fmt::Display for Literal {\n\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    match self {\n      Literal::Nil => write!(f, \"()\"),\n      Literal::Int(n) => write!(f, \"{}\", n),\n      Literal::Bool(true) => write!(f, \"#t\"),\n      Literal::Bool(false) => write!(f, \"#f\"),\n      Literal::Float(x) => write!(f, \"{}\", x),\n      Literal::String(s) => write!(f, \"\\\"{}\\\"\", insert_escapes(s)),\n      Literal::Symbol(s) => write!(f, \"{}\", s),\n    }\n  }\n\n}\n\nimpl From<i32> for Literal {\n  fn from(value: i32) -> Literal {\n    Literal::Int(value)\n  }\n}\n\nimpl From<f32> for Literal {\n  fn from(value: f32) -> Literal {\n    Literal::Float(value.into())\n  }\n}\n\nimpl From<OrderedFloat<f32>> for Literal {\n  fn from(value: OrderedFloat<f32>) -> Literal {\n    Literal::Float(value)\n  }\n}\n\nimpl From<String> for Literal {\n  fn from(value: String) -> Literal {\n    Literal::String(value)\n  }\n}\n\nimpl<'a> From<&'a str> for Literal {\n  fn from(value: &'a str) -> Literal {\n    Literal::String(String::from(value))\n  }\n}\n\nimpl From<bool> for Literal {\n  fn from(value: bool) -> Literal {\n    Literal::Bool(value)\n  }\n}\n"
  },
  {
    "path": "src/sxp/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Data-type and associated functionality for dealing with Lisp\n//! S-expressions.\n\npub mod ast;\npub mod dotted;\npub mod literal;\npub mod reify;\npub mod string;\npub mod syntax;\n"
  },
  {
    "path": "src/sxp/reify/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`Reify`] trait.\n\npub mod pretty;\n\nuse crate::gdscript::expr::Expr;\nuse crate::compile::names::fresh::FreshNameGenerator;\nuse super::ast::AST;\n\n/// This trait describes any type for which there is a reasonable way\n/// to convert a `&self` into [`crate::gdscript::expr::Expr`]. Note\n/// that this is subtly different from [`Into`], which requires\n/// ownership of `self` to do the conversion. The resulting `Expr`\n/// should not share any data with `&self`.\npub trait Reify {\n\n  /// Reify the value into a GDScript expression would should evaluate\n  /// to something comparable to `self`.\n  fn reify(&self) -> Expr;\n\n}\n\nimpl Reify for AST {\n\n  fn reify(&self) -> Expr {\n    let mut gen = FreshNameGenerator::new(vec!());\n    let (stmts, result) = pretty::reify_pretty_expr(self, u32::MAX, &mut gen);\n    assert!(stmts.is_empty()); // At u32::MAX, we should never have need to generate any helper statements.\n    result\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::source::SourceOffset;\n  use crate::sxp::ast::ASTF;\n\n  fn int(n: i32) -> AST {\n    AST::new(ASTF::int(n), SourceOffset::default())\n  }\n\n  fn nil() -> AST {\n    AST::nil(SourceOffset::default())\n  }\n\n  #[allow(dead_code)]\n  fn list(data: Vec<AST>) -> AST {\n    AST::dotted_list(data, nil())\n  }\n\n  fn cons(a: AST, b: AST) -> AST {\n    AST::new(ASTF::cons(a, b), SourceOffset::default())\n  }\n\n  #[test]\n  fn reify_test() {\n    assert_eq!(nil().reify().to_gd(), \"null\");\n    assert_eq!(cons(int(1), int(2)).reify().to_gd(), \"GDLisp.cons(1, 2)\");\n    assert_eq!(AST::new(ASTF::from(false), SourceOffset::default()).reify().to_gd(), \"false\");\n    assert_eq!(AST::new(ASTF::from(true), SourceOffset::default()).reify().to_gd(), \"true\");\n    assert_eq!(AST::symbol(\"foo\", SourceOffset::default()).reify().to_gd(), \"GDLisp.intern(\\\"foo\\\")\");\n  }\n\n}\n"
  },
  {
    "path": "src/sxp/reify/pretty.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides pretty-printing facilities for\n//! [`AST`](crate::sxp::ast::AST) nodes.\n\nuse crate::gdscript::expr::Expr;\nuse crate::gdscript::stmt::Stmt;\nuse crate::gdscript::library;\nuse crate::compile::names::generator::NameGenerator;\nuse crate::compile::stmt_wrapper::StmtWrapper;\nuse crate::pipeline::source::{Sourced, SourceOffset};\nuse crate::sxp::ast::{AST, ASTF};\nuse crate::sxp::literal::Literal;\n\n/// Reifies the expression, as though through\n/// [`Reify::reify`](super::Reify::reify). However, whereas\n/// `Reify::reify` will generate a single expression, this function\n/// will split the reification into several lines, using local\n/// variables to break the code up as needed.\n///\n/// `value` is the value to be reified. `max_depth` is the maximum\n/// depth of a single expression. After recursing deeper than this, a\n/// local variable will be constructed in GDScript to store the\n/// intermediate. A `max_depth` of zero will create a local variable\n/// at every recursion step. `gen` is used to generate the names for\n/// the reification helper variables.\n///\n/// This function returns a vector of intermediate variable\n/// declaration statements, as well as the final expression. The\n/// expression should be used in GDScript only after all of the\n/// statements have been run. If you know how the expression will be\n/// used, then [`reify_pretty`] may be more convenient.\npub fn reify_pretty_expr(value: &AST, max_depth: u32, gen: &mut impl NameGenerator) -> (Vec<Stmt>, Expr) {\n  let mut printer = PrettyPrinter::new(gen, max_depth);\n  let expr = printer.reify_pretty_rec(value, 0);\n  (printer.build(), expr)\n}\n\n/// As [`reify_pretty_expr`], but the final expression is wrapped\n/// using the statement wrapper `wrapper` and appended to the\n/// statements vector.\npub fn reify_pretty<W>(value: &AST, max_depth: u32, gen: &mut impl NameGenerator, wrapper: &W) -> Vec<Stmt>\nwhere W : StmtWrapper + ?Sized {\n  let (mut stmts, expr) = reify_pretty_expr(value, max_depth, gen);\n  stmts.push(wrapper.wrap_expr(expr));\n  stmts\n}\n\nstruct PrettyPrinter<'a, G: NameGenerator> {\n  gen: &'a mut G,\n  builder: Vec<Stmt>,\n  max_depth: u32,\n}\n\nimpl<'a, G: NameGenerator> PrettyPrinter<'a, G> {\n\n  fn new(gen: &'a mut G, max_depth: u32) -> Self {\n    PrettyPrinter { gen, max_depth, builder: Vec::new() }\n  }\n\n  fn reify_pretty_rec(&mut self, value: &AST, depth: u32) -> Expr {\n    if depth > self.max_depth {\n      // Make a local variable and store the result in it.\n      let inner = self.reify_pretty_rec(value, 0);\n      let name = self.gen.generate_with(\"_reify\");\n      self.builder.push(Stmt::var_decl(name.clone(), inner, value.get_source()));\n      Expr::var(&name, value.get_source())\n    } else {\n      match &value.value {\n        ASTF::Atom(lit) => {\n          reify_literal(lit, value.pos)\n        }\n        ASTF::Cons(a, b) => {\n          let a = self.reify_pretty_rec(a, depth + 1);\n          let b = self.reify_pretty_rec(b, depth + 1);\n          Expr::call(Some(library::gdlisp_root(value.pos)), \"cons\", vec!(a, b), value.pos)\n        }\n      }\n    }\n  }\n\n  fn build(self) -> Vec<Stmt> {\n    self.builder\n  }\n\n}\n\nfn reify_literal(value: &Literal, pos: SourceOffset) -> Expr {\n  match value {\n    Literal::Nil => {\n      Expr::null(pos)\n    }\n    Literal::Int(n) => {\n      Expr::from_value(*n, pos)\n    }\n    Literal::Bool(b) => {\n      Expr::from_value(*b, pos)\n    }\n    Literal::Float(f) => {\n      Expr::from_value(*f, pos)\n    }\n    Literal::String(s) => {\n      Expr::from_value(s.to_owned(), pos)\n    }\n    Literal::Symbol(s) => {\n      let s = Expr::from_value(s.to_owned(), pos);\n      Expr::call(Some(library::gdlisp_root(pos)), \"intern\", vec!(s), pos)\n    }\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::pipeline::source::SourceOffset;\n  use crate::compile::names::fresh::FreshNameGenerator;\n\n  fn cons(a: AST, b: AST) -> AST {\n    AST::new(ASTF::cons(a, b), SourceOffset::default())\n  }\n\n  fn int(n: i32) -> AST {\n    AST::new(ASTF::int(n), SourceOffset::default())\n  }\n\n  #[test]\n  fn reify_pretty_test_1() {\n    let (stmts, expr) = reify_pretty_expr(&cons(int(1), int(2)), u32::MAX, &mut FreshNameGenerator::new(vec!()));\n    let stmts: Vec<_> = stmts.into_iter().map(|s| s.to_gd(0)).collect();\n    assert!(stmts.is_empty());\n    assert_eq!(expr.to_gd(), \"GDLisp.cons(1, 2)\");\n  }\n\n  #[test]\n  fn reify_pretty_test_2() {\n    let (stmts, expr) = reify_pretty_expr(&cons(cons(cons(cons(int(1), int(2)), int(3)), int(4)), int(5)), u32::MAX, &mut FreshNameGenerator::new(vec!()));\n    let stmts: Vec<_> = stmts.into_iter().map(|s| s.to_gd(0)).collect();\n    assert!(stmts.is_empty());\n    assert_eq!(expr.to_gd(), \"GDLisp.cons(GDLisp.cons(GDLisp.cons(GDLisp.cons(1, 2), 3), 4), 5)\");\n  }\n\n  #[test]\n  fn reify_pretty_test_3() {\n    let ast = cons(cons(cons(cons(int(1), int(2)), int(3)), int(4)), cons(int(5), int(6)));\n    let (stmts, expr) = reify_pretty_expr(&ast, u32::MAX, &mut FreshNameGenerator::new(vec!()));\n    let stmts: Vec<_> = stmts.into_iter().map(|s| s.to_gd(0)).collect();\n    assert!(stmts.is_empty());\n    assert_eq!(expr.to_gd(), \"GDLisp.cons(GDLisp.cons(GDLisp.cons(GDLisp.cons(1, 2), 3), 4), GDLisp.cons(5, 6))\");\n  }\n\n  #[test]\n  fn reify_pretty_test_4() {\n    let ast = cons(cons(cons(cons(int(1), int(2)), int(3)), int(4)), cons(int(5), int(6)));\n    let (stmts, expr) = reify_pretty_expr(&ast, 3, &mut FreshNameGenerator::new(vec!()));\n    let stmts: Vec<_> = stmts.into_iter().map(|s| s.to_gd(0)).collect();\n    assert_eq!(stmts, vec!(\"var _reify_0 = 1\\n\", \"var _reify_1 = 2\\n\"));\n    assert_eq!(expr.to_gd(), \"GDLisp.cons(GDLisp.cons(GDLisp.cons(GDLisp.cons(_reify_0, _reify_1), 3), 4), GDLisp.cons(5, 6))\");\n  }\n\n  #[test]\n  fn reify_pretty_test_5() {\n    let ast = cons(cons(cons(cons(int(1), int(2)), int(3)), int(4)), cons(int(5), int(6)));\n    let (stmts, expr) = reify_pretty_expr(&ast, 2, &mut FreshNameGenerator::new(vec!()));\n    let stmts: Vec<_> = stmts.into_iter().map(|s| s.to_gd(0)).collect();\n    assert_eq!(stmts, vec!(\"var _reify_0 = GDLisp.cons(1, 2)\\n\", \"var _reify_1 = 3\\n\"));\n    assert_eq!(expr.to_gd(), \"GDLisp.cons(GDLisp.cons(GDLisp.cons(_reify_0, _reify_1), 4), GDLisp.cons(5, 6))\");\n  }\n\n  #[test]\n  fn reify_pretty_test_6() {\n    let ast = cons(cons(cons(cons(int(1), int(2)), int(3)), int(4)), cons(int(5), int(6)));\n    let (stmts, expr) = reify_pretty_expr(&ast, 1, &mut FreshNameGenerator::new(vec!()));\n    let stmts: Vec<_> = stmts.into_iter().map(|s| s.to_gd(0)).collect();\n    assert_eq!(stmts, vec!(\n      \"var _reify_0 = 1\\n\",\n      \"var _reify_1 = 2\\n\",\n      \"var _reify_2 = GDLisp.cons(GDLisp.cons(_reify_0, _reify_1), 3)\\n\",\n      \"var _reify_3 = 4\\n\",\n      \"var _reify_4 = 5\\n\",\n      \"var _reify_5 = 6\\n\",\n    ));\n    assert_eq!(expr.to_gd(), \"GDLisp.cons(GDLisp.cons(_reify_2, _reify_3), GDLisp.cons(_reify_4, _reify_5))\");\n  }\n\n  #[test]\n  fn reify_pretty_test_7() {\n    let ast = cons(cons(cons(cons(int(1), int(2)), int(3)), int(4)), cons(int(5), int(6)));\n    let (stmts, expr) = reify_pretty_expr(&ast, 0, &mut FreshNameGenerator::new(vec!()));\n    let stmts: Vec<_> = stmts.into_iter().map(|s| s.to_gd(0)).collect();\n    assert_eq!(stmts, vec!(\n      \"var _reify_0 = 1\\n\",\n      \"var _reify_1 = 2\\n\",\n      \"var _reify_2 = GDLisp.cons(_reify_0, _reify_1)\\n\",\n      \"var _reify_3 = 3\\n\",\n      \"var _reify_4 = GDLisp.cons(_reify_2, _reify_3)\\n\",\n      \"var _reify_5 = 4\\n\",\n      \"var _reify_6 = GDLisp.cons(_reify_4, _reify_5)\\n\",\n      \"var _reify_7 = 5\\n\",\n      \"var _reify_8 = 6\\n\",\n      \"var _reify_9 = GDLisp.cons(_reify_7, _reify_8)\\n\",\n    ));\n    assert_eq!(expr.to_gd(), \"GDLisp.cons(_reify_6, _reify_9)\");\n  }\n\n}\n"
  },
  {
    "path": "src/sxp/string.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Helper functions for working with strings within\n//! [`AST`](super::ast::AST) values.\n\nuse phf::{phf_map};\n\nuse std::collections::HashMap;\nuse std::fmt::Write;\n\n// Note: \\u{...} is a special case and is not listed here.\nconst ESCAPE_SEQUENCES: phf::Map<char, char> = phf_map! {\n  'n' => '\\n',\n  't' => '\\t',\n  'r' => '\\r',\n  'a' => '\\x07',\n  'b' => '\\x08',\n  'f' => '\\x0C',\n  'v' => '\\x0B',\n  '\"' => '\"',\n  '\\'' => '\\'',\n  '\\\\' => '\\\\',\n};\n\n/// The type of errors produced by [`parse_escapes`].\n#[derive(Debug)]\npub enum Error {\n  /// An escape sequence `\\` was not terminated.\n  UnfinishedEscape,\n  /// An escape sequence `\\` was not recognized.\n  InvalidEscape(char),\n  /// A Unicode escape sequence `\\u` was invalid.\n  InvalidUnicodeEscape,\n}\n\n/// Given a string, parse escape sequences beginning with `\\` and\n/// produce a string containing the result.\n///\n/// Note that our `\\u{...}` syntax is distinct from (but compatible\n/// with) GDScript's. GDLisp supports two different Unicode escapes.\n/// The first, identical to GDScript, consists of `\\u` followed by\n/// exactly four hexadecimal digits. This is capable of supporting any\n/// character in the basic multilingual plane. The second consists of\n/// `\\u` followed by one or more hexadecimal digits contained in\n/// mandatory curly braces. This is capable of representing *any*\n/// Unicode character.\n///\n/// # Examples\n///\n/// ```\n/// # use gdlisp::sxp::string::parse_escapes;\n/// assert_eq!(parse_escapes(r#\"foobar\"#).unwrap(), \"foobar\");\n/// assert_eq!(parse_escapes(r#\"foo\\\"\\\"bar\\\\\"#).unwrap(), \"foo\\\"\\\"bar\\\\\");\n/// assert_eq!(parse_escapes(r#\"\\u03B1\\u03b1\\u{3b1}\\u{3B1}\\u{000000003b1}\"#).unwrap(), \"ααααα\");\n/// ```\npub fn parse_escapes(input: &str) -> Result<String, Error> {\n  let length = input.chars().count();\n  let mut result = String::with_capacity(length);\n  let mut iter = input.chars();\n  while let Some(ch) = iter.next() {\n    if ch == '\\\\' {\n      match iter.next() {\n        Some('u') => {\n          parse_unicode_sequence(&mut iter, &mut result)?;\n        }\n        Some(esc) => {\n          if let Some(value_char) = ESCAPE_SEQUENCES.get(&esc) {\n            result.push(*value_char);\n          } else {\n            return Err(Error::InvalidEscape(esc));\n          }\n        }\n        None => {\n          return Err(Error::UnfinishedEscape);\n        }\n      }\n    } else {\n      result.push(ch);\n    }\n  }\n  Ok(result)\n}\n\n/// Parses a Unicode escape sequence. It is assumed that this function\n/// will be called immediately *after* parsing `\\u` in the iterator.\n/// This function can parse either four hexadecimal digits or a\n/// brace-enclosed collection of at least one hexadecimal character.\n///\n/// In either case, the hexadecimal digits are case insensitive.\nfn parse_unicode_sequence(iter: &mut impl Iterator<Item=char>,\n                          dest: &mut impl Write) -> Result<(), Error> {\n  let digits = match iter.next() {\n    Some(first) if is_hex_digit(first) => {\n      // A sequence of exactly four hexadecimal digits.\n      let mut digits: String = String::with_capacity(4);\n      digits.push(first);\n      for _ in 0..3 {\n        let next_char = iter.next().ok_or(Error::InvalidUnicodeEscape)?;\n        if !is_hex_digit(next_char) {\n          return Err(Error::InvalidUnicodeEscape);\n        }\n        digits.push(next_char);\n      }\n      digits\n    }\n    Some('{') => {\n      let mut digits: String = String::with_capacity(8);\n      loop {\n        let next_char = iter.next().ok_or(Error::InvalidUnicodeEscape)?;\n        if next_char == '}' {\n          break;\n        } else if !is_hex_digit(next_char) {\n          return Err(Error::InvalidUnicodeEscape);\n        }\n        digits.push(next_char);\n      }\n      if digits.is_empty() {\n        return Err(Error::InvalidUnicodeEscape);\n      }\n      digits\n    }\n    _ => {\n      return Err(Error::InvalidUnicodeEscape);\n    }\n  };\n  let codepoint = u32::from_str_radix(&digits, 16).expect(\"Internal error in parse_unicode_sequence\");\n  let ch = char::from_u32(codepoint).ok_or(Error::InvalidUnicodeEscape)?;\n  write!(dest, \"{}\", ch).expect(\"Internal error in parse_unicode_sequence\");\n  Ok(())\n}\n\nfn is_hex_digit(digit: char) -> bool {\n  ('0'..='9').contains(&digit) ||\n    ('a'..='f').contains(&digit) ||\n    ('A'..='F').contains(&digit)\n}\n\n/// Given a string, insert Godot-style escape sequences to make the\n/// string valid as a GDScript string literal. This involves escaping\n/// any control characters whose scalar values are less than 32, as\n/// well as quotes and backslashes. Characters outside of the standard\n/// ASCII range may or may not be escaped.\n///\n/// ```\n/// # use gdlisp::sxp::string::insert_escapes;\n/// assert_eq!(insert_escapes(r#\"foobar\"#), r#\"foobar\"#);\n/// assert_eq!(insert_escapes(r#\"a\"b'c\"#), r#\"a\\\"b\\'c\"#);\n/// assert_eq!(insert_escapes(r#\"A\\B\"#), r#\"A\\\\B\"#);\n/// assert_eq!(insert_escapes(\"\\n\"), \"\\\\n\");\n/// assert_eq!(insert_escapes(\"α\"), \"α\");\n/// assert_eq!(insert_escapes(\"😀\"), \"😀\");\n/// assert_eq!(insert_escapes(\"\\x06\"), r#\"\\u0006\"#);\n/// ```\npub fn insert_escapes(input: &str) -> String {\n  let mut result = String::with_capacity(input.len() + 16); // Just a bit of extra space for escape sequences.\n  let reverse_map = reverse_escape_map();\n  for ch in input.chars() {\n    if should_escape(ch) {\n      // Escape it\n      match reverse_map.get(&ch) {\n        None => {\n          // Fall back to \\uXXXX\n          write!(result, \"\\\\u{:04X}\", ch as u32).expect(\"Failed to write to local string\");\n        }\n        Some(esc) => {\n          // Built-in escape sequence\n          result.push_str(esc);\n        }\n      }\n    } else {\n      // Leave it\n      result.push(ch);\n    }\n  }\n  result\n}\n\nfn should_escape(ch: char) -> bool {\n  ((ch as u32) < 32) || (ch == '\"') || (ch == '\\'') || (ch == '\\\\')\n}\n\nfn reverse_escape_map() -> HashMap<char, String> {\n  let mut map: HashMap<char, String> = HashMap::new();\n  for (escape_char, value_char) in &ESCAPE_SEQUENCES {\n    map.insert(*value_char, format!(\"\\\\{}\", escape_char));\n  }\n  map\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn successful_escaping() {\n    assert_eq!(parse_escapes(r#\"I said, \\\"foobar\\\".\"#).unwrap(), \"I said, \\\"foobar\\\".\");\n    assert_eq!(parse_escapes(r#\"\\'\\'\\'\"#).unwrap(), \"\\'\\'\\'\");\n    assert_eq!(parse_escapes(r#\"\\\\\\\\\"#).unwrap(), \"\\\\\\\\\");\n    assert_eq!(parse_escapes(r#\"\\a\\b\\n\"#).unwrap(), \"\\x07\\x08\\n\");\n    assert_eq!(parse_escapes(r#\"\\f\\v\\r\\t\"#).unwrap(), \"\\x0C\\x0B\\r\\t\");\n  }\n\n  #[test]\n  fn failed_escaping() {\n    assert!(parse_escapes(r#\"\\\"#).is_err());\n    assert!(parse_escapes(r#\"\\I\"#).is_err());\n    assert!(parse_escapes(r#\"\\u\"#).is_err());\n    assert!(parse_escapes(r#\"\\u{00\"#).is_err());\n    assert!(parse_escapes(r#\"\\u13zz\"#).is_err());\n    assert!(parse_escapes(r#\"\\u{}\"#).is_err());\n    assert!(parse_escapes(r#\"\\u{potato}\"#).is_err());\n  }\n\n}\n"
  },
  {
    "path": "src/sxp/syntax.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! This module provides several helpers for syntactic forms which\n//! need to be special-cased in the parser.\n//!\n//! This includes forms such as `(quote x)`, which is generated by the\n//! `'x` syntax.\n\nuse super::ast::AST;\nuse crate::pipeline::source::SourceOffset;\nuse crate::ir::special_form::access_slot::ACCESS_SLOT_FORM_NAME;\n\n/// Produces an arbitrary syntactic expression of the form `(a b)`,\n/// where `a` is a symbol and `b` is an arbitrary [`AST`].\npub fn unary(head: &str, value: AST, pos: SourceOffset) -> AST {\n  AST::list(vec!(\n    AST::symbol(head, pos),\n    value\n  ), pos)\n}\n\n/// Produces an arbitrary syntactic expression of the form `(a b c)`,\n/// where `a` is a symbol, and `b` and `c` are arbitrary [`AST`]\n/// values.\npub fn binary(head: &str, b: AST, c: AST, pos: SourceOffset) -> AST {\n  AST::list(vec!(\n    AST::symbol(head, pos),\n    b,\n    c,\n  ), pos)\n}\n\n/// Produces an arbitrary syntactic expression of the form `(a b c)`,\n/// where `a` is a symbol, and `b`, `c`, and `d` are arbitrary [`AST`]\n/// values.\npub fn trinary(head: &str, b: AST, c: AST, d: AST, pos: SourceOffset) -> AST {\n  AST::list(vec!(\n    AST::symbol(head, pos),\n    b,\n    c,\n    d,\n  ), pos)\n}\n\n/// Produces the syntactic form `(quote value)`.\npub fn quote(value: AST, pos: SourceOffset) -> AST {\n  unary(\"quote\", value, pos)\n}\n\n/// Produces the syntactic form `(function value)`.\npub fn function(value: AST, pos: SourceOffset) -> AST {\n  unary(\"function\", value, pos)\n}\n\n/// Produces the syntactic form `(quasiquote value)`.\npub fn quasiquote(value: AST, pos: SourceOffset) -> AST {\n  unary(\"quasiquote\", value, pos)\n}\n\n/// Produces the syntactic form `(unquote value)`.\npub fn unquote(value: AST, pos: SourceOffset) -> AST {\n  unary(\"unquote\", value, pos)\n}\n\n/// Produces the syntactic form `(unquote-spliced value)`.\npub fn unquote_spliced(value: AST, pos: SourceOffset) -> AST {\n  unary(\"unquote-spliced\", value, pos)\n}\n\n/// Produces the syntactic form `(array ...)`, given the tail of the\n/// list of arguments.\npub fn array(tail: AST, pos: SourceOffset) -> AST {\n  AST::cons(AST::symbol(\"array\", pos), tail, pos)\n}\n\n/// Produces the syntactic form `(dict ...)`, given the tail of the\n/// list of arguments.\npub fn dict(tail: AST, pos: SourceOffset) -> AST {\n  AST::cons(AST::symbol(\"dict\", pos), tail, pos)\n}\n\n/// Produces the syntactic form `(vector x y)`\npub fn vector2(x: AST, y: AST, pos: SourceOffset) -> AST {\n  binary(\"vector\", x, y, pos)\n}\n\n/// Produces the syntactic form `(vector x y z)`\npub fn vector3(x: AST, y: AST, z: AST, pos: SourceOffset) -> AST {\n  trinary(\"vector\", x, y, z, pos)\n}\n\n/// Produces the syntactic form `(access-slot x y)`\npub fn access_slot(x: AST, y: AST, pos: SourceOffset) -> AST {\n  binary(ACCESS_SLOT_FORM_NAME, x, y, pos)\n}\n\n/// Produces a function call which will perform `(x:get-node y)`.\n///\n/// Written in full generality, this is the form `((access-slot x\n/// get-node) y)`.\npub fn get_node_on(x: AST, y: AST, pos: SourceOffset) -> AST {\n  AST::list(vec!(\n    AST::symbol(\"sys/get-node\", pos),\n    x,\n    y,\n  ), pos)\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n  use crate::sxp::ast::AST;\n\n  #[test]\n  fn test_unary() {\n    assert_eq!(\n      unary(\"foo\", AST::int(10, SourceOffset(1)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"foo\", SourceOffset(3)),\n          AST::int(10, SourceOffset(1)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  #[test]\n  fn test_binary() {\n    assert_eq!(\n      binary(\"bar\", AST::int(10, SourceOffset(1)), AST::int(20, SourceOffset(99)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"bar\", SourceOffset(3)),\n          AST::int(10, SourceOffset(1)),\n          AST::int(20, SourceOffset(99)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  #[test]\n  fn test_trinary() {\n    assert_eq!(\n      trinary(\"baz\", AST::int(10, SourceOffset(1)), AST::int(20, SourceOffset(99)), AST::int(30, SourceOffset(999)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"baz\", SourceOffset(3)),\n          AST::int(10, SourceOffset(1)),\n          AST::int(20, SourceOffset(99)),\n          AST::int(30, SourceOffset(999)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  #[test]\n  fn test_quote() {\n    assert_eq!(\n      quote(AST::int(9, SourceOffset(100)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"quote\", SourceOffset(3)),\n          AST::int(9, SourceOffset(100)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  #[test]\n  fn test_function() {\n    assert_eq!(\n      function(AST::int(9, SourceOffset(100)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"function\", SourceOffset(3)),\n          AST::int(9, SourceOffset(100)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  #[test]\n  fn test_quasiquote() {\n    assert_eq!(\n      quasiquote(AST::int(9, SourceOffset(100)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"quasiquote\", SourceOffset(3)),\n          AST::int(9, SourceOffset(100)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  #[test]\n  fn test_unquote() {\n    assert_eq!(\n      unquote(AST::int(9, SourceOffset(100)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"unquote\", SourceOffset(3)),\n          AST::int(9, SourceOffset(100)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  #[test]\n  fn test_unquote_spliced() {\n    assert_eq!(\n      unquote_spliced(AST::int(9, SourceOffset(100)), SourceOffset(3)),\n      AST::list(\n        vec!(\n          AST::symbol(\"unquote-spliced\", SourceOffset(3)),\n          AST::int(9, SourceOffset(100)),\n        ),\n        SourceOffset(3),\n      )\n    )\n  }\n\n  // TODO The rest of these\n\n}\n"
  },
  {
    "path": "src/util/debug_wrapper.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Wrapper for non-[`Debug`] types that prints a trivial `Debug`\n//! representation.\n\nuse serde::{Serialize, Deserialize};\n\nuse std::fmt::{self, Debug};\n\n/// A `DebugWrapper` is a very thin wrapper around a `T`.\n/// `DebugWrapper` is mostly uninteresting, except that it\n/// unconditionally implements [`Debug`] for all types. The debug\n/// representation for every instance of `DebugWrapper` is always an\n/// ellipsis `\"...\"`.\n#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]\n#[repr(transparent)]\npub struct DebugWrapper<T>(pub T);\n\nimpl<T> Debug for DebugWrapper<T> {\n\n  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n    write!(f, \"...\")\n  }\n\n}\n"
  },
  {
    "path": "src/util/group_by.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`group_by`] function and associated iterator.\n\nuse std::iter::Peekable;\n\n/// A grouping iterator. This iterator is constructed via [`group_by`]\n/// or [`group_with`] and produces vectors of elements in the original\n/// (input) iterator, grouped together by the predicate. See\n/// [`group_by`] for details.\npub struct GroupBy<I: Iterator, F> {\n  iter: Peekable<I>,\n  function: F,\n}\n\nimpl<I, F> Iterator for GroupBy<I, F>\nwhere I: Iterator,\n      F: FnMut(&<I as Iterator>::Item, &<I as Iterator>::Item) -> bool {\n\n  type Item = Vec<<I as Iterator>::Item>;\n\n  fn next(&mut self) -> Option<Self::Item> {\n    let mut result: Vec<<I as Iterator>::Item> = Vec::new();\n    let mut head = match self.iter.next() {\n      None => {\n        // Iterator is already exhausted, so there's no further groups.\n        return None;\n      }\n      Some(x) => {\n        x\n      }\n    };\n    // Loop until either we've exhausted the iterator or we've hit an\n    // element that doesn't match.\n    loop {\n      match self.iter.peek() {\n        None => {\n          // At end of iterator, so the group is complete.\n          break;\n        }\n        Some(next) => {\n          if (self.function)(&head, next) {\n            // next is part of the current group, so continue.\n            result.push(head);\n            head = self.iter.next().expect(\"Inconsistent behavior in Peekable\");\n          } else {\n            // next is not part of the current group, so break.\n            break;\n          }\n        }\n      }\n    }\n    // head is the final element of the current group.\n    result.push(head);\n    Some(result)\n  }\n\n}\n\n/// Groups elements of the iterator based on the predicate function.\n/// The resulting output iterator will produce vectors of items from\n/// the original iterator, where the subvectors are the longest\n/// subsequences for which the predicate returns true on consecutive\n/// elements.\n///\n/// See also [`group_with`].\npub fn group_by<I, F>(iter: I, function: F) -> GroupBy<I, F>\nwhere I: Iterator,\n      F: FnMut(&<I as Iterator>::Item, &<I as Iterator>::Item) -> bool {\n  GroupBy { iter: iter.peekable(), function }\n}\n\n/// Groups elements of the iterator based on the grouping function,\n/// similar to [`group_by`]. The grouping function takes one argument\n/// and returns a value that can be compared for equality. The\n/// resulting iterator is grouped by consecutive terms which, under\n/// the supplied unary function, produce values which are considered\n/// equal.\npub fn group_with<I, F, G>(iter: I, mut function: F) -> GroupBy<I, impl FnMut(&<I as Iterator>::Item, &<I as Iterator>::Item) -> bool>\nwhere I: Iterator,\n      F: FnMut(&<I as Iterator>::Item) -> G,\n      G: Eq {\n  group_by(iter, move |a, b| {\n    function(a) == function(b)\n  })\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn group_by_test_1() {\n    let vec = vec!(0, 0, 1, 1, 2, 3, 2, 2);\n    let grouped: Vec<_> = group_by(vec.into_iter(), |a, b| a == b).collect();\n    assert_eq!(grouped, vec!(vec!(0, 0), vec!(1, 1), vec!(2), vec!(3), vec!(2, 2)));\n  }\n\n  #[test]\n  fn group_by_test_2() {\n    let vec = vec!(0, 0, 1, 1, 2, 3, 2, 2, 1);\n    let grouped: Vec<_> = group_by(vec.into_iter(), |a, b| a == b).collect();\n    assert_eq!(grouped, vec!(vec!(0, 0), vec!(1, 1), vec!(2), vec!(3), vec!(2, 2), vec!(1)));\n  }\n\n  #[test]\n  fn group_by_test_3() {\n    let vec: Vec<i32> = vec!();\n    let grouped: Vec<_> = group_by(vec.into_iter(), |a, b| a == b).collect();\n    assert_eq!(grouped, Vec::<Vec<i32>>::new());\n  }\n\n  #[test]\n  fn group_with_test() {\n    let vec: Vec<i32> = vec!(0, 0, 1, 1, -1, 2, -3);\n    let grouped: Vec<_> = group_with(vec.into_iter(), |a| a.abs()).collect();\n    assert_eq!(grouped, vec!(vec!(0, 0), vec!(1, 1, -1), vec!(2), vec!(-3)));\n  }\n\n}\n"
  },
  {
    "path": "src/util/lattice.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Defines the [`Lattice`] trait, for mathematical\n//! [lattices](https://en.wikipedia.org/wiki/Lattice_(order)).\n\n/// A [lattice](https://en.wikipedia.org/wiki/Lattice_(order)) is a\n/// collection of values together with a join and meet operation.\n/// These operations should be associative and commutative, and they\n/// should satisfy the following rules, often called the *absorption\n/// laws*.\n///\n/// ```text\n/// a.join(a.meet(b)) == a\n/// a.meet(a.join(b)) == a\n/// ```\n///\n/// In the future, we may define relationships between this trait,\n/// [`PartialOrd`], and [`Ord`], since in order theory every total\n/// order is a lattice and every lattice is a partial order. These\n/// relations, as of now, are not established in Rust, and this trait\n/// is unrelated to the other two. In the spirit of providing an\n/// intuitive API, types which implement either `PartialOrd` or `Ord`\n/// *and* `Lattice` should make the instances compatible.\npub trait Lattice {\n\n  /// The lattice-theoretic join (or least upper bound) of the two\n  /// values.\n  fn join(self, other: Self) -> Self;\n\n  /// The lattice-theoretic meet (or greatest lower bound) of the two\n  /// values.\n  fn meet(self, other: Self) -> Self;\n\n}\n\n#[allow(clippy::unused_unit)]\nimpl Lattice for () {\n  fn join(self, _other: ()) -> () {\n    ()\n  }\n  fn meet(self, _other: ()) -> () {\n    ()\n  }\n}\n\n/// An implementation of the binary product lattice.\nimpl<A: Lattice, B: Lattice> Lattice for (A, B) {\n  fn join(self, other: (A, B)) -> (A, B) {\n    (self.0.join(other.0), self.1.join(other.1))\n  }\n  fn meet(self, other: (A, B)) -> (A, B) {\n    (self.0.meet(other.0), self.1.meet(other.1))\n  }\n}\n"
  },
  {
    "path": "src/util/lazy.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`Lazy`] type for lazy initialization of data.\n\n/// A lazy data structure, containing a `T` which may or may not be\n/// initialized yet.\n#[derive(Clone, Debug, Copy, Eq, PartialEq, Ord, PartialOrd)]\npub struct Lazy<T, F> {\n  value: Option<T>,\n  init: Option<F>,\n}\n\nimpl<T, F: FnOnce() -> T> Lazy<T, F> {\n\n  // Implementation detail: A Lazy<T, F> shall always have exactly one\n  // of `value` or `init` defined. The other shall be empty. An\n  // uninitialized lazy value has only `init` and an initialized one\n  // has only `value`. If both or neither is defined at once, the\n  // object is in an illegal state.\n\n  pub fn new(init: F) -> Lazy<T, F> {\n    Lazy { value: None, init: Some(init) }\n  }\n\n  pub fn force_mut(&mut self) -> &mut T {\n    if self.value.is_none() {\n      let init = self.init.take().expect(\"Internal error in gdlisp::util::lazy\");\n      self.value = Some(init());\n    }\n    self.value.as_mut().expect(\"Internal error in gdlisp::util::lazy\")\n  }\n\n}\n"
  },
  {
    "path": "src/util/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Various utility functions that don't have a better home.\n\npub mod debug_wrapper;\npub mod group_by;\npub mod lattice;\npub mod lazy;\npub mod one;\npub mod prefix_matcher;\npub mod recursive;\n\nuse std::collections::HashMap;\nuse std::hash::Hash;\nuse std::io::{self, Read};\nuse std::convert::Infallible;\n\n/// `fold1` acts like [`Iterator::fold`] but without a \"starting\"\n/// value.\n///\n/// If `iter` is nonempty, then the first element is used as the\n/// starting value, and iteration begins formally at the second. If\n/// `iter` is empty, then [`None`] is returned.\npub fn fold1<I : Iterator, F : FnMut(I::Item, I::Item) -> I::Item>(iter: I, mut f: F) -> Option<I::Item> {\n  iter.fold(None, |x, y| match x {\n    None => Some(y),\n    Some(x) => Some(f(x, y)),\n  })\n}\n\n/// The type of iterator returned by [`each_pair`].\npub struct PairIter<T, I>(Option<T>, I);\n\n/// Return an iterator over each pair of adjacent elements in `iter`.\n///\n/// # Examples\n///\n/// ```\n/// # use gdlisp::util::each_pair;\n/// let vec1: Vec<i32> = vec!();\n/// assert_eq!(each_pair(vec1.into_iter()).collect::<Vec<_>>(), vec!());\n///\n/// let vec2: Vec<i32> = vec!(10);\n/// assert_eq!(each_pair(vec2.into_iter()).collect::<Vec<_>>(), vec!());\n///\n/// let vec3: Vec<i32> = vec!(10, 20);\n/// assert_eq!(each_pair(vec3.into_iter()).collect::<Vec<_>>(), vec!((10, 20)));\n///\n/// let vec4: Vec<i32> = vec!(10, 20, 30);\n/// assert_eq!(each_pair(vec4.into_iter()).collect::<Vec<_>>(), vec!((10, 20), (20, 30)));\n/// ```\npub fn each_pair<T, I>(iter: I) -> PairIter<T, I> where I : Iterator<Item=T> {\n  PairIter(None, iter)\n}\n\nimpl<T, I> Iterator for PairIter<T, I> where I : Iterator<Item=T>, T : Clone {\n  type Item = (T, T);\n\n  fn next(&mut self) -> Option<(T, T)> {\n    let fst = self.0.clone().or_else(|| self.1.next());\n    let snd = self.1.next();\n    self.0 = snd.clone();\n    fst.and_then(|fst| snd.map(|snd| (fst, snd)))\n  }\n\n}\n\n/// The type of iterator returned by [`each_non_overlapping_pair`].\npub struct NonOverlappingPairIter<I>(I);\n\n/// Return an iterator over each non-overlapping pair of adjacent\n/// elements in `iter`. If there are an odd number of elements, then\n/// the final element will not be included in any of the pairs.\n///\n/// # Examples\n///\n/// ```\n/// # use gdlisp::util::each_non_overlapping_pair;\n/// let vec1: Vec<i32> = vec!();\n/// assert_eq!(each_non_overlapping_pair(vec1.into_iter()).collect::<Vec<_>>(), vec!());\n///\n/// let vec2: Vec<i32> = vec!(10);\n/// assert_eq!(each_non_overlapping_pair(vec2.into_iter()).collect::<Vec<_>>(), vec!());\n///\n/// let vec3: Vec<i32> = vec!(10, 20);\n/// assert_eq!(each_non_overlapping_pair(vec3.into_iter()).collect::<Vec<_>>(), vec!((10, 20)));\n///\n/// let vec4: Vec<i32> = vec!(10, 20, 30);\n/// assert_eq!(each_non_overlapping_pair(vec4.into_iter()).collect::<Vec<_>>(), vec!((10, 20)));\n///\n/// let vec5: Vec<i32> = vec!(10, 20, 30, 40);\n/// assert_eq!(each_non_overlapping_pair(vec5.into_iter()).collect::<Vec<_>>(), vec!((10, 20), (30, 40)));\n/// ```\npub fn each_non_overlapping_pair<I: Iterator>(iter: I) -> NonOverlappingPairIter<I> {\n  NonOverlappingPairIter(iter)\n}\n\nimpl<I: Iterator> Iterator for NonOverlappingPairIter<I> {\n  type Item = (<I as Iterator>::Item, <I as Iterator>::Item);\n\n  fn next(&mut self) -> Option<Self::Item> {\n    let fst = self.0.next();\n    let snd = self.0.next();\n    fst.and_then(|fst| snd.map(|snd| (fst, snd)))\n  }\n\n}\n\n/// Given two [`HashMap`] values `a` and `b`, merge `b` into `a`.\n///\n/// For every key of `b` which is not in `a`, that key-value pair is\n/// added verbatim to `a`. For every key of `b` which *is* present in\n/// `a`, apply `merge_fn` to the two values to combine them, storing\n/// the result in `a`. The `a` value is passed to `merge_fn` before\n/// the `b` value.\npub fn merge_hashmap_inplace<K : Eq + Hash, V>(a: &mut HashMap<K, V>,\n                                               b: HashMap<K, V>,\n                                               mut merge_fn: impl FnMut(V, V) -> V) {\n  for (k, v) in b {\n    match a.remove(&k) {\n      None => a.insert(k, v),\n      Some(v1) => a.insert(k, merge_fn(v, v1)),\n    };\n  }\n}\n\n/// Consume the `Read` instance, as though using [`Read::read_to_end`].\n///\n/// Accumulates the result into a string, returning an error of kind\n/// [`io::ErrorKind::InvalidData`] if the data is not valid UTF-8.\npub fn read_to_end(input: &mut impl Read) -> io::Result<String> {\n  let mut vec = Vec::new();\n  input.read_to_end(&mut vec)?;\n  String::from_utf8(vec).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))\n}\n\n/// Convert the `Option` to a `Vec`, returning a vector of length\n/// either zero or one.\npub fn option_to_vec<T>(value: Option<T>) -> Vec<T> {\n  match value {\n    None => vec!(),\n    Some(x) => vec!(x),\n  }\n}\n\n/// This is equivalent to the nightly-only Rust function\n/// `Result::into_ok`, for safely unwrapping `Result` values which can\n/// provably never be an error.\npub fn extract_err<T>(value: Result<T, Infallible>) -> T {\n  match value {\n    Ok(value) => value,\n    Err(contra) => match contra {},\n  }\n}\n\n/// Unzip an iterator into two collections, taking error values into\n/// consideration. This is the spiritual composition of\n/// [`Iterator::unzip`] and the\n/// [`FromIterator`](std::iter::FromIterator) implementation on\n/// [`Result`].\npub fn unzip_err<E, FromA, FromB, I, A, B>(iter: I) -> Result<(FromA, FromB), E>\nwhere I : Iterator<Item=Result<(A, B), E>>,\n      FromA : Default + Extend<A>,\n      FromB : Default + Extend<B> {\n  let mut from_a = FromA::default();\n  let mut from_b = FromB::default();\n  for term in iter {\n    let (a, b) = term?;\n    from_a.extend(one::One(a));\n    from_b.extend(one::One(b));\n  }\n  Ok((from_a, from_b))\n}\n\n/// Returns a matching element from the vector, mutably.\n///\n/// If no matching element is found, then a new element is appended\n/// and returned.\n#[allow(clippy::redundant_closure)] // Clippy seems to have made a mistake here, suggested fix fails the borrow checker\npub fn find_or_else_mut<T>(vec: &mut Vec<T>, default: impl FnOnce() -> T, mut pred: impl FnMut(&T) -> bool) -> &mut T {\n  if vec.iter().any(|x| pred(x)) {\n    vec.iter_mut().find(|x| pred(x)).expect(\"Internal error in find_or_else_mut\")\n  } else {\n    vec.push(default());\n    vec.last_mut().expect(\"Internal error in find_or_else_mut\")\n  }\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[derive(Eq, PartialEq, Copy, Clone, Debug)]\n  struct FakeError;\n\n  #[test]\n  fn test_fold1() {\n    let vec1: Vec<i32> = vec!(1, 2, 3, 4);\n    assert_eq!(fold1(vec1.into_iter(), |x, y| x + y), Some(10));\n\n    let vec2: Vec<i32> = vec!();\n    assert_eq!(fold1(vec2.into_iter(), |x, y| x + y), None);\n\n    let vec3: Vec<i32> = vec!(1, 2, 3, 4);\n    assert_eq!(fold1(vec3.into_iter(), |x, y| x - y), Some(-8));\n  }\n\n  #[test]\n  fn test_extract_err() {\n    assert_eq!(extract_err(Ok(1)), 1);\n  }\n\n  #[test]\n  fn test_unzip_err_1() {\n    let vec1: Vec<Result<(i32, i32), FakeError>> = vec!(Ok((1, 10)), Ok((2, 20)), Ok((3, 30)));\n    assert_eq!(unzip_err(vec1.into_iter()), Ok((vec!(1, 2, 3), vec!(10, 20, 30))));\n\n    let vec2: Vec<Result<(i32, i32), FakeError>> = vec!(Ok((1, 10)), Err(FakeError), Ok((3, 30)));\n    let unzip_result2: Result<(Vec<_>, Vec<_>), _> = unzip_err(vec2.into_iter());\n    assert_eq!(unzip_result2, Err(FakeError));\n\n    let vec3: Vec<Result<(i32, i32), FakeError>> = vec!();\n    assert_eq!(unzip_err(vec3.into_iter()), Ok((vec!(), vec!())));\n  }\n\n  #[test]\n  fn test_unzip_err_2() {\n    // Make sure errors actually abort the process and the iterator is\n    // lazy.\n    let mut count = 0;\n    let vec = vec!(1, 2, 3, 4, 5);\n    let iter = vec.into_iter().map(|x| {\n      match x {\n        1 => { count += 1; }\n        3 => { return Err(FakeError) }\n        5 => { count += 100; }\n        _ => {}\n      };\n      Ok((x, 0))\n    });\n    let unzip_result: Result<(Vec<_>, Vec<_>), _> = unzip_err(iter);\n    assert_eq!(unzip_result, Err(FakeError));\n    // Only the count += 1 should have run, not the count += 100\n    assert_eq!(count, 1);\n  }\n\n  #[test]\n  fn find_or_else_mut_1() {\n    // If it already exists\n    let mut v = vec!(1, 2, 3, 4);\n    {\n      let m = find_or_else_mut(&mut v, || panic!(\"default case called unexpectedly\"), |x| *x == 3);\n      assert_eq!(m, &mut 3);\n    }\n    assert_eq!(v, vec!(1, 2, 3, 4));\n  }\n\n  #[test]\n  fn find_or_else_mut_2() {\n    // If it doesn't exist\n    let mut v = vec!(1, 2, 3, 4);\n    {\n      let m = find_or_else_mut(&mut v, || 10, |x| *x == 10);\n      assert_eq!(m, &mut 10);\n    }\n    assert_eq!(v, vec!(1, 2, 3, 4, 10));\n  }\n\n}\n"
  },
  {
    "path": "src/util/one.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`One`] type, a trivial collection which always\n//! contains one value.\n\nuse std::mem::swap;\n\n/// A `One<T>` always contains a single `T`. `One<T>` is a collection\n/// type and can be iterated over.\n#[derive(Clone, Debug, Copy, Eq, PartialEq, Ord, PartialOrd)]\npub struct One<T>(pub T);\n\n/// The underlying iterator for [`One`]. Returned by [`One::iter`].\npub struct Iter<'a, T>(Option<&'a T>);\n\n/// The underlying mutable iterator for [`One`]. Returned by [`One::iter_mut`].\npub struct IterMut<'a, T>(Option<&'a mut T>);\n\n/// The underlying owning iterator for [`One`]. Returned by [`One::into_iter`].\n#[derive(Clone)]\npub struct IntoIter<T>(Option<T>);\n\nimpl<T> One<T> {\n\n  pub fn iter(&self) -> Iter<'_, T> {\n    Iter(Some(&self.0))\n  }\n\n  pub fn iter_mut(&mut self) -> IterMut<'_, T> {\n    IterMut(Some(&mut self.0))\n  }\n\n}\n\n// Default implementation imposes T: Clone, which is unnecessary.\nimpl<'a, T> Clone for Iter<'a, T> {\n  fn clone(&self) -> Self {\n    Iter(self.0)\n  }\n}\n\nimpl<T> IntoIterator for One<T> {\n  type Item = T;\n  type IntoIter = IntoIter<T>;\n\n  fn into_iter(self) -> IntoIter<T> {\n    IntoIter(Some(self.0))\n  }\n\n}\n\nimpl<'a, T> Iterator for Iter<'a, T> {\n  type Item = &'a T;\n\n  fn next(&mut self) -> Option<&'a T> {\n    let mut result = None;\n    swap(&mut self.0, &mut result);\n    result\n  }\n\n}\n\nimpl<'a, T> Iterator for IterMut<'a, T> {\n  type Item = &'a mut T;\n\n  fn next(&mut self) -> Option<&'a mut T> {\n    let mut result = None;\n    swap(&mut self.0, &mut result);\n    result\n  }\n\n}\n\nimpl<T> Iterator for IntoIter<T> {\n  type Item = T;\n\n  fn next(&mut self) -> Option<T> {\n    let mut result = None;\n    swap(&mut self.0, &mut result);\n    result\n  }\n\n}\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn one_iter() {\n    let one = One(100);\n    assert_eq!(one.iter().collect::<Vec<_>>(), vec!(&100));\n  }\n\n  #[test]\n  fn one_iter_mut() {\n    let mut one = One(100);\n    for x in one.iter_mut() {\n      *x = 200;\n    }\n    assert_eq!(one, One(200));\n  }\n\n\n  #[test]\n  fn one_into_iter() {\n    let one = One(100);\n    for x in one {\n      assert_eq!(x, 100);\n    }\n  }\n\n}\n"
  },
  {
    "path": "src/util/prefix_matcher.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n/// Provides the [`PrefixMatcher`] type, for efficiently finding a\n/// matching prefix of a given string.\n\nuse std::collections::HashMap;\nuse std::iter;\nuse std::borrow::Borrow;\n\n/// [`PrefixMatcher`] is a state machine designed for identifying one\n/// of a finite number of prefixes.\npub struct PrefixMatcher<'a> {\n  characters: HashMap<char, usize>,\n  table: PrefixTable<usize>,\n  terminal_states: Vec<Option<&'a str>>,\n}\n\n// A 2D array masquerading as a vector.\nstruct PrefixTable<T> {\n  width_impl: usize,\n  table: Vec<T>,\n}\n\nimpl<T> PrefixTable<T> {\n\n  fn new(width: usize) -> PrefixTable<T> {\n    PrefixTable { width_impl: width, table: Vec::new() }\n  }\n\n  fn width(&self) -> usize {\n    self.width_impl\n  }\n\n  fn height(&self) -> usize {\n    self.table.len() / self.width()\n  }\n\n  fn get(&self, y: usize, x: usize) -> Option<&T> {\n    if y >= self.height() || x >= self.width() {\n      None\n    } else {\n      Some(&self.table[y * self.width() + x])\n    }\n  }\n\n  fn get_mut(&mut self, y: usize, x: usize) -> Option<&mut T> {\n    if y >= self.height() || x >= self.width() {\n      None\n    } else {\n      let w = self.width();\n      Some(&mut self.table[y * w + x])\n    }\n  }\n\n  fn add_row(&mut self, row: impl Iterator<Item=T>) {\n    let row: Vec<_> = row.collect();\n    assert_eq!(row.len(), self.width());\n    self.table.extend(row);\n  }\n\n}\n\nimpl<T: Clone> PrefixTable<T> {\n\n  fn add_row_value(&mut self, value: T) {\n    let iter = iter::repeat(value).take(self.width());\n    self.add_row(iter);\n  }\n\n}\n\nimpl<'a> PrefixMatcher<'a> {\n\n  /// Build a prefix matcher from a collection of strings. The slice\n  /// of strings should not contain any duplicates, but it is\n  /// permitted to contain strings which are prefixes of each other.\n  pub fn build<I, S>(options: I) -> Self\n  where S: Borrow<str> + ?Sized + 'a,\n        I: Iterator<Item=&'a S> {\n    let options: Vec<_> = options.collect();\n    let characters = PrefixMatcher::build_chars_map(&options);\n\n    let mut table = PrefixTable::new(characters.len());\n    let mut terminal_states = Vec::new();\n\n    table.add_row_value(EMPTY_CELL);\n    terminal_states.push(None);\n\n    for opt in options {\n      let mut current_state: usize = 0;\n      for ch in opt.borrow().chars() {\n        let column = characters[&ch];\n        let mut new_state = *table.get(current_state, column).unwrap();\n        if new_state == EMPTY_CELL {\n          new_state = table.height();\n          table.add_row_value(EMPTY_CELL);\n          terminal_states.push(None);\n          *table.get_mut(current_state, column).unwrap() = new_state;\n        }\n        current_state = new_state;\n      }\n      terminal_states[current_state] = Some(opt.borrow());\n    }\n\n    PrefixMatcher { characters, table, terminal_states }\n  }\n\n  fn build_chars_map<S>(options: &[&S]) -> HashMap<char, usize>\n  where S: Borrow<str> + ?Sized + 'a {\n    let mut next_index: usize = 0;\n    let mut result = HashMap::new();\n    for string in options {\n      for ch in (*string).borrow().chars() {\n        result.entry(ch).or_insert_with(|| {\n          next_index += 1;\n          next_index - 1\n        });\n      }\n    }\n    result\n  }\n\n  /// Returns the longest prefix in this [`PrefixMatcher`] which is a\n  /// prefix of the input string. If there is no match, returns\n  /// `None`.\n  pub fn identify_prefix(&self, string: &str) -> Option<&'a str> {\n    let mut final_result: Option<&'a str> = None;\n    let mut state: usize = 0;\n    for ch in string.chars() {\n      if let Some(new_result) = self.terminal_states[state] {\n        final_result = Some(new_result);\n      }\n      match self.characters.get(&ch) {\n        None => {\n          // Unknown character, we're done.\n          break;\n        }\n        Some(column) => {\n          let new_state = *self.table.get(state, *column).unwrap();\n          if new_state == EMPTY_CELL {\n            // Unknown prefix, we're done.\n            break;\n          }\n          state = new_state;\n        }\n      }\n    }\n    if let Some(new_result) = self.terminal_states[state] {\n      final_result = Some(new_result);\n    }\n    final_result\n  }\n\n}\n\nconst EMPTY_CELL: usize = usize::MAX;\n\n#[cfg(test)]\nmod tests {\n  use super::*;\n\n  #[test]\n  fn test_chars_map() {\n    let options = vec!(\"abc\", \"def\", \"aBC\", \"g\");\n    let intended_result = HashMap::from([\n      ('a', 0), ('b', 1), ('c', 2), ('d', 3), ('e', 4), ('f', 5), ('B', 6), ('C', 7), ('g', 8),\n    ]);\n    assert_eq!(PrefixMatcher::build_chars_map(&options), intended_result);\n  }\n\n  #[test]\n  fn test_empty_prefix_identifier() {\n    let options: Vec<&'static str> = vec!();\n    let matcher = PrefixMatcher::build(options.iter());\n    assert_eq!(matcher.identify_prefix(\"abc\"), None);\n    assert_eq!(matcher.identify_prefix(\"def\"), None);\n    assert_eq!(matcher.identify_prefix(\"\"), None);\n    assert_eq!(matcher.identify_prefix(\"---\"), None);\n  }\n\n  #[test]\n  fn test_empty_string_prefix_identifier() {\n    let options = vec!(\"\");\n    let matcher = PrefixMatcher::build(options.iter());\n    assert_eq!(matcher.identify_prefix(\"abc\"), Some(\"\"));\n    assert_eq!(matcher.identify_prefix(\"def\"), Some(\"\"));\n    assert_eq!(matcher.identify_prefix(\"\"), Some(\"\"));\n    assert_eq!(matcher.identify_prefix(\"---\"), Some(\"\"));\n  }\n\n  #[test]\n  fn test_prefix_identifier_foobar() {\n    let options = vec!(\"foo\", \"bar\", \"foobar\");\n    let matcher = PrefixMatcher::build(options.iter());\n    assert_eq!(matcher.identify_prefix(\"abc\"), None);\n    assert_eq!(matcher.identify_prefix(\"\"), None);\n    assert_eq!(matcher.identify_prefix(\"foo\"), Some(\"foo\"));\n    assert_eq!(matcher.identify_prefix(\"football\"), Some(\"foo\"));\n    assert_eq!(matcher.identify_prefix(\"bar\"), Some(\"bar\"));\n    assert_eq!(matcher.identify_prefix(\"barbecue\"), Some(\"bar\"));\n    assert_eq!(matcher.identify_prefix(\"foobaz\"), Some(\"foo\"));\n    assert_eq!(matcher.identify_prefix(\"foobar\"), Some(\"foobar\"));\n    assert_eq!(matcher.identify_prefix(\"foobarbaz\"), Some(\"foobar\"));\n  }\n\n  #[test]\n  fn test_prefix_identifier_foobar_with_empty() {\n    // Empty string should act as a fallback if nothing else matches.\n    let options = vec!(\"\", \"foo\", \"bar\", \"foobar\");\n    let matcher = PrefixMatcher::build(options.iter());\n    assert_eq!(matcher.identify_prefix(\"abc\"), Some(\"\"));\n    assert_eq!(matcher.identify_prefix(\"\"), Some(\"\"));\n    assert_eq!(matcher.identify_prefix(\"foo\"), Some(\"foo\"));\n    assert_eq!(matcher.identify_prefix(\"football\"), Some(\"foo\"));\n    assert_eq!(matcher.identify_prefix(\"bar\"), Some(\"bar\"));\n    assert_eq!(matcher.identify_prefix(\"barbecue\"), Some(\"bar\"));\n    assert_eq!(matcher.identify_prefix(\"foobaz\"), Some(\"foo\"));\n    assert_eq!(matcher.identify_prefix(\"foobar\"), Some(\"foobar\"));\n    assert_eq!(matcher.identify_prefix(\"foobarbaz\"), Some(\"foobar\"));\n  }\n\n}\n"
  },
  {
    "path": "src/util/recursive.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n//! Provides the [`Recursive`] trait.\n\n/// A `Recursive` data structure is one that (as the name implies) can\n/// recursively contain elements of itself. Further, implementations\n/// of this trait provide a mechanism for determining the maximum\n/// recursion depth of the trait.\npub trait Recursive {\n\n  /// The maximum depth of the recursive data structure. Atomic nodes,\n  /// those which do not contain any further instances of the data\n  /// structure, should have a depth of 1. Any node which contains\n  /// other nodes has depth equal to the maximum of the depths of its\n  /// subnodes plus 1.\n  fn depth(&self) -> u32;\n\n}\n"
  },
  {
    "path": "tests/integration_tests.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod test;\n"
  },
  {
    "path": "tests/test/builtin_function_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\nuse gdlisp::runner::version::{get_godot_version, Version};\n\nuse super::common::*;\n\n#[test]\npub fn vector_test() {\n  assert_eq!(parse_compile_and_output(\"(vector 9 10)\"), \"return Vector2(9, 10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(vector 9 10 11)\"), \"return Vector3(9, 10, 11)\\n\");\n}\n\n#[test]\npub fn vector_syntax_test() {\n  assert_eq!(parse_compile_and_output(\"V{9 10}\"), \"return Vector2(9, 10)\\n\");\n  assert_eq!(parse_compile_and_output(\"V{9 10 11}\"), \"return Vector3(9, 10, 11)\\n\");\n}\n\n#[test]\npub fn array_test() {\n  assert_eq!(parse_compile_and_output(\"(array 9 10)\"), \"return [9, 10]\\n\");\n  assert_eq!(parse_compile_and_output(\"(array 9 10 11)\"), \"return [9, 10, 11]\\n\");\n}\n\n#[test]\npub fn array_syntax_test() {\n  assert_eq!(parse_compile_and_output(\"[1 2 3 4]\"), \"return [1, 2, 3, 4]\\n\");\n}\n\n#[test]\npub fn dictionary_test() {\n  assert_eq!(parse_compile_and_output(\"(dict 1 2 3 4)\"), \"return {1: 2, 3: 4}\\n\");\n}\n\n#[test]\npub fn dictionary_odd_args_test() {\n  // Drops the last arg silently.\n  assert_eq!(parse_compile_and_output(\"(dict 1 2 3)\"), \"return {1: 2}\\n\");\n}\n\n#[test]\npub fn dictionary_literal_test() {\n  assert_eq!(parse_compile_and_output(\"{1 2 3 4}\"), \"return {1: 2, 3: 4}\\n\");\n}\n\n#[test]\npub fn yield_test() {\n  assert_eq!(parse_compile_and_output(\"(yield)\"), \"return yield()\\n\");\n  assert_eq!(parse_compile_and_output(\"(yield 1 2)\"), \"return yield(1, 2)\\n\");\n}\n\n#[test]\npub fn assert_test() {\n  assert_eq!(parse_compile_and_output(\"(assert #t)\"), \"return assert(true)\\n\");\n  assert_eq!(parse_compile_and_output(\"(assert #t \\\"a\\\")\"), \"return assert(true, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn attribute_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((foo 1)) foo:bar)\"), \"var foo = 1\\nreturn foo.bar\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((foo 1)) foo:bar 2)\"), \"var foo = 1\\nreturn 2\\n\");\n}\n\n#[test]\npub fn method_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((foo 1)) (foo:bar))\"), \"var foo = 1\\nreturn foo.bar()\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((foo 1)) (foo:bar 100) 2)\"), \"var foo = 1\\nfoo.bar(100)\\nreturn 2\\n\");\n}\n\n#[test]\npub fn simple_builtin_test() {\n  assert_eq!(parse_compile_and_output(\"(cons 1 2)\"), \"return GDLisp.cons(1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(cons 1 (cons 2 3))\"), \"return GDLisp.cons(1, GDLisp.cons(2, 3))\\n\");\n  assert_eq!(parse_compile_and_output(\"(intern 10)\"), \"return GDLisp.intern(10)\\n\");\n}\n\n#[test]\npub fn known_gdscript_classes_test_1() {\n  assert_eq!(parse_compile_and_output(\"[Sprite Node Node2D GDScript Object]\"),\n             \"return [Sprite, Node, Node2D, GDScript, GDLisp._Object]\\n\");\n}\n\n#[test]\npub fn known_gdscript_classes_test_2() {\n  // Note: They all get checked, but all except the last is elided by the statefulness rules.\n  assert_eq!(parse_compile_and_output(\"(progn Sprite Node Node2D GDScript Object)\"), \"return GDLisp._Object\\n\");\n}\n\n#[test]\npub fn unknown_gdscript_classes_test() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(progn NotARealClass Node2D GDScript Object)\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"NotARealClass\")), SourceOffset(7)))),\n  );\n}\n\n#[test]\npub fn return_test() {\n  assert_eq!(parse_compile_and_output(\"(progn (if 1 (return 2)) 3)\"),\n             \"if 1:\\n    return 2\\nelse:\\n    if true:\\n        pass\\n    else:\\n        pass\\nreturn 3\\n\");\n}\n\n#[test]\npub fn yield_running_test() {\n  let result = parse_and_run(r#\"\n    ((defn foo ()\n       (print 2)\n       (yield)\n       (print 4)\n       6)\n     (print 1)\n     (let ((x (foo)))\n       (print 3)\n       (let ((final (x:resume)))\n         (print 5)\n         (print final))))\n  \"#);\n  assert_eq!(result, \"\\n1\\n2\\n3\\n4\\n5\\n6\\n\");\n}\n\n#[test]\npub fn yield_star_running_test() {\n  let result = parse_and_run(r#\"\n    ((defn foo ()\n       (print 2)\n       (yield)\n       (print 4)\n       6)\n     (defn bar ()\n       (yield* (foo)))\n     (print 1)\n     (let ((x (bar)))\n       (print 3)\n       (let ((final (x:resume)))\n         (print 5)\n         (print final))))\n  \"#);\n  assert_eq!(result, \"\\n1\\n2\\n3\\n4\\n5\\n6\\n\");\n}\n\n#[test]\npub fn custom_call_magic_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo (x y) (sys/call-magic ADDITION) 9) (defn run () (foo 10 20)))\"),\n             r#\"extends Reference\n\n\nstatic func foo(x, y):\n    return 9\n\n\nstatic func run():\n    return 10 + 20\n\"#);\n}\n\n#[test]\npub fn custom_call_magic_test_failed() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defn foo (x y) (sys/call-magic THIS-MAGIC-DOES-NOT-EXIST) 9))\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchMagic(String::from(\"THIS-MAGIC-DOES-NOT-EXIST\")), SourceOffset(2)))),\n  );\n}\n\n#[test]\npub fn split_call_test_1() {\n  assert_eq!(parse_compile_and_output(\"(sys/split 0:car):car:car:car\"),\n             r#\"var _split = 0.car\nreturn _split.car.car.car\n\"#);\n}\n\n#[test]\npub fn split_call_test_2() {\n  assert_eq!(parse_compile_and_output(\"(sys/split (sys/split 0:car):car):car:car\"),\n             r#\"var _split = 0.car\nvar _split_0 = _split.car\nreturn _split_0.car.car\n\"#);\n}\n\n#[test]\npub fn wrapped_random_functions_test() {\n  assert_eq!(parse_compile_and_output(\"(randomize)\"), \"return randomize()\\n\");\n  assert_eq!(parse_compile_and_output(\"(randi)\"), \"return randi()\\n\");\n  assert_eq!(parse_compile_and_output(\"(randf)\"), \"return randf()\\n\");\n  assert_eq!(parse_compile_and_output(\"(rand-range 0 1)\"), \"return rand_range(0, 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(seed 999)\"), \"return seed(999)\\n\");\n  assert_eq!(parse_compile_and_output(\"(rand-seed 999)\"), \"return rand_seed(999)\\n\");\n}\n\n#[test]\npub fn wrapped_math_functions_test() {\n  assert_eq!(parse_compile_and_output(\"(clamp 0 1 2)\"), \"return clamp(0, 1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(abs 0)\"), \"return abs(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(acos 0)\"), \"return acos(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(asin 0)\"), \"return asin(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(atan 0)\"), \"return atan(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(atan2 0 1)\"), \"return atan2(0, 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(cos 0)\"), \"return cos(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(cosh 0)\"), \"return cosh(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(sin 0)\"), \"return sin(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(sinh 0)\"), \"return sinh(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(tan 0)\"), \"return tan(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(tanh 0)\"), \"return tanh(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(ceil 0)\"), \"return ceil(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(exp 0)\"), \"return exp(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(floor 0)\"), \"return floor(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(sqrt 10)\"), \"return sqrt(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(pow 10 2)\"), \"return pow(10, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(fmod 10 2)\"), \"return fmod(10, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(fposmod 10 2)\"), \"return fposmod(10, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(posmod 10 2)\"), \"return posmod(10, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(sign 10)\"), \"return sign(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(is-nan 10)\"), \"return is_nan(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(is-inf 10)\"), \"return is_inf(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(is-equal-approx 10 10)\"), \"return is_equal_approx(10, 10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(is-zero-approx 10)\"), \"return is_zero_approx(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(stepify 0.5 2)\"), \"return stepify(5e-1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(step-decimals 0.5)\"), \"return step_decimals(5e-1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(deg2rad 60)\"), \"return deg2rad(60)\\n\");\n  assert_eq!(parse_compile_and_output(\"(rad2deg PI)\"), \"return rad2deg(PI)\\n\");\n  assert_eq!(parse_compile_and_output(\"(linear2db 0)\"), \"return linear2db(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(db2linear 0)\"), \"return db2linear(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(log 10)\"), \"return log(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(round 10)\"), \"return round(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(wrapf 10 20 15)\"), \"return wrapf(10, 20, 15)\\n\");\n  assert_eq!(parse_compile_and_output(\"(wrapi 10 20 15)\"), \"return wrapi(10, 20, 15)\\n\");\n  assert_eq!(parse_compile_and_output(\"(cartesian2polar 10 10)\"), \"return cartesian2polar(10, 10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(polar2cartesian 10 PI)\"), \"return polar2cartesian(10, PI)\\n\");\n  assert_eq!(parse_compile_and_output(\"(nearest-po2 17)\"), \"return nearest_po2(17)\\n\");\n}\n\n#[test]\npub fn wrapped_error_functions_test() {\n  assert_eq!(parse_compile_and_output(\"(push-error \\\"A\\\")\"), \"return push_error(\\\"A\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(push-warning \\\"A\\\")\"), \"return push_warning(\\\"A\\\")\\n\");\n}\n\n#[test]\npub fn wrapped_load_functions_test() {\n  assert_eq!(parse_compile_and_output(\"(load \\\"A\\\")\"), \"return load(\\\"A\\\")\\n\");\n}\n\n#[test]\npub fn failed_preload_function_test() {\n  assert_eq!(parse_compile_and_output_err(\"(preload \\\"A\\\")\"),\n             Err(PError::from(GDError::new(GDErrorF::BadPreloadArgument(String::from(\"A\")), SourceOffset(0)))));\n}\n\n#[test]\npub fn wrapped_range_functions_test() {\n  assert_eq!(parse_compile_and_output(\"(inverse-lerp 0 10 6)\"), \"return inverse_lerp(0, 10, 6)\\n\");\n  assert_eq!(parse_compile_and_output(\"(lerp 0 10 0.4)\"), \"return lerp(0, 10, 4e-1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(lerp-angle 0 10 0.4)\"), \"return lerp_angle(0, 10, 4e-1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(range-lerp 5 0 10 -10 -20)\"), \"return range_lerp(5, 0, 10, -10, -20)\\n\");\n  assert_eq!(parse_compile_and_output(\"(move-toward 10 5 1)\"), \"return move_toward(10, 5, 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(ease 0.7 2.0)\"), \"return ease(7e-1, 2e0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(smoothstep 1.0 5.0 0.5)\"), \"return smoothstep(1e0, 5e0, 5e-1)\\n\");\n}\n\n#[test]\npub fn wrapped_misc_functions_test() {\n  assert_eq!(parse_compile_and_output(\"(hash \\\"A\\\")\"), \"return hash(\\\"A\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(get-stack)\"), \"return get_stack()\\n\");\n  assert_eq!(parse_compile_and_output(\"(print-stack)\"), \"return print_stack()\\n\");\n  assert_eq!(parse_compile_and_output(\"(is-instance-valid (Reference:new))\"), \"return is_instance_valid(Reference.new())\\n\");\n  assert_eq!(parse_compile_and_output(\"(parse-json nil)\"), \"return parse_json(GDLisp.nil)\\n\");\n  assert_eq!(parse_compile_and_output(\"(to-json \\\"{}\\\")\"), \"return to_json(\\\"{}\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(validate-json \\\"{}\\\")\"), \"return validate_json(\\\"{}\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(dict2inst {})\"), \"return dict2inst({})\\n\");\n  assert_eq!(parse_compile_and_output(\"(inst2dict (Reference:new))\"), \"return inst2dict(Reference.new())\\n\");\n  assert_eq!(parse_compile_and_output(\"(str2var \\\"{}\\\")\"), \"return str2var(\\\"{}\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(var2str (Reference:new))\"), \"return var2str(Reference.new())\\n\");\n  assert_eq!(parse_compile_and_output(\"(weakref (Reference:new))\"), \"return weakref(Reference.new())\\n\");\n  assert_eq!(parse_compile_and_output(\"(funcref (Reference:new) \\\"potato\\\")\"), \"return funcref(Reference.new(), \\\"potato\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(type-exists \\\"Reference\\\")\"), \"return type_exists(\\\"Reference\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(Color8 0 0 0)\"), \"return Color8(0, 0, 0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Color8 0 0 0 255)\"), \"return Color8(0, 0, 0, 255)\\n\");\n  assert_eq!(parse_compile_and_output(\"(ColorN \\\"red\\\")\"), \"return ColorN(\\\"red\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(ColorN \\\"red\\\" 1.0)\"), \"return ColorN(\\\"red\\\", 1e0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(var2bytes 0)\"), \"return var2bytes(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(var2bytes 0 #f)\"), \"return var2bytes(0, false)\\n\");\n  assert_eq!(parse_compile_and_output(\"(bytes2var ())\"), \"return bytes2var(null)\\n\");\n  assert_eq!(parse_compile_and_output(\"(bytes2var () #f)\"), \"return bytes2var(null, false)\\n\");\n}\n\n#[test]\npub fn convert_function_test() {\n  assert_eq!(parse_compile_and_output(\"(convert 1 Int)\"), \"return GDLisp._convert(1, GDLisp.Int)\\n\");\n  assert_eq!(parse_compile_and_output(\"(convert 1 TYPE_INT)\"), \"return GDLisp._convert(1, TYPE_INT)\\n\");\n}\n\n#[test]\npub fn convert_function_run_test() {\n  assert_eq!(parse_and_run(\"((print (convert 1.5 Int)))\"), \"\\n1\\n\");\n  assert_eq!(parse_and_run(\"((print (convert 1.5 TYPE_INT)))\"), \"\\n1\\n\");\n}\n\n#[test]\npub fn instance_from_id_function_test() {\n  assert_eq!(parse_compile_and_output(\"(instance-from-id 9)\"), \"return instance_from_id(9)\\n\");\n}\n\n#[test]\npub fn instance_from_id_function_run_test() {\n  assert_eq!(parse_and_run(r#\"((defclass Foo ()\n                                 (defvar foo \"mystring\"))\n                               (let* ((x (Foo:new))\n                                      (id (x:get-instance-id)))\n                                 (print (instance-from-id id):foo)))\"#),\n             \"\\nmystring\\n\");\n}\n\n#[test]\npub fn str_test() {\n  assert_eq!(parse_compile_and_output(\"(str 3)\"), \"return str(3)\\n\");\n  assert_eq!(parse_compile_and_output(\"(str 3 \\\"a\\\")\"), \"return str(3, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn printerr_test() {\n  assert_eq!(parse_compile_and_output(\"(printerr 3 \\\"a\\\")\"), \"return printerr(3, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn print_test() {\n  assert_eq!(parse_compile_and_output(\"(print 3 \\\"a\\\")\"), \"return print(3, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn printt_test() {\n  assert_eq!(parse_compile_and_output(\"(printt 3 \\\"a\\\")\"), \"return printt(3, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn prints_test() {\n  assert_eq!(parse_compile_and_output(\"(prints 3 \\\"a\\\")\"), \"return prints(3, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn printraw_test() {\n  assert_eq!(parse_compile_and_output(\"(printraw 3 \\\"a\\\")\"), \"return printraw(3, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn print_debug_test() {\n  assert_eq!(parse_compile_and_output(\"(print-debug 3 \\\"a\\\")\"), \"return print_debug(3, \\\"a\\\")\\n\");\n}\n\n#[test]\npub fn builtin_constant_on_type_test() {\n  assert_eq!(parse_compile_and_output(\"(print Transform2D:IDENTITY)\"), \"return print(GDLisp._Transform2D.IDENTITY)\\n\");\n}\n\n#[test]\npub fn builtin_type_constructor_test() {\n  assert_eq!(parse_compile_and_output(\"(Bool 0)\"), \"return bool(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Int 99.1)\"), \"return int(9.91e1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Float \\\"0\\\")\"), \"return float(\\\"0\\\")\\n\");\n  assert_eq!(parse_compile_and_output(\"(String 10)\"), \"return String(10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Rect2 V{0 0} V{1 1})\"), \"return Rect2(Vector2(0, 0), Vector2(1, 1))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Rect2 0 0 1 1)\"), \"return Rect2(0, 0, 1, 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(AABB V{0 0 0} V{1 1 1})\"), \"return AABB(Vector3(0, 0, 0), Vector3(1, 1, 1))\\n\");\n  assert_eq!(parse_compile_and_output(\"(RID (Reference:new))\"), \"return RID(Reference.new())\\n\");\n  assert_eq!(parse_compile_and_output(\"(Dictionary {})\"), \"return Dictionary({})\\n\");\n  assert_eq!(parse_compile_and_output(\"(Array [])\"), \"return Array([])\\n\");\n  assert_eq!(parse_compile_and_output(\"(PoolColorArray [])\"), \"return PoolColorArray([])\\n\");\n  assert_eq!(parse_compile_and_output(\"(PoolByteArray [])\"), \"return PoolByteArray([])\\n\");\n  assert_eq!(parse_compile_and_output(\"(PoolIntArray [])\"), \"return PoolIntArray([])\\n\");\n  assert_eq!(parse_compile_and_output(\"(PoolRealArray [])\"), \"return PoolRealArray([])\\n\");\n  assert_eq!(parse_compile_and_output(\"(PoolVector2Array [])\"), \"return PoolVector2Array([])\\n\");\n  assert_eq!(parse_compile_and_output(\"(PoolVector3Array [])\"), \"return PoolVector3Array([])\\n\");\n  assert_eq!(parse_compile_and_output(\"(Vector2 1 2)\"), \"return Vector2(1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Vector3 0 1 2)\"), \"return Vector3(0, 1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Transform2D ())\"), \"return Transform2D(null)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Transform2D V{0 0} V{0 0} V{0 0})\"), \"return Transform2D(Vector2(0, 0), Vector2(0, 0), Vector2(0, 0))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Transform2D 0 V{1 1})\"), \"return Transform2D(0, Vector2(1, 1))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Plane V{0 0 0} 10)\"), \"return Plane(Vector3(0, 0, 0), 10)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Plane V{0 0 0} V{0 0 1} V{1 0 0})\"), \"return Plane(Vector3(0, 0, 0), Vector3(0, 0, 1), Vector3(1, 0, 0))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Plane 0 0 0 1)\"), \"return Plane(0, 0, 0, 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Quat ())\"), \"return Quat(null)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Quat V{0 0 0} 1)\"), \"return Quat(Vector3(0, 0, 0), 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Quat 0 0 0 1)\"), \"return Quat(0, 0, 0, 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Basis ())\"), \"return Basis(null)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Basis V{0 0 0})\"), \"return Basis(Vector3(0, 0, 0))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Basis V{0 0 0} 1)\"), \"return Basis(Vector3(0, 0, 0), 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Basis V{0 0 0} V{0 0 1} V{1 0 0})\"), \"return Basis(Vector3(0, 0, 0), Vector3(0, 0, 1), Vector3(1, 0, 0))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Transform ())\"), \"return Transform(null)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Transform () V{0 0 0})\"), \"return Transform(null, Vector3(0, 0, 0))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Transform V{0 0 0} V{0 0 1} V{1 0 0} V{2 2 2})\"), \"return Transform(Vector3(0, 0, 0), Vector3(0, 0, 1), Vector3(1, 0, 0), Vector3(2, 2, 2))\\n\");\n  assert_eq!(parse_compile_and_output(\"(Color 0)\"), \"return Color(0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Color 0 0 0)\"), \"return Color(0, 0, 0)\\n\");\n  assert_eq!(parse_compile_and_output(\"(Color 0 0 0 1)\"), \"return Color(0, 0, 0, 1)\\n\");\n\n}\n\n#[test]\npub fn nodepath_constructor_test() {\n  assert_eq!(parse_compile_and_output(r#\"(let ((x \"A\")) (NodePath x))\"#), r#\"var x = \"A\"\nreturn GDLisp._NodePath(x)\n\"#);\n  assert_eq!(parse_compile_and_output(r#\"(NodePath 0)\"#), \"return GDLisp._NodePath(0)\\n\");\n  assert_eq!(parse_compile_and_output(r#\"(NodePath \"a\")\"#), \"return @\\\"a\\\"\\n\");\n}\n\n#[test]\npub fn str_running_test() {\n  assert_eq!(parse_and_run(\"((print (str 1 2 #t)))\"), \"\\n12True\\n\");\n}\n\n#[test]\npub fn str_running_test_indirect() {\n  assert_eq!(parse_and_run(\"((print (funcall #'str 1 2 #t)))\"), \"\\n12True\\n\");\n}\n\n#[test]\npub fn printerr_running_test() {\n  let StringOutput { stdout, stderr } = parse_and_run_with_stderr(\"((printerr \\\"printerr_running_test OUTPUT\\\"))\");\n  assert_eq!(stdout, \"\\n\");\n  assert!(stderr.contains(\"printerr_running_test OUTPUT\"));\n}\n\n#[test]\npub fn printerr_running_test_indirect() {\n  let StringOutput { stdout, stderr } = parse_and_run_with_stderr(\"((funcall #'printerr \\\"printerr_running_test_indirect OUTPUT\\\"))\");\n  assert_eq!(stdout, \"\\n\");\n  assert!(stderr.contains(\"printerr_running_test_indirect OUTPUT\"));\n}\n\n#[test]\npub fn printraw_running_test() {\n  assert_eq!(parse_and_run(\"((printraw 1 2 #t))\"), \"\\n12True\"); // Note: No \\n at end\n}\n\n#[test]\npub fn printraw_running_test_indirect() {\n  assert_eq!(parse_and_run(\"((funcall #'printraw 1 2 #t))\"), \"\\n12True\"); // Note: No \\n at end\n}\n\n#[test]\npub fn print_running_test() {\n  assert_eq!(parse_and_run(\"((print 1 2 #t))\"), \"\\n12True\\n\");\n}\n\n#[test]\npub fn range_running_test() {\n  assert_eq!(parse_and_run(\"((print (range 5)) (print (range 1 5)) (print (range 1 5 2)) (print (range 5 1 -1)))\"),\n             \"\\n[0, 1, 2, 3, 4]\\n[1, 2, 3, 4]\\n[1, 3]\\n[5, 4, 3, 2]\\n\");\n}\n\n#[test]\npub fn range_running_test_indirect() {\n  assert_eq!(parse_and_run(\"((print (funcall #'range 5)) (print (funcall #'range 1 5)) (print (funcall #'range 1 5 2)) (print (funcall #'range 5 1 -1)))\"),\n             \"\\n[0, 1, 2, 3, 4]\\n[1, 2, 3, 4]\\n[1, 3]\\n[5, 4, 3, 2]\\n\");\n}\n\n#[test]\npub fn print_running_test_indirect() {\n  assert_eq!(parse_and_run(\"((funcall #'print 1 2 #t))\"), \"\\n12True\\n\");\n}\n\n#[test]\npub fn deep_equal_test() {\n  // deep_equal is only available in Godot 3.5 and later, so we need\n  // to disable this test if we're running on an older version.\n  let godot_version = get_godot_version().unwrap().version;\n  if godot_version >= Version::new(3, 5, 0) {\n    assert_eq!(parse_and_run(\"((print (deep-equal {\\\"a\\\" 1} {\\\"a\\\" 1})))\"), \"\\nTrue\\n\");\n  }\n}\n\n#[test]\npub fn cons_accessor_test_1() {\n  assert_eq!(parse_and_run(\"((print (quote (1 . 2)):car))\"), \"\\n1\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (1 . 2)):cdr))\"), \"\\n2\\n\");\n}\n\n#[test]\npub fn cons_accessor_test_2() {\n  assert_eq!(parse_and_run(\"((print (quote ((1 . 2) . (3 . 4))):caar))\"), \"\\n1\\n\");\n  assert_eq!(parse_and_run(\"((print (quote ((1 . 2) . (3 . 4))):cadr))\"), \"\\n2\\n\");\n  assert_eq!(parse_and_run(\"((print (quote ((1 . 2) . (3 . 4))):cdar))\"), \"\\n3\\n\");\n  assert_eq!(parse_and_run(\"((print (quote ((1 . 2) . (3 . 4))):cddr))\"), \"\\n4\\n\");\n}\n\n#[test]\npub fn cons_accessor_test_3() {\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):caaar))\"), \"\\n1\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):caadr))\"), \"\\n2\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):cadar))\"), \"\\n3\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):caddr))\"), \"\\n4\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):cdaar))\"), \"\\n5\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):cdadr))\"), \"\\n6\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):cddar))\"), \"\\n7\\n\");\n  assert_eq!(parse_and_run(\"((print (quote (((1 . 2) . (3 . 4)) . ((5 . 6) . (7 . 8)))):cdddr))\"), \"\\n8\\n\");\n}\n\n#[test]\npub fn nodepath_running_test() {\n  assert_eq!(parse_and_run(\"((let ((x (NodePath (let ((y \\\"foo/bar\\\")) y)))) (print (x:get-name 0)) (print (x:get-name 1))))\"), \"\\nfoo\\nbar\\n\");\n  assert_eq!(parse_and_run(\"((let ((x (NodePath \\\"foo/bar\\\"))) (print (x:get-name 0)) (print (x:get-name 1))))\"), \"\\nfoo\\nbar\\n\");\n}\n\n#[test]\npub fn vector_constant_print_test() {\n  assert_eq!(parse_and_run(\"((print Vector2:LEFT) (print Vector3:AXIS_Z))\"), \"\\n(-1, 0)\\n2\\n\");\n}\n\n#[test]\npub fn object_constant_value_test() {\n  // We had to hard-code these values in, so validate that they match\n  // what Godot expects.\n  assert_eq!(parse_and_run(r#\"((print (= CONNECT_DEFERRED ConnectFlags:DEFERRED))\n                               (print (= CONNECT_PERSIST ConnectFlags:PERSIST))\n                               (print (= CONNECT_ONESHOT ConnectFlags:ONESHOT))\n                               (print (= CONNECT_REFERENCE_COUNTED ConnectFlags:REFERENCE_COUNTED))\n                               (print (= NOTIFICATION_POSTINITIALIZE Notification:POSTINITIALIZE))\n                               (print (= NOTIFICATION_PREDELETE Notification:PREDELETE)))\"#),\n             \"\\nTrue\\nTrue\\nTrue\\nTrue\\nTrue\\nTrue\\n\");\n}\n\n#[test]\npub fn thread_macro_test() {\n  assert_eq!(parse_compile_and_output(\"(-> 1 foo1 (foo1) (foo2 2))\"), \"return foo2(foo1(foo1(1)), 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(-> 1)\"), \"return 1\\n\");\n}\n\n#[test]\npub fn last_thread_macro_test() {\n  assert_eq!(parse_compile_and_output(\"(->> 1 foo1 (foo1) (foo2 2))\"), \"return foo2(2, foo1(foo1(1)))\\n\");\n  assert_eq!(parse_compile_and_output(\"(->> 1)\"), \"return 1\\n\");\n}\n\n#[test]\npub fn as_thread_macro_test_1() {\n  assert_eq!(parse_compile_and_output(\"(as-> 1 v)\"),\n             \"return 1\\n\");\n}\n\n#[test]\npub fn as_thread_macro_test_2() {\n  assert_eq!(parse_compile_and_output(\"(as-> 1 v (foo1 v) (foo2 v 2) (foo2 2 v))\"),\n             r#\"var v = 1\nvar v_0 = foo1(v)\nvar v_1 = foo2(v_0, 2)\nreturn foo2(2, v_1)\n\"#);\n}\n\n#[test]\npub fn as_thread_macro_test_3() {\n  assert_eq!(parse_compile_and_output(\"(as-> 1 v (foo1 v) (foo2 v v) (foo2 2 v))\"),\n             r#\"var v = 1\nvar v_0 = foo1(v)\nvar v_1 = foo2(v_0, v_0)\nreturn foo2(2, v_1)\n\"#);\n}\n\n#[test]\npub fn list_is_not_shared_running_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((let* ((first-list (list 1 2 3 4))\n            (second-list (apply #'list first-list)))\n       (set first-list:car 0)\n       (print (list->array first-list))\n       (print (list->array second-list))))\"#),\n             \"\\n[0, 2, 3, 4]\\n[1, 2, 3, 4]\\n\");\n}\n\n#[test]\npub fn array_find_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((let ((arr [1 2 3 4 5 6]))\n       (print (array/find (lambda (x) (> x 3)) arr))\n       (print (array/find (lambda (x) (> x 10)) arr))))\"#),\n             \"\\n4\\nNull\\n\");\n}\n\n#[test]\npub fn list_find_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((let ((list '(1 2 3 4 5 6)))\n       (print (list/find (lambda (x) (> x 3)) list))\n       (print (list/find (lambda (x) (> x 10)) list))))\"#),\n             \"\\n4\\nNull\\n\");\n}\n\n#[test]\npub fn dict_find_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((let ((dict {1 2 3 4 5 6}))\n       (print (dict/find (lambda (k v) (> k 1)) dict))\n       (print (dict/find (lambda (k v) (> v 1)) dict))\n       (print (dict/find (lambda (k v) (> k 100)) dict))))\"#),\n             \"\\n3\\n1\\nNull\\n\");\n}\n\n#[test]\npub fn vector_map_test() {\n  assert_eq!(parse_and_run(\"((let ((a V{1 3})) (print (vector/map (lambda (x) (+ x 1)) a))))\"),\n             \"\\n(2, 4)\\n\");\n  assert_eq!(parse_and_run(\"((let ((a V{1 3 10})) (print (vector/map (lambda (x) (+ x 2)) a))))\"),\n             \"\\n(3, 5, 12)\\n\");\n}\n\n#[test]\npub fn vector_map_two_arguments_test() {\n  assert_eq!(parse_and_run(\"((let ((a V{1 3}) (b V{10 30})) (print (vector/map #'+ a b))))\"),\n             \"\\n(11, 33)\\n\");\n  assert_eq!(parse_and_run(\"((let ((a V{1 3 10}) (b V{9 8 7})) (print (vector/map #'+ a b))))\"),\n             \"\\n(10, 11, 17)\\n\");\n}\n\n#[test]\npub fn array_concat_running_test() {\n  assert_eq!(parse_and_run(\"((print (array/concat)) (print (array/concat [1 2] [3 4])))\"),\n             \"\\n[]\\n[1, 2, 3, 4]\\n\");\n}\n\n// TODO Test gensym at runtime once we can pretty-print symbols\n"
  },
  {
    "path": "tests/test/class_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse gdlisp::ir::identifier::ClassNamespace;\nuse gdlisp::ir::modifier::{ParseError as ModifierParseError, ParseErrorF as ModifierParseErrorF};\nuse gdlisp::compile::args::Expecting;\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse super::common::*;\n\n#[test]\npub fn empty_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node)))\"), r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\"#);\n}\n\n#[test]\npub fn simple_class_test_1() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x) (defn _init (y)) (defn foo () 2)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(y):\n        pass\n\n    var x\n\n    func foo():\n        return 2\n\"#);\n}\n\n#[test]\npub fn simple_class_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName () (defvar x) (defn _init (y)) (defn foo () 2)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Reference:\n\n    func _init(y):\n        pass\n\n    var x\n\n    func foo():\n        return 2\n\"#);\n}\n\n#[test]\npub fn parent_constructor_class_test_1() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x) (defn _init (y) (super y y)) (defn foo () 2)))\"), r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(y).(y, y):\n        pass\n\n    var x\n\n    func foo():\n        return 2\n\"#);\n}\n\n#[test]\npub fn parent_constructor_class_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x) (defn _init (y) (super (progn y))) (defn foo () 2)))\"), r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(y).(y):\n        pass\n\n    var x\n\n    func foo():\n        return 2\n\"#);\n}\n\n#[test]\npub fn parent_constructor_class_test_3() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x) (defn _init (y) (super (if y 1 2))) (defn foo () 2)))\"), r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var y\n\n    func _init(y):\n        self.y = y\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        var _cond = null\n        if y:\n            _cond = 1\n        else:\n            if true:\n                _cond = 2\n            else:\n                _cond = null\n        return _cond\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass ClassName extends Node:\n\n    func _init(y).(GDLisp.sys_DIV_funcall(_LambdaBlock.new(y), null)):\n        pass\n\n    var x\n\n    func foo():\n        return 2\n\"#);\n}\n\n#[test]\npub fn parent_constructor_class_test_4() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x) (defn _init (@x y) (super (if y 1 2))) (defn foo () 2)))\"), r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var y\n\n    func _init(y):\n        self.y = y\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        var _cond = null\n        if y:\n            _cond = 1\n        else:\n            if true:\n                _cond = 2\n            else:\n                _cond = null\n        return _cond\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass ClassName extends Node:\n\n    func _init(x_0, y).(GDLisp.sys_DIV_funcall(_LambdaBlock.new(y), null)):\n        self.x = x_0\n\n    var x\n\n    func foo():\n        return 2\n\"#);\n}\n\n#[test]\npub fn parent_constructor_class_test_5() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x-x) (defn _init (@x-x y) (super (if y 1 2))) (defn foo () 2)))\"), r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var y\n\n    func _init(y):\n        self.y = y\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        var _cond = null\n        if y:\n            _cond = 1\n        else:\n            if true:\n                _cond = 2\n            else:\n                _cond = null\n        return _cond\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass ClassName extends Node:\n\n    func _init(x_x_0, y).(GDLisp.sys_DIV_funcall(_LambdaBlock.new(y), null)):\n        self.x_x = x_x_0\n\n    var x_x\n\n    func foo():\n        return 2\n\"#);\n}\n\n#[test]\npub fn parent_constructor_class_running_test() {\n  assert_eq!(parse_and_run(\"((defclass A (Reference) (defvar x) (defn _init (x) (set self:x x))) (defclass B (A) (defn _init (y) (super (if y 1 2)))) (print (B:new #t):x))\"), \"\\n1\\n\");\n}\n\n#[test]\npub fn super_call_in_class_test_1() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () (defn foo () (super:foo))))\"#),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo():\n        return .foo()\n\"#);\n}\n\n#[test]\npub fn super_call_in_class_test_2() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () (defn foo () (super:bar)) (defn bar () (super:foo))))\"#),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo():\n        return .bar()\n\n    func bar():\n        return .foo()\n\"#);\n}\n\n#[test]\npub fn super_call_in_class_test_3() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () main (defn foo () (super:foo)) (defn bar () (super:foo))))\"#),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return .foo()\n\n\nfunc bar():\n    return .foo()\n\"#);\n}\n\n#[test]\npub fn super_call_in_class_test_4() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () (defn _init () (super:foo))))\"#),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        .foo()\n\"#);\n}\n\n#[test]\npub fn super_call_in_class_test_5() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () (defn foo-bar () (super:foo-bar))))\"#),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo_bar():\n        return .foo_bar()\n\"#);\n}\n\n#[test]\npub fn super_call_closed_in_class_test_1() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () (defn foo () (lambda () (super:foo)))))\"#),\n             r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var _self_0\n\n    func _init(_self_0):\n        self._self_0 = _self_0\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return _self_0.__gdlisp_super_1()\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo():\n        return _LambdaBlock.new(self)\n\n    func __gdlisp_super_1():\n        return .foo()\n\"#);\n}\n\n#[test]\npub fn super_call_closed_in_class_test_2() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () (defn foo () (lambda () (super:foo) (super:bar)))))\"#),\n             r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var _self_0\n\n    func _init(_self_0):\n        self._self_0 = _self_0\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        _self_0.__gdlisp_super_1()\n        return _self_0.__gdlisp_super_2()\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo():\n        return _LambdaBlock.new(self)\n\n    func __gdlisp_super_1():\n        return .foo()\n\n    func __gdlisp_super_2():\n        return .bar()\n\"#);\n}\n\n#[test]\npub fn super_call_closed_in_class_test_3() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo () (defn foo () (lambda () (super:foo-bar) (super:bar-bar)))))\"#),\n             r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var _self_0\n\n    func _init(_self_0):\n        self._self_0 = _self_0\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        _self_0.__gdlisp_super_1()\n        return _self_0.__gdlisp_super_2()\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo():\n        return _LambdaBlock.new(self)\n\n    func __gdlisp_super_1():\n        return .foo_bar()\n\n    func __gdlisp_super_2():\n        return .bar_bar()\n\"#);\n}\n\n#[test]\npub fn member_var_class_test_1() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x) (defn get-x () self:x)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    var x\n\n    func get_x():\n        return self.x\n\"#);\n}\n\n#[test]\npub fn member_var_class_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x) (defn _init (x) (set self:x x)) (defn get-x () self:x)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(x):\n        self.x = x\n\n    var x\n\n    func get_x():\n        return self.x\n\"#);\n}\n\n#[test]\npub fn member_var_class_test_3() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvar x 999) (defn _init (x) (set self:x x)) (defn get-x () self:x)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(x):\n        self.x = x\n\n    var x = 999\n\n    func get_x():\n        return self.x\n\"#);\n}\n\n#[test]\npub fn member_var_class_test_4() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) main (defvar x (export int 1 2)) (defn _init (x) (set self:x x)) (defn get-x () self:x)))\"),\n             r#\"extends Node\n\n\nfunc _init(x):\n    self.x = x\n\n\nexport(int, 1, 2) var x\n\n\nfunc get_x():\n    return self.x\n\"#);\n}\n\n#[test]\npub fn member_var_class_test_5() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main (defvar x \"foo\" (export String \"foo\" \"bar\")) (defn _init (x) (set self:x x)) (defn get-x () self:x)))\"#),\n             r#\"extends Node\n\n\nfunc _init(x):\n    self.x = x\n\n\nexport(String, \"foo\", \"bar\") var x = \"foo\"\n\n\nfunc get_x():\n    return self.x\n\"#);\n}\n\n#[test]\npub fn member_var_class_test_6() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvars x y z)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    var x\n    var y\n    var z\n\"#);\n}\n\n#[test]\npub fn member_var_class_test_7() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main (defvar x \"foo\" (export String \"foo\" \"bar\")) (defn _init (x) (set @x x)) (defn get-x () @x)))\"#),\n             r#\"extends Node\n\n\nfunc _init(x):\n    self.x = x\n\n\nexport(String, \"foo\", \"bar\") var x = \"foo\"\n\n\nfunc get_x():\n    return self.x\n\"#);\n}\n\n#[test]\npub fn init_member_var_class_test_1() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvars x y z) (defn _init (@x))))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(x_0):\n        self.x = x_0\n\n    var x\n    var y\n    var z\n\"#);\n}\n\n#[test]\npub fn init_member_var_class_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvars x y) (defvar z 10) (defn _init (@x @y))))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(x_0, y_1):\n        self.x = x_0\n        self.y = y_1\n\n    var x\n    var y\n    var z = 10\n\"#);\n}\n\n#[test]\npub fn init_member_var_class_test_3() {\n  // sys/split is just to force the variable initialization to go into\n  // the constructor rather than be inline on the 'var' line.\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defvars x y) (defvar z (sys/split 3)) (defn _init (@x @y))))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init(x_0, y_1):\n        var _split = 3\n        self.z = _split\n        self.x = x_0\n        self.y = y_1\n\n    var x\n    var y\n    var z\n\"#);\n}\n\n#[test]\npub fn ready_member_var_class_test_1() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main (defvar x \"foo\" onready)))\"#),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nonready var x = \"foo\"\n\"#);\n}\n\n#[test]\npub fn ready_member_var_class_test_2() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main (defvar x \"foo\" (export String) onready)))\"#),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nexport(String) onready var x = \"foo\"\n\"#);\n}\n\n#[test]\npub fn complicated_member_var_class_test() {\n  assert_eq!(\n    parse_compile_decl(\"((defclass ClassName (Node) main (defvar x (if 1 2 3)) (defn _init (x) (set self:x x)) (defn get-x () self:x)))\"),\n    r#\"extends Node\n\n\nfunc _init(x):\n    var _cond = null\n    if 1:\n        _cond = 2\n    else:\n        if true:\n            _cond = 3\n        else:\n            _cond = null\n    self.x = _cond\n    self.x = x\n\n\nvar x\n\n\nfunc get_x():\n    return self.x\n\"#);\n}\n\n#[test]\npub fn complicated_ready_member_var_class_test() {\n  assert_eq!(\n    parse_compile_decl(\"((defclass ClassName (Node) main (defvar x (if 1 2 3) onready) (defn _init (x) (set self:x x)) (defn get-x () self:x)))\"),\n    r#\"extends Node\n\n\nfunc _init(x):\n    self.x = x\n\n\nvar x\n\n\nfunc get_x():\n    return self.x\n\n\nfunc _ready():\n    var _cond = null\n    if 1:\n        _cond = 2\n    else:\n        if true:\n            _cond = 3\n        else:\n            _cond = null\n    self.x = _cond\n\"#);\n}\n\n#[test]\npub fn bad_member_var_class_test_1() {\n  // Can't have export on inner class\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass ClassName (Node) (defvar x (export int)) (defn _init (x) (set self:x x)) (defn get-x () self:x)))\"),\n    Err(PError::from(GDError::new(GDErrorF::ExportOnInnerClassVar(String::from(\"x\")), SourceOffset(28)))),\n  );\n}\n\n#[test]\npub fn bad_self_static_ref_class_test() {\n  // Can't reference self from static context\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass ClassName (Node) (defn get-self () static self)))\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"self\")), SourceOffset(53)))),\n  );\n}\n\n#[test]\npub fn bad_member_const_class_test() {\n  // Consts must be initialized\n  let result = parse_compile_decl_err(\"((defclass ClassName (Node) (defconst x)))\");\n  assert_eq!(\n    result,\n    Err(PError::GDError(GDError::new(GDErrorF::WrongNumberArgs(String::from(\"defconst\"), Expecting::exactly(2), 1), SourceOffset(28)))),\n  );\n}\n\n#[test]\npub fn bad_static_constructor_class_test() {\n  // Constructors cannot be static\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass ClassName (Node) (defn _init () static)))\"),\n    Err(PError::from(GDError::new(GDErrorF::StaticConstructor, SourceOffset(29)))),\n  );\n}\n\n#[test]\npub fn bad_nullargs_constructor_class_test() {\n  // Constructors cannot be sys/nullargs\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass ClassName (Node) (defn _init () sys/nullargs)))\"),\n    Err(PError::from(GDError::new(GDErrorF::NullargsConstructor, SourceOffset(29)))),\n  );\n}\n\n#[test]\npub fn bad_super_in_instance_function_test() {\n  // Can't have super in non-constructor method\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass ClassName (Node) (defn foo () (super 1))))\"),\n    Err(PError::from(GDError::new(GDErrorF::BadSuperCall(String::from(\"(init)\")), SourceOffset(42)))),\n  );\n}\n\n#[test]\npub fn signal_class_test_1() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defsignal my-signal)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    signal my_signal\n\"#);\n}\n\n#[test]\npub fn signal_class_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defsignal my-signal ())))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    signal my_signal\n\"#);\n}\n\n#[test]\npub fn signal_class_test_3() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defsignal my-signal (foo bar))))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    signal my_signal(foo, bar)\n\"#);\n}\n\n#[test]\npub fn signal_class_test_4() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defsignal my-signal (foo bar)) (defsignal my-other-signal)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    signal my_signal(foo, bar)\n    signal my_other_signal\n\"#);\n}\n\n#[test]\npub fn const_in_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defconst x 1)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    const x = 1\n\"#);\n}\n\n#[test]\npub fn const_in_main_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) main (defconst x 1)))\"),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nconst x = 1\n\"#);\n}\n\n#[test]\npub fn static_in_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defn foo () static 1)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    static func foo():\n        return 1\n\"#);\n}\n\n#[test]\npub fn nullargs_in_class_test_1() {\n  // No effect since there are no arguments.\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defn foo () sys/nullargs 1)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    func foo():\n        return 1\n\"#);\n}\n\n#[test]\npub fn nullargs_in_class_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defn foo (x y z) sys/nullargs 1)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    func foo(x = null, y = null, z = null):\n        return 1\n\"#);\n}\n\n#[test]\npub fn nullargs_in_class_test_3() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defn foo (x y z) sys/nullargs static 1)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    static func foo(x = null, y = null, z = null):\n        return 1\n\"#);\n}\n\n#[test]\npub fn nullargs_in_class_test_4() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) (defn foo (x y z) static sys/nullargs 1)))\"),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    static func foo(x = null, y = null, z = null):\n        return 1\n\"#);\n}\n\n#[test]\npub fn static_in_main_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass ClassName (Node) main (defn foo () static 1)))\"),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nstatic func foo():\n    return 1\n\"#);\n}\n\n#[test]\npub fn simple_self_closure_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Node) (defn test () (lambda () self))))\"),\n             r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var _self_0\n\n    func _init(_self_0):\n        self._self_0 = _self_0\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return _self_0\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass Foo extends Node:\n\n    func _init():\n        pass\n\n    func test():\n        return _LambdaBlock.new(self)\n\"#);\n}\n\n#[test]\npub fn labels_self_closure_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Node) (defn test () (labels ((foo (x) (foo self))) (foo 76)))))\"),\n             r#\"extends Reference\n\n\nclass _Labels extends Reference:\n\n    var _self_0\n\n    func _init(_self_0):\n        self._self_0 = _self_0\n\n    func _fn_foo_1(x):\n        return _fn_foo_1(_self_0)\n\n\nclass Foo extends Node:\n\n    func _init():\n        pass\n\n    func test():\n        var _locals = _Labels.new(self)\n        return _locals._fn_foo_1(76)\n\"#);\n}\n\n#[test]\npub fn labels_self_closure_class_with_contrived_const_test() {\n  assert_eq!(parse_compile_decl(\"((defconst _Labels 10) (defclass Foo (Node) (defn test () (labels ((foo (x) (foo self))) (foo 76)))))\"),\n             r#\"extends Reference\n\n\nconst _Labels = 10\n\n\nclass _Labels_0 extends Reference:\n\n    var _self_0\n\n    func _init(_self_0):\n        self._self_0 = _self_0\n\n    func _fn_foo_1(x):\n        return _fn_foo_1(_self_0)\n\n\nclass Foo extends Node:\n\n    func _init():\n        pass\n\n    func test():\n        var _locals = _Labels_0.new(self)\n        return _locals._fn_foo_1(76)\n\"#);\n}\n\n#[test]\npub fn simple_self_run_class_test_1() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defvar x)\n       (defn _init (x)\n         (set self:x x))\n       (defn double ()\n         (* self:x 2)))\n     (let ((foo (Foo:new 100)))\n       (print (foo:double))\n       (set foo:x 101)\n       (print (foo:double))))\n  \"#), \"\\n200\\n202\\n\");\n}\n\n#[test]\npub fn simple_self_run_class_test_2() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defvar x)\n       (defn _init (x)\n         (set @x x))\n       (defn double ()\n         (* @x 2)))\n     (let ((foo (Foo:new 100)))\n       (print (foo:double))\n       (set foo:x 101)\n       (print (foo:double))))\n  \"#), \"\\n200\\n202\\n\");\n}\n\n#[test]\npub fn simple_self_run_class_test_3() {\n  // Mixing self and @\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defvar x)\n       (defn _init (x)\n         (set @x x))\n       (defn double ()\n         (* self:x 2)))\n     (let ((foo (Foo:new 100)))\n       (print (foo:double))\n       (set foo:x 101)\n       (print (foo:double))))\n  \"#), \"\\n200\\n202\\n\");\n}\n\n#[test]\npub fn self_with_closure_run_class_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defvar x)\n       (defn _init ()\n         (set self:x 1))\n       (defn increment ()\n         (lambda ()\n           (set self:x (+ self:x 1)))))\n     (let ((fn (let ((tmp (Foo:new))) (tmp:increment))))\n       (print (funcall fn))\n       (print (funcall fn))\n       (print (funcall fn))))\n  \"#), \"\\n2\\n3\\n4\\n\");\n}\n\n#[test]\npub fn macro_in_class_test_1() {\n  assert_eq!(parse_compile_decl(r#\"\n    ((defmacro add-one (x)\n       (+ x 1))\n     (defn example (x) x)\n     (defclass Foo (Reference)\n       (defn _init ()\n         (example (add-one 2)))))\"#),\n             r#\"extends Reference\n\n\nstatic func add_one(x):\n    return x + 1\n\n\nstatic func example(x):\n    return x\n\n\nclass Foo extends Reference:\n\n    func _init():\n        __gdlisp_outer_class_0.example(3)\n\n    var __gdlisp_outer_class_0 = load(\"res://TEST.gd\")\n\"#);\n}\n\n#[test]\npub fn macro_in_class_test_2() {\n  assert_eq!(parse_and_run(r#\"\n    ((defmacro declare-function (name)\n       `(defn ,name () 99))\n     (defclass Foo (Reference)\n       (declare-function fn1)\n       (declare-function fn2))\n     (let ((foo (Foo:new)))\n       (print (foo:fn1))\n       (print (foo:fn2))))\"#),\n             \"\\n99\\n99\\n\");\n}\n\n#[test]\npub fn macro_in_class_test_3() {\n  assert_eq!(parse_and_run(r#\"\n    ((defmacro declare-functions ()\n       '(progn (defn a () 1) (defn b () 2)))\n     (defclass Foo (Reference)\n       (declare-functions))\n     (let ((foo (Foo:new)))\n       (print (foo:a))\n       (print (foo:b))))\"#),\n             \"\\n1\\n2\\n\");\n}\n\n#[test]\npub fn macro_uses_class_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defvar x))\n     (defmacro through-foo ()\n       (let ((foo (Foo:new)))\n         (set foo:x 5)\n         foo:x))\n     (print (through-foo)))\"#),\n             \"\\n5\\n\");\n}\n\n#[test]\npub fn reference_static_test_1() {\n  assert_eq!(parse_compile_decl(\"((defn foo ()) (defclass Foo (Node2D) (defn example () (foo))))\"),\n             r#\"extends Reference\n\n\nstatic func foo():\n    return null\n\n\nclass Foo extends Node2D:\n\n    func _init():\n        pass\n\n    func example():\n        return __gdlisp_outer_class_0.foo()\n\n    var __gdlisp_outer_class_0 = load(\"res://TEST.gd\")\n\"#);\n}\n\n#[test]\npub fn reference_static_test_2() {\n  assert_eq!(parse_compile_decl(\"((defn foo ()) (defclass Foo (Node2D) main (defn example () (foo))))\"),\n             r#\"extends Node2D\n\n\nstatic func foo():\n    return null\n\n\nfunc _init():\n    pass\n\n\nfunc example():\n    return foo()\n\"#);\n}\n\n#[test]\npub fn reference_static_test_3() {\n  assert_eq!(parse_compile_decl(\"((defn foo ()) (defclass Foo (Node2D) (defn example () static (foo))))\"),\n             r#\"extends Reference\n\n\nstatic func foo():\n    return null\n\n\nclass Foo extends Node2D:\n\n    func _init():\n        pass\n\n    static func example():\n        return load(\"res://TEST.gd\").foo()\n\"#);\n}\n\n#[test]\npub fn reference_static_test_4() {\n  assert_eq!(parse_compile_decl(\"((defn foo ()) (defclass Foo (Node2D) main (defn example () static (foo))))\"),\n             r#\"extends Node2D\n\n\nstatic func foo():\n    return null\n\n\nfunc _init():\n    pass\n\n\nstatic func example():\n    return foo()\n\"#);\n}\n\n#[test]\npub fn main_class_test_1() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Node2D) main))\"),\n             r#\"extends Node2D\n\n\nfunc _init():\n    pass\n\"#);\n}\n\n#[test]\npub fn main_class_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Node2D) main (defn foo () 1)))\"),\n             r#\"extends Node2D\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return 1\n\"#);\n}\n\n#[test]\npub fn main_class_test_3() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Node2D) main (defn foo () 1)) (defn run () Foo))\"),\n             r#\"extends Node2D\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return 1\n\n\nstatic func run():\n    return load(\"res://TEST.gd\")\n\"#);\n}\n\n#[test]\npub fn main_class_test_4() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo () main))\"),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\"#);\n}\n\n#[test]\npub fn macro_uses_main_class_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference) main\n       (defvar x))\n     (defmacro through-foo ()\n       (let ((foo (Foo:new)))\n         (set foo:x 5)\n         foo:x))\n     (print (through-foo)))\"#),\n             \"\\n5\\n\");\n}\n\n#[test]\npub fn reference_to_const_in_class_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defconst CONSTANT 100))\n     (print Foo:CONSTANT)\n     (print (Foo:new):CONSTANT))\"#),\n             \"\\n100\\n100\\n\");\n}\n\n#[test]\npub fn reference_to_static_in_class_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defn foo () static 98))\n     (print (Foo:foo))\n     (print ((Foo:new):foo)))\"#),\n             \"\\n98\\n98\\n\");\n}\n\n#[test]\npub fn reference_to_outer_in_class_test_1() {\n  let output = parse_and_run(r#\"\n    ((defn outer () 100)\n     (defclass Foo (Reference)\n       (defn foo () (outer)))\n     (print ((Foo:new):foo)))\"#);\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn reference_to_outer_in_class_test_2() {\n  let output = parse_and_run(r#\"\n    ((defn outer () 100)\n     (defclass Foo (Reference)\n       (defn foo () static (outer)))\n     (print ((Foo:new):foo))\n     (print (Foo:foo)))\"#);\n  assert_eq!(output, \"\\n100\\n100\\n\");\n}\n\n#[test]\npub fn reference_to_outer_in_class_test_3() {\n  let output = parse_and_run(r#\"\n    ((defn outer () 100)\n     (defclass Foo (Reference) main\n       (defn foo () (outer)))\n     (print ((Foo:new):foo)))\"#);\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn reference_to_outer_in_class_test_4() {\n  let output = parse_and_run(r#\"\n    ((defn outer () 100)\n     (defclass Foo (Reference) main\n       (defn foo () static (outer)))\n     (print ((Foo:new):foo))\n     (print (Foo:foo)))\"#);\n  assert_eq!(output, \"\\n100\\n100\\n\");\n}\n\n#[test]\npub fn constructor_with_parent_class_test_1() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference) (defn _init (x) (print x)))\n     (defclass Bar (Foo) (defn _init (x) (super (+ x 1))))\n     (Bar:new 10))\"#);\n  assert_eq!(output, \"\\n11\\n\");\n}\n\n#[test]\npub fn constructor_with_parent_class_test_2() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference) (defn _init (x) (print x)))\n     (defclass Bar (Foo) (defn _init (x) (super (if (> x 10) (+ x 1) (- x 1)))))\n     (Bar:new 20)\n     (Bar:new 3))\"#);\n  assert_eq!(output, \"\\n21\\n2\\n\");\n}\n\n#[test]\npub fn constructor_with_parent_class_test_3() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference) (defvar z) (defn _init (z) (set self:z z)))\n     (defclass Bar (Foo) (defn _init () (super self)))\n     (let ((bar (Bar:new)))\n       (print (= bar bar:z))\n       (set bar:z nil)))\"#);\n  assert_eq!(output, \"\\nTrue\\n\");\n}\n\n#[test]\npub fn constructor_with_parent_class_test_4() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference) (defvar z) (defn _init (z) (set self:z z)))\n     (defclass Bar (Foo) (defn _init (x) (super (lambda (y) (+ x y)))))\n     (let ((bar (Bar:new 64)))\n       (print (funcall bar:z 1))\n       (print (funcall bar:z -1))))\"#);\n  assert_eq!(output, \"\\n65\\n63\\n\");\n}\n\n#[test]\npub fn constructor_with_parent_class_test_5() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference) (defvar z) (defn _init (z) (set self:z z)))\n     (defclass Bar (Foo) (defn _init (x) (super (flet ((f (y) (+ x y))) #'f))))\n     (let ((bar (Bar:new 64)))\n       (print (funcall bar:z 1))\n       (print (funcall bar:z -1))))\"#);\n  assert_eq!(output, \"\\n65\\n63\\n\");\n}\n\n#[test]\npub fn get_node_on_self_class_test() {\n  assert_eq!(parse_compile_decl(r#\"\n    ((defclass Foo (Spatial) main\n       (defn test () $Target/Node)))\n    \"#), r#\"extends Spatial\n\n\nfunc _init():\n    pass\n\n\nfunc test():\n    return $Target/Node\n\"#);\n}\n\n#[test]\npub fn get_node_on_explicit_target_class_test() {\n  assert_eq!(parse_compile_decl(r#\"\n    ((defclass Foo (Spatial) main\n       (defn test (x) x:$Target/Node)))\n    \"#), r#\"extends Spatial\n\n\nfunc _init():\n    pass\n\n\nfunc test(x):\n    return x.get_node(\"Target/Node\")\n\"#);\n}\n\n#[test]\npub fn nonsense_modifier_class_test_1() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo (Node) main main))\"#),\n    Err(PError::from(ModifierParseError::new(ModifierParseErrorF::UniquenessError(String::from(\"main\")), SourceOffset(27)))),\n  );\n}\n\n#[test]\npub fn nonsense_modifier_class_test_2() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo (Node) public public))\"#),\n    Err(PError::from(ModifierParseError::new(ModifierParseErrorF::UniquenessError(String::from(\"visibility\")), SourceOffset(29)))),\n  );\n}\n\n#[test]\npub fn nonsense_modifier_class_test_3() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo (Node) public private))\"#),\n    Err(PError::from(ModifierParseError::new(ModifierParseErrorF::UniquenessError(String::from(\"visibility\")), SourceOffset(29)))),\n  );\n}\n\n#[test]\npub fn nonsense_modifier_class_test_4() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo (Node) public (defn example () static static)))\"#),\n    Err(PError::from(ModifierParseError::new(ModifierParseErrorF::UniquenessError(String::from(\"static\")), SourceOffset(53)))),\n  );\n}\n\n#[test]\npub fn duplicate_main_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass Foo (Node) main) (defclass Bar (Node) main))\"),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateMainClass, SourceOffset(29)))),\n  );\n}\n\n#[test]\npub fn no_self_in_scope_test() {\n  assert_eq!(\n    parse_compile_and_output_err(\"@test\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"self\")), SourceOffset(0)))),\n  );\n}\n\n#[test]\npub fn duplicate_constructor_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass Foo (Node) (defn _init ()) (defn _init ())))\"),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateConstructor, SourceOffset(39)))),\n  );\n}\n\n#[test]\npub fn class_setget_test_1() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node)\n                                      (defvar x)))\"#),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    var x\n\"#);\n}\n\n#[test]\npub fn class_setget_test_2() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node)\n                                      (defn (get x) () 10)))\"#),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    func __gdlisp_get_x():\n        return 10\n\n    func __gdlisp_set_x(_unused):\n        push_error(\"Cannot assign to nonexistent field \\'x\\'\")\n\n    var x setget __gdlisp_set_x, __gdlisp_get_x\n\"#);\n}\n\n#[test]\npub fn class_setget_test_3() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node)\n                                      (defn (set x) (a))))\"#),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    func __gdlisp_set_x(a):\n        pass\n\n    func __gdlisp_get_x():\n        push_error(\"Cannot access nonexistent field \\'x\\'\")\n\n    var x setget __gdlisp_set_x, __gdlisp_get_x\n\"#);\n}\n\n#[test]\npub fn class_setget_test_4() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node)\n                                      (defn (set x) (a))\n                                      (defn (get x) () 10)))\"#),\n             r#\"extends Reference\n\n\nclass ClassName extends Node:\n\n    func _init():\n        pass\n\n    func __gdlisp_set_x(a):\n        pass\n\n    func __gdlisp_get_x():\n        return 10\n\n    var x setget __gdlisp_set_x, __gdlisp_get_x\n\"#);\n}\n\n#[test]\npub fn class_setget_test_5() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main\n                                      (defn (set x) (a))\n                                      (defn (get x) () 10)))\"#),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nfunc __gdlisp_set_x(a):\n    pass\n\n\nfunc __gdlisp_get_x():\n    return 10\n\n\nvar x setget __gdlisp_set_x, __gdlisp_get_x\n\"#);\n}\n\n#[test]\npub fn class_setget_test_6() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main\n                                      (defn (set x-y) (a))\n                                      (defn (get x-y) () 10)))\"#),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nfunc __gdlisp_set_x_y(a):\n    pass\n\n\nfunc __gdlisp_get_x_y():\n    return 10\n\n\nvar x_y setget __gdlisp_set_x_y, __gdlisp_get_x_y\n\"#);\n}\n\n#[test]\npub fn class_setget_test_7() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main\n                                      (defn (set x-y) (a))))\"#),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nfunc __gdlisp_set_x_y(a):\n    pass\n\n\nfunc __gdlisp_get_x_y():\n    push_error(\"Cannot access nonexistent field \\'x_y\\'\")\n\n\nvar x_y setget __gdlisp_set_x_y, __gdlisp_get_x_y\n\"#);\n}\n\n#[test]\npub fn class_setget_test_8() {\n  assert_eq!(parse_compile_decl(r#\"((defclass ClassName (Node) main\n                                      (defn (get x-y) () 10)))\"#),\n             r#\"extends Node\n\n\nfunc _init():\n    pass\n\n\nfunc __gdlisp_get_x_y():\n    return 10\n\n\nfunc __gdlisp_set_x_y(_unused):\n    push_error(\"Cannot assign to nonexistent field \\'x_y\\'\")\n\n\nvar x_y setget __gdlisp_set_x_y, __gdlisp_get_x_y\n\"#);\n}\n\n#[test]\npub fn class_setget_conflict_test_1() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defn (set x) (a))\n                                         (defn (set x) (b))))\"#),\n             Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"__gdlisp_set_x\")), SourceOffset(130)))));\n}\n\n#[test]\npub fn class_setget_conflict_test_2() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defn (get x) ())\n                                         (defn (get x) ())))\"#),\n             Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"__gdlisp_get_x\")), SourceOffset(129)))));\n}\n\n#[test]\npub fn class_setget_conflict_test_3() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defvar x)\n                                         (defn (get x) ())))\"#),\n             Err(PError::from(GDError::new(GDErrorF::FieldAccessorConflict(String::from(\"x\")), SourceOffset(70)))));\n}\n\n#[test]\npub fn class_setget_conflict_test_4() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defvar x)\n                                         (defn (set x) (a))))\"#),\n             Err(PError::from(GDError::new(GDErrorF::FieldAccessorConflict(String::from(\"x\")), SourceOffset(70)))));\n}\n\n#[test]\npub fn class_setget_bad_signature_1() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defn (get x) (a))))\"#),\n             Err(PError::from(GDError::new(GDErrorF::BadGetterArguments(String::from(\"x\")), SourceOffset(70)))));\n}\n\n#[test]\npub fn class_setget_bad_signature_2() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defn (get x) () static)))\"#),\n             Err(PError::from(GDError::new(GDErrorF::BadGetterArguments(String::from(\"x\")), SourceOffset(70)))));\n}\n\n#[test]\npub fn class_setget_bad_signature_3() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defn (set x) (a) static)))\"#),\n             Err(PError::from(GDError::new(GDErrorF::BadSetterArguments(String::from(\"x\")), SourceOffset(70)))));\n}\n\n#[test]\npub fn class_setget_bad_signature_4() {\n  assert_eq!(parse_compile_decl_err(r#\"((defclass ClassName (Node)\n                                         (defn (set x) ())))\"#),\n             Err(PError::from(GDError::new(GDErrorF::BadSetterArguments(String::from(\"x\")), SourceOffset(70)))));\n}\n\n#[test]\npub fn class_setget_runner_test_1() {\n  assert_eq!(parse_and_run(r#\"((defclass ClassName (Reference)\n                                 (defvar private-field)\n                                 (defn _init ()\n                                   (set @private-field 1))\n                                 (defn (set x) (a) (set @private-field a))\n                                 (defn (get x) () @private-field))\n                               (let ((foo (ClassName:new)))\n                                 (set foo:x 92)\n                                 (print foo:x)))\"#),\n             \"\\n92\\n\");\n}\n\n#[test]\npub fn class_setget_runner_test_2() {\n  assert_eq!(parse_and_run(r#\"((defclass ClassName (Reference) main\n                                 (defvar private-field)\n                                 (defn _init ()\n                                   (set @private-field 1))\n                                 (defn (set x) (a) (set @private-field a))\n                                 (defn (get x) () @private-field))\n                               (let ((foo (ClassName:new)))\n                                 (set foo:x 92)\n                                 (print foo:x)))\"#),\n             \"\\n92\\n\");\n}\n\n#[test]\npub fn super_call_runner_test_1() {\n  assert_eq!(parse_and_run(r#\"((defclass Foo (Reference)\n                                 (defn foo ()\n                                   99))\n                               (defclass Bar (Foo)\n                                 (defn foo ()\n                                   101)\n                                 (defn call-foo ()\n                                   (super:foo)))\n                               (let ((bar (Bar:new)))\n                                 (print (bar:foo))\n                                 (print (bar:call-foo))))\"#),\n             \"\\n101\\n99\\n\");\n}\n\n#[test]\npub fn super_call_runner_test_2() {\n  assert_eq!(parse_and_run(r#\"((defclass Foo (Reference)\n                                 (defn foo ()\n                                   99))\n                               (defclass Bar (Foo)\n                                 (defn foo ()\n                                   101)\n                                 (defn call-foo ()\n                                   (lambda () (super:foo))))\n                               (let* ((bar (Bar:new))\n                                      (delegator (bar:call-foo)))\n                                 (print (bar:foo))\n                                 (print (funcall delegator))))\"#),\n             \"\\n101\\n99\\n\");\n}\n\n#[test]\npub fn super_call_runner_test_3() {\n  assert_eq!(parse_and_run(r#\"((defclass Foo (Reference)\n                                 (defvar x)\n                                 (defn _init (x)\n                                   (set @x x))\n                                 (defn foo ()\n                                   99))\n                               (defclass Bar (Foo)\n                                 (defn _init ()\n                                   (super (lambda () (super:foo))))\n                                 (defn foo ()\n                                   101))\n                               (let* ((bar (Bar:new)))\n                                 (print (bar:foo))\n                                 (print (funcall bar:x))))\"#),\n             \"\\n101\\n99\\n\");\n}\n\n#[test]\npub fn bad_super_call_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defn foo () (super:foo)))\"),\n    Err(PError::from(GDError::new(GDErrorF::BadSuperCall(String::from(\"foo\")), SourceOffset(14)))),\n  );\n}\n\n#[test]\npub fn builtin_patched_class_test() {\n  // Make sure the patched classes (`PATCHED_CLASS_NAMES` in\n  // `class_loader.rs`) are being loaded correctly.\n  assert_eq!(parse_and_run(\"((print (instance? (File:new) File))\n                             (print (= (typeof (File:new)) File)))\"),\n             \"\\nTrue\\nTrue\\n\");\n}\n\n#[test]\npub fn builtin_singleton_class_test() {\n  assert_eq!(parse_and_run(\"((print (typeof Engine):name) (print _Engine:name) (print (Engine:get_class)))\"),\n             \"\\n_Engine\\n_Engine\\n_Engine\\n\");\n}\n\n#[test]\npub fn lambdas_inside_class_and_out_test() {\n  // This is a regression test for issue #139.\n  let result = parse_compile_decl(r#\"\n   ((defclass Foo () (defn foo () (lambda () 1)))\n     (defn bar () (lambda () 2)))\n  \"#);\n  assert_eq!(result, r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return 1\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo():\n        return _LambdaBlock.new()\n\n\nclass _LambdaBlock_0 extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return 2\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nstatic func bar():\n    return _LambdaBlock_0.new()\n\"#);\n}\n\n#[test]\npub fn lambdas_inside_class_static_and_out_test() {\n  // This is a regression test for issue #139.\n  let result = parse_compile_decl(r#\"\n   ((defclass Foo () (defn foo () (lambda () 1)) (defn bar () static (lambda () 2)))\n     (defn baz () (lambda () 3)))\n  \"#);\n  assert_eq!(result, r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return 1\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass _LambdaBlock_0 extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return 2\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func foo():\n        return _LambdaBlock.new()\n\n    static func bar():\n        return _LambdaBlock_0.new()\n\n\nclass _LambdaBlock_1 extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return 3\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nstatic func baz():\n    return _LambdaBlock_1.new()\n\"#);\n}\n"
  },
  {
    "path": "tests/test/collection_conversion_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::parse_and_run;\n\n#[test]\nfn array_roundtrip_test() {\n  assert_eq!(parse_and_run(\"\n  ((let ((arr [10 20 30 40]))\n     (let ((arr1 (list->array (array->list arr))))\n       (print (elt arr1 0))\n       (print (elt arr1 1))\n       (print (elt arr1 2))\n       (print (elt arr1 3)))))\n   \"), \"\\n10\\n20\\n30\\n40\\n\");\n}\n\n#[test]\nfn array_list_length_test() {\n  assert_eq!(parse_and_run(\"((print (len (array->list [9 10 11]))))\"), \"\\n3\\n\");\n}\n\n#[test]\nfn list_array_length_test() {\n  assert_eq!(parse_and_run(\"((print (len (list->array '(9 10 11)))))\"), \"\\n3\\n\");\n}\n\n#[test]\nfn array_varargs_test_1() {\n  assert_eq!(parse_and_run(\"((defn foo (&arr arr) (elt arr 0)) (print (foo 10 20 30)))\"),\n             \"\\n10\\n\");\n}\n\n#[test]\nfn array_varargs_test_2() {\n  assert_eq!(parse_and_run(\"((let ((foo (lambda (&arr arr) (elt arr 0)))) (print (funcall foo 10 20 30))))\"),\n             \"\\n10\\n\");\n}\n"
  },
  {
    "path": "tests/test/common/import.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n// Some infrastructure for spoofing files to test import syntax.\n\nuse gdlisp::pipeline::resolver::NameResolver;\n\nuse std::collections::HashMap;\nuse std::path::Path;\nuse std::io::{self, Read, Write};\nuse std::cmp::min;\n\n// Behaves like the read instance on &[u8] but takes ownership of its\n// string.\n#[derive(Clone)]\nstruct StringReader {\n  string: String,\n  position: usize\n}\n\n#[derive(Default)]\npub struct MockFileLoader {\n  files: HashMap<String, String>,\n}\n\nimpl MockFileLoader {\n\n  pub fn new() -> MockFileLoader {\n    MockFileLoader::default()\n  }\n\n  pub fn add_file(&mut self, name: &str, contents: &str) {\n    self.files.insert(name.to_owned(), contents.to_owned());\n  }\n\n}\n\nimpl StringReader {\n\n  pub fn new(s: String) -> StringReader {\n    StringReader { string: s, position: 0 }\n  }\n\n}\n\nimpl Read for StringReader {\n  fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {\n    let bytes = self.string.as_bytes();\n    let to_copy = min(buf.len(), bytes.len() - self.position);\n    buf[0..to_copy].copy_from_slice(&bytes[self.position..self.position+to_copy]);\n    self.position += to_copy;\n    Ok(to_copy)\n  }\n}\n\nimpl NameResolver for MockFileLoader {\n\n  fn resolve_input_path(&self, filename: &Path) -> io::Result<Box<dyn Read>> {\n    let filename = filename.file_name().unwrap().to_string_lossy().to_owned();\n    let file_contents = StringReader::new(self.files.get(&*filename).unwrap().to_owned());\n    Ok(Box::new(file_contents))\n  }\n\n  fn resolve_output_path(&self, _filename: &Path) -> io::Result<Box<dyn Write>> {\n    Ok(Box::new(io::sink()))\n  }\n\n}\n"
  },
  {
    "path": "tests/test/common/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\n#![allow(dead_code)]\n\nextern crate gdlisp;\n\npub mod import;\n\nuse gdlisp::compile::Compiler;\nuse gdlisp::compile::stmt_wrapper;\nuse gdlisp::compile::names::fresh::FreshNameGenerator;\nuse gdlisp::compile::body::builder::{CodeBuilder, StmtBuilder};\nuse gdlisp::compile::body::class_scope::OutsideOfClass;\nuse gdlisp::compile::symbol_table::SymbolTable;\nuse gdlisp::compile::symbol_table::local_var::LocalVar;\nuse gdlisp::compile::symbol_table::function_call::{FnCall, FnScope, FnSpecs};\nuse gdlisp::compile::symbol_table::call_magic::CallMagic;\nuse gdlisp::compile::preload_resolver::DefaultPreloadResolver;\nuse gdlisp::runner::godot::GodotCommand;\nuse gdlisp::runner::into_gd_file::IntoGDFile;\nuse gdlisp::runner::path::{RPathBuf, PathSrc};\nuse gdlisp::runner::version::VersionInfo;\nuse gdlisp::AST_PARSER;\nuse gdlisp::ir;\nuse gdlisp::ir::incremental::IncCompiler;\nuse gdlisp::ir::main_function::{StaticMainFunctionHandler, DisallowMainFunctionHandler};\nuse gdlisp::gdscript::library;\nuse gdlisp::gdscript::decl;\nuse gdlisp::gdscript::class_extends::ClassExtends;\nuse gdlisp::gdscript::spacing::SpacedDeclPrinter;\nuse gdlisp::pipeline::Pipeline;\nuse gdlisp::pipeline::error::{PError, IOError};\nuse gdlisp::pipeline::config::ProjectConfig;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse tempfile::{Builder, TempDir};\n\nuse std::process::{Output, Stdio};\nuse std::path::PathBuf;\nuse std::fs::{File, copy};\nuse std::io::{self, Write};\nuse std::str::FromStr;\n\npub const TEST_FUNCTION_NAME: &'static str = \"run_test\";\n\npub const BEGIN_GDLISP_TESTS: &'static str = \"__BEGIN_GDLISP_TESTS__\";\n\n/// A type containing string output from both `stdout` and `stderr`.\n#[derive(Debug, Clone, PartialEq, Eq)]\npub struct StringOutput {\n  pub stdout: String,\n  pub stderr: String,\n}\n\n/*\nfn template_contents<P : AsRef<Path>>(filename: P) -> String {\n  format!(r#\"\nextends SceneTree\n\nfunc _init():\n    var file = load(\"{}\")\n    print(\"{}\")\n    file.run()\n\"#, filename.as_ref().display(), BEGIN_GDLISP_TESTS)\n}\n\npub fn write_to_file<T>(data: &T) -> io::Result<NamedTempFile>\nwhere T : IntoGDFile + ?Sized {\n  let mut tmp = Builder::new()\n    .prefix(\"__gdlisp_test\")\n    .suffix(\".gd\")\n    .rand_bytes(5)\n    .tempfile()?;\n  data.write_to_gd(&mut tmp)?;\n  tmp.flush()?;\n  Ok(tmp)\n}\n\npub fn run_temporary<T>(data: &T) -> io::Result<String>\nwhere T : IntoGDFile + ?Sized {\n  let temp_file = write_to_file(data)?;\n  let runner_text = template_contents(temp_file.path());\n  runner::run_with_temporary(&runner_text)\n}\n*/\n\nfn main_function_handler() -> StaticMainFunctionHandler {\n  StaticMainFunctionHandler::new(TEST_FUNCTION_NAME.to_owned())\n}\n\npub fn dummy_config() -> ProjectConfig {\n  ProjectConfig {\n    root_directory: PathBuf::from_str(\".\").unwrap(), // Infallible\n    optimizations: false,\n    godot_version: VersionInfo::default(),\n  }\n}\n\npub fn dummy_pipeline() -> Pipeline {\n  let mut pipeline = Pipeline::new(dummy_config());\n  let path = RPathBuf::new(PathSrc::Res, PathBuf::from(\"TEST.lisp\")).unwrap();\n  pipeline.set_currently_loading_file(path);\n  pipeline\n}\n\nfn bind_helper_symbols(_table: &mut SymbolTable) {\n  // Does nothing right now. May remove this later.\n}\n\npub fn dump_files<T>(dir: &mut TempDir, data: &T) -> io::Result<()>\nwhere T : IntoGDFile + ?Sized {\n\n  // The target file itself\n  let mut target_file = File::create(dir.path().join(\"TEST.gd\"))?;\n  data.write_to_gd(&mut target_file)?;\n\n  // The runner shim\n  let mut temp_file = File::create(dir.path().join(\"main.tscn\"))?;\n  write!(temp_file, r#\"\n[gd_scene load_steps=2 format=2]\n\n[ext_resource path=\"res://main.gd\" type=\"Script\" id=1]\n\n[node name=\"main\" type=\"Node\"]\nscript = ExtResource( 1 )\n\"#)?;\n  let mut temp_scr_file = File::create(dir.path().join(\"main.gd\"))?;\n  write!(temp_scr_file, r#\"\nextends Node\n\nfunc _ready():\n    var file = load(\"res://TEST.gd\")\n    print(\"{}\")\n    file.run_test()\n    get_tree().quit()\n\n\"#, BEGIN_GDLISP_TESTS)?;\n\n  // The GDLisp.gd file\n  copy(\"GDLisp.gd\", dir.path().join(\"GDLisp.gd\"))?;\n\n  // Project file\n  let mut project_file = File::create(dir.path().join(\"project.godot\"))?;\n  write!(project_file, r#\"\nconfig_version=4\n\n[application]\n\nrun/main_scene=\"res://main.tscn\"\n\n[autoload]\n\nGDLisp=\"*res://GDLisp.gd\"\n\n\"#)?;\n\n  Ok(())\n\n}\n\nfn parse_and_run_err_impl(input: &str, runner: &mut GodotCommand) -> Result<Output, PError> {\n  let value = AST_PARSER.parse(input)?;\n  let used_names = value.all_symbols();\n  let mut table = SymbolTable::new();\n  bind_helper_symbols(&mut table);\n  library::bind_builtins(&mut table, false);\n\n  let mut pipeline = dummy_pipeline();\n\n  let (decls, _macros) = ir::compile_and_check(&mut pipeline, &value, &main_function_handler())?;\n  let mut compiler = Compiler::new(FreshNameGenerator::new(used_names), Box::new(DefaultPreloadResolver), decls.minimalist_flag);\n  let mut builder = CodeBuilder::new(ClassExtends::SimpleIdentifier(String::from(\"Reference\")));\n  compiler.frame(&mut pipeline, &mut builder, &mut table, &mut OutsideOfClass).compile_toplevel(&decls)?;\n\n  let mut temp_dir = Builder::new().prefix(\"__gdlisp_test\").rand_bytes(5).tempdir().map_err(|err| IOError::new(err, SourceOffset(0)))?;\n  let code_output = builder.build();\n  // println!(\"{}\", code_output.to_gd());\n  dump_files(&mut temp_dir, &code_output).map_err(|err| IOError::new(err, SourceOffset(0)))?;\n\n  runner.project_dir(temp_dir.path()).output()\n    .map_err(|err| PError::from(IOError::new(err, SourceOffset(0))))\n}\n\nfn strip_cr(input: &str) -> String {\n  // Windows outputs \\r\\n and Linux outputs \\n. We strip \\r so our\n  // tests can look for \\n in every case.\n  input.replace(\"\\r\", \"\")\n}\n\npub fn parse_and_run_err(input: &str) -> Result<String, PError> {\n  let mut runner = GodotCommand::base();\n  let Output { stdout, .. } = parse_and_run_err_impl(input, &mut runner)?;\n  let result = String::from_utf8_lossy(&stdout);\n  match result.find(BEGIN_GDLISP_TESTS) {\n    None => Ok(strip_cr(&result)),\n    Some(idx) => Ok(strip_cr(&result[idx + BEGIN_GDLISP_TESTS.bytes().count()..])),\n  }\n}\n\npub fn parse_and_run(input: &str) -> String {\n  parse_and_run_err(input).unwrap()\n}\n\npub fn parse_and_run_with_stderr_err(input: &str) -> Result<StringOutput, PError> {\n  let mut runner = GodotCommand::base();\n  runner.stderr(Stdio::piped());\n  let Output { stdout, stderr, .. } = parse_and_run_err_impl(input, &mut runner)?;\n\n  let stdout = String::from_utf8_lossy(&stdout);\n  let stderr = String::from_utf8_lossy(&stderr);\n\n  let stdout = match stdout.find(BEGIN_GDLISP_TESTS) {\n    None => strip_cr(&stdout),\n    Some(idx) => strip_cr(&stdout[idx + BEGIN_GDLISP_TESTS.bytes().count()..]),\n  };\n  let stderr = strip_cr(&stderr);\n\n  Ok(StringOutput { stdout, stderr })\n}\n\npub fn parse_and_run_with_stderr(input: &str) -> StringOutput {\n  parse_and_run_with_stderr_err(input).unwrap()\n}\n\nfn bind_helper_symbols_comp(table: &mut SymbolTable) {\n  // Binds a few helper names to the symbol table for the sake of\n  // debugging.\n  table.set_fn(String::from(\"foo\"), FnCall::file_constant(FnSpecs::new(0, 0, None), FnScope::Global, String::from(\"foo\")), CallMagic::DefaultCall);\n  table.set_fn(String::from(\"foo1\"), FnCall::file_constant(FnSpecs::new(1, 0, None), FnScope::Global, String::from(\"foo1\")), CallMagic::DefaultCall);\n  table.set_fn(String::from(\"foo2\"), FnCall::file_constant(FnSpecs::new(2, 0, None), FnScope::Global, String::from(\"foo2\")), CallMagic::DefaultCall);\n  table.set_fn(String::from(\"bar\"), FnCall::file_constant(FnSpecs::new(0, 0, None), FnScope::Global, String::from(\"bar\")), CallMagic::DefaultCall);\n  table.set_var(String::from(\"foobar\"), LocalVar::read(String::from(\"foobar\")));\n  table.set_var(String::from(\"glob\"), LocalVar::file_constant(String::from(\"glob\")));\n}\n\npub fn parse_compile_and_output_err(input: &str) -> Result<String, PError> {\n  parse_compile_and_output_err_h(input).map(|x| x.0)\n}\n\npub fn parse_compile_and_output_err_h(input: &str) -> Result<(String, String), PError> {\n  let value = AST_PARSER.parse(input)?;\n  let used_names = value.all_symbols();\n  let mut compiler = Compiler::new(FreshNameGenerator::new(used_names), Box::new(DefaultPreloadResolver), false);\n  let mut table = SymbolTable::new();\n  bind_helper_symbols_comp(&mut table);\n  library::bind_builtins(&mut table, false);\n\n  let mut pipeline = dummy_pipeline();\n\n  let mut builder = StmtBuilder::new();\n  let value = {\n    let mut icompiler = IncCompiler::new(value.all_symbols());\n    icompiler.bind_builtin_macros(&mut pipeline);\n    let expr = icompiler.compile_expr(&mut pipeline, &value)?;\n    ir::loops::check_expr(&expr)?;\n    expr\n  };\n  {\n    let mut class_scope = OutsideOfClass;\n    let mut frame = compiler.frame(&mut pipeline, &mut builder, &mut table, &mut class_scope);\n    let () = frame.compile_stmt(&mut stmt_wrapper::Return, &value)?;\n  }\n  let (stmts, helpers) = builder.build();\n  let a = stmts.into_iter().map(|stmt| stmt.to_gd(0)).collect::<String>();\n  let b = output_decls(helpers);\n  Ok((a, b))\n}\n\nfn output_decls(decls: Vec<decl::Decl>) -> String {\n  let printer = SpacedDeclPrinter::new();\n  let mut string = String::new();\n  printer.write_gd(&mut string, decls.iter()).unwrap();\n  string\n}\n\npub fn parse_compile_and_output(input: &str) -> String {\n  parse_compile_and_output_err(input).unwrap()\n}\n\npub fn parse_compile_and_output_h(input: &str) -> (String, String) {\n  parse_compile_and_output_err_h(input).unwrap()\n}\n\npub fn parse_compile_decl_err(input: &str) -> Result<String, PError> {\n  let value = AST_PARSER.parse(input)?;\n  let used_names = value.all_symbols();\n  let mut table = SymbolTable::new();\n  library::bind_builtins(&mut table, false);\n\n  let mut pipeline = dummy_pipeline();\n\n  let mut builder = CodeBuilder::new(ClassExtends::SimpleIdentifier(\"Reference\".to_owned()));\n  let (decls, _macros) = ir::compile_and_check(&mut pipeline, &value, &DisallowMainFunctionHandler)?;\n  let mut compiler = Compiler::new(FreshNameGenerator::new(used_names), Box::new(DefaultPreloadResolver), decls.minimalist_flag);\n  compiler.frame(&mut pipeline, &mut builder, &mut table, &mut OutsideOfClass).compile_toplevel(&decls)?;\n  let class = builder.build();\n\n  Ok(class.to_gd())\n\n}\n\npub fn parse_compile_decl(input: &str) -> String {\n  parse_compile_decl_err(input).unwrap()\n}\n"
  },
  {
    "path": "tests/test/concurrency_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\nuse std::thread;\n\n// Mostly failed attempts at reproducing #44, but the test is still\n// technically good, so I'm leaving it.\n#[test]\nfn concurrent_runs_test() {\n\n  fn thread_fn(_n: u32) {\n    assert_eq!(parse_compile_and_output(\"(if 1 2 3)\"),\n               \"var _cond = null\\nif 1:\\n    _cond = 2\\nelse:\\n    if true:\\n        _cond = 3\\n    else:\\n        _cond = null\\nreturn _cond\\n\");\n    assert_eq!(parse_and_run(\"((let ((r (Reference:new))) (print (= (typeof r) Reference))))\"),\n               \"\\nTrue\\n\");\n  }\n\n  let runs = 4;\n  let join_handles: Vec<_> = (0..runs).map(|n| thread::spawn(move || thread_fn(n))).collect();\n\n  // Let them run concurrently\n  thread::yield_now();\n\n  // Now join the handles\n  for handle in join_handles {\n    handle.join().unwrap();\n  }\n\n}\n"
  },
  {
    "path": "tests/test/cond_if_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::{parse_compile_and_output, parse_and_run};\n\n#[test]\npub fn if_tests_expr() {\n  assert_eq!(parse_compile_and_output(\"(if 1 2 3)\"), \"var _cond = null\\nif 1:\\n    _cond = 2\\nelse:\\n    if true:\\n        _cond = 3\\n    else:\\n        _cond = null\\nreturn _cond\\n\");\n  assert_eq!(parse_compile_and_output(\"(if 1 2)\"), \"var _cond = null\\nif 1:\\n    _cond = 2\\nelse:\\n    if true:\\n        _cond = null\\n    else:\\n        _cond = null\\nreturn _cond\\n\");\n  assert_eq!(parse_compile_and_output(\"(if 1 2 ())\"), \"var _cond = null\\nif 1:\\n    _cond = 2\\nelse:\\n    if true:\\n        _cond = null\\n    else:\\n        _cond = null\\nreturn _cond\\n\");\n  assert_eq!(parse_compile_and_output(\"(if 1 (foo) (bar))\"), \"var _cond = null\\nif 1:\\n    _cond = foo()\\nelse:\\n    if true:\\n        _cond = bar()\\n    else:\\n        _cond = null\\nreturn _cond\\n\");\n}\n\n#[test]\npub fn if_tests_stmt() {\n  assert_eq!(parse_compile_and_output(\"(progn (if 1 2 3) 1)\"), \"if 1:\\n    pass\\nelse:\\n    if true:\\n        pass\\n    else:\\n        pass\\nreturn 1\\n\");\n  assert_eq!(parse_compile_and_output(\"(progn (if 1 (foo) (bar)) 1)\"), \"if 1:\\n    foo()\\nelse:\\n    if true:\\n        bar()\\n    else:\\n        pass\\nreturn 1\\n\");\n}\n\n#[test]\npub fn cond_tests_expr() {\n  assert_eq!(parse_compile_and_output(\"(cond ((bar) (foo)) (foobar (bar)))\"), \"var _cond = null\\nif bar():\\n    _cond = foo()\\nelse:\\n    if foobar:\\n        _cond = bar()\\n    else:\\n        _cond = null\\nreturn _cond\\n\");\n}\n\n#[test]\npub fn cond_tests_stmt() {\n  assert_eq!(parse_compile_and_output(\"(progn (cond ((bar) (foo)) (foobar (bar))) 1)\"), \"if bar():\\n    foo()\\nelse:\\n    if foobar:\\n        bar()\\n    else:\\n        pass\\nreturn 1\\n\");\n}\n\n#[test]\npub fn cond_tests_abbr_expr() {\n  assert_eq!(parse_compile_and_output(\"(cond ((foo) (foo)) ((bar)))\"), \"var _cond = null\\nif foo():\\n    _cond = foo()\\nelse:\\n    var _cond_0 = bar()\\n    if _cond_0:\\n        _cond = _cond_0\\n    else:\\n        _cond = null\\nreturn _cond\\n\");\n}\n\n#[test]\npub fn cond_tests_abbr_stmt() {\n  assert_eq!(parse_compile_and_output(\"(progn (cond ((foo) (foo)) ((bar))) 1)\"), \"if foo():\\n    foo()\\nelse:\\n    var _cond = bar()\\n    if _cond:\\n        pass\\n    else:\\n        pass\\nreturn 1\\n\");\n}\n\n#[test]\npub fn or_test() {\n  let result0 = parse_compile_and_output(\"(or 1 2 3)\");\n  assert_eq!(result0, r#\"var _cond = null\nvar _cond_1 = 1\nif _cond_1:\n    _cond = _cond_1\nelse:\n    var _cond_0 = 2\n    if _cond_0:\n        _cond = _cond_0\n    else:\n        if true:\n            _cond = 3\n        else:\n            _cond = null\nreturn _cond\n\"#);\n}\n\n#[test]\npub fn and_test() {\n  let result0 = parse_compile_and_output(\"(and 1 2 3)\");\n  assert_eq!(result0, r#\"var _cond = null\nif !1:\n    _cond = false\nelse:\n    if !2:\n        _cond = false\n    else:\n        if true:\n            _cond = 3\n        else:\n            _cond = null\nreturn _cond\n\"#);\n}\n\n#[test]\npub fn when_test() {\n  let result0 = parse_compile_and_output(\"(when 1 (foo) (bar))\");\n  assert_eq!(result0, r#\"var _cond = null\nif 1:\n    foo()\n    _cond = bar()\nelse:\n    _cond = null\nreturn _cond\n\"#);\n}\n\n#[test]\npub fn unless_test() {\n  let result0 = parse_compile_and_output(\"(unless 1 (foo) (bar))\");\n  assert_eq!(result0, r#\"var _cond = null\nif 1:\n    _cond = null\nelse:\n    if true:\n        foo()\n        _cond = bar()\n    else:\n        _cond = null\nreturn _cond\n\"#);\n}\n\n#[test]\nfn if_test_1() {\n  let output = parse_and_run(r#\"\n  ((let ((x 1))\n    (print (if x 10 20))))\n  \"#);\n  assert_eq!(output, \"\\n10\\n\");\n}\n\n#[test]\nfn if_test_2() {\n  let output = parse_and_run(r#\"\n  ((let ((x 0))\n    (print (if x 10 20))))\n  \"#);\n  assert_eq!(output, \"\\n20\\n\");\n}\n"
  },
  {
    "path": "tests/test/const_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse super::common::*;\n\n#[test]\npub fn const_test() {\n  assert_eq!(parse_compile_decl(\"((defconst A 10))\"), r#\"extends Reference\n\n\nconst A = 10\n\"#);\n  assert_eq!(parse_compile_decl(\"((defconst A \\\"foo\\\"))\"), r#\"extends Reference\n\n\nconst A = \"foo\"\n\"#);\n}\n\n#[test]\npub fn const_test_nonconst() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defconst B (list->array 1)))\"),\n    Err(PError::from(GDError::new(GDErrorF::NotConstantEnough(String::from(\"B\")), SourceOffset(13)))),\n  );\n}\n\n#[test]\npub fn const_test_nonconst_in_class() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass Foo (Reference) (defconst B (list->array 1))))\"),\n    Err(PError::from(GDError::new(GDErrorF::NotConstantEnough(String::from(\"B\")), SourceOffset(39)))),\n  );\n}\n\n#[test]\npub fn const_test_nonconst_in_enum() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defenum Foo (A (list->array 1))))\"),\n    Err(PError::from(GDError::new(GDErrorF::NotConstantEnough(String::from(\"Foo\")), SourceOffset(17)))),\n  );\n}\n\n#[test]\npub fn const_test_run() {\n  let output = parse_and_run(r#\"\n  ((defconst A 100) (print A))\n  \"#);\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn builtin_const_test() {\n  // I don't care what this outputs; I just want to know that Godot\n  // recognizes all of the constants I'm compiling these to.\n  parse_and_run(r#\"([\n    ;; GDScript primitive constants\n    PI INF\n    ;; GDScript builtin names\n    BUTTON_LEFT CORNER_BOTTOM_LEFT ERR_BUSY ERR_BUG\n    FAILED HALIGN_CENTER HORIZONTAL\n    JOY_ANALOG_L2 JOY_BUTTON_10 KEY_A KEY_AE\n    KEY_SYSREQ OP_MAX OP_XOR PROPERTY_HINT_DIR\n    PROPERTY_HINT_ENUM PROPERTY_USAGE_GROUP\n    PROPERTY_USAGE_NETWORK SPKEY VALIGN_TOP\n    VERTICAL OK MIDI_MESSAGE_AFTERTOUCH\n    METHOD_FLAG_CONST METHOD_FLAG_NORMAL\n    MARGIN_TOP MARGIN_RIGHT\n    ;; Our custom enums\n    Mouse:LEFT Margin:BOTTOM Corner:TOP_RIGHT\n    Orientation:VERTICAL HAlign:LEFT VAlign:TOP\n    Key:A Key:THORN Key:EXCLAM Key:KP_8 Key:KP-9\n    KeyMask:CTRL Joy:BUTTON-13 Joy:R3 Joy:ANALOG-R2\n    MidiMessage:NOTE_ON Orientation:HORIZONTAL\n    PropertyHint:ENUM PropertyUsage:GROUP Op:MAX\n    Type:NIL Err:OK Err:FAILED MethodFlag:METHOD_FLAGS_DEFAULT\n  ])\"#);\n}\n\n#[test]\npub fn builtin_type_const_test() {\n  // I don't care what this outputs; I just want to know that Godot\n  // recognizes all of the constants I'm compiling these to.\n  parse_and_run(r#\"([\n    ;; GDLisp type names\n    Null Int Bool Float String Vector2 Rect2 Vector3 Transform2D Plane Quat AABB\n    Basis Transform Color NodePath RID Object Dictionary Array PoolByteArray PoolIntArray\n    PoolStringArray PoolRealArray PoolVector2Array PoolVector3Array PoolColorArray\n    Any AnyRef AnyVal Number BaseArray Nothing\n    ;; GDScript original names\n    TYPE_NIL TYPE_BOOL TYPE_INT TYPE_REAL TYPE_STRING TYPE_VECTOR2 TYPE_RECT2 TYPE_VECTOR3\n    TYPE_TRANSFORM2D TYPE_PLANE TYPE_QUAT TYPE_AABB TYPE_BASIS TYPE_TRANSFORM TYPE_COLOR\n    TYPE_NODE_PATH TYPE_RID TYPE_OBJECT TYPE_DICTIONARY TYPE_ARRAY TYPE_RAW_ARRAY\n    TYPE_INT_ARRAY TYPE_REAL_ARRAY TYPE_STRING_ARRAY TYPE_VECTOR2_ARRAY TYPE_VECTOR3_ARRAY\n    TYPE_COLOR_ARRAY\n  ])\"#);\n}\n"
  },
  {
    "path": "tests/test/declaration_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse gdlisp::ir::identifier::ClassNamespace;\nuse gdlisp::ir::modifier::{ParseError as ModifierParseError, ParseErrorF as ModifierParseErrorF};\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse super::common::{parse_compile_decl, parse_compile_decl_err};\n\n#[test]\npub fn empty_file_test() {\n  // TODO Should not have init here (empty class exception, which I\n  // don't think is needed anymore); also applies in macro_test::simple_minimalist_test\n  assert_eq!(parse_compile_decl(\"()\"), r#\"extends Reference\n\n\nfunc _init():\n    pass\n\"#);\n}\n\n#[test]\npub fn simple_function_declaration_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo (x) x))\"),\n             r#\"extends Reference\n\n\nstatic func foo(x):\n    return x\n\"#);\n}\n\n#[test]\npub fn lambda_in_function_declaration_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo (x) (lambda () x) x))\"), r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var x\n\n    func _init(x):\n        self.x = x\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return x\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nstatic func foo(x):\n    return x\n\"#);\n}\n\n#[test]\npub fn closed_rw_in_function_declaration_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo (x) (lambda () (set x 1)) x))\"), r#\"extends Reference\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var x\n\n    func _init(x):\n        self.x = x\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        x.contents = 1\n        return x.contents\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\n\nstatic func foo(x):\n    x = GDLisp.Cell.new(x)\n    return x.contents\n\"#);\n}\n\n#[test]\npub fn mutually_recursive_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo () (bar)) (defn bar () (foo)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return bar()\n\n\nstatic func bar():\n    return foo()\n\"#);\n}\n\n#[test]\npub fn nonexistent_function_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defn foo () (bar)))\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"bar\")), SourceOffset(14)))),\n  );\n}\n\n#[test]\npub fn progn_decl_test() {\n  assert_eq!(parse_compile_decl(\"((progn (progn (defn foo () ()) (defn bar () ()))))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return null\n\n\nstatic func bar():\n    return null\n\"#);\n}\n\n#[test]\npub fn declare_value_test_1() {\n  assert_eq!(parse_compile_decl(\"((sys/declare value x) (defn foo () x))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return x\n\"#);\n}\n\n#[test]\npub fn declare_value_test_2() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superglobal x) (defn foo () x))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return x\n\"#);\n}\n\n#[test]\npub fn declare_value_test_3() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superglobal x) (defconst y x))\"), r#\"extends Reference\n\n\nconst y = x\n\"#);\n}\n\n#[test]\npub fn declare_value_test_4() {\n  assert_eq!(parse_compile_decl(\"((sys/declare constant x) (defconst y x))\"), r#\"extends Reference\n\n\nconst y = x\n\"#);\n}\n\n#[test]\npub fn declare_value_non_const_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((sys/declare value x) (defconst y x))\"),\n    Err(PError::from(GDError::new(GDErrorF::NotConstantEnough(String::from(\"y\")), SourceOffset(35)))),\n  );\n}\n\n#[test]\npub fn declare_function_test_1() {\n  assert_eq!(parse_compile_decl(\"((sys/declare function f ()) (defn foo () (f)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return f()\n\"#);\n}\n\n#[test]\npub fn declare_function_test_2() {\n  assert_eq!(parse_compile_decl(\"((sys/declare function f (a &opt b)) (defn foo () (f 1) (f 1 2)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    f(1, null)\n    return f(1, 2)\n\"#);\n}\n\n#[test]\npub fn declare_function_test_3() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superfunction f (a &opt b)) (defn foo () (f 1) (f 1 2)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    f(1, null)\n    return f(1, 2)\n\"#);\n}\n\n#[test]\npub fn nonsense_modifier_function_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn foo () public private 1))\"#),\n    Err(PError::from(ModifierParseError::new(ModifierParseErrorF::UniquenessError(String::from(\"visibility\")), SourceOffset(21)))),\n  );\n}\n\n#[test]\npub fn declare_function_inner_test_1() {\n  assert_eq!(parse_compile_decl(\"((sys/declare function f ()) (defclass Foo (Reference) (defn _init () (f))))\"),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        __gdlisp_outer_class_0.f()\n\n    var __gdlisp_outer_class_0 = load(\"res://TEST.gd\")\n\"#);\n}\n\n#[test]\npub fn declare_function_inner_test_2() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superfunction f ()) (defclass Foo (Reference) (defn _init () (f))))\"),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        f()\n\"#);\n}\n\n#[test]\npub fn duplicate_const_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defconst A 1) (defconst A 1))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Value, String::from(\"A\")), SourceOffset(17)))),\n  );\n}\n\n#[test]\npub fn duplicate_const_in_main_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defconst A 1) (defclass Foo () main (defconst A 1)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateNameConflictInMainClass(ClassNamespace::Value, String::from(\"A\")), SourceOffset(39)))),\n  );\n}\n\n#[test]\npub fn duplicate_const_with_var_in_main_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defconst A 1) (defclass Foo () main (defvar A 1)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateNameConflictInMainClass(ClassNamespace::Value, String::from(\"A\")), SourceOffset(39)))),\n  );\n}\n\n#[test]\npub fn duplicate_const_in_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo () (defconst A 1) (defconst A 1)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Value, String::from(\"A\")), SourceOffset(34)))),\n  );\n}\n\n#[test]\npub fn duplicate_var_in_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo () (defvar A 1) (defvar A 1)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Value, String::from(\"A\")), SourceOffset(32)))),\n  );\n}\n\n#[test]\npub fn duplicate_var_const_in_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo () (defvar A 1) (defconst A 1)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Value, String::from(\"A\")), SourceOffset(32)))),\n  );\n}\n\n#[test]\npub fn duplicate_fn_in_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo () (defn foo ()) (defn foo ())))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(33)))),\n  );\n}\n\n#[test]\npub fn duplicate_static_fn_in_class_test_1() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo () (defn foo ()) (defn foo () static)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(33)))),\n  );\n}\n\n#[test]\npub fn duplicate_static_fn_in_class_test_2() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo () (defn foo () static) (defn foo () static)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(40)))),\n  );\n}\n\n#[test]\npub fn duplicate_signal_in_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass Foo () (defsignal foo) (defsignal foo ())))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Signal, String::from(\"foo\")), SourceOffset(35)))),\n  );\n}\n\n#[test]\npub fn duplicate_fn_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn foo () 1) (defn foo () 2))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(18)))),\n  );\n}\n\n#[test]\npub fn duplicate_fn_main_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn foo () 1) (defclass Foo (Reference) main (defn foo () 2)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateNameConflictInMainClass(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(49)))),\n  );\n}\n\n#[test]\npub fn duplicate_fn_main_class_test_static() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn foo () 1) (defclass Foo (Reference) main (defn foo () static 2)))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateNameConflictInMainClass(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(49)))),\n  );\n}\n\n#[test]\npub fn overlapping_namespaces_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo ()) (defconst foo 1))\"),\n             r#\"extends Reference\n\n\nstatic func foo():\n    return null\n\n\nconst foo = 1\n\"#);\n}\n\n#[test]\npub fn overlapping_namespaces_in_class_test() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo () (defsignal foo) (defn foo ()) (defconst foo 1)))\"),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    signal foo\n\n    func foo():\n        return null\n\n    const foo = 1\n\"#);\n}\n#[test]\npub fn duplicate_const_in_lambda_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn foo () (new Reference (defconst A 1) (defconst A 1))))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Value, String::from(\"A\")), SourceOffset(45)))),\n  );\n}\n\n#[test]\npub fn duplicate_var_in_lambda_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn foo () (new Reference (defvar A 1) (defvar A 1))))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Value, String::from(\"A\")), SourceOffset(43)))),\n  );\n}\n\n#[test]\npub fn duplicate_var_const_in_lambda_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn foo () (new Reference (defvar A 1) (defconst A 1))))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Value, String::from(\"A\")), SourceOffset(43)))),\n  );\n}\n\n#[test]\npub fn duplicate_fn_in_lambda_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn xyz () (new Reference (defn foo ()) (defn foo ()))))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(44)))),\n  );\n}\n\n#[test]\npub fn duplicate_signal_in_lambda_class_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defn xyz () (new Reference (defsignal foo) (defsignal foo ()))))\"#),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Signal, String::from(\"foo\")), SourceOffset(46)))),\n  );\n}\n\n#[test]\npub fn declare_value_reserved_word_test_1() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superglobal while) (defconst y while))\"), r#\"extends Reference\n\n\nconst y = _while\n\"#);\n}\n\n#[test]\npub fn declare_value_reserved_word_test_2() {\n  // Invalid syntax in GDScript, but that's what you get for using a\n  // `sys/` primitive irresponsibly ¯\\_(ツ)_/¯\n  assert_eq!(parse_compile_decl(\"((sys/declare superglobal (x while)) (defconst y x))\"), r#\"extends Reference\n\n\nconst y = while\n\"#);\n}\n\n#[test]\npub fn declare_function_reserved_word_test_1() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superfunction self ()) (defn foo () (self)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return _self()\n\"#);\n}\n\n#[test]\npub fn declare_function_reserved_word_test_2() {\n  // Invalid syntax in GDScript, but that's what you get for using a\n  // `sys/` primitive irresponsibly ¯\\_(ツ)_/¯\n  assert_eq!(parse_compile_decl(\"((sys/declare superfunction (x self) ()) (defn foo () (x)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return self()\n\"#);\n}\n\n#[test]\npub fn declare_value_custom_name_test_1() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superglobal (x pizza)) (defconst y x))\"), r#\"extends Reference\n\n\nconst y = pizza\n\"#);\n}\n\n#[test]\npub fn declare_value_custom_name_test_2() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superglobal (x pepperoni-pizza)) (defconst y x))\"),\n             r#\"extends Reference\n\n\nconst y = pepperoni_pizza\n\"#);\n}\n\n#[test]\npub fn declare_function_custom_name_test_1() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superfunction (x pizza) ()) (defn foo () (x)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return pizza()\n\"#);\n}\n\n#[test]\npub fn declare_function_custom_name_test_2() {\n  assert_eq!(parse_compile_decl(\"((sys/declare superfunction (x pepperoni-pizza) ()) (defn foo () (x)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return pepperoni_pizza()\n\"#);\n}\n\n#[test]\npub fn bad_sys_declare_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((sys/declare not-a-valid-declaration-type foobar))\"),\n    Err(PError::from(GDError::new(GDErrorF::BadSysDeclare(String::from(\"not-a-valid-declaration-type\")), SourceOffset(1)))),\n  );\n}\n"
  },
  {
    "path": "tests/test/dependencies_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::dummy_pipeline;\n\nuse gdlisp::ir;\nuse gdlisp::ir::declaration_table::DeclarationTable;\nuse gdlisp::ir::depends::Dependencies;\nuse gdlisp::ir::identifier::{IdLike, Id, Namespace};\nuse gdlisp::ir::main_function::DisallowMainFunctionHandler;\nuse gdlisp::AST_PARSER;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse std::collections::{HashMap, HashSet};\n\n// TODO Test some dependency analysis that involves importing multiple files.\n\nfn dependencies_of<'a>(input: &str, target_name: &(dyn IdLike<NS=Namespace> + 'a), pos: SourceOffset) -> Dependencies {\n  let ast = AST_PARSER.parse(input).unwrap();\n  let (toplevel, _macros) = ir::compile_and_check(&mut dummy_pipeline(), &ast, &DisallowMainFunctionHandler).unwrap();\n  ir::scope::check_scopes(&toplevel).unwrap();\n  let table = DeclarationTable::from(toplevel.decls);\n  Dependencies::identify(&table, &HashSet::new(), target_name, pos)\n}\n\nfn make_deps(known: Vec<(&str, usize)>, unknown: Vec<(&str, usize)>) -> Dependencies {\n  Dependencies {\n    known: known.into_iter().map(|(x, p)| (Id::new(Namespace::Function, x.to_owned()), SourceOffset::from(p))).collect(),\n    imports: HashMap::new(),\n    unknown: unknown.into_iter().map(|(x, p)| (Id::new(Namespace::Function, x.to_owned()), SourceOffset::from(p))).collect(),\n  }\n}\n\n#[test]\npub fn dependencies_test_empty() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () ())\n     (defn bar () ())\n     (defn baz () ()))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(10)),\n             make_deps(vec!((\"baz\", 10)), vec!()));\n}\n\n#[test]\npub fn dependencies_test_forward() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () ())\n     (defn bar () ())\n     (defn baz () (bar)))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(999)),\n             make_deps(vec!((\"baz\", 999), (\"bar\", 63)), vec!()));\n}\n\n#[test]\npub fn dependencies_test_backward() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () ())\n     (defn bar () (baz))\n     (defn baz () ()))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(9)),\n             make_deps(vec!((\"baz\", 9)), vec!()));\n}\n\n#[test]\npub fn dependencies_test_two() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () ())\n     (defn bar () ())\n     (defn baz () (foo) (bar)))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(0)),\n             make_deps(vec!((\"baz\", 0), (\"bar\", 69), (\"foo\", 63)), vec!()));\n}\n\n#[test]\npub fn dependencies_test_transitive() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () (bar))\n     (defn bar () ())\n     (defn baz () (foo)))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(0)),\n             make_deps(vec!((\"baz\", 0), (\"bar\", 19), (\"foo\", 66)), vec!()));\n}\n\n#[test]\npub fn dependencies_test_recursion() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () (bar))\n     (defn bar () (bar))\n     (defn baz () (foo) (baz)))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(999)),\n             make_deps(vec!((\"baz\", 75), (\"bar\", 19), (\"foo\", 69)), vec!()));\n}\n\n#[test]\npub fn dependencies_test_cycle() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () (baz))\n     (defn bar () (foo))\n     (defn baz () (bar)))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(999)),\n             make_deps(vec!((\"baz\", 19), (\"bar\", 69), (\"foo\", 44)), vec!()));\n}\n\n#[test]\npub fn dependencies_test_unknowns_1() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () (aaa))\n     (defn bar () (foo))\n     (defn baz () (bar)))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(999)),\n             make_deps(vec!((\"baz\", 999), (\"bar\", 69), (\"foo\", 44)), vec!((\"aaa\", 19))));\n}\n\n#[test]\npub fn dependencies_test_unknowns_2() {\n  assert_eq!(dependencies_of(r#\"\n    ((defn foo () (aaa))\n     (defn bar () (bbb))\n     (defn baz () (bar)))\"#, &*Id::build(Namespace::Function, \"baz\"), SourceOffset(999)),\n             make_deps(vec!((\"baz\", 999), (\"bar\", 69)), vec!((\"bbb\", 44))));\n}\n"
  },
  {
    "path": "tests/test/dictionary_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::{parse_compile_and_output, parse_and_run};\n\n#[test]\nfn dict_test_1() {\n  assert_eq!(parse_compile_and_output(\"{}\"), \"return {}\\n\");\n}\n\n#[test]\nfn dict_test_2() {\n  assert_eq!(parse_compile_and_output(\"{1 2 3 4}\"), \"return {1: 2, 3: 4}\\n\");\n}\n\n#[test]\nfn dict_test_running() {\n  let result = parse_and_run(r#\"((let ((a {\"b\" 3})) (print (dict/elt a \"b\"))))\"#);\n  assert_eq!(result, \"\\n3\\n\");\n}\n\n#[test]\nfn dict_test_set_running() {\n  let result = parse_and_run(r#\"((let ((a {\"b\" 3}))\n                                   (set (dict/elt a \"c\") 100)\n                                   (print (dict/elt a \"b\"))\n                                   (print (dict/elt a \"c\"))\n                                   (set (dict/elt a \"b\") -3)\n                                   (print (dict/elt a \"b\"))))\"#);\n  assert_eq!(result, \"\\n3\\n100\\n-3\\n\");\n}\n"
  },
  {
    "path": "tests/test/enum_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse super::common::*;\n\n#[test]\npub fn empty_enum_test() {\n  assert_eq!(parse_compile_decl(\"((defenum MyEnum))\"), r#\"extends Reference\n\n\nenum MyEnum {\n}\n\"#);\n}\n\n#[test]\npub fn unvalued_enum_test() {\n  assert_eq!(parse_compile_decl(\"((defenum MyEnum A B C))\"), r#\"extends Reference\n\n\nenum MyEnum {\n    A,\n    B,\n    C,\n}\n\"#);\n}\n\n#[test]\npub fn valued_enum_test() {\n  assert_eq!(parse_compile_decl(\"((defenum MyEnum (A 1) (B 2) (C 3)))\"), r#\"extends Reference\n\n\nenum MyEnum {\n    A = 1,\n    B = 2,\n    C = 3,\n}\n\"#);\n}\n\n#[test]\npub fn mixed_enum_test() {\n  assert_eq!(parse_compile_decl(\"((defenum MyEnum A (B 2) (C 3)))\"), r#\"extends Reference\n\n\nenum MyEnum {\n    A,\n    B = 2,\n    C = 3,\n}\n\"#);\n}\n\n#[test]\npub fn enum_runner_test() {\n  let result = parse_and_run(\"((defenum MyEnum (A 1) (B 2) (C 3)) (print MyEnum:A) (print MyEnum:B) (print MyEnum:C))\");\n  assert_eq!(result, \"\\n1\\n2\\n3\\n\");\n}\n\n#[test]\npub fn invalid_enum_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defenum MyEnum (A 1) (B 2) (C 3)) (defn foo () MyEnum:D))\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchEnumValue(String::from(\"MyEnum\"), String::from(\"D\")), SourceOffset(49)))),\n  );\n}\n\n#[test]\npub fn builtin_enum_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo () Mouse:LEFT))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return GDLisp.Mouse.LEFT\n\"#);\n}\n"
  },
  {
    "path": "tests/test/error_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse gdlisp::compile::symbol_table::local_var::VarNameIntoExtendsError;\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\nuse gdlisp::sxp::ast::AST;\n\nuse super::common::*;\n\n#[test]\npub fn bad_call_compile_test_1() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(5 6)\"),\n    Err(PError::from(GDError::new(GDErrorF::CannotCall(AST::from_value(5, SourceOffset(1))), SourceOffset(1)))),\n  );\n}\n\n#[test]\npub fn bad_call_compile_test_2() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(() 6)\"),\n    Err(PError::from(GDError::new(GDErrorF::CannotCall(AST::nil(SourceOffset(1))), SourceOffset(1)))),\n  );\n}\n\n#[test]\npub fn cannot_extend_local() {\n  assert_eq!(\n    parse_compile_and_output_err(r#\"(let ((x 1)) (let ((y (new x))) y))\"#),\n    Err(PError::from(GDError::from_value(VarNameIntoExtendsError::CannotExtendLocal(String::from(\"x\")), SourceOffset(22)))),\n  );\n}\n\n#[test]\npub fn cannot_extend_current_file() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defclass MyMainClass () main) (defclass MySubclass (MyMainClass)))\"#),\n    Err(PError::from(GDError::from_value(VarNameIntoExtendsError::CannotExtendCurrentFile(String::from(\"res://TEST.gd\")), SourceOffset(33)))),\n  );\n}\n"
  },
  {
    "path": "tests/test/even_odd_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::parse_and_run;\n\n#[test]\nfn even_odd_test_labels() {\n  let output = parse_and_run(r#\"\n  ((labels ((is-even (x) (if (= x 0) #t (is-odd  (- x 1))))\n            (is-odd  (x) (if (= x 0) #f (is-even (- x 1)))))\n    (let ((x 0))\n      (while (< x 11)\n        (print (is-even x))\n        (print (is-odd x))\n        (set x (+ x 1))))))\n  \"#);\n  assert_eq!(output, \"\\nTrue\\nFalse\\nFalse\\nTrue\\nTrue\\nFalse\\nFalse\\nTrue\\nTrue\\nFalse\\nFalse\\nTrue\\nTrue\\nFalse\\nFalse\\nTrue\\nTrue\\nFalse\\nFalse\\nTrue\\nTrue\\nFalse\\n\");\n}\n"
  },
  {
    "path": "tests/test/factorial_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::parse_and_run;\n\n#[test]\nfn factorial_test_global() {\n  let output = parse_and_run(r#\"\n  ((defn fact (x)\n     (if (<= x 1)\n       1\n       (* x (fact (- x 1)))))\n    (let ((x 0))\n      (while (< x 6)\n        (print (fact x))\n        (set x (+ x 1)))))\n  \"#);\n  assert_eq!(output, \"\\n1\\n1\\n2\\n6\\n24\\n120\\n\");\n}\n\n#[test]\nfn factorial_test_labels() {\n  let output = parse_and_run(r#\"\n  ((labels ((fact (x)\n              (if (<= x 1)\n                1\n                (* x (fact (- x 1))))))\n     (let ((x 0))\n        (while (< x 6)\n           (print (fact x))\n           (set x (+ x 1))))))\n  \"#);\n  assert_eq!(output, \"\\n1\\n1\\n2\\n6\\n24\\n120\\n\");\n}\n"
  },
  {
    "path": "tests/test/flet_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::{parse_compile_and_output_h, parse_and_run};\n\n#[test]\nfn simple_flet_test_1() {\n  let output = parse_and_run(\"((flet ((f (x) x)) (print (f 1))))\");\n  assert_eq!(output, \"\\n1\\n\");\n}\n\n#[test]\nfn simple_flet_test_2() {\n  let output = parse_and_run(\"((flet ((f (x) (* x 2))) (print (f 1)) (print (f 2))))\");\n  assert_eq!(output, \"\\n2\\n4\\n\");\n}\n\n#[test]\nfn simple_flet_test_3() {\n  let output = parse_and_run(\"((defn foo (x) (* x 2)) (flet ((f (x) (foo x))) (print (f 1)) (print (f 2))))\");\n  assert_eq!(output, \"\\n2\\n4\\n\");\n}\n\n#[test]\nfn closed_flet_test_1() {\n  let output = parse_and_run(r#\"\n    ((let ((x 0))\n       (flet ((f () (set x (+ x 1))))\n         (print (f))\n         (print (f)))))\"#);\n  assert_eq!(output, \"\\n1\\n2\\n\");\n}\n\n#[test]\nfn closed_flet_test_2() {\n  let output = parse_and_run(r#\"\n    ((let ((g (let ((x 0))\n                (flet ((f () (set x (+ x 1))))\n                  (lambda () (f))))))\n       (print (funcall g))\n       (print (funcall g))))\n  \"#);\n  assert_eq!(output, \"\\n1\\n2\\n\");\n}\n\n#[test]\nfn nested_flet_test() {\n  let output = parse_and_run(r#\"\n    ((let ((g (let ((x 0))\n                (flet ((f () (set x (+ x 1))))\n                  (flet ((g () (f)))\n                    (function g))))))\n       (print (funcall g))\n       (print (funcall g))))\n  \"#);\n  assert_eq!(output, \"\\n1\\n2\\n\");\n}\n\n#[test]\npub fn semiglobal_flet_test() {\n\n  let result0 = parse_compile_and_output_h(\"(flet ((f (x) (+ x 1))) (f 10))\");\n  assert_eq!(result0.0, \"return _flet(10)\\n\");\n  assert_eq!(result0.1, \"static func _flet(x):\\n    return x + 1\\n\");\n\n}\n\n#[test]\npub fn semiglobal_flet_test_indirect() {\n\n  let result0 = parse_compile_and_output_h(\"(flet ((f (x) (+ x 1))) (funcall (function f) 10))\");\n  assert_eq!(result0.0, \"return GDLisp.funcall(_FunctionRefBlock.new(), GDLisp.cons(10, null))\\n\");\n  assert_eq!(result0.1, r#\"static func _flet(x):\n    return x + 1\n\n\nclass _FunctionRefBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(arg0):\n        return load(\"res://TEST.gd\")._flet(arg0)\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n}\n\n#[test]\npub fn local_flet_test() {\n\n  let result0 = parse_compile_and_output_h(r#\"\n    (let ((x 1))\n      (flet ((f () (+ x 1)))\n        (f)))\n  \"#);\n  assert_eq!(result0.0, \"var x = 1\\nvar _flet = _LambdaBlock.new(x)\\nreturn _flet.call_func()\\n\");\n  assert_eq!(result0.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    var x\n\n    func _init(x):\n        self.x = x\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return x + 1\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n}\n\n#[test]\npub fn local_flet_test_indirect() {\n\n  let result0 = parse_compile_and_output_h(r#\"\n    (let ((x 1))\n      (flet ((f () (+ x 1)))\n        (funcall (function f))))\n  \"#);\n  assert_eq!(result0.0, \"var x = 1\\nvar _flet = _LambdaBlock.new(x)\\nreturn GDLisp.funcall(_flet, null)\\n\");\n  assert_eq!(result0.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    var x\n\n    func _init(x):\n        self.x = x\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return x + 1\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n}\n\n#[test]\npub fn local_flet_closure_test() {\n  let result0 = parse_compile_and_output_h(r#\"\n    (let ((x 1))\n      (flet ((f () x))\n        (let ((g (lambda () (f))))\n          (funcall g))))\n  \"#);\n  assert_eq!(result0.0, \"var x = 1\\nvar _flet = _LambdaBlock.new(x)\\nvar g = _LambdaBlock_0.new(_flet)\\nreturn GDLisp.funcall(g, null)\\n\");\n}\n"
  },
  {
    "path": "tests/test/floating_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::parse_and_run;\n\n#[test]\nfn floating_test() {\n  // I'm using Rust's built-in to_string on f32 to get floating-point\n  // output. I want to make sure nothing funny happens on the Godot\n  // side when I do that.\n  let output = parse_and_run(r#\"\n    ((let (x)\n      (set x 1.0000)\n      (set x -0.29199)\n      (set x 3e19)\n      (set x +3e19)\n      (set x -4.1E3)\n      (set x -4.1E+3)\n      (set x 1e-2)))\n  \"#);\n  assert_eq!(output, \"\\n\");\n}\n"
  },
  {
    "path": "tests/test/for_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\nuse gdlisp::ir::loops::error::LoopPrimitiveError;\n\n#[test]\nfn for_loop_test() {\n  let output = parse_and_run(r#\"\n    ((for variable 3 (print variable)))\n  \"#);\n  assert_eq!(output, \"\\n0\\n1\\n2\\n\");\n}\n\n#[test]\npub fn for_tests() {\n  assert_eq!(parse_compile_and_output(\"(for x 1)\"), \"for x in 1:\\n    pass\\nreturn null\\n\");\n  assert_eq!(parse_compile_and_output(\"(for x 1 2)\"), \"for x in 1:\\n    pass\\nreturn null\\n\");\n  assert_eq!(parse_compile_and_output(\"(for x 1 (foo))\"), \"for x in 1:\\n    foo()\\nreturn null\\n\");\n  assert_eq!(parse_compile_and_output(\"(for x-y 1 (foo))\"), \"for x_y in 1:\\n    foo()\\nreturn null\\n\");\n}\n\n#[test]\npub fn for_test_with_break() {\n  assert_eq!(parse_compile_and_output(\"(for i 1 (break))\"), r#\"for i in 1:\n    break\nreturn null\n\"#);\n}\n\n#[test]\npub fn for_test_with_continue() {\n  assert_eq!(parse_compile_and_output(\"(for i 1 (continue))\"), r#\"for i in 1:\n    continue\nreturn null\n\"#);\n}\n\n#[test]\npub fn bad_break_in_for_loop_lambda_test() {\n  assert_eq!(parse_compile_and_output_err(\"(for i 1 (lambda () (break)))\"),\n             Err(PError::from(LoopPrimitiveError::break_error(SourceOffset(20)).in_closure())));\n}\n\n#[test]\npub fn bad_continue_in_for_loop_lambda_test() {\n  assert_eq!(parse_compile_and_output_err(\"(for i 1 (lambda () (continue)))\"),\n             Err(PError::from(LoopPrimitiveError::continue_error(SourceOffset(20)).in_closure())));\n}\n\n#[test]\npub fn bad_continue_in_for_loop_lambda_class_test() {\n  assert_eq!(parse_compile_and_output_err(\"(for i 1 (new Reference (defn foo () (continue))))\"),\n             Err(PError::from(LoopPrimitiveError::continue_error(SourceOffset(37)).in_closure())));\n}\n\n#[test]\npub fn bad_continue_in_for_loop_iter_test() {\n  assert_eq!(parse_compile_and_output_err(\"(for i (continue) 2)\"),\n             Err(PError::from(LoopPrimitiveError::continue_error(SourceOffset(7)))));\n}\n"
  },
  {
    "path": "tests/test/import_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse super::common::import::MockFileLoader;\nuse super::common::dummy_config;\n\nuse gdlisp::ir::identifier::{Id, Namespace};\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::Pipeline;\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\nfn setup_simple_file_loader(loader: &mut MockFileLoader) {\n  loader.add_file(\"example.lisp\", \"(defn one () 1) (defn two () 2)\");\n}\n\nfn load_and_output_simple_file(input: &str) -> String {\n  load_and_output_simple_file_err(input).unwrap()\n}\n\nfn load_and_output_simple_file_err(input: &str) -> Result<String, PError> {\n  let mut loader = MockFileLoader::new();\n  setup_simple_file_loader(&mut loader);\n  loader.add_file(\"main.lisp\", input);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0))?.gdscript.to_gd();\n  Ok(result)\n}\n\n#[test]\nfn qualified_import_test() {\n  assert_eq!(load_and_output_simple_file(r#\"\n    (use \"res://example.lisp\")\n    (defn run ()\n      (example/one)\n      (example/two))\n  \"#), r#\"extends Node\n\n\nconst example_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    example_0.one()\n    return example_0.two()\n\"#);\n}\n\n#[test]\nfn aliased_import_test() {\n  assert_eq!(load_and_output_simple_file(r#\"\n    (use \"res://example.lisp\" as example-name)\n    (defn run ()\n      (example-name/one)\n      (example-name/two))\n  \"#), r#\"extends Node\n\n\nconst example_name_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    example_name_0.one()\n    return example_name_0.two()\n\"#);\n}\n\n#[test]\nfn restricted_import_test() {\n  assert_eq!(load_and_output_simple_file(r#\"\n    (use \"res://example.lisp\" (one))\n    (defn run ()\n      (one))\n  \"#), r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return _Import_0.one()\n\"#);\n}\n\n#[test]\nfn restricted_import_test_failed() {\n  assert_eq!(\n    load_and_output_simple_file_err(r#\"\n      (use \"res://example.lisp\" (one))\n      (defn run ()\n        (two))\n    \"#),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"two\")), SourceOffset(67)))),\n  );\n}\n\n#[test]\nfn restricted_import_alias_test() {\n  assert_eq!(load_and_output_simple_file(r#\"\n    (use \"res://example.lisp\" ((one as my-one)))\n    (defn run ()\n      (my-one))\n  \"#), r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return _Import_0.one()\n\"#);\n}\n\n#[test]\nfn restricted_import_alias_test_failed() {\n  assert_eq!(\n    load_and_output_simple_file_err(r#\"\n      (use \"res://example.lisp\" ((one as my-one)))\n      (defn run ()\n        (one))\n    \"#),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"one\")), SourceOffset(79)))),\n  );\n}\n\n#[test]\nfn open_import_test() {\n  assert_eq!(load_and_output_simple_file(r#\"\n    (use \"res://example.lisp\" open)\n    (defn run ()\n      (one)\n      (two))\n  \"#), r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    _Import_0.one()\n    return _Import_0.two()\n\"#);\n}\n\n#[test]\nfn nonexistent_import_test() {\n  assert_eq!(\n    load_and_output_simple_file_err(r#\"\n      (use \"res://example.lisp\" (nonexistent-function-name))\n    \"#),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function,\n                                                                     name: String::from(\"nonexistent-function-name\") }),\n                                  SourceOffset(12)))),\n  );\n}\n\n#[test]\nfn macro_uses_other_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn add-one (x) (+ x 1))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defmacro f (x) (add-one x))\n    (defn run ()\n      (f 43))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func f(x):\n    return _Import_0.add_one(x)\n\n\nstatic func run():\n    return 44\n\"#);\n}\n\n#[test]\nfn import_declared_value_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(sys/declare value a public)\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn f (x) a)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func f(x):\n    return _Import_0.a\n\"#);\n}\n\n#[test]\nfn import_declared_constant_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(sys/declare constant a public)\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defconst y a)\n    (defn f (x) a)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\nconst y = _Import_0.a\n\n\nstatic func f(x):\n    return _Import_0.a\n\"#);\n}\n\n#[test]\nfn import_declared_superglobal_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(sys/declare superglobal a public)\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defconst y a)\n    (defn f (x) a)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\nconst y = a\n\n\nstatic func f(x):\n    return a\n\"#);\n}\n\n#[test]\nfn import_declared_function_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(sys/declare function a () public)\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn f (x) (a))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func f(x):\n    return _Import_0.a()\n\"#);\n}\n\n#[test]\nfn import_declared_superfunction_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(sys/declare superfunction a () public)\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn f (x) (a))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func f(x):\n    return a()\n\"#);\n}\n\n#[test]\nfn imported_name_to_class_var_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(sys/declare value A public) (defconst B 100)\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defclass Main (Reference) main\n      (defvar a A)\n      (defvar b B)\n      (defvar a1 A onready)\n      (defvar b1 B onready))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Reference\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nfunc _init():\n    self.a = _Import_0.A\n\n\nvar a\nvar b = _Import_0.B\nvar a1\nonready var b1 = _Import_0.B\n\n\nfunc _ready():\n    self.a1 = _Import_0.A\n\"#);\n}\n\n#[test]\nfn cyclic_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(use \\\"res://b.lisp\\\" open) (defn foo (x) (bar))\");\n  loader.add_file(\"b.lisp\", \"(use \\\"res://a.lisp\\\" open) (defn bar (x) (foo))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://b.lisp\" open)\n    (defn run ()\n      (bar))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0));\n\n  let error_filename = String::from(if cfg!(windows) { \".\\\\b.lisp\" } else { \"./b.lisp\" });\n\n  assert_eq!(result.map(|_| ()),\n             Err(PError::from(\n               GDError::new(GDErrorF::CyclicImport(error_filename), SourceOffset(0)),\n             )));\n}\n\n#[test]\nfn macro_uses_preload_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn add-one (x) (+ x 1))\");\n  // Note: We have to explicitly import the file, even if we later\n  // load it using `preload`. Macro expansion only uses imports for\n  // resolution. I may loosen this constraint later, but for right now\n  // it is required.\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\")\n    (defmacro f (x) ((preload \"res://example.lisp\"):add-one x))\n    (defn run ()\n      (f 43))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst example_0 = preload(\"res://example.gd\")\n\n\nstatic func f(x):\n    return preload(\"res://example.gd\").add_one(x)\n\n\nstatic func run():\n    return 44\n\"#);\n}\n\n#[test]\nfn macro_uses_preload_without_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn add-one (x) (+ x 1))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (defmacro f (x) ((preload \"res://example.lisp\"):add-one x))\n    (defn run ()\n      (f 43))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0));\n  let expected_error_path = String::from(\"res://example.gd\");\n  assert_eq!(result.map(|_| ()),\n             Err(PError::from(\n               GDError::new(GDErrorF::NoSuchFile(expected_error_path), SourceOffset(22)),\n             )));\n}\n\n#[test]\nfn symbol_macro_uses_other_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn add-one (x) (+ x 1))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (define-symbol-macro x (add-one 2))\n    (defn run ()\n      x)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func __gdlisp_SymbolMacroFunction_x():\n    return _Import_0.add_one(2)\n\n\nstatic func run():\n    return 3\n\"#);\n}\n\n#[test]\nfn macro_from_other_file_import_test_1() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defmacro add-one (x) (+ x 1))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn run ()\n      (add-one 43))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return 44\n\"#);\n}\n\n#[test]\nfn macro_from_other_file_import_test_2() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn outer () 44) (defclass Foo (Reference) (defn go () (outer))) (defmacro go () ((Foo:new):go))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn run ()\n      (go))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return 44\n\"#);\n}\n\n#[test]\nfn macro_from_other_file_import_test_3() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn outer () 44) (defclass Foo (Reference) (defn go () static (outer))) (defmacro go () (Foo:go))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn run ()\n      (go))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return 44\n\"#);\n}\n\n#[test]\nfn symbol_macro_from_other_file_import_test_1() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(define-symbol-macro x 10)\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn run ()\n      x)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return 10\n\"#);\n}\n\n#[test]\nfn symbol_macro_from_other_file_import_test_2() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn outer () 44) (defclass Foo (Reference) (defn go () (outer))) (define-symbol-macro go ((Foo:new):go))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn run ()\n      go)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return 44\n\"#);\n}\n\n#[test]\nfn symbol_macro_from_other_file_import_test_3() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"example.lisp\", \"(defn outer () 44) (defclass Foo (Reference) (defn go () static (outer))) (define-symbol-macro go (Foo:go))\");\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://example.lisp\" open)\n    (defn run ()\n      go)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://example.gd\")\n\n\nstatic func run():\n    return 44\n\"#);\n}\n\n#[test]\nfn macro_several_files_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn add-one-f (x) (+ x 1))\");\n  loader.add_file(\"b.lisp\", r#\"(use \"res://a.lisp\" open) (defmacro f (x) (add-one-f x))\"#);\n  loader.add_file(\"c.lisp\", r#\"(use \"res://b.lisp\" open) (defmacro g (x) (+ x (f 87)))\"#);\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://c.lisp\")\n    (defn run ()\n      (c/g 3))\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst c_0 = preload(\"res://c.gd\")\n\n\nstatic func run():\n    return 91\n\"#);\n}\n\n#[test]\nfn symbol_macro_several_files_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn add-one-f (x) (+ x 1))\");\n  loader.add_file(\"b.lisp\", r#\"(use \"res://a.lisp\" open) (define-symbol-macro f (add-one-f 3))\"#);\n  loader.add_file(\"c.lisp\", r#\"(use \"res://b.lisp\" open) (define-symbol-macro g f)\"#);\n  loader.add_file(\"main.lisp\", r#\"\n    (use \"res://c.lisp\")\n    (defn run ()\n      c/g)\n  \"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst c_0 = preload(\"res://c.gd\")\n\n\nstatic func run():\n    return 4\n\"#);\n}\n\n#[test]\nfn main_class_import_test_1() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass Foo (Reference) main)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" open) (defn run () Foo)\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nstatic func run():\n    return _Import_0\n\"#);\n}\n\n#[test]\nfn main_class_import_test_2() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass Foo (Reference) main) (defconst VALUE 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" open) (defn run () [Foo VALUE])\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nstatic func run():\n    return [_Import_0, _Import_0.VALUE]\n\"#);\n}\n\n#[test]\nfn import_declare_test_failed_1() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(sys/declare value a)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" open) (defn run () a)\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"a\")), SourceOffset(39)))),\n  );\n}\n\n#[test]\nfn import_declare_test_failed_2() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(sys/declare value a)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (a))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"a\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn public_fn_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo () public 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\") (defn run () (a/foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst a_0 = preload(\"res://a.gd\")\n\n\nstatic func run():\n    return a_0.foo()\n\"#);\n}\n\n#[test]\nfn private_fn_import_test_1() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo () private 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\")\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst a_0 = preload(\"res://a.gd\")\n\"#);\n}\n\n#[test]\nfn private_fn_import_test_2() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo () private 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" open)\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\"#);\n}\n\n#[test]\nfn private_fn_import_test_3() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo () private 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" open) (defn run () (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"foo\")), SourceOffset(39)))),\n  );\n}\n\n#[test]\nfn private_fn_import_test_4() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo () private 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn public_macro_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defmacro foo () public 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\"#);\n}\n\n#[test]\nfn private_macro_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defmacro foo () private 1)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn public_const_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defconst foo 1 public)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\"#);\n}\n\n#[test]\nfn private_const_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defconst foo 1 private)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn public_enum_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defenum foo public A B)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\"#);\n}\n\n#[test]\nfn private_enum_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defenum foo private A B)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn public_class_import_test_1() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass foo (Node) public (defvar example 1))\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\"#);\n}\n\n#[test]\nfn public_class_import_test_2() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass foo (Node) main public (defvar example 1))\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\"#);\n}\n\n#[test]\nfn private_class_import_test_1() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass foo (Node) private (defvar example 1))\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn private_class_import_test_2() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass foo (Node) main private (defvar example 1))\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn private_lazy_val_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(deflazy foo 10 private)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn private_symbol_macro_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(define-symbol-macro foo 10 private)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::UnknownImportedName(Id { namespace: Namespace::Function, name: String::from(\"foo\") }), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn lazy_val_import_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(deflazy foo 10)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo)) (defn run () foo)\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nstatic func run():\n    return load(\"res://a.gd\")._lazy_0()\n\"#);\n}\n\n#[test]\nfn lazy_val_import_run_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(deflazy foo 10)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo)) (defmacro bar () foo) (defn run () (bar))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nstatic func bar():\n    return load(\"res://a.gd\")._lazy_0()\n\n\nstatic func run():\n    return 10\n\"#);\n}\n\n#[test]\nfn ambiguous_import_namespace_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo ()) (defconst foo 10)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  assert_eq!(\n    pipeline.load_file(\"main.lisp\", SourceOffset(0)).map(|_| ()),\n    Err(PError::from(GDError::new(GDErrorF::AmbiguousNamespace(String::from(\"foo\")), SourceOffset(5)))),\n  );\n}\n\n#[test]\nfn ambiguous_import_namespace_disambiguate_value_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo ()) (defconst foo 10)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" ((foo value))) (defn bar () foo)\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nstatic func bar():\n    return _Import_0.foo\n\"#);\n}\n\n#[test]\nfn ambiguous_import_namespace_disambiguate_function_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defn foo ()) (defconst foo 10)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" ((foo function))) (defn bar () (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nstatic func bar():\n    return _Import_0.foo()\n\"#);\n}\n\n#[test]\nfn inner_class_extends_inner_class_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass A (Reference))\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (A)) (defclass B (A))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nclass B extends _Import_0.A:\n\n    func _init():\n        pass\n\"#);\n}\n\n#[test]\nfn inner_class_extends_main_class_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass A (Reference) main)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (A)) (defclass B (A))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends Node\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nclass B extends _Import_0:\n\n    func _init():\n        pass\n\"#);\n}\n\n#[test]\nfn main_class_extends_inner_class_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass A (Reference))\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (A)) (defclass B (A) main)\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends \"res://a.gd\".A\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nfunc _init():\n    pass\n\"#);\n}\n\n#[test]\nfn main_class_extends_main_class_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", \"(defclass A (Reference) main)\");\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (A)) (defclass B (A) main)\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap().gdscript.to_gd();\n  assert_eq!(result, r#\"extends \"res://a.gd\"\n\n\nconst _Import_0 = preload(\"res://a.gd\")\n\n\nfunc _init():\n    pass\n\"#);\n}\n\n#[test]\nfn load_non_gdlisp_resource_in_macro_test() {\n  let mut loader = MockFileLoader::new();\n  loader.add_file(\"a.lisp\", r#\"(use \"res://some_resource.png\") (defmacro foo () some_resource)\"#);\n  loader.add_file(\"main.lisp\", r#\"(use \"res://a.lisp\" (foo)) (defn bar () (foo))\"#);\n  let mut pipeline = Pipeline::with_resolver(dummy_config(), Box::new(loader));\n  let result = pipeline.load_file(\"main.lisp\", SourceOffset(0)).unwrap_err();\n  assert_eq!(result, PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"some_resource\")),\n                                               SourceOffset(49))));\n}\n"
  },
  {
    "path": "tests/test/labels_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::{parse_compile_and_output_h, parse_and_run, parse_compile_decl};\n\n#[test]\nfn simple_labels_test_1() {\n  let output = parse_and_run(\"((labels ((f (x) x)) (print (f 1))))\");\n  assert_eq!(output, \"\\n1\\n\");\n}\n\n#[test]\nfn labels_test_outer_scope_static_1() {\n  let output = parse_and_run(\"((defn foo (x) (+ x 10)) (labels ((f (x) (if #f (f x) (foo x)))) (print (f 1))))\");\n  assert_eq!(output, \"\\n11\\n\");\n}\n\n#[test]\nfn labels_test_outer_scope_static_2() {\n  let output = parse_and_run(\"((defn foo (x) (+ x 10)) (labels ((f (x) (if #f (f x) (foo x)))) (print (funcall #'f 1))))\");\n  assert_eq!(output, \"\\n11\\n\");\n}\n\n#[test]\nfn labels_test_outer_scope_static_3() {\n  let output = parse_and_run(\"((defn foo (x y) (+ x y)) (let ((y 1)) (labels ((f (x) (foo x y))) (print (f 5)))))\");\n  assert_eq!(output, \"\\n6\\n\");\n}\n\n#[test]\nfn labels_test_outer_scope_static_4() {\n  let output = parse_and_run(\"((defn foo (x y) (+ x y)) (let ((y 1)) (labels ((f (x) (foo x y))) (print (funcall #'f 5)))))\");\n  assert_eq!(output, \"\\n6\\n\");\n}\n\n#[test]\nfn labels_test_outer_scope_static_5() {\n  let output = parse_and_run(\"((defn foo (x y) (+ x y)) (labels ((f (x) (foo x 1))) (print (f 5))))\");\n  assert_eq!(output, \"\\n6\\n\");\n}\n\n#[test]\nfn labels_test_outer_scope_static_6() {\n  let output = parse_and_run(\"((defn foo (x y) (+ x y)) (labels ((f (x) (foo x 1))) (print (funcall #'f 5))))\");\n  assert_eq!(output, \"\\n6\\n\");\n}\n\n#[test]\npub fn semiglobal_labels_test() {\n\n  let result0 = parse_compile_and_output_h(\"(labels ((f (x) (+ x 1))) (f 10))\");\n  assert_eq!(result0.0, \"return _flet(10)\\n\");\n  assert_eq!(result0.1, \"static func _flet(x):\\n    return x + 1\\n\");\n\n}\n\n#[test]\npub fn semiglobal_labels_test_indirect() {\n\n  let result0 = parse_compile_and_output_h(\"(labels ((f (x) (+ x 1))) (funcall (function f) 10))\");\n  assert_eq!(result0.0, \"return GDLisp.funcall(_FunctionRefBlock.new(), GDLisp.cons(10, null))\\n\");\n  assert_eq!(result0.1, r#\"static func _flet(x):\n    return x + 1\n\n\nclass _FunctionRefBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(arg0):\n        return load(\"res://TEST.gd\")._flet(arg0)\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n}\n\n#[test]\npub fn recursive_single_labels_test() {\n  let result0 = parse_compile_and_output_h(\"(labels ((f (x) (f x))) (f 1))\");\n  assert_eq!(result0.0, \"var _locals = _Labels.new()\\nreturn _locals._fn_f_0(1)\\n\");\n  assert_eq!(result0.1, r#\"class _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_0(x):\n        return _fn_f_0(x)\n\"#);\n}\n\n#[test]\npub fn recursive_single_labels_with_contrived_local_name_test() {\n  let result = parse_compile_decl(\"((defconst x 0) (defn test () (let ((x 1)) (labels ((f (x_0) (f x_0) (f x))) (f 1)))))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nconst x = 0\n\n\nclass _Labels extends Reference:\n\n    var x_0\n\n    func _init(x_0):\n        self.x_0 = x_0\n\n    func _fn_f_0(x_0_0):\n        _fn_f_0(x_0_0)\n        return _fn_f_0(x_0)\n\n\nstatic func test():\n    var x_0 = 1\n    var _locals = _Labels.new(x_0)\n    return _locals._fn_f_0(1)\n\"#);\n}\n\n#[test]\npub fn recursive_double_labels_test() {\n  let result0 = parse_compile_and_output_h(\"(labels ((f (x) (g x)) (g (x) (f x))) (g (f 1)))\");\n  assert_eq!(result0.0, \"var _locals = _Labels.new()\\nreturn _locals._fn_g_1(_locals._fn_f_0(1))\\n\");\n  assert_eq!(result0.1, r#\"class _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_0(x):\n        return _fn_g_1(x)\n\n    func _fn_g_1(x):\n        return _fn_f_0(x)\n\"#);\n}\n\n#[test]\npub fn recursive_single_with_extra_beginning_labels_test() {\n  let result0 = parse_compile_and_output_h(\"(labels ((f (x) (f (g x))) (g (x) 10)) (f 1))\");\n  assert_eq!(result0.0, \"var _locals = _Labels.new()\\nreturn _locals._fn_f_1(1)\\n\");\n  assert_eq!(result0.1, r#\"static func _flet(x):\n    return 10\n\n\nclass _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_1(x):\n        return _fn_f_1(__gdlisp_outer_class_0._flet(x))\n\n    var __gdlisp_outer_class_0 = load(\"res://TEST.gd\")\n\"#);\n}\n\n#[test]\npub fn recursive_single_with_extra_end_labels_test() {\n  let result0 = parse_compile_and_output_h(\"(labels ((f (x) (f x)) (g (x) (f x))) (g 1))\");\n  assert_eq!(result0.0, \"var _locals = _Labels.new()\\nvar _flet = _LambdaBlock.new(_locals)\\nreturn _flet.call_func(1)\\n\");\n  assert_eq!(result0.1, r#\"class _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_0(x):\n        return _fn_f_0(x)\n\n\nclass _LambdaBlock extends GDLisp.Function:\n\n    var _locals\n\n    func _init(_locals):\n        self._locals = _locals\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(x):\n        return _locals._fn_f_0(x)\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n}\n\n#[test]\npub fn recursive_single_indirect_labels_test() {\n  let result0 = parse_compile_and_output_h(\"(labels ((f (x) (f x))) (funcall (function f) 1))\");\n  assert_eq!(result0.0, \"var _locals = _Labels.new()\\nreturn GDLisp.funcall(_FunctionRefBlock.new(_locals), GDLisp.cons(1, null))\\n\");\n  assert_eq!(result0.1, r#\"class _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_0(x):\n        return _fn_f_0(x)\n\n\nclass _FunctionRefBlock extends GDLisp.Function:\n\n    var _locals\n\n    func _init(_locals):\n        self._locals = _locals\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(arg0):\n        return _locals._fn_f_0(arg0)\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n}\n\n#[test]\npub fn recursive_single_labels_decl_test_1() {\n  let result = parse_compile_decl(\"((defn test () (labels ((f (x) (f x))) (f 1))))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nclass _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_0(x):\n        return _fn_f_0(x)\n\n\nstatic func test():\n    var _locals = _Labels.new()\n    return _locals._fn_f_0(1)\n\"#);\n}\n\n#[test]\npub fn recursive_single_labels_decl_test_2() {\n  // Contrived introduction of a constant named _locals. The\n  // contextual name generator should detect this and work around it.\n  let result = parse_compile_decl(\"((defconst _locals 0) (defn test () (labels ((f (x) (f x))) (f 1))))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nconst _locals = 0\n\n\nclass _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_0(x):\n        return _fn_f_0(x)\n\n\nstatic func test():\n    var _locals_0 = _Labels.new()\n    return _locals_0._fn_f_0(1)\n\"#);\n}\n\n#[test]\npub fn contrived_nested_labels_test() {\n  // Contrived introduction of a constant named _locals. The\n  // contextual name generator should detect this and work around it.\n  let result = parse_compile_decl(r#\"((defn test ()\n                                        (labels ((f (x) (f x)))\n                                          (labels ((g (x) (f x) (g x)))\n                                            (f 1)\n                                            (g 2)))\n                                        (labels ((f (x) (f x)))\n                                          (labels ((g (x) (f x) (g x)))\n                                            (f 1)\n                                            (g 2)))))\"#);\n  assert_eq!(result, r#\"extends Reference\n\n\nclass _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_0(x):\n        return _fn_f_0(x)\n\n\nclass _Labels_0 extends Reference:\n\n    var _locals\n\n    func _init(_locals):\n        self._locals = _locals\n\n    func _fn_g_1(x):\n        _locals._fn_f_0(x)\n        return _fn_g_1(x)\n\n\nclass _Labels_1 extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_f_2(x):\n        return _fn_f_2(x)\n\n\nclass _Labels_2 extends Reference:\n\n    var _locals_1\n\n    func _init(_locals_1):\n        self._locals_1 = _locals_1\n\n    func _fn_g_3(x):\n        _locals_1._fn_f_2(x)\n        return _fn_g_3(x)\n\n\nstatic func test():\n    var _locals = _Labels.new()\n    var _locals_0 = _Labels_0.new(_locals)\n    _locals._fn_f_0(1)\n    _locals_0._fn_g_1(2)\n    var _locals_1 = _Labels_1.new()\n    var _locals_2 = _Labels_2.new(_locals_1)\n    _locals_1._fn_f_2(1)\n    return _locals_2._fn_g_3(2)\n\"#);\n}\n"
  },
  {
    "path": "tests/test/lambda_class_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\nuse gdlisp::ir::identifier::ClassNamespace;\n\nuse super::common::*;\n\n#[test]\npub fn basic_lambda_class_test() {\n\n  let result0 = parse_compile_and_output_h(\"(new Node)\");\n  assert_eq!(result0.0, \"return _AnonymousClass.new()\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init():\n        pass\n\"#);\n\n  let result1 = parse_compile_and_output_h(\"(new Node (defn foo (x) (+ x 1)))\");\n  assert_eq!(result1.0, \"return _AnonymousClass.new()\\n\");\n  assert_eq!(result1.1, r#\"class _AnonymousClass extends Node:\n\n    func _init():\n        pass\n\n    func foo(x):\n        return x + 1\n\"#);\n\n}\n\n#[test]\npub fn constructor_lambda_class_test() {\n\n  let result0 = parse_compile_and_output_h(\"(new Node (defvar x) (defn _init () (set self:x 1)) (defn foo () self:x))\");\n  assert_eq!(result0.0, \"return _AnonymousClass.new()\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init():\n        self.x = 1\n\n    var x\n\n    func foo():\n        return self.x\n\"#);\n\n}\n\n#[test]\npub fn constructor_args_lambda_class_test() {\n\n  let result0 = parse_compile_and_output_h(\"(new (Node 99) (defvar x) (defvar y) (defn _init (y) (set self:x 1) (set self:y y)) (defn foo () [self:x self:y]))\");\n  assert_eq!(result0.0, \"return _AnonymousClass.new(99)\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init(y):\n        self.x = 1\n        self.y = y\n\n    var x\n    var y\n\n    func foo():\n        return [self.x, self.y]\n\"#);\n\n}\n\n#[test]\npub fn constructor_init_args_lambda_class_test() {\n\n  let result0 = parse_compile_and_output_h(\"(new (Node 99) (defvar x) (defvar y) (defn _init (@x y) (set self:y y)) (defn foo () [self:x self:y]))\");\n  assert_eq!(result0.0, \"return _AnonymousClass.new(99)\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init(x_0, y):\n        self.x = x_0\n        self.y = y\n\n    var x\n    var y\n\n    func foo():\n        return [self.x, self.y]\n\"#);\n\n}\n\n#[test]\npub fn closure_lambda_class_test() {\n\n  let result0 = parse_compile_and_output_h(\"(let ((a 1)) (new Node (defn foo (x) (+ x a))))\");\n  assert_eq!(result0.0, \"var a = 1\\nreturn _AnonymousClass.new(a)\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init(a):\n        self.a = a\n\n    var a\n\n    func foo(x):\n        return x + a\n\"#);\n\n}\n\n#[test]\npub fn closure_and_args_lambda_class_test() {\n\n  let result0 = parse_compile_and_output_h(\"(let ((a 1)) (new (Node 77) (defvar z) (defn _init (z) (set self:z z)) (defn foo () (+ self:z a))))\");\n  assert_eq!(result0.0, \"var a = 1\\nreturn _AnonymousClass.new(a, 77)\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init(a, z):\n        self.a = a\n        self.z = z\n\n    var a\n    var z\n\n    func foo():\n        return self.z + a\n\"#);\n\n}\n\n#[test]\npub fn closure_and_init_args_lambda_class_test_1() {\n\n  let result0 = parse_compile_and_output_h(\"(let ((a 1)) (new (Node 77) (defvar z) (defvar t) (defn _init (z @t) (set self:z z)) (defn foo () (+ self:z a))))\");\n  assert_eq!(result0.0, \"var a = 1\\nreturn _AnonymousClass.new(a, 77)\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init(a, z, t_0):\n        self.a = a\n        self.t = t_0\n        self.z = z\n\n    var a\n    var z\n    var t\n\n    func foo():\n        return self.z + a\n\"#);\n\n}\n\n#[test]\npub fn closure_and_init_args_lambda_class_test_2() {\n\n  let result0 = parse_compile_and_output_h(\"(let ((a 1)) (new (Node 77) (defvar w (sys/split 1)) (defvar z) (defvar t) (defn _init (z @t) (set self:z z)) (defn foo () (+ self:z a))))\");\n  assert_eq!(result0.0, \"var a = 1\\nreturn _AnonymousClass.new(a, 77)\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init(a, z, t_0):\n        var _split = 1\n        self.w = _split\n        self.a = a\n        self.t = t_0\n        self.z = z\n\n    var a\n    var w\n    var z\n    var t\n\n    func foo():\n        return self.z + a\n\"#);\n\n}\n\n#[test]\npub fn closure_and_args_lambda_class_with_name_conflict_test() {\n\n  let result0 = parse_compile_and_output_h(\"(let ((z 1)) (new (Node 77) (defvar z) (defn _init (z) (set self:z z)) (defn foo () (+ self:z z))))\");\n  assert_eq!(result0.0, \"var z = 1\\nreturn _AnonymousClass.new(z, 77)\\n\");\n  assert_eq!(result0.1, r#\"class _AnonymousClass extends Node:\n\n    func _init(z_0, z_1):\n        self.z_0 = z_0\n        self.z = z_1\n\n    var z_0\n    var z\n\n    func foo():\n        return self.z + z_0\n\"#);\n\n}\n\n#[test]\npub fn closure_and_args_lambda_class_with_argument_name_conflict_test() {\n\n  let result0 = parse_compile_decl(\"((defn foo (z) (new (Node 77) (defvar z) (defn _init (z) (set self:z z)) (defn foo () (+ self:z z)))))\");\n  assert_eq!(result0, r#\"extends Reference\n\n\nclass _AnonymousClass extends Node:\n\n    func _init(z_0, z_1):\n        self.z_0 = z_0\n        self.z = z_1\n\n    var z_0\n    var z\n\n    func foo():\n        return self.z + z_0\n\n\nstatic func foo(z):\n    return _AnonymousClass.new(z, 77)\n\"#);\n\n}\n\n#[test]\npub fn capture_self_lambda_class_test() {\n  let result = parse_compile_decl(\"((defclass Foo (Reference) (defn f () (new Reference (defn g () self)))))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nclass _AnonymousClass extends Reference:\n\n    func _init():\n        pass\n\n    func g():\n        return self\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func f():\n        return _AnonymousClass.new()\n\"#);\n}\n\n#[test]\npub fn constructor_uses_outer_ref_lambda_class_test() {\n  let result = parse_compile_decl(\"((defn foo () 1) (defn bar () (new Reference (defn _init () (foo)))))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo():\n    return 1\n\n\nclass _AnonymousClass extends Reference:\n\n    func _init():\n        __gdlisp_outer_class_0.foo()\n\n    var __gdlisp_outer_class_0 = load(\"res://TEST.gd\")\n\n\nstatic func bar():\n    return _AnonymousClass.new()\n\"#);\n}\n\n#[test]\npub fn constructor_uses_outer_ref_lambda_class_test_run() {\n  let result = parse_and_run(\"((defn foo () (print 10)) (new Reference (defn _init () (foo))))\");\n  assert_eq!(result, \"\\n10\\n\");\n}\n\n#[test]\npub fn bad_static_in_lambda_class_test() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(new (Node) (defn example () static 1))\"),\n    Err(PError::from(GDError::new(GDErrorF::StaticOnLambdaClass(String::from(\"example\")), SourceOffset(13)))),\n  );\n}\n\n#[test]\npub fn bad_const_in_lambda_class_test() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(new (Node) (defconst FOO 1))\"),\n    Err(PError::from(GDError::new(GDErrorF::StaticOnLambdaClass(String::from(\"FOO\")), SourceOffset(13)))),\n  );\n}\n\n#[test]\npub fn lambda_class_running_test_1() {\n  let output = parse_and_run(\"((let ((x (new Reference (defn foo () 100)))) (print (x:foo))))\");\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn lambda_class_running_test_2() {\n  let output = parse_and_run(\"((let ((x (let ((y 99)) (new Reference (defn foo () y))))) (print (x:foo))))\");\n  assert_eq!(output, \"\\n99\\n\");\n}\n\n#[test]\npub fn lambda_class_running_test_3() {\n  let output = parse_and_run(r#\"\n    ((let ((inst (let ((x 768))\n                   (flet ((f () x))\n                     (new Reference (defn foo () (f)))))))\n      (print (inst:foo))))\n  \"#);\n  assert_eq!(output, \"\\n768\\n\");\n}\n\n#[test]\npub fn lambda_class_running_test_4() {\n  let output = parse_and_run(r#\"\n    ((let ((inst (let ((x 768))\n                   (labels ((f () (if #f (f) x)))\n                     (new Reference (defn foo () (funcall #'f)))))))\n      (print (inst:foo))))\n  \"#);\n  assert_eq!(output, \"\\n768\\n\");\n}\n\n#[test]\npub fn lambda_class_running_test_5() {\n  // Note: This one requires x to be wrapped in a Cell.\n  let output = parse_and_run(r#\"\n    ((let ((x 1))\n       (let ((foo (new Reference (defn increment () (set x (+ x 1))))))\n         (print x)\n         (print (foo:increment))\n         (print x))))\n  \"#);\n  assert_eq!(output, \"\\n1\\n2\\n2\\n\");\n}\n\n#[test]\npub fn lambda_class_running_test_6() {\n  let output = parse_and_run(\"((let ((x (let ((y 99)) (new (Reference 1) (defvar z) (defn _init (z) (set self:z z)) (defn foo () (+ self:z y)))))) (print (x:foo))))\");\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn lambda_class_access_static_fn_test_1() {\n  let output = parse_and_run(\"((defn foo () 100) (let ((x (new Reference (defn test () (foo))))) (print (x:test))))\");\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn lambda_class_access_static_fn_test_2() {\n  let output = parse_and_run(\"((defn foo () 100) (let ((x (new Reference (defvar x) (defn _init () (set self:x (foo)))))) (print x:x)))\");\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn lambda_class_super_test_1() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defn foo ()\n         99))\n     (let ((x (new Foo (defn foo () (+ (super:foo) 2)))))\n       (print (x:foo))))\n  \"#);\n  assert_eq!(output, \"\\n101\\n\");\n}\n\n#[test]\npub fn lambda_class_super_test_2() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defn foo ()\n         99))\n     (let ((x (new Foo (defn foo () (lambda () (+ 2 (super:foo)))))))\n       (print (funcall (x:foo)))))\n  \"#);\n  assert_eq!(output, \"\\n101\\n\");\n}\n\n#[test]\npub fn lambda_class_init_test() {\n  let output = parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defvar x)\n       (defn _init (@x)))\n     (let ((foo (new (Foo 100) (defn _init (x) (super x)))))\n       (print foo:x)))\n  \"#);\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\npub fn lambda_class_duplicate_constructor_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defn function-name () (new Node (defn _init ()) (defn _init ()))))\"),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateConstructor, SourceOffset(51)))),\n  );\n}\n\n#[test]\npub fn lambda_class_duplicate_name_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defn function-name () (new Node (defn foo ()) (defn foo ()))))\"),\n    Err(PError::from(GDError::new(GDErrorF::DuplicateName(ClassNamespace::Function, String::from(\"foo\")), SourceOffset(49)))),\n  );\n}\n\n#[test]\npub fn initialized_var_in_lambda_class_test() {\n  let result = parse_compile_and_output_h(\"(new (Node) (defvar example []))\");\n  assert_eq!(result.0, \"return _AnonymousClass.new()\\n\");\n  assert_eq!(result.1, r#\"class _AnonymousClass extends Node:\n\n    func _init():\n        pass\n\n    var example = []\n\"#);\n}\n"
  },
  {
    "path": "tests/test/lambda_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\n#[test]\nfn simple_lambda_running_test() {\n  let output = parse_and_run(\"((print (funcall (lambda (a) a) 1)))\");\n  assert_eq!(output, \"\\n1\\n\");\n}\n\n#[test]\nfn closure_lambda_running_test() {\n  let output = parse_and_run(r#\"\n  ((let ((a (let ((x 99))\n              (let ((f (lambda (z) x)))\n                f))))\n    (print (funcall a ()))))\n  \"#);\n  assert_eq!(output, \"\\n99\\n\");\n}\n\n#[test]\nfn modifying_closure_test() {\n  let output = parse_and_run(r#\"\n  ((let ((a 0))\n    (funcall (lambda () (set a 1)))\n    (print a)))\n  \"#);\n  assert_eq!(output, \"\\n1\\n\");\n}\n\n#[test]\nfn modifying_closure_inside_lambda_test() {\n  // Basically, modifying_closure_test stuffed inside an extra lambda layer and then called.\n  let output = parse_and_run(r#\"\n  ((let ((f (lambda (x) (funcall (lambda () (set x 1))) x)))\n    (print (funcall f 0))))\n  \"#);\n  assert_eq!(output, \"\\n1\\n\");\n}\n\n#[test]\nfn lambda_access_outer_function_test() {\n  let output = parse_and_run(r#\"\n  ((defn foo () 100)\n   (let ((f (lambda () (foo))))\n     (print (funcall f))))\n  \"#);\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\nfn simple_function_ref_run_test() {\n  let output = parse_and_run(r#\"\n  ((defn foo () 100)\n   (let ((f #'foo))\n     (print (funcall f))))\n  \"#);\n  assert_eq!(output, \"\\n100\\n\");\n}\n\n#[test]\nfn nonexistent_var_in_lambda_test() {\n  let output = parse_compile_and_output_err(r#\"(lambda () x)\"#);\n  assert_eq!(output,\n             Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"x\")), SourceOffset(11)))));\n}\n\n#[test]\nfn nonexistent_fn_in_lambda_test() {\n  let output = parse_compile_and_output_err(r#\"(lambda () (abc))\"#);\n  assert_eq!(output,\n             Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"abc\")), SourceOffset(11)))));\n}\n\n#[test]\npub fn basic_lambda_test() {\n\n  let result0 = parse_compile_and_output_h(\"(lambda ())\");\n  assert_eq!(result0.0, \"return _LambdaBlock.new()\\n\");\n  assert_eq!(result0.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return null\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n  let result1 = parse_compile_and_output_h(\"(lambda (a) a)\");\n  assert_eq!(result1.0, \"return _LambdaBlock.new()\\n\");\n  assert_eq!(result1.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(a):\n        return a\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n  let result2 = parse_compile_and_output_h(\"(progn (lambda (a) a) 1)\");\n  assert_eq!(result2.0, \"return 1\\n\");\n  assert_eq!(result2.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(a):\n        return a\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n}\n\n#[test]\npub fn closure_lambda_test() {\n\n  let result0 = parse_compile_and_output_h(\"(let (a) (lambda () a))\");\n  assert_eq!(result0.0, \"var a = null\\nreturn _LambdaBlock.new(a)\\n\");\n  assert_eq!(result0.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    var a\n\n    func _init(a):\n        self.a = a\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return a\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n}\n\n#[test]\npub fn non_closure_lambda_test() {\n\n  let result0 = parse_compile_and_output_h(\"(let (a) (lambda () (let (a) a)))\");\n  assert_eq!(result0.0, \"var a = null\\nreturn _LambdaBlock.new()\\n\");\n  assert_eq!(result0.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        var a = null\n        return a\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n}\n\n#[test]\npub fn basic_funcall_test() {\n  assert_eq!(parse_compile_and_output(\"(funcall 1)\"), \"return GDLisp.funcall(1, null)\\n\");\n  assert_eq!(parse_compile_and_output(\"(progn (funcall 1) 2)\"), \"GDLisp.funcall(1, null)\\nreturn 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(funcall 1 2 3)\"), \"return GDLisp.funcall(1, GDLisp.cons(2, GDLisp.cons(3, null)))\\n\");\n}\n\n#[test]\npub fn funcall_lambda_test() {\n\n  let result0 = parse_compile_and_output_h(\"(let ((f (lambda (a) a))) (funcall f 100))\");\n  assert_eq!(result0.0, \"var f = _LambdaBlock.new()\\nreturn GDLisp.funcall(f, GDLisp.cons(100, null))\\n\");\n  assert_eq!(result0.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(a):\n        return a\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n}\n\n#[test]\npub fn function_ref_test() {\n  let result0 = parse_compile_and_output_h(\"(function foo1)\");\n  assert_eq!(result0.0, \"return _FunctionRefBlock.new()\\n\");\n  assert_eq!(result0.1, r#\"class _FunctionRefBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(arg0):\n        return load(\"res://TEST.gd\").foo1(arg0)\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n  let result1 = parse_compile_and_output_h(\"#'foo1\");\n  assert_eq!(result1.0, \"return _FunctionRefBlock.new()\\n\");\n  assert_eq!(result1.1, r#\"class _FunctionRefBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 1\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func(arg0):\n        return load(\"res://TEST.gd\").foo1(arg0)\n\n    func call_funcv(args):\n        var required_0 = null\n        if args == null:\n            push_error(\"Not enough arguments\")\n        else:\n            required_0 = args.car\n            args = args.cdr\n        if args == null:\n            return call_func(required_0)\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n}\n\n#[test]\npub fn outer_class_in_nested_lambda_test() {\n  let result = parse_and_run(r#\"\n    ((defn example () 3)\n     (defn foo ()\n       (lambda () (lambda () (example))))\n     (let ((fn (funcall (foo))))\n       (print (funcall fn))))\n  \"#);\n  assert_eq!(result, \"\\n3\\n\");\n}\n\n#[test]\npub fn outer_class_in_nested_lambda_class_test_1() {\n  let result = parse_and_run(r#\"\n    ((defn example () 3)\n     (defn foo ()\n       (new (Reference) (defn bar () (lambda () (example)))))\n     (let ((x (foo)))\n       (print (funcall (x:bar)))))\n  \"#);\n  assert_eq!(result, \"\\n3\\n\");\n}\n\n#[test]\npub fn outer_class_in_nested_lambda_class_test_2() {\n  let result = parse_and_run(r#\"\n    ((defn example () 3)\n     (defn foo ()\n       (lambda () (new (Reference) (defn bar () (example)))))\n     (let ((x (funcall (foo))))\n       (print (x:bar))))\n  \"#);\n  assert_eq!(result, \"\\n3\\n\");\n}\n\n#[test]\npub fn outer_class_in_nested_lambda_class_test_3() {\n  let result = parse_and_run(r#\"\n    ((defn example () 3)\n     (defn foo ()\n       (new (Reference) (defn bar () (new Reference (defn bar () (example))))))\n     (let ((x (foo)))\n       (print ((x:bar):bar))))\n  \"#);\n  assert_eq!(result, \"\\n3\\n\");\n}\n\n#[test]\npub fn several_nested_lambdas_test() {\n  // This is a regression test for issue #139.\n  let result = parse_and_run(r#\"\n    ((defn foo1 () (lambda () (lambda () (lambda ()))))\n     (defn foo2 () (lambda () (lambda ())))\n     (defn foo3 () (lambda () (lambda () (lambda ()))))\n     (defn foo4 () (lambda ()))\n     (print 1))\n  \"#);\n  assert_eq!(result, \"\\n1\\n\");\n}\n\n#[test]\npub fn several_nested_lambda_classes_test() {\n  // This is a regression test for issue #139.\n  let result = parse_and_run(r#\"\n    ((defn foo1 () (new Reference (defn foo () (new Reference))))\n     (defn foo2 () (new Reference))\n     (defn foo3 () (new Reference))\n     (print 1))\n  \"#);\n  assert_eq!(result, \"\\n1\\n\");\n}\n\n#[test]\npub fn several_nested_labels_test() {\n  // This is a regression test for issue #139.\n  let result = parse_and_run(r#\"\n    ((defn foo1 () (labels ((f (x) (f x) (labels ((g (x) (g x))) (f x))))))\n     (defn foo2 () (labels ((f (x) (f x) (labels ((g (x) (g x))) (f x))))))\n     (print 1))\n  \"#);\n  assert_eq!(result, \"\\n1\\n\");\n}\n"
  },
  {
    "path": "tests/test/lazy_val_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse super::common::*;\n\n#[test]\npub fn simple_lazy_test() {\n  assert_eq!(parse_compile_decl(\"((deflazy x 100))\"),\n             r#\"extends Reference\n\n\nstatic func _lazy_0():\n    var _this_file_1 = load(\"res://TEST.gd\")\n    var _cond = null\n    if _this_file_1.has_meta(\"__gdlisp_Lazy__G_3\"):\n        _cond = _this_file_1.get_meta(\"__gdlisp_Lazy__G_3\")\n    else:\n        if true:\n            var _value_2 = 100\n            _this_file_1.set_meta(\"__gdlisp_Lazy__G_3\", _value_2)\n            _cond = _value_2\n        else:\n            _cond = null\n    return _cond\n\n\nstatic func __gdlisp_SymbolMacroFunction_x():\n    return GDLisp.cons(GDLisp.cons(GDLisp.intern(\"access-slot\"), GDLisp.cons(GDLisp.cons(GDLisp.intern(\"contextual-load\"), GDLisp.cons(\"res://TEST.gd\", null)), GDLisp.cons(GDLisp.intern(\"_lazy_0\"), null))), null)\n\"#);\n}\n\n#[test]\npub fn simple_private_lazy_test() {\n  // The private modifier should parse and work correctly, but it\n  // doesn't change the output compared to the simple_lazy_test case.\n  assert_eq!(parse_compile_decl(\"((deflazy x 100 private))\"),\n             r#\"extends Reference\n\n\nstatic func _lazy_0():\n    var _this_file_1 = load(\"res://TEST.gd\")\n    var _cond = null\n    if _this_file_1.has_meta(\"__gdlisp_Lazy__G_3\"):\n        _cond = _this_file_1.get_meta(\"__gdlisp_Lazy__G_3\")\n    else:\n        if true:\n            var _value_2 = 100\n            _this_file_1.set_meta(\"__gdlisp_Lazy__G_3\", _value_2)\n            _cond = _value_2\n        else:\n            _cond = null\n    return _cond\n\n\nstatic func __gdlisp_SymbolMacroFunction_x():\n    return GDLisp.cons(GDLisp.cons(GDLisp.intern(\"access-slot\"), GDLisp.cons(GDLisp.cons(GDLisp.intern(\"contextual-load\"), GDLisp.cons(\"res://TEST.gd\", null)), GDLisp.cons(GDLisp.intern(\"_lazy_0\"), null))), null)\n\"#);\n}\n\n#[test]\npub fn simple_lazy_run_test_1() {\n  assert_eq!(parse_and_run(r#\"\n    ((deflazy x 100)\n     (print x))\n  \"#), \"\\n100\\n\");\n}\n\n#[test]\npub fn simple_lazy_run_test_2() {\n  // The 3 should only print once\n  assert_eq!(parse_and_run(r#\"\n    ((deflazy x (progn (print 3) 100))\n     (print x)\n     (print x))\n  \"#), \"\\n3\\n100\\n100\\n\");\n}\n\n// TODO Test lazy vals getting reinitialized after a file is unloaded and reloaded\n"
  },
  {
    "path": "tests/test/let_var_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::{parse_compile_and_output, parse_compile_and_output_h};\n\n#[test]\npub fn let_tests() {\n  assert_eq!(parse_compile_and_output(\"(let () 1)\"), \"return 1\\n\");\n  assert_eq!(parse_compile_and_output(\"(let (a) 1)\"), \"var a = null\\nreturn 1\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a)) 1)\"), \"var a = null\\nreturn 1\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) (foo1 a))\"), \"var a = 1\\nreturn foo1(a)\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1) (b 2)) (foo2 a b))\"), \"var a = 1\\nvar b = 2\\nreturn foo2(a, b)\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a (foo) (bar))) (foo1 a))\"), \"foo()\\nvar a = bar()\\nreturn foo1(a)\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a) b) 1)\"), \"var a = null\\nvar b = null\\nreturn 1\\n\");\n  assert_eq!(parse_compile_and_output(\"(let (a (b)) 1)\"), \"var a = null\\nvar b = null\\nreturn 1\\n\");\n}\n\n#[test]\npub fn let_name_trans_tests() {\n  assert_eq!(parse_compile_and_output(\"(let ((a-b 1)) a-b)\"), \"var a_b = 1\\nreturn a_b\\n\");\n}\n\n#[test]\npub fn var_shadowing() {\n  assert_eq!(parse_compile_and_output(\"(let ((a)) (let ((a a)) a))\"), \"var a = null\\nvar a_0 = a\\nreturn a_0\\n\");\n}\n\n#[test]\npub fn inline_if_in_let_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a (if (foo) (bar) (foo)))) a)\"), \"var _cond = null\\nif foo():\\n    _cond = bar()\\nelse:\\n    if true:\\n        _cond = foo()\\n    else:\\n        _cond = null\\nvar a = _cond\\nreturn a\\n\");\n}\n\n#[test]\npub fn closure_var_test() {\n  let result0 = parse_compile_and_output_h(\"(lambda () foobar)\");\n  assert_eq!(result0.0, \"return _LambdaBlock.new(foobar)\\n\");\n  assert_eq!(result0.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    var foobar\n\n    func _init(foobar):\n        self.foobar = foobar\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return foobar\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n\n\n  let result1 = parse_compile_and_output_h(\"(lambda () glob)\");\n  assert_eq!(result1.0, \"return _LambdaBlock.new()\\n\");\n  assert_eq!(result1.1, r#\"class _LambdaBlock extends GDLisp.Function:\n\n    func _init():\n        self.__gdlisp_required = 0\n        self.__gdlisp_optional = 0\n        self.__gdlisp_rest = 0\n\n    func call_func():\n        return glob\n\n    func call_funcv(args):\n        if args == null:\n            return call_func()\n        else:\n            push_error(\"Too many arguments\")\n\"#);\n}\n\n#[test]\npub fn let_star_test_1() {\n  assert_eq!(parse_compile_and_output(\"(let* ((a 1) (b a)) b)\"), \"var a = 1\\nvar b = a\\nreturn b\\n\");\n}\n\n#[test]\npub fn let_star_test_2() {\n  assert_eq!(parse_compile_and_output(\"(let* ((a 1) (a a) (a a)) a)\"),\n             \"var a = 1\\nvar a_0 = a\\nvar a_1 = a_0\\nreturn a_1\\n\");\n}\n"
  },
  {
    "path": "tests/test/list_operator_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\nuse gdlisp::pipeline::error::PError;\n\n#[test]\npub fn simple_length_test() {\n  assert_eq!(parse_compile_and_output(\"(len ())\"), \"return GDLisp._len(null)\\n\");\n}\n\n#[test]\npub fn list_test() {\n  assert_eq!(parse_compile_and_output(\"(list 1 2 3)\"), \"return GDLisp.cons(1, GDLisp.cons(2, GDLisp.cons(3, null)))\\n\");\n}\n\n#[test]\npub fn array_function_test() {\n  assert_eq!(parse_compile_and_output(\"(array 1 2 3)\"), \"return [1, 2, 3]\\n\");\n}\n\n#[test]\npub fn array_test() {\n  assert_eq!(parse_compile_and_output(\"[]\"), \"return []\\n\");\n  assert_eq!(parse_compile_and_output(\"[1 2 3]\"), \"return [1, 2, 3]\\n\");\n  assert_eq!(parse_compile_and_output(\"[2]\"), \"return [2]\\n\");\n  assert_eq!(parse_compile_and_output(\"[(foo)]\"), \"return [foo()]\\n\");\n  assert_eq!(parse_compile_and_output(\"(progn [(foo)] [2])\"), \"[foo()]\\nreturn [2]\\n\");\n  assert_eq!(parse_compile_and_output(\"[(if 1 2 3)]\"), \"var _cond = null\\nif 1:\\n    _cond = 2\\nelse:\\n    if true:\\n        _cond = 3\\n    else:\\n        _cond = null\\nreturn [_cond]\\n\");\n}\n\n#[test]\npub fn reverse_test_1() {\n  let result = parse_and_run(r#\"\n    ((print (array/reverse [1 2 3 4])))\n  \"#);\n  assert_eq!(result, \"\\n[4, 3, 2, 1]\\n\");\n}\n\n#[test]\npub fn reverse_test_2() {\n  let result = parse_and_run(r#\"\n    ((let ((list (list/reverse '(1 2 3 4))))\n       (print (len list))\n       (print (list/elt list 0))\n       (print (list/elt list 1))\n       (print (list/elt list 2))\n       (print (list/elt list 3))))\n  \"#);\n  assert_eq!(result, \"\\n4\\n4\\n3\\n2\\n1\\n\");\n}\n\n#[test]\npub fn map_test_1() {\n  let result = parse_and_run(r#\"\n    ((print (array/map (lambda (x) (+ x 1)) [4 5 6])))\n  \"#);\n  assert_eq!(result, \"\\n[5, 6, 7]\\n\");\n}\n\n#[test]\npub fn map_test_2() {\n  let result = parse_and_run(r#\"\n    ((let ((x (list/map (lambda (x) (+ x 1)) '(4 5 6))))\n       (print (len x))\n       (print x:car)\n       (print x:cdr:car)\n       (print x:cdr:cdr:car)))\n  \"#);\n  assert_eq!(result, \"\\n3\\n5\\n6\\n7\\n\");\n}\n\n#[test]\npub fn filter_test_1() {\n  let result = parse_and_run(r#\"\n    ((print (array/filter (lambda (x) (= (mod x 2) 0)) [1 2 3 4 5 6])))\n  \"#);\n  assert_eq!(result, \"\\n[2, 4, 6]\\n\");\n}\n\n#[test]\npub fn filter_test_2() {\n  let result = parse_and_run(r#\"\n    ((let ((x (list/filter (lambda (x) (= (mod x 2) 0)) '(1 2 3 4 5 6))))\n       (print (len x))\n       (print x:car)\n       (print x:cdr:car)\n       (print x:cdr:cdr:car)))\n  \"#);\n  assert_eq!(result, \"\\n3\\n2\\n4\\n6\\n\");\n}\n\n#[test]\npub fn filter_test_3() {\n  let result = parse_and_run(r#\"\n    ((let ((x (list/filter (lambda (x) #f) '(1 2 3 4 5 6))))\n       (print (len x))))\n  \"#);\n  assert_eq!(result, \"\\n0\\n\");\n}\n\n#[test]\npub fn length_test() {\n  assert_eq!(parse_and_run(\"((print (len nil)))\"), \"\\n0\\n\");\n  assert_eq!(parse_and_run(\"((print (len '(1 2 3 4))))\"), \"\\n4\\n\");\n  assert_eq!(parse_and_run(\"((print (len [1 2 3 4])))\"), \"\\n4\\n\");\n}\n\n#[test]\npub fn append_test_1() {\n  let result = parse_and_run(r#\"\n    ((print (list->array (append))))\n  \"#);\n  assert_eq!(result, \"\\n[]\\n\");\n}\n\n#[test]\npub fn append_test_2() {\n  let result = parse_and_run(r#\"\n    ((print (list->array (append '(1 2 3 4)))))\n  \"#);\n  assert_eq!(result, \"\\n[1, 2, 3, 4]\\n\");\n}\n\n#[test]\npub fn append_test_3() {\n  let result = parse_and_run(r#\"\n    ((print (list->array (append '(1 2 3 4) '(5 6 7 8)))))\n  \"#);\n  assert_eq!(result, \"\\n[1, 2, 3, 4, 5, 6, 7, 8]\\n\");\n}\n\n#[test]\npub fn append_test_4() {\n  let result = parse_and_run(r#\"\n    ((print (list->array (append '(1 2 3 4) () '(5 6 7 8) () ()))))\n  \"#);\n  assert_eq!(result, \"\\n[1, 2, 3, 4, 5, 6, 7, 8]\\n\");\n}\n\n#[test]\npub fn array_running_test() {\n  let result = parse_and_run(r#\"\n    ((print (array 1 2 3)))\n  \"#);\n  assert_eq!(result, \"\\n[1, 2, 3]\\n\");\n}\n\n#[test]\npub fn array_syntax_running_test() {\n  let result = parse_and_run(r#\"\n    ((print [1 2 3]))\n  \"#);\n  assert_eq!(result, \"\\n[1, 2, 3]\\n\");\n}\n\n#[test]\npub fn array_running_test_indirect() {\n  let result = parse_and_run(r#\"\n    ((print (funcall #'array 1 2 3)))\n  \"#);\n  assert_eq!(result, \"\\n[1, 2, 3]\\n\");\n}\n\n#[test]\npub fn dict_running_test() {\n  let result = parse_and_run(r#\"\n    ((print (dict 1 2 3 4)))\n  \"#);\n  assert_eq!(result, \"\\n{1:2, 3:4}\\n\");\n}\n\n#[test]\npub fn dict_syntax_running_test() {\n  let result = parse_and_run(r#\"\n    ((print {1 2 3 4}))\n  \"#);\n  assert_eq!(result, \"\\n{1:2, 3:4}\\n\");\n}\n\n#[test]\npub fn dict_running_test_indirect() {\n  let result = parse_and_run(r#\"\n    ((print (funcall #'dict 1 2 3 4)))\n  \"#);\n  assert_eq!(result, \"\\n{1:2, 3:4}\\n\");\n}\n\n#[test]\npub fn dict_syntax_odd_error_test() {\n  let result = parse_compile_and_output_err(r#\"\n    ((print {1}))\n  \"#);\n  assert!(matches!(result, Err(PError::ParseError(_))));\n}\n\n#[test]\npub fn list_tail_test_1() {\n  let result = parse_and_run(r#\"\n    ((print (list->array (list/tail '(1 2 3 4) 0))))\n  \"#);\n  assert_eq!(result, \"\\n[1, 2, 3, 4]\\n\");\n}\n\n#[test]\npub fn list_tail_test_2() {\n  let result = parse_and_run(r#\"\n    ((print (list->array (list/tail '(1 2 3 4) 2))))\n  \"#);\n  assert_eq!(result, \"\\n[3, 4]\\n\");\n}\n\n#[test]\npub fn list_elt_test_1() {\n  let result = parse_and_run(r#\"\n    ((print (list/elt '(1 2 3 4) 0)))\n  \"#);\n  assert_eq!(result, \"\\n1\\n\");\n}\n\n#[test]\npub fn list_elt_test_2() {\n  let result = parse_and_run(r#\"\n    ((print (list/elt '(1 2 3 4) 2)))\n  \"#);\n  assert_eq!(result, \"\\n3\\n\");\n}\n\n#[test]\npub fn list_foreach_test() {\n  let result = parse_and_run(r#\"\n    ((let ((foo '(1 2 3 4 5))\n           (total 0))\n       (list/for x foo\n         (set total (+ total x)))\n       (print total)))\n  \"#);\n  assert_eq!(result, \"\\n15\\n\");\n}\n\n#[test]\npub fn fold_test_1() {\n  let result = parse_and_run(r#\"\n    ((let ((foo '(1 2 3 4 5)))\n       (print (list/fold #'+ foo))\n       (print (list/fold #'+ foo 100))))\n  \"#);\n  assert_eq!(result, \"\\n15\\n115\\n\");\n}\n\n#[test]\npub fn fold_test_2() {\n  let result = parse_and_run(r#\"\n    ((let ((foo [1 2 3 4 5]))\n       (print (array/fold #'+ foo))\n       (print (array/fold #'+ foo 100))))\n  \"#);\n  assert_eq!(result, \"\\n15\\n115\\n\");\n}\n"
  },
  {
    "path": "tests/test/macro_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse gdlisp::ir::modifier::{ParseError as ModifierParseError, ParseErrorF as ModifierParseErrorF};\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::compile::args::Expecting;\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse super::common::*;\n\n#[test]\npub fn simple_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo (x) x))\"), r#\"extends Reference\n\n\nstatic func foo(x):\n    return x\n\"#)\n}\n\n#[test]\npub fn constant_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo () 10) (defn bar () (foo)))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return 10\n\n\nstatic func bar():\n    return 10\n\"#);\n}\n\n#[test]\npub fn symbol_macro_test() {\n  assert_eq!(parse_compile_decl(\"((define-symbol-macro foo 10) (defn bar () foo))\"), r#\"extends Reference\n\n\nstatic func __gdlisp_SymbolMacroFunction_foo():\n    return 10\n\n\nstatic func bar():\n    return 10\n\"#);\n}\n\n#[test]\npub fn builtin_symbol_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defn run () [PI SPKEY (let ((PI 1)) PI)]))\"), r#\"extends Reference\n\n\nstatic func run():\n    var _PI = 1\n    return [PI, SPKEY, _PI]\n\"#);\n}\n\n#[test]\npub fn arithmetic_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo (x) (+ x 100)) (defn run () (foo 9)))\"), r#\"extends Reference\n\n\nstatic func foo(x):\n    return x + 100\n\n\nstatic func run():\n    return 109\n\"#);\n}\n\n#[test]\npub fn quote_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro my-quote (x) (cons 'quote (cons x ()))) (defn run () (my-quote abc)))\"), r#\"extends Reference\n\n\nstatic func my_quote(x):\n    return GDLisp.cons(GDLisp.intern(\"quote\"), GDLisp.cons(x, null))\n\n\nstatic func run():\n    return GDLisp.intern(\"abc\")\n\"#);\n}\n\n#[test]\npub fn macro_in_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo (x) (+ x 1)) (defmacro bar () (foo 0)) (defn run () (bar)))\"), r#\"extends Reference\n\n\nstatic func foo(x):\n    return x + 1\n\n\nstatic func bar():\n    return 1\n\n\nstatic func run():\n    return 1\n\"#);\n}\n\n#[test]\npub fn macro_from_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo (x) (+ x 1)) (defmacro bar (x) (cons 'foo (cons x ()))) (defn baz () (bar 2)))\"), r#\"extends Reference\n\n\nstatic func foo(x):\n    return x + 1\n\n\nstatic func bar(x):\n    return GDLisp.cons(GDLisp.intern(\"foo\"), GDLisp.cons(x, null))\n\n\nstatic func baz():\n    return 3\n\"#);\n}\n\n#[test]\npub fn bad_args_macro_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defmacro foo (x) (+ x 1)) (defmacro bar (x) (cons 'foo (cons x ()))) (defn run () (bar)))\"),\n    Err(PError::from(GDError::new(GDErrorF::WrongNumberArgs(String::from(\"bar\"), Expecting::exactly(1), 0), SourceOffset(84)))),\n  );\n}\n\n#[test]\npub fn rest_args_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo (&rest x) x) (defn bar () (foo + 1 2)))\"), r#\"extends Reference\n\n\nstatic func foo(x):\n    return x\n\n\nstatic func bar():\n    return 1 + 2\n\"#);\n}\n\n#[test]\npub fn optional_args_macro_test_1() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo (&opt x) x) (defn bar () (foo 1)))\"), r#\"extends Reference\n\n\nstatic func foo(x):\n    return x\n\n\nstatic func bar():\n    return 1\n\"#);\n}\n\n#[test]\npub fn optional_args_macro_test_2() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo (&opt x) x) (defn bar () (foo)))\"), r#\"extends Reference\n\n\nstatic func foo(x):\n    return x\n\n\nstatic func bar():\n    return null\n\"#);\n}\n\n#[test]\npub fn to_decl_macro_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo () '(defn bar () ())) (foo))\"), r#\"extends Reference\n\n\nstatic func foo():\n    return GDLisp.cons(GDLisp.intern(\"defn\"), GDLisp.cons(GDLisp.intern(\"bar\"), GDLisp.cons(null, GDLisp.cons(null, null))))\n\n\nstatic func bar():\n    return null\n\"#);\n}\n\n#[test]\npub fn to_progn_macro_test() {\n  assert_eq!(parse_and_run(\"((defmacro foo () '(progn (defmacro bar (x) x) (print (bar 3)))) (foo))\"), \"\\n3\\n\");\n}\n\n#[test]\npub fn symbol_macro_run_test() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo (progn 2)) (print foo))\"), \"\\n2\\n\");\n}\n\n#[test]\npub fn symbol_macro_shadowing_test() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo (progn 2)) (print foo) (print (let ((foo 3)) foo)) (print foo))\"), \"\\n2\\n3\\n2\\n\");\n}\n\n#[test]\npub fn symbol_macro_to_macro_test_1() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo '(bar)) (defmacro bar () 9) (print foo))\"), \"\\n9\\n\");\n}\n\n#[test]\npub fn symbol_macro_to_macro_test_2() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo '(bar)) (defmacro bar () 9) (print (flet ((bar () 10)) foo)))\"), \"\\n10\\n\");\n}\n\n#[test]\npub fn symbol_macro_to_macro_test_3() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo '(bar)) (defmacro bar () 9) (print (macrolet ((bar () 10)) foo)))\"), \"\\n10\\n\");\n}\n\n#[test]\npub fn symbol_macro_to_symbol_macro_test_1() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo 'bar) (define-symbol-macro bar 9) (print foo))\"), \"\\n9\\n\");\n}\n\n#[test]\npub fn symbol_macro_to_symbol_macro_test_2() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo 'bar) (define-symbol-macro bar 9) (print (let ((bar 10)) foo)))\"), \"\\n10\\n\");\n}\n\n#[test]\npub fn symbol_macro_to_symbol_macro_test_3() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro foo 'bar) (define-symbol-macro bar 9) (print (flet ((bar () 10)) foo)))\"), \"\\n9\\n\");\n}\n\n#[test]\npub fn macro_to_symbol_macro_test_1() {\n  assert_eq!(parse_and_run(\"((defmacro foo () 'bar) (define-symbol-macro bar 9) (print (foo)))\"), \"\\n9\\n\");\n}\n\n#[test]\npub fn macro_to_symbol_macro_test_2() {\n  assert_eq!(parse_and_run(\"((defmacro foo () 'bar) (define-symbol-macro bar 9) (print (let ((bar 10)) (foo))))\"), \"\\n10\\n\");\n}\n\n#[test]\npub fn macro_uses_symbol_macro_test() {\n  assert_eq!(parse_and_run(\"((define-symbol-macro bar 9) (defmacro foo () bar) (print (let ((bar 10)) (foo))))\"), \"\\n9\\n\");\n}\n\n#[test]\npub fn symbol_macrolet_out_of_scope_test() {\n  assert_eq!(parse_and_run(\"((defconst foo 3) (defn bar () foo) (symbol-macrolet ((foo 2)) (print foo) (print (bar))))\"), \"\\n2\\n3\\n\");\n}\n\n#[test]\npub fn macrolet_basic_test() {\n  let result = parse_compile_and_output(\"(macrolet ((foo () 100)) (foo))\");\n  assert_eq!(result, \"return 100\\n\");\n}\n\n#[test]\npub fn symbol_macrolet_basic_test() {\n  let result = parse_compile_and_output(\"(symbol-macrolet ((foo 100)) foo)\");\n  assert_eq!(result, \"return 100\\n\");\n}\n\n#[test]\npub fn macrolet_shadowing_test() {\n  let result = parse_compile_and_output(\"(macrolet ((foo () 100)) [(foo) (macrolet ((foo () 99)) (foo)) (foo)])\");\n  assert_eq!(result, \"return [100, 99, 100]\\n\");\n}\n\n#[test]\npub fn symbol_macrolet_shadowing_test() {\n  let result = parse_compile_and_output(\"(symbol-macrolet ((foo 100)) [foo (symbol-macrolet ((foo 99)) foo) foo])\");\n  assert_eq!(result, \"return [100, 99, 100]\\n\");\n}\n\n#[test]\npub fn macrolet_global_shadowing_test() {\n  let result = parse_compile_decl(\"((defmacro foo () 100) (defn run () [(foo) (macrolet ((foo () 99)) (foo)) (foo)]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo():\n    return 100\n\n\nstatic func run():\n    return [100, 99, 100]\n\"#);\n}\n\n#[test]\npub fn symbol_macrolet_global_shadowing_test() {\n  let result = parse_compile_decl(\"((defconst foo 100) (defn run () [foo (symbol-macrolet ((foo 99)) foo) foo]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nconst foo = 100\n\n\nstatic func run():\n    return [foo, 99, foo]\n\"#);\n}\n\n#[test]\npub fn symbol_macro_shared_name_with_function_test() {\n  assert_eq!(parse_and_run(r#\"((define-symbol-macro foo 100)\n                               (defn foo () 101)\n                               (print foo)\n                               (print (foo)))\"#),\n             \"\\n100\\n101\\n\");\n}\n\n#[test]\npub fn symbol_macro_shared_name_with_macro_test() {\n  assert_eq!(parse_and_run(r#\"((define-symbol-macro foo 100)\n                               (defmacro foo () 101)\n                               (print foo)\n                               (print (foo)))\"#),\n             \"\\n100\\n101\\n\");\n}\n\n#[test]\npub fn macrolet_global_function_shadowing_test_1() {\n  let result = parse_compile_decl(\"((defn foo () 100) (defn run () [(foo) (macrolet ((foo () 99)) (foo)) (foo)]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo():\n    return 100\n\n\nstatic func run():\n    return [foo(), 99, foo()]\n\"#);\n}\n\n#[test]\npub fn macrolet_global_function_shadowing_test_2() {\n  let result = parse_compile_decl(\"((defn foo () 100) (defn run () [(foo) (macrolet ((foo () (foo))) (foo)) (foo)]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo():\n    return 100\n\n\nstatic func run():\n    return [foo(), 100, foo()]\n\"#);\n}\n\n#[test]\npub fn symbol_macrolet_global_function_shadowing_test_1() {\n  let result = parse_compile_decl(\"((defconst foo 100) (defn run () [foo (symbol-macrolet ((foo 99)) foo) foo]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nconst foo = 100\n\n\nstatic func run():\n    return [foo, 99, foo]\n\"#);\n}\n\n#[test]\npub fn symbol_macrolet_global_function_shadowing_test_2() {\n  let result = parse_compile_decl(\"((defconst foo 100) (defn run () [foo (symbol-macrolet ((foo foo)) foo) foo]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nconst foo = 100\n\n\nstatic func run():\n    return [foo, 100, foo]\n\"#);\n}\n\n#[test]\npub fn symbol_macrolet_global_function_shadowing_test_3() {\n  let result = parse_compile_decl(\"((define-symbol-macro foo 100) (defn run () [foo (symbol-macrolet ((foo foo)) foo) foo]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func __gdlisp_SymbolMacroFunction_foo():\n    return 100\n\n\nstatic func run():\n    return [100, 100, 100]\n\"#);\n}\n\n#[test]\npub fn symbol_macrolet_global_function_shadowing_test_4() {\n  let result = parse_compile_decl(\"((define-symbol-macro foo 100) (defn run () [foo (symbol-macrolet ((foo 99)) foo) foo]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func __gdlisp_SymbolMacroFunction_foo():\n    return 100\n\n\nstatic func run():\n    return [100, 99, 100]\n\"#);\n}\n\n#[test]\npub fn flet_global_macro_shadowing_test() {\n  let result = parse_compile_decl(\"((defmacro foo () 100) (defn run () [(foo) (flet ((foo () 99)) (foo)) (foo)]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo():\n    return 100\n\n\nstatic func _flet():\n    return 99\n\n\nstatic func run():\n    return [100, _flet(), 100]\n\"#);\n}\n\n#[test]\npub fn closure_macrolet_test_1() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(let ((a 1)) (macrolet ((foo () a)) (foo)))\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"a\")), SourceOffset(32)))),\n  );\n}\n\n#[test]\npub fn closure_macrolet_test_2() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(flet ((f () 1)) (macrolet ((foo () (f))) (foo)))\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"f\")), SourceOffset(36)))),\n  );\n}\n\n#[test]\npub fn labels_global_macro_shadowing_test() {\n  let result = parse_compile_decl(\"((defmacro foo () 100) (defn run () [(foo) (labels ((foo () (foo))) (foo)) (foo)]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo():\n    return 100\n\n\nclass _Labels extends Reference:\n\n    func _init():\n        pass\n\n    func _fn_foo_0():\n        return _fn_foo_0()\n\n\nstatic func run():\n    var _locals = _Labels.new()\n    return [100, _locals._fn_foo_0(), 100]\n\"#);\n}\n\n#[test]\npub fn gensym_test_1() {\n  let result = parse_compile_decl(\"((defmacro foo (a) (let ((x (gensym))) `(let ((,x ,a)) [,x ,x]))) (defn bar () (foo 10)))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo(a):\n    var x = GDLisp.gensym(null)\n    var _quasiquote = null\n    var _quasiquote_0 = GDLisp.cons(x, null)\n    return GDLisp.cons(GDLisp.intern(\"let\"), GDLisp.cons(GDLisp.cons(GDLisp.cons(x, GDLisp.cons(a, _quasiquote)), null), GDLisp.cons(GDLisp.cons(GDLisp.intern(\"array\"), GDLisp.cons(x, _quasiquote_0)), null)))\n\n\nstatic func bar():\n    var _G_0 = 10\n    return [_G_0, _G_0]\n\"#);\n}\n\n#[test]\npub fn gensym_test_2() {\n  let result = parse_compile_decl(\"((defmacro foo (a) (let ((x (gensym))) `(let ((,x ,a)) [,x ,x]))) (defn bar () [(foo 10) '_G_0]))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func foo(a):\n    var x = GDLisp.gensym(null)\n    var _quasiquote = null\n    var _quasiquote_0 = GDLisp.cons(x, null)\n    return GDLisp.cons(GDLisp.intern(\"let\"), GDLisp.cons(GDLisp.cons(GDLisp.cons(x, GDLisp.cons(a, _quasiquote)), null), GDLisp.cons(GDLisp.cons(GDLisp.intern(\"array\"), GDLisp.cons(x, _quasiquote_0)), null)))\n\n\nstatic func bar():\n    var _G_1 = 10\n    return [[_G_1, _G_1], GDLisp.intern(\"_G_0\")]\n\"#);\n}\n\n#[test]\npub fn macro_inner_class_test_1() {\n  let result = parse_compile_decl(\"((defclass Foo (Reference)) (defmacro foo () (Foo:new) 1) (defn run () (foo)))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n\nstatic func foo():\n    Foo.new()\n    return 1\n\n\nstatic func run():\n    return 1\n\"#);\n}\n\n#[test]\npub fn macro_inner_class_test_2() {\n  let result = parse_compile_decl(\"((defclass Foo (Reference) (defn g () (f))) (defn f () 9) (defmacro foo () ((Foo:new):g)) (defn run () (foo)))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func g():\n        return __gdlisp_outer_class_0.f()\n\n    var __gdlisp_outer_class_0 = load(\"res://TEST.gd\")\n\n\nstatic func f():\n    return 9\n\n\nstatic func foo():\n    return Foo.new().g()\n\n\nstatic func run():\n    return 9\n\"#);\n}\n\n#[test]\npub fn macro_inner_class_test_3() {\n  let result = parse_compile_decl(\"((defclass Foo (Reference) (defn g () static (f))) (defn f () 9) (defmacro foo () (Foo:g)) (defn run () (foo)))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    static func g():\n        return load(\"res://TEST.gd\").f()\n\n\nstatic func f():\n    return 9\n\n\nstatic func foo():\n    return Foo.g()\n\n\nstatic func run():\n    return 9\n\"#);\n}\n\n#[test]\npub fn macro_inner_class_test_4() {\n  // Can we do it with inheritance in the way?\n  let result = parse_compile_decl(r#\"\n    ((defn add-one (x) (+ x 1))\n     (defclass Foo (Reference)\n       (defn f () (add-one 10)))\n     (defclass Bar (Foo)\n       (defn g () (add-one 5)))\n     (defmacro foo ()\n       (let ((x (Bar:new)))\n         (+ (x:f) (x:g))))\n     (defn run-test () (foo)))\n\"#);\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func add_one(x):\n    return x + 1\n\n\nclass Foo extends Reference:\n\n    func _init():\n        pass\n\n    func f():\n        return __gdlisp_outer_class_0.add_one(10)\n\n    var __gdlisp_outer_class_0 = load(\"res://TEST.gd\")\n\n\nclass Bar extends Foo:\n\n    func _init():\n        pass\n\n    func g():\n        return __gdlisp_outer_class_1.add_one(5)\n\n    var __gdlisp_outer_class_1 = load(\"res://TEST.gd\")\n\n\nstatic func foo():\n    var x = Bar.new()\n    return x.f() + x.g()\n\n\nstatic func run_test():\n    return 17\n\"#);\n}\n\n#[test]\npub fn nonsense_modifier_macro_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defmacro foo () public private 1))\"#),\n    Err(PError::from(ModifierParseError::new(ModifierParseErrorF::UniquenessError(String::from(\"visibility\")), SourceOffset(25)))),\n  );\n}\n\n#[test]\npub fn macro_in_minimalist_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((sys/nostdlib) (defmacro foo () 10) (foo))\"),\n    Err(PError::from(GDError::new(GDErrorF::MacroInMinimalistError(String::from(\"foo\")), SourceOffset(37)))),\n  );\n}\n\n#[test]\npub fn simple_minimalist_test() {\n  assert_eq!(parse_compile_decl(\"((sys/nostdlib))\"), r#\"extends Reference\n\n\nfunc _init():\n    pass\n\"#);\n}\n\n#[test]\npub fn quit_macro_test() {\n  let result = parse_compile_and_output(\"(quit)\");\n  assert_eq!(result, \"return GDLisp.get_tree().quit()\\n\");\n}\n\n#[test]\npub fn recursive_macro_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defmacro foo () (foo)))\"),\n    Err(PError::from(GDError::new(GDErrorF::MacroBeforeDefinitionError(String::from(\"foo\")), SourceOffset(18)))),\n  );\n}\n\n#[test]\npub fn recursive_macrolet_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((macrolet ((foo () (foo))) ()))\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchFn(String::from(\"foo\")), SourceOffset(20)))),\n  );\n}\n\n#[test]\npub fn bad_order_macro_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defn bar () (foo)) (defmacro foo () 1))\"),\n    Err(PError::from(GDError::new(GDErrorF::MacroBeforeDefinitionError(String::from(\"foo\")), SourceOffset(14)))),\n  );\n}\n\n#[test]\npub fn bad_preload_in_macro_test() {\n  assert_eq!(\n    parse_compile_decl_err(r#\"((defmacro foo ()\n                                  (sys/context-filename \"res://not-a-real-file.lisp\")))\"#),\n    Err(PError::from(GDError::new(GDErrorF::ContextualFilenameUnresolved, SourceOffset(52)))),\n  );\n}\n"
  },
  {
    "path": "tests/test/meta_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::parse_and_run;\nuse gdlisp::gdscript::metadata;\n\n#[test]\nfn meta_test_cons() {\n  let code = format!(r#\"((let ((arg (cons 1 2))) (print (bool (arg:get-meta \"{}\")))))\"#, metadata::CONS_META);\n  let output = parse_and_run(&code);\n  assert_eq!(output, \"\\nTrue\\n\");\n}\n\n#[test]\nfn meta_test_cons_quoted() {\n  let code = format!(r#\"((let ((arg '(1 . 2))) (print (bool (arg:get-meta \"{}\")))))\"#, metadata::CONS_META);\n  let output = parse_and_run(&code);\n  assert_eq!(output, \"\\nTrue\\n\");\n}\n\n#[test]\nfn meta_test_symbol() {\n  let code = format!(r#\"((let ((arg (intern \"sym\"))) (print (bool (arg:get-meta \"{}\")))))\"#, metadata::SYMBOL_META);\n  let output = parse_and_run(&code);\n  assert_eq!(output, \"\\nTrue\\n\");\n}\n\n#[test]\nfn meta_test_symbol_quoted() {\n  let code = format!(r#\"((let ((arg 'sym)) (print (bool (arg:get-meta \"{}\")))))\"#, metadata::SYMBOL_META);\n  let output = parse_and_run(&code);\n  assert_eq!(output, \"\\nTrue\\n\");\n}\n\n#[test]\nfn meta_test_symbol_not_on_cons() {\n  let code = format!(r#\"((let ((arg '(1 . 2))) (print (arg:has-meta \"{}\"))))\"#, metadata::SYMBOL_META);\n  let output = parse_and_run(&code);\n  assert_eq!(output, \"\\nFalse\\n\");\n}\n\n#[test]\nfn meta_test_cons_not_on_symbol() {\n  let code = format!(r#\"((let ((arg 'sym)) (print (arg:has-meta \"{}\"))))\"#, metadata::CONS_META);\n  let output = parse_and_run(&code);\n  assert_eq!(output, \"\\nFalse\\n\");\n}\n"
  },
  {
    "path": "tests/test/mod.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\npub mod common;\n\npub mod builtin_function_test;\npub mod class_test;\npub mod collection_conversion_test;\npub mod concurrency_test;\npub mod cond_if_test;\npub mod const_test;\npub mod declaration_test;\npub mod dependencies_test;\npub mod dictionary_test;\npub mod enum_test;\npub mod error_test;\npub mod even_odd_test;\npub mod factorial_test;\npub mod flet_test;\npub mod floating_test;\npub mod for_test;\npub mod import_test;\npub mod labels_test;\npub mod lambda_class_test;\npub mod lambda_test;\npub mod lazy_val_test;\npub mod let_var_test;\npub mod list_operator_test;\npub mod macro_test;\npub mod meta_test;\npub mod name_translation_test;\npub mod object_test;\npub mod operator_test;\npub mod quoting_test;\npub mod signal_test;\npub mod simple_expr_test;\npub mod string_test;\npub mod type_checking_test;\npub mod typecast_test;\npub mod while_test;\n"
  },
  {
    "path": "tests/test/name_translation_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\n#[test]\npub fn translate_local_vars_test_1() {\n  assert_eq!(parse_compile_decl(\"((defn foo () (let ((x 1)) x)))\"),\n             r#\"extends Reference\n\n\nstatic func foo():\n    var x = 1\n    return x\n\"#);\n}\n\n#[test]\npub fn translate_local_vars_test_2() {\n  assert_eq!(parse_compile_decl(\"((defn foo () (let* ((x 1) (x 2)) x)))\"),\n             r#\"extends Reference\n\n\nstatic func foo():\n    var x = 1\n    var x_0 = 2\n    return x_0\n\"#);\n}\n\n#[test]\npub fn translate_local_vars_test_3() {\n  assert_eq!(parse_compile_decl(\"((defn foo () (let* ((x-y 1) (x-y 2)) x-y)))\"),\n             r#\"extends Reference\n\n\nstatic func foo():\n    var x_y = 1\n    var x_y_0 = 2\n    return x_y_0\n\"#);\n}\n\n#[test]\npub fn translate_arguments_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo (a-b) a-b))\"),\n             r#\"extends Reference\n\n\nstatic func foo(a_b):\n    return a_b\n\"#);\n}\n\n#[test]\npub fn translate_constructor_arguments_test_1() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Reference) (defn _init (a-b))))\"),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init(a_b):\n        pass\n\"#);\n}\n\n#[test]\npub fn translate_constructor_arguments_test_2() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Reference) (defn _init (a-b) (super a-b))))\"),\n             r#\"extends Reference\n\n\nclass Foo extends Reference:\n\n    func _init(a_b).(a_b):\n        pass\n\"#);\n}\n\n#[test]\npub fn translate_function_name_test() {\n  assert_eq!(parse_compile_decl(\"((defn foo-bar ()))\"),\n             r#\"extends Reference\n\n\nstatic func foo_bar():\n    return null\n\"#);\n}\n\n#[test]\npub fn translate_macro_name_test() {\n  assert_eq!(parse_compile_decl(\"((defmacro foo-bar ()))\"),\n             r#\"extends Reference\n\n\nstatic func foo_bar():\n    return null\n\"#);\n}\n\n#[test]\npub fn translate_const_name_test() {\n  assert_eq!(parse_compile_decl(\"((defconst FOO-BAR 1))\"),\n             r#\"extends Reference\n\n\nconst FOO_BAR = 1\n\"#);\n\n}\n\n#[test]\npub fn translate_class_name_test() {\n  assert_eq!(parse_compile_decl(\"((defclass Foo-Bar (Reference)))\"),\n             r#\"extends Reference\n\n\nclass Foo_Bar extends Reference:\n\n    func _init():\n        pass\n\"#);\n}\n\n#[test]\npub fn translate_enum_name_test_1() {\n  assert_eq!(parse_compile_decl(\"((defenum Foo-Bar A-B))\"),\n             r#\"extends Reference\n\n\nenum Foo_Bar {\n    A_B,\n}\n\"#);\n}\n\n#[test]\npub fn translate_enum_name_test_2() {\n  assert_eq!(parse_compile_decl(\"((defenum Foo-Bar (A-B 1)))\"),\n             r#\"extends Reference\n\n\nenum Foo_Bar {\n    A_B = 1,\n}\n\"#);\n}\n\n#[test]\npub fn translate_declare_name_test() {\n  assert_eq!(parse_compile_decl(\"((sys/declare function foo-bar ()) (defn run () (foo-bar)))\"),\n             r#\"extends Reference\n\n\nstatic func run():\n    return foo_bar()\n\"#);\n}\n"
  },
  {
    "path": "tests/test/object_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nextern crate gdlisp;\n\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\n\nuse super::common::*;\n\n#[test]\npub fn empty_object_test() {\n  assert_eq!(parse_compile_decl(\"((defobject Foo (Reference)))\"),\n             r#\"extends Reference\n\n\nclass _AnonymousClass extends Reference:\n\n    func _init():\n        pass\n\n\nstatic func _lazy_0():\n    var _this_file_1 = load(\"res://TEST.gd\")\n    var _cond = null\n    if _this_file_1.has_meta(\"__gdlisp_Lazy__G_3\"):\n        _cond = _this_file_1.get_meta(\"__gdlisp_Lazy__G_3\")\n    else:\n        if true:\n            var _value_2 = _AnonymousClass.new()\n            _this_file_1.set_meta(\"__gdlisp_Lazy__G_3\", _value_2)\n            _cond = _value_2\n        else:\n            _cond = null\n    return _cond\n\n\nstatic func __gdlisp_SymbolMacroFunction_Foo():\n    return GDLisp.cons(GDLisp.cons(GDLisp.intern(\"access-slot\"), GDLisp.cons(GDLisp.cons(GDLisp.intern(\"contextual-load\"), GDLisp.cons(\"res://TEST.gd\", null)), GDLisp.cons(GDLisp.intern(\"_lazy_0\"), null))), null)\n\"#);\n}\n\n#[test]\npub fn main_object_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defobject Foo (Reference) main))\"),\n    Err(PError::from(GDError::new(GDErrorF::DottedListError, SourceOffset(1)))),\n  );\n}\n\n#[test]\npub fn simple_self_run_object_test_1() {\n  assert_eq!(parse_and_run(r#\"\n    ((defobject Foo (Reference)\n       (defvar x 1)\n       (defn _init ())\n       (defn double ()\n         (* self:x 2)))\n     (print Foo:x)\n     (print (Foo:double))\n     (set Foo:x 10)\n     (print (Foo:double)))\n  \"#), \"\\n1\\n2\\n20\\n\");\n}\n\n#[test]\npub fn simple_self_run_object_test_2() {\n  assert_eq!(parse_and_run(r#\"\n    ((defobject Foo Reference\n       (defvar x 1)\n       (defn _init ())\n       (defn double ()\n         (* self:x 2)))\n     (print Foo:x)\n     (print (Foo:double))\n     (set Foo:x 10)\n     (print (Foo:double)))\n  \"#), \"\\n1\\n2\\n20\\n\");\n}\n\n#[test]\npub fn simple_self_run_object_test_3() {\n  assert_eq!(parse_and_run(r#\"\n    ((defobject Foo (Reference) public\n       (defvar x 1)\n       (defn _init ())\n       (defn double ()\n         (* self:x 2)))\n     (print Foo:x)\n     (print (Foo:double))\n     (set Foo:x 10)\n     (print (Foo:double)))\n  \"#), \"\\n1\\n2\\n20\\n\");\n}\n\n#[test]\npub fn simple_self_run_object_test_4() {\n  assert_eq!(parse_and_run(r#\"\n    ((defobject Foo (Reference) private\n       (defvar x 1)\n       (defn _init ())\n       (defn double ()\n         (* self:x 2)))\n     (print Foo:x)\n     (print (Foo:double))\n     (set Foo:x 10)\n     (print (Foo:double)))\n  \"#), \"\\n1\\n2\\n20\\n\");\n}\n\n#[test]\npub fn empty_object_run_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defobject Foo (Reference)) Foo (print 1))\n  \"#), \"\\n1\\n\");\n}\n\n#[test]\npub fn self_with_closure_run_object_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defobject Foo (Reference)\n       (defvar x 1)\n       (defn increment ()\n         (lambda ()\n           (set self:x (+ self:x 1)))))\n     (let ((fn (Foo:increment)))\n       (print (funcall fn))\n       (print (funcall fn))\n       (print (funcall fn))))\n  \"#), \"\\n2\\n3\\n4\\n\");\n}\n\n#[test]\npub fn macro_in_object_test_1() {\n  assert_eq!(parse_and_run(r#\"\n    ((defmacro declare-fn (name)\n       `(defn ,name () 99))\n     (defobject Foo (Reference)\n       (declare-fn aa)\n       (declare-fn bb))\n     (print (Foo:aa))\n     (print (Foo:bb)))\n  \"#), \"\\n99\\n99\\n\");\n}\n\n#[test]\npub fn macro_in_object_test_2() {\n  assert_eq!(parse_and_run(r#\"\n    ((defmacro my-value ()\n       630)\n     (defobject Foo (Reference)\n       (defn foo () (my-value)))\n     (print (Foo:foo)))\n  \"#), \"\\n630\\n\");\n}\n\n#[test]\npub fn macro_in_object_test_3() {\n  assert_eq!(parse_and_run(r#\"\n    ((defmacro declare-fns ()\n       `(progn (defn a () 67) (defn b () 68)))\n     (defobject Foo (Reference)\n       (declare-fns))\n     (print (Foo:a))\n     (print (Foo:b)))\n  \"#), \"\\n67\\n68\\n\");\n}\n\n/* /////\n#[test]\npub fn macro_uses_object_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defobject Foo (Reference)\n       (defvar x))\n     (defmacro through-foo ()\n       (set Foo:x 5)\n       Foo:x)\n     (print (through-foo)))\"#),\n             \"\\n5\\n\");\n}\n*/\n\n/* /////\n#[test]\npub fn reference_to_outer_in_object_test_1() {\n  // See Issue #85\n  let output = parse_and_run(r#\"\n    ((defn outer () 100)\n     (defobject Foo (Reference)\n       (defn foo () (outer)))\n     (print (Foo:foo))\n     (set (elt Foo \"__gdlisp_outer_class_1\") nil))\"#); // Nasty hack to break the cyclic reference (have to use elt and a string or GDLisp will catch on to what I'm doing)\n  assert_eq!(output, \"\\n100\\n\");\n}\n*/\n\n#[test]\npub fn initialization_of_object_test() {\n  // Make sure that a singleton object is only initialized once, even\n  // if we reference it a couple of times.\n  let output = parse_and_run(r#\"\n    ((defobject Foo (Reference)\n       (defn _init ()\n         (print \"Initializing\"))\n       (defn foo ()\n         18))\n     (print \"Start\")\n     (print (Foo:foo))\n     (print \"Middle\")\n     (print (Foo:foo))\n     (print \"End\"))\"#);\n  assert_eq!(output, \"\\nStart\\nInitializing\\n18\\nMiddle\\n18\\nEnd\\n\");\n}\n\n#[test]\npub fn object_uses_gdlisp_functions_test() {\n  let output = parse_and_run(r#\"\n    ((defobject MyObject (Reference)\n       (defvar example-var [1 2 3 4])\n       (defn sum ()\n         (array/fold #'+ @example-var 0)))\n     (print (MyObject:sum)))\n  \"#);\n  assert_eq!(output, \"\\n10\\n\");\n}\n"
  },
  {
    "path": "tests/test/operator_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\n#[test]\nfn addition_test_1() {\n  assert_eq!(parse_and_run(\"((print (+)))\"), \"\\n0\\n\");\n}\n\n#[test]\nfn addition_test_2() {\n  assert_eq!(parse_and_run(\"((print (+ 1)))\"), \"\\n1\\n\");\n}\n\n#[test]\nfn addition_test_3() {\n  assert_eq!(parse_and_run(\"((print (+ 1 5)))\"), \"\\n6\\n\");\n}\n\n#[test]\nfn addition_test_4() {\n  assert_eq!(parse_and_run(\"((print (+ 1 5 2)))\"), \"\\n8\\n\");\n}\n\n#[test]\nfn addition_test_5() {\n  assert_eq!(parse_and_run(\"((print (+ 1 5 2 -4)))\"), \"\\n4\\n\");\n}\n\n#[test]\nfn addition_test_6() {\n  assert_eq!(parse_and_run(\"((print (+ (vector 1 1) (vector 2 3))))\"), \"\\n(3, 4)\\n\");\n}\n\n#[test]\nfn addition_test_7() {\n  assert_eq!(parse_and_run(\"((print (+ (vector 1 1))))\"), \"\\n(1, 1)\\n\");\n}\n\n#[test]\nfn addition_test_indirect_1() {\n  assert_eq!(parse_and_run(\"((print (funcall (function +) 1 5 2 -4)))\"), \"\\n4\\n\");\n}\n\n#[test]\nfn addition_test_indirect_2() {\n  assert_eq!(parse_and_run(\"((print (funcall (function +))))\"), \"\\n0\\n\");\n}\n\n#[test]\nfn addition_test_indirect_3() {\n  assert_eq!(parse_and_run(\"((print (funcall (function +) 3)))\"), \"\\n3\\n\");\n}\n\n#[test]\nfn addition_test_indirect_4() {\n  assert_eq!(parse_and_run(\"((print (apply (function +) 1 2 '(3 4))))\"), \"\\n10\\n\");\n}\n\n#[test]\nfn multiplication_test_1() {\n  assert_eq!(parse_and_run(\"((print (*)))\"), \"\\n1\\n\");\n}\n\n#[test]\nfn multiplication_test_2() {\n  assert_eq!(parse_and_run(\"((print (* 4)))\"), \"\\n4\\n\");\n}\n\n#[test]\nfn multiplication_test_3() {\n  assert_eq!(parse_and_run(\"((print (* 4 3)))\"), \"\\n12\\n\");\n}\n\n#[test]\nfn multiplication_test_4() {\n  assert_eq!(parse_and_run(\"((print (* 4 3 2)))\"), \"\\n24\\n\");\n}\n\n#[test]\nfn multiplication_test_5() {\n  assert_eq!(parse_and_run(\"((print (* 4 (vector 1 2) 2 (vector 2 3))))\"), \"\\n(16, 48)\\n\");\n}\n\n#[test]\nfn multiplication_test_indirect_1() {\n  assert_eq!(parse_and_run(\"((print (funcall (function *) 4 3 2)))\"), \"\\n24\\n\");\n}\n\n#[test]\nfn multiplication_test_indirect_2() {\n  assert_eq!(parse_and_run(\"((print (funcall (function *))))\"), \"\\n1\\n\");\n}\n\n#[test]\nfn multiplication_test_indirect_3() {\n  assert_eq!(parse_and_run(\"((print (funcall (function *) 3)))\"), \"\\n3\\n\");\n}\n\n#[test]\nfn subtraction_test_1() {\n  assert_eq!(parse_and_run(\"((print (- 4)))\"), \"\\n-4\\n\");\n}\n\n#[test]\nfn subtraction_test_2() {\n  assert_eq!(parse_and_run(\"((print (- 4 3)))\"), \"\\n1\\n\");\n}\n\n#[test]\nfn subtraction_test_3() {\n  assert_eq!(parse_and_run(\"((print (- 4 3 2)))\"), \"\\n-1\\n\");\n}\n\n#[test]\nfn subtraction_test_indirect_1() {\n  assert_eq!(parse_and_run(\"((print (funcall (function -) 4 3 2)))\"), \"\\n-1\\n\");\n}\n\n#[test]\nfn subtraction_test_indirect_2() {\n  assert_eq!(parse_and_run(\"((print (funcall (function -) 3)))\"), \"\\n-3\\n\");\n}\n\n#[test]\nfn division_test_1() {\n  assert_eq!(parse_and_run(\"((print (/ 4.0)))\"), \"\\n0.25\\n\");\n}\n\n#[test]\nfn division_test_2() {\n  assert_eq!(parse_and_run(\"((print (/ 4)))\"), \"\\n0\\n\");\n}\n\n#[test]\nfn division_test_3() {\n  assert_eq!(parse_and_run(\"((print (/ 4 2)))\"), \"\\n2\\n\");\n}\n\n#[test]\nfn division_test_4() {\n  assert_eq!(parse_and_run(\"((print (/ 4 2 2)))\"), \"\\n1\\n\");\n}\n\n#[test]\nfn division_test_5() {\n  assert_eq!(parse_and_run(\"((print (/ (vector 4 2 4) 2 2)))\"), \"\\n(1, 0.5, 1)\\n\");\n}\n\n#[test]\nfn division_test_indirect_1() {\n  assert_eq!(parse_and_run(\"((print (funcall (function /) 4.0)))\"), \"\\n0.25\\n\");\n}\n\n#[test]\nfn division_test_indirect_2() {\n  assert_eq!(parse_and_run(\"((print (funcall (function /) 4)))\"), \"\\n0\\n\");\n}\n\n#[test]\nfn division_test_indirect_3() {\n  assert_eq!(parse_and_run(\"((print (funcall (function /) 3 2)))\"), \"\\n1\\n\");\n}\n\n#[test]\nfn division_test_indirect_4() {\n  assert_eq!(parse_and_run(\"((print (funcall (function /) 3.0 2)))\"), \"\\n1.5\\n\");\n}\n\n#[test]\nfn eq_test_1() {\n  assert_eq!(parse_and_run(\"((print (= 4)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn eq_test_2() {\n  assert_eq!(parse_and_run(\"((print (= 4 4)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn eq_test_3() {\n  assert_eq!(parse_and_run(\"((print (= 2 2 4)))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn eq_test_stateful_1() {\n  assert_eq!(parse_and_run(\"((print (= (print 1))))\"), \"\\n1\\nTrue\\n\");\n}\n\n#[test]\nfn eq_test_stateful_2() {\n  assert_eq!(parse_and_run(\"((print (= (print 1) (print 2) (print 3))))\"), \"\\n1\\n2\\n3\\nTrue\\n\");\n}\n\n#[test]\nfn eq_test_indirect_1() {\n  assert_eq!(parse_and_run(\"((print (funcall (function =) 1)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn eq_test_indirect_2() {\n  assert_eq!(parse_and_run(\"((print (funcall (function =) 1 2)))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn eq_test_indirect_3() {\n  assert_eq!(parse_and_run(\"((print (funcall (function =) 1 1 1)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn lt_test_indirect() {\n  assert_eq!(parse_and_run(\"((print (funcall (function <) 1 2 3)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn gt_test_indirect() {\n  assert_eq!(parse_and_run(\"((print (funcall (function >) 3 2 1)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn le_test_indirect() {\n  assert_eq!(parse_and_run(\"((print (funcall (function <=) 1 2 2 3)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn ge_test_indirect() {\n  assert_eq!(parse_and_run(\"((print (funcall (function >=) 3 2 2 1)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn ne_test_1() {\n  assert_eq!(parse_and_run(\"((print (/= 1 1 1)))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn ne_test_2() {\n  assert_eq!(parse_and_run(\"((print (/= 1 3 2)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn ne_test_3() {\n  assert_eq!(parse_and_run(\"((print (/= 1 2 1)))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn not_test_1() {\n  assert_eq!(parse_and_run(\"((print (not #t)))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn not_test_2() {\n  assert_eq!(parse_and_run(\"((print (not #f)))\"), \"\\nTrue\\n\");\n}\n\n#[test]\npub fn addition_compile_test() {\n  assert_eq!(parse_compile_and_output(\"(+)\"), \"return 0\\n\");\n  assert_eq!(parse_compile_and_output(\"(+ 1)\"), \"return 1\\n\");\n  assert_eq!(parse_compile_and_output(\"(+ 1 2)\"), \"return 1 + 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(+ 1 2 3)\"), \"return 1 + 2 + 3\\n\");\n}\n\n#[test]\npub fn multiplication_compile_test() {\n  assert_eq!(parse_compile_and_output(\"(*)\"), \"return 1\\n\");\n  assert_eq!(parse_compile_and_output(\"(* 2)\"), \"return 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(* 2 3)\"), \"return 2 * 3\\n\");\n  assert_eq!(parse_compile_and_output(\"(* 2 3 4)\"), \"return 2 * 3 * 4\\n\");\n}\n\n#[test]\npub fn subtraction_compile_test() {\n  assert_eq!(parse_compile_and_output(\"(- 2)\"), \"return -2\\n\");\n  assert_eq!(parse_compile_and_output(\"(- 2 3)\"), \"return 2 - 3\\n\");\n  assert_eq!(parse_compile_and_output(\"(- 2 3 4)\"), \"return 2 - 3 - 4\\n\");\n}\n\n#[test]\npub fn division_compile_test() {\n  assert_eq!(parse_compile_and_output(\"(/ 2)\"), \"return 1 / 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(/ 2 3)\"), \"return 2 / 3\\n\");\n  assert_eq!(parse_compile_and_output(\"(/ 2 3 4)\"), \"return 2 / 3 / 4\\n\");\n  assert_eq!(parse_compile_and_output(\"(/ 2.0 3 4.0)\"), \"return 2e0 / 3 / 4e0\\n\");\n}\n\n#[test]\npub fn eq_compile_test() {\n  assert_eq!(parse_compile_and_output(\"(= 1)\"), \"return true\\n\");\n  assert_eq!(parse_compile_and_output(\"(= 1 2)\"), \"return 1 == 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(= 1 2 3)\"), \"return 1 == 2 && 2 == 3\\n\");\n}\n\n#[test]\npub fn eq_compile_test_stateful() {\n  assert_eq!(parse_compile_and_output(\"(= (foo))\"), \"foo()\\nreturn true\\n\");\n  assert_eq!(parse_compile_and_output(\"(= (foo) (foo))\"), \"return foo() == foo()\\n\");\n  assert_eq!(parse_compile_and_output(\"(= (foo) (foo) (foo))\"), \"var _cmp = foo()\\nvar _cmp_0 = foo()\\nvar _cmp_1 = foo()\\nreturn _cmp == _cmp_0 && _cmp_0 == _cmp_1\\n\");\n}\n\n#[test]\npub fn cmp_compile_test() {\n  assert_eq!(parse_compile_and_output(\"(< 1 2)\"), \"return 1 < 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(> 1 2 3)\"), \"return 1 > 2 && 2 > 3\\n\");\n  assert_eq!(parse_compile_and_output(\"(<= 1 2)\"), \"return 1 <= 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(>= 1 2 3)\"), \"return 1 >= 2 && 2 >= 3\\n\");\n}\n\n#[test]\npub fn cmp_compile_test_stateful() {\n  assert_eq!(parse_compile_and_output(\"(< (foo))\"), \"foo()\\nreturn true\\n\");\n  assert_eq!(parse_compile_and_output(\"(<= (foo) (foo))\"), \"return foo() <= foo()\\n\");\n  assert_eq!(parse_compile_and_output(\"(> (foo) (foo) (foo))\"), \"var _cmp = foo()\\nvar _cmp_0 = foo()\\nvar _cmp_1 = foo()\\nreturn _cmp > _cmp_0 && _cmp_0 > _cmp_1\\n\");\n  assert_eq!(parse_compile_and_output(\"(>= (foo) (foo))\"), \"return foo() >= foo()\\n\");\n}\n\n#[test]\npub fn ne_compile_test() {\n  assert_eq!(parse_compile_and_output(\"(/= 1)\"), \"return true\\n\");\n  assert_eq!(parse_compile_and_output(\"(/= (foo))\"), \"foo()\\nreturn true\\n\");\n  assert_eq!(parse_compile_and_output(\"(/= 1 2)\"), \"return 1 != 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(/= 1 2 3)\"), \"return GDLisp._DIV__EQ_(1, GDLisp.cons(2, GDLisp.cons(3, null)))\\n\");\n}\n\n#[test]\npub fn array_subscript_test() {\n  assert_eq!(parse_compile_and_output(\"(elt 1 2)\"), \"return 1[2]\\n\");\n}\n\n#[test]\npub fn dict_subscript_test() {\n  assert_eq!(parse_compile_and_output(\"(dict/elt 1 2)\"), \"return 1[2]\\n\");\n}\n\n#[test]\npub fn array_subscript_test_run() {\n  assert_eq!(parse_and_run(\"((let ((x [10 20 30])) (print (elt x 2))))\"), \"\\n30\\n\");\n}\n\n#[test]\npub fn array_subscript_test_run_indirect() {\n  assert_eq!(parse_and_run(\"((let ((x [10 20 30])) (print (funcall #'elt x 2))))\"), \"\\n30\\n\");\n}\n\n#[test]\npub fn dict_subscript_test_run() {\n  assert_eq!(parse_and_run(\"((let ((x {10 20})) (print (dict/elt x 10))))\"), \"\\n20\\n\");\n}\n\n#[test]\npub fn dict_subscript_test_run_indirect() {\n  assert_eq!(parse_and_run(\"((let ((x {10 20})) (print (funcall #'dict/elt x 10))))\"), \"\\n20\\n\");\n}\n\n#[test]\npub fn array_subscript_assign_test_run_1() {\n  assert_eq!(parse_and_run(\"((let ((x [10 20 30])) (set (elt x 1) 999) (print x)))\"), \"\\n[10, 999, 30]\\n\");\n}\n\n#[test]\npub fn array_subscript_assign_test_run_2() {\n  assert_eq!(parse_and_run(\"((let ((x [10 20 30])) (set-elt 999 x 1) (print x)))\"), \"\\n[10, 999, 30]\\n\");\n}\n\n#[test]\npub fn array_subscript_assign_test_run_3() {\n  assert_eq!(parse_and_run(\"((let ((x [10 20 30])) (funcall #'set-elt 999 x 1) (print x)))\"), \"\\n[10, 999, 30]\\n\");\n}\n\n#[test]\npub fn dict_subscript_assign_test_run_1() {\n  assert_eq!(parse_and_run(\"((let ((x {})) (set (dict/elt x 1) 999) (print x)))\"), \"\\n{1:999}\\n\");\n}\n\n#[test]\npub fn dict_subscript_assign_test_run_2() {\n  assert_eq!(parse_and_run(\"((let ((x {2 0})) (set (dict/elt x 2) 1) (print x)))\"), \"\\n{2:1}\\n\");\n}\n\n#[test]\npub fn min_max_test_1() {\n  assert_eq!(parse_compile_and_output(\"(min 0 1)\"), \"return min(0, 1)\\n\");\n  assert_eq!(parse_compile_and_output(\"(max 0 1)\"), \"return max(0, 1)\\n\");\n}\n\n#[test]\npub fn min_max_test_2() {\n  assert_eq!(parse_compile_and_output(\"(min 0)\"), \"return 0\\n\");\n  assert_eq!(parse_compile_and_output(\"(max 0)\"), \"return 0\\n\");\n}\n\n#[test]\npub fn min_max_test_3() {\n  assert_eq!(parse_compile_and_output(\"(min)\"), \"return INF\\n\");\n  assert_eq!(parse_compile_and_output(\"(max)\"), \"return -INF\\n\");\n}\n\n#[test]\npub fn min_max_test_4() {\n  assert_eq!(parse_compile_and_output(\"(min 1 2 3)\"), \"return min(min(1, 2), 3)\\n\");\n  assert_eq!(parse_compile_and_output(\"(max 1 2 3)\"), \"return max(max(1, 2), 3)\\n\");\n}\n\n#[test]\npub fn min_max_run_test() {\n  assert_eq!(parse_and_run(\"((print (min 1 2 3)))\"), \"\\n1\\n\");\n  assert_eq!(parse_and_run(\"((print (max 1 2 3)))\"), \"\\n3\\n\");\n}\n\n#[test]\npub fn gcd_run_test() {\n  assert_eq!(parse_and_run(\"((print (gcd)))\"), \"\\n0\\n\");\n  assert_eq!(parse_and_run(\"((print (gcd 100)))\"), \"\\n100\\n\");\n  assert_eq!(parse_and_run(\"((print (gcd 75 50)))\"), \"\\n25\\n\");\n  assert_eq!(parse_and_run(\"((print (gcd 75 10 50)))\"), \"\\n5\\n\");\n}\n\n#[test]\npub fn lcm_run_test() {\n  assert_eq!(parse_and_run(\"((print (lcm)))\"), \"\\n1\\n\");\n  assert_eq!(parse_and_run(\"((print (lcm 100)))\"), \"\\n100\\n\");\n  assert_eq!(parse_and_run(\"((print (lcm 75 50)))\"), \"\\n150\\n\");\n  assert_eq!(parse_and_run(\"((print (lcm 2 3 4 5)))\"), \"\\n60\\n\");\n}\n\n#[test]\nfn eq_num_test() {\n  assert_eq!(parse_and_run(\"((print (= 4 4)) (print (= 4 3)) (print (= 4 4.0)))\"), \"\\nTrue\\nFalse\\nTrue\\n\");\n}\n\n#[test]\nfn eq_str_test() {\n  assert_eq!(parse_and_run(\"((print (= \\\"a\\\" \\\"a\\\")))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn eq_symbol_test() {\n  assert_eq!(parse_and_run(\"((print (= 'a 'a)) (print (= 'b 'c)) (print (= 'b 'B)))\"), \"\\nTrue\\nFalse\\nFalse\\n\");\n}\n\n#[test]\nfn eq_array_test() {\n  assert_eq!(parse_and_run(\"((print (= [1] [1])))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn eq_dict_test() {\n  assert_eq!(parse_and_run(\"((print (= {'a 1} {'a 1})))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn eq_cons_test() {\n  // Cons cells follow reference semantics, since they're mutable and\n  // (according to GDScript) non-primitive.\n  assert_eq!(parse_and_run(\"((print (= '(1 2) '(1 2))))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn equal_num_test() {\n  assert_eq!(parse_and_run(\"((print (equal? 4 4)) (print (equal? 4 3)) (print (equal? 4 4.0)))\"),\n             \"\\nTrue\\nFalse\\nTrue\\n\");\n}\n\n#[test]\nfn equal_str_test() {\n  assert_eq!(parse_and_run(\"((print (equal? \\\"a\\\" \\\"a\\\")))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn equal_symbol_test() {\n  assert_eq!(parse_and_run(\"((print (equal? 'a 'a)) (print (equal? 'b 'c)) (print (equal? 'b 'B)))\"),\n             \"\\nTrue\\nFalse\\nFalse\\n\");\n}\n\n#[test]\nfn equal_array_test() {\n  assert_eq!(parse_and_run(\"((print (equal? [1] [1])) (print (equal? [1] [1 2])))\"), \"\\nTrue\\nFalse\\n\");\n}\n\n#[test]\nfn equal_dict_test() {\n  assert_eq!(parse_and_run(\"((print (equal? {'a 1} {'a 1})))\"), \"\\nTrue\\n\");\n  assert_eq!(parse_and_run(\"((print (equal? {'a 1} {'a 2})))\"), \"\\nFalse\\n\");\n  assert_eq!(parse_and_run(\"((print (equal? {\\\"b\\\" 2 'a 1} {'a 1 \\\"b\\\" 2})))\"), \"\\nTrue\\n\");\n}\n\n#[test]\nfn equal_cons_test() {\n  assert_eq!(parse_and_run(\"((print (equal? '(1 2) '(1 2))))\"), \"\\nTrue\\n\");\n  assert_eq!(parse_and_run(\"((print (equal? '(1 2) '(1))))\"), \"\\nFalse\\n\");\n  assert_eq!(parse_and_run(\"((print (equal? '(1) '(1 2))))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn equal_nonmatching_test() {\n  assert_eq!(parse_and_run(\"((print (equal? '(1 2) [1 2])))\"), \"\\nFalse\\n\");\n  assert_eq!(parse_and_run(\"((print (equal? 0 \\\"0\\\")))\"), \"\\nFalse\\n\");\n  assert_eq!(parse_and_run(\"((print (equal? {'a 1} nil)))\"), \"\\nFalse\\n\");\n}\n\n#[test]\nfn equal_nested_test() {\n  assert_eq!(parse_and_run(\"((print (equal? {'a ['b '(7)]} {'a ['b '(7)]})))\"), \"\\nTrue\\n\");\n}\n"
  },
  {
    "path": "tests/test/quoting_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\n#[test]\npub fn quote_test() {\n  assert_eq!(parse_compile_and_output(\"(quote 10)\"), \"return 10\\n\");\n  assert_eq!(parse_compile_and_output(\"(quote (1 2))\"), \"return GDLisp.cons(1, GDLisp.cons(2, null))\\n\");\n  assert_eq!(parse_compile_and_output(\"(quote (1 . 2))\"), \"return GDLisp.cons(1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(quote [1 2])\"), \"return GDLisp.cons(GDLisp.intern(\\\"array\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n  assert_eq!(parse_compile_and_output(\"(quote {1 2})\"), \"return GDLisp.cons(GDLisp.intern(\\\"dict\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n}\n\n#[test]\npub fn quote_syntax_test() {\n  assert_eq!(parse_compile_and_output(\"'10\"), \"return 10\\n\");\n  assert_eq!(parse_compile_and_output(\"'(1 2)\"), \"return GDLisp.cons(1, GDLisp.cons(2, null))\\n\");\n  assert_eq!(parse_compile_and_output(\"'(1 . 2)\"), \"return GDLisp.cons(1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"'[1 2]\"), \"return GDLisp.cons(GDLisp.intern(\\\"array\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n  assert_eq!(parse_compile_and_output(\"'{1 2}\"), \"return GDLisp.cons(GDLisp.intern(\\\"dict\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n}\n\n#[test]\npub fn full_quasiquote_test() {\n  assert_eq!(parse_compile_and_output(\"(quasiquote 10)\"), \"return 10\\n\");\n  assert_eq!(parse_compile_and_output(\"(quasiquote (1 2))\"), \"return GDLisp.cons(1, GDLisp.cons(2, null))\\n\");\n  assert_eq!(parse_compile_and_output(\"(quasiquote (1 . 2))\"), \"return GDLisp.cons(1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"(quasiquote [1 2])\"), \"return GDLisp.cons(GDLisp.intern(\\\"array\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n  assert_eq!(parse_compile_and_output(\"(quasiquote {1 2})\"), \"return GDLisp.cons(GDLisp.intern(\\\"dict\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n}\n\n#[test]\npub fn full_quasiquote_syntax_test() {\n  assert_eq!(parse_compile_and_output(\"`10\"), \"return 10\\n\");\n  assert_eq!(parse_compile_and_output(\"`(1 2)\"), \"return GDLisp.cons(1, GDLisp.cons(2, null))\\n\");\n  assert_eq!(parse_compile_and_output(\"`(1 . 2)\"), \"return GDLisp.cons(1, 2)\\n\");\n  assert_eq!(parse_compile_and_output(\"`[1 2]\"), \"return GDLisp.cons(GDLisp.intern(\\\"array\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n  assert_eq!(parse_compile_and_output(\"`{1 2}\"), \"return GDLisp.cons(GDLisp.intern(\\\"dict\\\"), GDLisp.cons(1, GDLisp.cons(2, null)))\\n\");\n}\n\n#[test]\npub fn partial_quasiquote_test_1() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `,a)\"), \"var a = 1\\nreturn a\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `(a . ,a))\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"a\\\"), a)\\n\");\n}\n\n#[test]\npub fn partial_quasiquote_test_2() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) (quasiquote ,a))\"), \"var a = 1\\nreturn a\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) (quasiquote (a . ,a)))\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"a\\\"), a)\\n\");\n}\n\n#[test]\npub fn partial_quasiquote_test_3() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) (quasiquote (unquote a)))\"), \"var a = 1\\nreturn a\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) (quasiquote (a . (unquote a))))\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"a\\\"), a)\\n\");\n}\n\n#[test]\npub fn partial_quasiquote_test_4() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `(unquote a))\"), \"var a = 1\\nreturn a\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `(a . (unquote a)))\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"a\\\"), a)\\n\");\n}\n\n#[test]\npub fn array_quasiquote_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `[a ,a a])\"), r#\"var a = 1\nreturn GDLisp.cons(GDLisp.intern(\"array\"), GDLisp.cons(GDLisp.intern(\"a\"), GDLisp.cons(a, GDLisp.cons(GDLisp.intern(\"a\"), null))))\n\"#);\n}\n\n#[test]\npub fn dict_quasiquote_key_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `{,a a})\"), r#\"var a = 1\nreturn GDLisp.cons(GDLisp.intern(\"dict\"), GDLisp.cons(a, GDLisp.cons(GDLisp.intern(\"a\"), null)))\n\"#);\n}\n\n#[test]\npub fn dict_quasiquote_value_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `{a ,a})\"), r#\"var a = 1\nreturn GDLisp.cons(GDLisp.intern(\"dict\"), GDLisp.cons(GDLisp.intern(\"a\"), GDLisp.cons(a, null)))\n\"#);\n}\n\n#[test]\npub fn vector_quote_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) 'V{a a})\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"vector\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), null)))\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) 'V{a a a})\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"vector\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), null))))\\n\");\n}\n\n#[test]\npub fn vector_quasiquote_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `V{a ,a})\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"vector\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), GDLisp.cons(a, null)))\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) `V{a ,a a})\"), \"var a = 1\\nreturn GDLisp.cons(GDLisp.intern(\\\"vector\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), GDLisp.cons(a, GDLisp.cons(GDLisp.intern(\\\"a\\\"), null))))\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_list_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a [2 3])) `(1 ,.a 4))\"),\n             \"var a = [2, 3]\\nreturn GDLisp.cons(1, GDLisp.append(GDLisp.cons(GDLisp.sys_DIV_qq_smart_list(a), GDLisp.cons(GDLisp.cons(4, null), null))))\\n\");\n}\n\n#[test]\npub fn quasiquote_nested_test() {\n  assert_eq!(parse_compile_and_output(\"``(,a)\"),\n             \"var _quasiquote = null\\nreturn GDLisp.cons(GDLisp.intern(\\\"quasiquote\\\"), GDLisp.cons(GDLisp.cons(GDLisp.cons(GDLisp.intern(\\\"unquote\\\"), GDLisp.cons(GDLisp.intern(\\\"a\\\"), _quasiquote)), null), null))\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_list_test_runner_1() {\n  assert_eq!(parse_and_run(\"((let ((a [2 3])) (print (list->array `(1 ,.a 4)))))\"), \"\\n[1, 2, 3, 4]\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_list_test_runner_2() {\n  assert_eq!(parse_and_run(\"((let ((a '(2 3))) (print (list->array `(1 ,.a 4)))))\"), \"\\n[1, 2, 3, 4]\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_array_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a [2 3])) `[1 ,.a 4])\"),\n             \"var a = [2, 3]\\nreturn GDLisp.cons(GDLisp.intern(\\\"array\\\"), GDLisp.cons(1, GDLisp.append(GDLisp.cons(GDLisp.sys_DIV_qq_smart_list(a), GDLisp.cons(GDLisp.cons(4, null), null)))))\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_array_test_runner_1() {\n  // (list->array ...:cdr) removes the 'array term from the head and\n  // then prints as a Godot array.\n  assert_eq!(parse_and_run(\"((let ((a [3 4])) (print (list->array (quasiquote [1 2 ,.a 5 6]):cdr))))\"), \"\\n[1, 2, 3, 4, 5, 6]\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_array_test_runner_2() {\n  // (list->array ...:cdr) removes the 'array term from the head and\n  // then prints as a Godot array.\n  assert_eq!(parse_and_run(\"((let ((a '(3 4))) (print (list->array (quasiquote [1 2 ,.a 5 6]):cdr))))\"), \"\\n[1, 2, 3, 4, 5, 6]\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_array_test_runner_3() {\n  // (list->array ...:cdr) removes the 'array term from the head and\n  // then prints as a Godot array.\n  assert_eq!(parse_and_run(\"((let ((a '(3 4))) (print (list->array (quasiquote [1 2 ,.a]):cdr))))\"), \"\\n[1, 2, 3, 4]\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_array_test_runner_4() {\n  // (list->array ...:cdr) removes the 'array term from the head and\n  // then prints as a Godot array.\n  assert_eq!(parse_and_run(\"((let ((a '(3 4))) (print (list->array (quasiquote [1 2 ,.a ,.a]):cdr))))\"), \"\\n[1, 2, 3, 4, 3, 4]\\n\");\n}\n\n#[test]\npub fn quasiquote_unquote_spliced_array_test_runner_5() {\n  // (list->array ...:cdr) removes the 'array term from the head and\n  // then prints as a Godot array.\n  assert_eq!(parse_and_run(\"((let ((a '(3 4))) (print (list->array (quasiquote [,.a 10 ,.a]):cdr))))\"), \"\\n[3, 4, 10, 3, 4]\\n\");\n}\n\n#[test]\npub fn quasiquote_nesting_test() {\n  assert_eq!(parse_compile_and_output(\"`(1)\"),\n             \"return GDLisp.cons(1, null)\\n\");\n  assert_eq!(parse_compile_and_output(\"`(1 2)\"),\n             \"return GDLisp.cons(1, GDLisp.cons(2, null))\\n\");\n  assert_eq!(parse_compile_and_output(\"`(1 2 3)\"),\n             \"return GDLisp.cons(1, GDLisp.cons(2, GDLisp.cons(3, null)))\\n\");\n  assert_eq!(parse_compile_and_output(\"`(1 2 3 4)\"),\n             \"return GDLisp.cons(1, GDLisp.cons(2, GDLisp.cons(3, GDLisp.cons(4, null))))\\n\");\n  assert_eq!(parse_compile_and_output(\"`(1 2 3 4 5)\"),\n             \"var _quasiquote = null\\nreturn GDLisp.cons(1, GDLisp.cons(2, GDLisp.cons(3, GDLisp.cons(4, GDLisp.cons(5, _quasiquote)))))\\n\");\n  assert_eq!(parse_compile_and_output(\"`(1 2 3 4 5 6)\"),\n             \"var _quasiquote = GDLisp.cons(6, null)\\nreturn GDLisp.cons(1, GDLisp.cons(2, GDLisp.cons(3, GDLisp.cons(4, GDLisp.cons(5, _quasiquote)))))\\n\");\n}\n"
  },
  {
    "path": "tests/test/signal_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\n#[test]\npub fn basic_connect_to_signal_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defsignal frobnicated))\n     (let ((foo (Foo:new)))\n       (connect>> foo \"frobnicated\" (lambda () (print \"Received\")))\n       (print \"A\")\n       (foo:emit-signal \"frobnicated\")\n       (print \"B\")))\n  \"#), \"\\nA\\nReceived\\nB\\n\");\n}\n\n#[test]\npub fn signal_multiple_fires_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defsignal frobnicated))\n     (let ((foo (Foo:new)))\n       (connect>> foo \"frobnicated\" (lambda () (print \"Received\")))\n       (print \"A\")\n       (foo:emit-signal \"frobnicated\")\n       (foo:emit-signal \"frobnicated\")\n       (foo:emit-signal \"frobnicated\")\n       (print \"B\")))\n  \"#), \"\\nA\\nReceived\\nReceived\\nReceived\\nB\\n\");\n}\n\n#[test]\npub fn signal_multiple_fires_oneshot_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defsignal frobnicated))\n     (let ((foo (Foo:new)))\n       (connect1>> foo \"frobnicated\" (lambda () (print \"Received\")))\n       (print \"A\")\n       (foo:emit-signal \"frobnicated\")\n       (foo:emit-signal \"frobnicated\")\n       (foo:emit-signal \"frobnicated\")\n       (print \"B\")))\n  \"#), \"\\nA\\nReceived\\nB\\n\");\n}\n\n#[test]\npub fn signal_connect_disconnect_test() {\n  assert_eq!(parse_and_run(r#\"\n    ((defclass Foo (Reference)\n       (defsignal frobnicated))\n     (let ((foo (Foo:new)))\n       (let ((index (connect>> foo \"frobnicated\" (lambda () (print \"1\")))))\n         (print \"A\")\n         (foo:emit-signal \"frobnicated\")\n         (print \"B\")\n         (disconnect>> foo \"frobnicated\" index)\n         (foo:emit-signal \"frobnicated\")\n         (connect>> foo \"frobnicated\" (lambda () (print \"2\")))\n         (print \"C\")\n         (foo:emit-signal \"frobnicated\")\n         (print \"D\"))))\n  \"#), \"\\nA\\n1\\nB\\nC\\n2\\nD\\n\");\n}\n"
  },
  {
    "path": "tests/test/simple_expr_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse gdlisp::compile::error::{GDError, GDErrorF};\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\nuse gdlisp::ir::expr::Expr;\n\nuse super::common::*;\n\n#[test]\npub fn expr_tests() {\n  assert_eq!(parse_compile_and_output(\"100\"), \"return 100\\n\");\n  assert_eq!(parse_compile_and_output(\"(progn 100 200 300)\"), \"return 300\\n\");\n  assert_eq!(parse_compile_and_output(\"()\"), \"return null\\n\");\n}\n\n#[test]\npub fn progn_tests() {\n  assert_eq!(parse_compile_and_output(\"(progn (foo) (bar) (foo))\"), \"foo()\\nbar()\\nreturn foo()\\n\");\n  assert_eq!(parse_compile_and_output(\"(progn)\"), \"return null\\n\");\n  assert_eq!(parse_compile_and_output(\"(progn (progn))\"), \"return null\\n\");\n  assert_eq!(parse_compile_and_output(\"(progn ())\"), \"return null\\n\");\n}\n\n#[test]\npub fn nonexistent_assignment_test() {\n  assert_eq!(\n    parse_compile_and_output_err(\"(set nonexistent-var 0)\"),\n    Err(PError::from(GDError::new(GDErrorF::NoSuchVar(String::from(\"nonexistent-var\")), SourceOffset(5)))),\n  );\n}\n\n#[test]\npub fn assignment_test() {\n\n  // No cell; only accessed directly\n  let result0 = parse_compile_and_output(\"(let ((x 1)) (set x 2))\");\n  assert_eq!(result0, \"var x = 1\\nx = 2\\nreturn x\\n\");\n\n  // No cell; only accessed directly\n  let result1 = parse_compile_and_output(\"(let ((x 1)) (set x 2) 3)\");\n  assert_eq!(result1, \"var x = 1\\nx = 2\\nreturn 3\\n\");\n\n  // Cell; accessed inside lambda\n  let result2 = parse_compile_and_output(\"(let ((x 1)) (lambda () (set x 2)))\");\n  assert_eq!(result2, \"var x = GDLisp.Cell.new(1)\\nreturn _LambdaBlock.new(x)\\n\");\n\n  // Cell; accessed both inside and outside lambda\n  let result3 = parse_compile_and_output(\"(let ((x 1)) (lambda () (set x 2)) (set x 3))\");\n  assert_eq!(result3, \"var x = GDLisp.Cell.new(1)\\nx.contents = 3\\nreturn x.contents\\n\");\n\n  // No cell; read-only\n  let result4 = parse_compile_and_output(\"(let ((x 1)) (lambda () x) x)\");\n  assert_eq!(result4, \"var x = 1\\nreturn x\\n\");\n\n  // Cell; closure and access separately\n  let result5 = parse_compile_and_output(\"(let ((x 1)) (lambda () (set x 1)) x)\");\n  assert_eq!(result5, \"var x = GDLisp.Cell.new(1)\\nreturn x.contents\\n\");\n\n}\n\n#[test]\npub fn custom_assign_test() {\n  let result = parse_compile_decl(\"((defn set-foo (c a b)) (defn example () (set (foo 1 2) 3)))\");\n  assert_eq!(result, r#\"extends Reference\n\n\nstatic func set_foo(c, a, b):\n    return null\n\n\nstatic func example():\n    return set_foo(3, 1, 2)\n\"#);\n}\n\n#[test]\npub fn expr_at_toplevel_test() {\n  // Note: We're using `parse_compile_decl_err` here, not\n  // `parse_compile_and_output_err`, so the system is *expecting*\n  // declarations. We gave it an expression and expect it to come back\n  // with an `ExprAtTopLevel` error.\n  let result = parse_compile_decl_err(\"((+ 1 1))\");\n  assert_eq!(\n    result,\n    Err(PError::from(GDError::new(\n      GDErrorF::ExprAtTopLevel(\n        Expr::call(\"+\", vec!(Expr::from_value(1, SourceOffset(4)), Expr::from_value(1, SourceOffset(6))), SourceOffset(1)),\n      ),\n      SourceOffset(1),\n    ))),\n  );\n}\n\n#[test]\npub fn slot_assign_test_1() {\n  let result = parse_compile_and_output(\"(let ((a 1)) (set a:b 3))\");\n  assert_eq!(result, \"var a = 1\\na.b = 3\\nreturn 3\\n\");\n}\n\n#[test]\npub fn slot_assign_test_2() {\n  let result = parse_compile_and_output(\"(let ((a 1)) (set (access-slot a b) 3))\");\n  assert_eq!(result, \"var a = 1\\na.b = 3\\nreturn 3\\n\");\n}\n\n#[test]\npub fn slot_assign_test_3() {\n  let result = parse_compile_and_output(\"(progn (let ((a 1)) (set (access-slot a b) 3)) 0)\");\n  assert_eq!(result, \"var a = 1\\na.b = 3\\nreturn 0\\n\");\n}\n\n#[test]\npub fn slot_assign_test_4() {\n  let result = parse_compile_and_output(\"(let ((a 1)) (set (access-slot a b) (a:foo)))\");\n  assert_eq!(result, \"var a = 1\\nvar _assign = a.foo()\\na.b = _assign\\nreturn _assign\\n\");\n}\n\n#[test]\npub fn slot_assign_test_5() {\n  let result = parse_compile_and_output(\"(progn (let ((a 1)) (set (access-slot a b) (a:foo))) 0)\");\n  assert_eq!(result, \"var a = 1\\na.b = a.foo()\\nreturn 0\\n\");\n}\n\n#[test]\npub fn assign_to_self_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defclass Foo (Node) (defn _init () (set self 1))))\"),\n    Err(PError::from(GDError::new(GDErrorF::CannotAssignTo(String::from(\"self\")), SourceOffset(47)))),\n  );\n}\n\n#[test]\npub fn assign_to_const_test() {\n  assert_eq!(\n    parse_compile_decl_err(\"((defconst CONSTANT 1) (defn foo () (set CONSTANT 2)))\"),\n    Err(PError::from(GDError::new(GDErrorF::CannotAssignTo(String::from(\"CONSTANT\")), SourceOffset(50)))),\n  );\n}\n\n#[test]\npub fn weird_name_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a-b-c 1)) (a-b-c:d-e-f))\"),\n             \"var a_b_c = 1\\nreturn a_b_c.d_e_f()\\n\");\n}\n\n#[test]\npub fn assign_to_slot_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((x 1)) (set x:foo 100) 2)\"),\n             \"var x = 1\\nx.foo = 100\\nreturn 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(let ((x 1)) (set x:foo 100))\"),\n             \"var x = 1\\nx.foo = 100\\nreturn 100\\n\");\n  assert_eq!(parse_compile_and_output(\"(flet ((f () 1)) (set (f):foo 100) 2)\"),\n             \"_flet().foo = 100\\nreturn 2\\n\");\n  assert_eq!(parse_compile_and_output(\"(flet ((f () 1)) (set (f):foo 100))\"),\n             \"_flet().foo = 100\\nreturn 100\\n\");\n}\n\n#[test]\npub fn sys_this_file_test() {\n  assert_eq!(parse_compile_and_output(\"(sys/special-ref this-file)\"),\n             \"return load(\\\"res://TEST.gd\\\")\\n\");\n}\n\n#[test]\npub fn this_file_test() {\n  assert_eq!(parse_compile_and_output(\"(this-file)\"),\n             \"return load(\\\"res://TEST.gd\\\")\\n\");\n}\n\n#[test]\npub fn sys_this_file_run_test() {\n  let output = parse_and_run(r#\"\n    ((defn foo (x) (* x 2))\n     (print (foo 124))\n     (print ((sys/special-ref this-file):foo 124)))\"#);\n  assert_eq!(output, \"\\n248\\n248\\n\");\n}\n\n#[test]\npub fn sys_this_file_run_in_macro_test() {\n  let output = parse_and_run(r#\"\n    ((defn foo (x) (* x 2))\n     (defmacro baz () '((sys/special-ref this-file):foo 124))\n     (print (baz)))\"#);\n  assert_eq!(output, \"\\n248\\n\");\n}\n\n#[test]\npub fn this_file_run_test() {\n  let output = parse_and_run(r#\"\n    ((defn foo (x) (* x 2))\n     (print (foo 124))\n     (print ((this-file):foo 124)))\"#);\n  assert_eq!(output, \"\\n248\\n248\\n\");\n}\n\n#[test]\npub fn this_file_run_in_macro_test() {\n  let output = parse_and_run(r#\"\n    ((defn foo (x) (* x 2))\n     (defmacro baz () '((this-file):foo 124))\n     (print (baz)))\"#);\n  assert_eq!(output, \"\\n248\\n\");\n}\n\n#[test]\npub fn assign_to_vec_test_1() {\n  let output = parse_and_run(r#\"\n    ((let ((v (vector 1 1 1)))\n       (print v:x)\n       (set v:x 10)\n       (print v:x)))\n  \"#);\n  assert_eq!(output, \"\\n1\\n10\\n\");\n}\n\n#[test]\npub fn assign_to_vec_test_2() {\n  let output = parse_and_run(r#\"\n    ((let ((v (vector 1 1 1)))\n       (print v:x)\n       (funcall (lambda () (set v:x 10)))\n       (print v:x)))\n  \"#);\n  assert_eq!(output, \"\\n1\\n10\\n\");\n}\n\n#[test]\npub fn compound_assignment_test() {\n  assert_eq!(parse_compile_and_output(\"(let ((a 1)) (update a (+ 2)))\"), r#\"var a = 1\na = a + 2\nreturn a\n\"#);\n}\n\n#[test]\npub fn compound_assignment_run_test_1() {\n  assert_eq!(parse_and_run(\"((let ((a 1)) (update a (+ 2)) (print a)))\"), \"\\n3\\n\");\n}\n\n#[test]\npub fn compound_assignment_run_test_2() {\n  assert_eq!(parse_and_run(\"((defn foo (x) (* x 2)) (let ((a 9)) (update a foo) (print a)))\"), \"\\n18\\n\");\n}\n"
  },
  {
    "path": "tests/test/string_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\n#[test]\npub fn basic_string_lit_tests() {\n  assert_eq!(parse_compile_and_output(r#\"\"a\"\"#), \"return \\\"a\\\"\\n\");\n  assert_eq!(parse_compile_and_output(r#\"\"ab\\\"cd\"\"#), \"return \\\"ab\\\\\\\"cd\\\"\\n\");\n  assert_eq!(parse_compile_and_output(r#\"\"α\"\"#), \"return \\\"α\\\"\\n\");\n  assert_eq!(parse_compile_and_output(\"\\\"α\\nβ\\\"\"), \"return \\\"α\\\\nβ\\\"\\n\");\n}\n\n#[test]\npub fn basic_node_path_test_1() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo (Reference) main (defn foo () $a)))\"#),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return $a\n\"#);\n}\n\n#[test]\npub fn basic_node_path_test_2() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo (Reference) main (defn foo () $\"a\")))\"#),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return $a\n\"#);\n}\n\n#[test]\npub fn basic_node_path_test_3() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo (Reference) main (defn foo () $b-c)))\"#),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return $\"b-c\"\n\"#);\n}\n\n#[test]\npub fn basic_node_path_test_4() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo (Reference) main (defn foo () $\"b c\")))\"#),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return $\"b c\"\n\"#);\n}\n\n#[test]\npub fn basic_node_path_test_5() {\n  assert_eq!(parse_compile_decl(r#\"((defclass Foo (Reference) main (defn foo () $\"5\\nc\")))\"#),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return $\"5\\nc\"\n\"#);\n}\n\n#[test]\npub fn basic_node_path_test_6() {\n  // Note: \\n is a *literal* newline in the code here, which will be\n  // converted to a \"backslash-n\" sequence in GDScript.\n  assert_eq!(parse_compile_decl(\"((defclass Foo (Reference) main (defn foo () $\\\"5\\nc\\\")))\"),\n             r#\"extends Reference\n\n\nfunc _init():\n    pass\n\n\nfunc foo():\n    return $\"5\\nc\"\n\"#);\n}\n"
  },
  {
    "path": "tests/test/type_checking_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\n#[test]\npub fn primitive_instance_check_test_1() {\n  let result = parse_and_run(r#\"\n    ((print (instance? 1 Int))\n     (print (instance? 1.0 Float))\n     (print (instance? 1 Float))\n     (print (instance? (Reference:new) Object))\n     (print (instance? (Reference:new) Reference))\n     (print (instance? (Reference:new) Node))\n     (print (instance? (Reference:new) Array)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nTrue\\nFalse\\nTrue\\nTrue\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn primitive_instance_check_test_2() {\n  let result = parse_and_run(r#\"\n    ((print (instance? V{1 2} Vector3))\n     (print (instance? V{1 2 3} Vector3))\n     (print (instance? V{1 2} Vector2))\n     (print (instance? V{1 2 3} Vector2))\n     (print (instance? 0 Vector2))\n     (print (instance? \"A\" Vector3)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nTrue\\nTrue\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn primitive_instance_check_test_3() {\n  let result = parse_and_run(r#\"\n    ((print (instance? V{1 2} Transform2D))\n     (print (instance? V{1 2 3} Transform2D))\n     (print (instance? (Transform2D 0 V{1 1}) Transform2D)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\n\");\n}\n\n#[test]\npub fn primitive_instance_check_test_4() {\n  let result = parse_and_run(r#\"\n    ((print (instance? (Color 0 0 0 1) Color))\n     (print (instance? Color:red Color))\n     (print (instance? 0.1 Color)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nTrue\\nFalse\\n\");\n}\n\n#[test]\npub fn primitive_instance_check_test_5() {\n  let result = parse_and_run(r#\"\n    ((print (instance? Transform:IDENTITY Transform))\n     (print (instance? Color:red Transform))\n     (print (instance? Plane:PLANE_YZ Plane))\n     (print (instance? Color:red Plane)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nFalse\\nTrue\\nFalse\\n\");\n}\n\n#[test]\npub fn primitive_instance_check_test_6() {\n  let result = parse_and_run(r#\"\n    ((print (instance? Basis:IDENTITY Basis))\n     (print (instance? Color:red Basis))\n     (print (instance? Quat:IDENTITY Quat))\n     (print (instance? Color:red Quat)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nFalse\\nTrue\\nFalse\\n\");\n}\n\n#[test]\npub fn object_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node Object))\n      (print (instance? (Reference:new) Object))\n      (print (instance? 3 Object))\n      (print (instance? [3] Object))\n      (print (instance? {\"a\" 3} Object))\n      (print (instance? nil Object))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nTrue\\nFalse\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn reference_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node Reference))\n      (print (instance? (Reference:new) Reference))\n      (print (instance? 3 Reference))\n      (print (instance? [3] Reference))\n      (print (instance? {\"a\" 3} Reference))\n      (print (instance? nil Reference))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nTrue\\nFalse\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn node_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node Node))\n      (print (instance? (Reference:new) Node))\n      (print (instance? 3 Node))\n      (print (instance? [3] Node))\n      (print (instance? {\"a\" 3} Node))\n      (print (instance? nil Node))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nFalse\\nFalse\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn custom_reference_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((defclass Foo (Reference))\n     (let ((my-node (Node:new)))\n      (print (instance? my-node Foo))\n      (print (instance? (Reference:new) Foo))\n      (print (instance? (Foo:new) Foo))\n      (print (instance? 3 Foo))\n      (print (instance? [3] Foo))\n      (print (instance? {\"a\" 3} Foo))\n      (print (instance? nil Foo))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\nFalse\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn custom_node_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((defclass Foo (Node))\n     (let ((my-node (Node:new))\n           (my-foo (Foo:new)))\n      (print (instance? my-node Foo))\n      (print (instance? (Reference:new) Foo))\n      (print (instance? my-foo Foo))\n      (print (instance? 3 Foo))\n      (print (instance? [3] Foo))\n      (print (instance? {\"a\" 3} Foo))\n      (print (instance? nil Foo))\n      (my-node:free)\n      (my-foo:free)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\nFalse\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn number_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node Number))\n      (print (instance? (Reference:new) Number))\n      (print (instance? 3 Number))\n      (print (instance? 3.1 Number))\n      (print (instance? [3] Number))\n      (print (instance? {\"a\" 3} Number))\n      (print (instance? nil Number))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\nTrue\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn any_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node Any))\n      (print (instance? (Reference:new) Any))\n      (print (instance? 3 Any))\n      (print (instance? 3.1 Any))\n      (print (instance? [3] Any))\n      (print (instance? {\"a\" 3} Any))\n      (print (instance? nil Any))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nTrue\\nTrue\\nTrue\\nTrue\\nTrue\\nTrue\\n\");\n}\n\n#[test]\npub fn anyref_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node AnyRef))\n      (print (instance? (Reference:new) AnyRef))\n      (print (instance? 3 AnyRef))\n      (print (instance? 3.1 AnyRef))\n      (print (instance? [3] AnyRef))\n      (print (instance? {\"a\" 3} AnyRef))\n      (print (instance? nil AnyRef))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nTrue\\nFalse\\nFalse\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn anyval_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node AnyVal))\n      (print (instance? (Reference:new) AnyVal))\n      (print (instance? 3 AnyVal))\n      (print (instance? 3.1 AnyVal))\n      (print (instance? [3] AnyVal))\n      (print (instance? {\"a\" 3} AnyVal))\n      (print (instance? nil AnyVal))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\nTrue\\nTrue\\nTrue\\nTrue\\n\");\n}\n\n#[test]\npub fn nothing_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((let ((my-node (Node:new)))\n      (print (instance? my-node Nothing))\n      (print (instance? (Reference:new) Nothing))\n      (print (instance? 3 Nothing))\n      (print (instance? 3.1 Nothing))\n      (print (instance? [3] Nothing))\n      (print (instance? {\"a\" 3} Nothing))\n      (print (instance? nil Nothing))\n      (my-node:free)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nFalse\\nFalse\\nFalse\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn array_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((print (instance? (Reference:new) Array))\n     (print (instance? 3 Array))\n     (print (instance? [3] Array))\n     (print (instance? ((literally PoolIntArray) [3]) Array)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\nFalse\\n\");\n}\n\n#[test]\npub fn specific_array_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((print (instance? (Reference:new) PoolIntArray))\n     (print (instance? 3 PoolIntArray))\n     (print (instance? [3] PoolIntArray))\n     (print (instance? ((literally PoolIntArray) [3]) PoolIntArray)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nFalse\\nTrue\\n\");\n}\n\n#[test]\npub fn base_array_instance_check_test() {\n  let result = parse_and_run(r#\"\n    ((print (instance? (Reference:new) BaseArray))\n     (print (instance? 3 BaseArray))\n     (print (instance? [3] BaseArray))\n     (print (instance? ((literally PoolIntArray) [3]) BaseArray)))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\nTrue\\n\");\n}\n\n#[test]\npub fn typeof_primitive_int_test() {\n  let result = parse_and_run(r#\"\n    ((let ((t (typeof 100)))\n       (print (instance? 100 t))\n       (print (instance? -2 t))\n       (print (instance? \"A\" t))\n       (print (instance? (Reference:new) t))))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nTrue\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn typeof_primitive_string_test() {\n  let result = parse_and_run(r#\"\n    ((let ((t (typeof \"A\")))\n       (print (instance? 100 t))\n       (print (instance? -2 t))\n       (print (instance? \"A\" t))\n       (print (instance? (Reference:new) t))))\n  \"#);\n  assert_eq!(result, \"\\nFalse\\nFalse\\nTrue\\nFalse\\n\");\n}\n\n#[test]\npub fn typeof_array_test() {\n  let result = parse_and_run(r#\"\n    ((let ((t (typeof [])))\n       (print (instance? [] t))\n       (print (instance? [3] t))\n       (print (instance? ((literally PoolIntArray) []) t))\n       (print (instance? (Reference:new) t))))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\nTrue\\nFalse\\nFalse\\n\");\n}\n\n#[test]\npub fn typeof_class_test() {\n  let result = parse_and_run(r#\"\n    ((defclass Foo (Reference))\n     (let ((foo (Foo:new)))\n       (print (= (typeof foo) Foo))))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\n\");\n}\n\n#[test]\npub fn typeof_ref_test() {\n  let result = parse_and_run(r#\"\n    ((let ((r (Reference:new)))\n       (print (= (typeof r) Reference))))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\n\");\n}\n\n#[test]\npub fn typeof_node_test() {\n  let result = parse_and_run(r#\"\n    ((let ((node (Node2D:new)))\n       (print (= (typeof node) Node2D))\n       (node:free)))\n  \"#);\n  assert_eq!(result, \"\\nTrue\\n\");\n}\n"
  },
  {
    "path": "tests/test/typecast_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\n\n#[test]\npub fn int_test() {\n  assert_eq!(parse_compile_and_output(\"(int 10)\"), \"return int(10)\\n\");\n}\n\n#[test]\npub fn int_running_test() {\n  assert_eq!(parse_and_run(\"((print (int 10.5)))\"), \"\\n10\\n\");\n  assert_eq!(parse_and_run(\"((print (int \\\"4\\\")))\"), \"\\n4\\n\");\n}\n\n#[test]\npub fn str_test() {\n  assert_eq!(parse_compile_and_output(\"(str 10)\"), \"return str(10)\\n\");\n}\n\n#[test]\npub fn str_running_test() {\n  assert_eq!(parse_and_run(\"((print (str 10.5)))\"), \"\\n10.5\\n\");\n  assert_eq!(parse_and_run(\"((print (str 4)))\"), \"\\n4\\n\");\n  assert_eq!(parse_and_run(\"((print (str \\\"foo\\\")))\"), \"\\nfoo\\n\");\n  assert_eq!(parse_and_run(\"((print (str nil)))\"), \"\\nNull\\n\");\n}\n\n#[test]\npub fn bool_test() {\n  // We wrap bool(...) to support more types.\n  assert_eq!(parse_compile_and_output(\"(bool 10)\"), \"return GDLisp._bool(10)\\n\");\n}\n\n#[test]\npub fn char_test() {\n  assert_eq!(parse_compile_and_output(\"(char 10)\"), \"return char(10)\\n\");\n}\n\n#[test]\npub fn ord_test() {\n  assert_eq!(parse_compile_and_output(\"(ord \\\"A\\\")\"), \"return ord(\\\"A\\\")\\n\");\n}\n\n#[test]\npub fn bool_running_test() {\n  assert_eq!(parse_and_run(\"((print (bool 10))\n                             (print (bool 0))\n                             (print (bool (Reference:new))))\"),\n             \"\\nTrue\\nFalse\\nTrue\\n\");\n}\n"
  },
  {
    "path": "tests/test/while_test.rs",
    "content": "// Copyright 2023 Silvio Mayolo\n//\n// This file is part of GDLisp.\n//\n// GDLisp is free software: you can redistribute it and/or modify it\n// 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// GDLisp is distributed in the hope that it will be useful, but\n// WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n// General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with GDLisp. If not, see <https://www.gnu.org/licenses/>.\n\nuse super::common::*;\nuse gdlisp::pipeline::error::PError;\nuse gdlisp::pipeline::source::SourceOffset;\nuse gdlisp::ir::loops::error::LoopPrimitiveError;\n\n#[test]\npub fn while_tests() {\n  assert_eq!(parse_compile_and_output(\"(while 1)\"), \"while 1:\\n    pass\\nreturn null\\n\");\n  assert_eq!(parse_compile_and_output(\"(while (foo) (foo1 0) (foo2 0 0))\"), \"while foo():\\n    foo1(0)\\n    foo2(0, 0)\\nreturn null\\n\");\n  assert_eq!(parse_compile_and_output(\"(foo1 (while (bar)))\"), \"while bar():\\n    pass\\nreturn foo1(null)\\n\");\n}\n\n#[test]\npub fn compound_while_tests() {\n  // If expressions cannot be compiled into a single GDScript\n  // expression, so this forces the while loop to use the \"compound\"\n  // form.\n  assert_eq!(parse_compile_and_output(\"(while (if 1 2 3) (foo))\"), \"while true:\\n    var _cond = null\\n    if 1:\\n        _cond = 2\\n    else:\\n        if true:\\n            _cond = 3\\n        else:\\n            _cond = null\\n    if !_cond:\\n        break\\n    foo()\\nreturn null\\n\")\n}\n\n#[test]\npub fn while_test_with_break() {\n  assert_eq!(parse_compile_and_output(\"(while 1 (break))\"), r#\"while 1:\n    break\nreturn null\n\"#);\n}\n\n#[test]\npub fn while_test_with_continue() {\n  assert_eq!(parse_compile_and_output(\"(while 1 (continue))\"), r#\"while 1:\n    continue\nreturn null\n\"#);\n}\n\n#[test]\npub fn while_test_with_break_in_if() {\n  assert_eq!(parse_compile_and_output(\"(while 1 (if 1 (break) (continue)))\"), r#\"while 1:\n    if 1:\n        break\n    else:\n        if true:\n            continue\n        else:\n            pass\nreturn null\n\"#);\n}\n\n#[test]\npub fn while_test_with_break_in_if_cond() {\n  assert_eq!(parse_compile_and_output(\"(while (if 1 (break) (continue)) (foo))\"), r#\"while true:\n    var _cond = null\n    if 1:\n        break\n        _cond = null\n    else:\n        if true:\n            continue\n            _cond = null\n        else:\n            _cond = null\n    if !_cond:\n        break\n    foo()\nreturn null\n\"#);\n}\n\n#[test]\npub fn bad_break_test() {\n  assert_eq!(parse_compile_and_output_err(\"(break)\"),\n             Err(PError::from(LoopPrimitiveError::break_error(SourceOffset(0)))));\n}\n\n#[test]\npub fn bad_continue_test() {\n  assert_eq!(parse_compile_and_output_err(\"(continue)\"),\n             Err(PError::from(LoopPrimitiveError::continue_error(SourceOffset(0)))));\n}\n\n#[test]\npub fn bad_break_in_plain_lambda_test() {\n  assert_eq!(parse_compile_and_output_err(\"(lambda () (break))\"),\n             Err(PError::from(LoopPrimitiveError::break_error(SourceOffset(11)))));\n}\n\n#[test]\npub fn bad_continue_in_plain_lambda_test() {\n  assert_eq!(parse_compile_and_output_err(\"(lambda () (continue))\"),\n             Err(PError::from(LoopPrimitiveError::continue_error(SourceOffset(11)))));\n}\n\n#[test]\npub fn bad_break_in_loop_lambda_test() {\n  assert_eq!(parse_compile_and_output_err(\"(while 1 (lambda () (break)))\"),\n             Err(PError::from(LoopPrimitiveError::break_error(SourceOffset(20)).in_closure())));\n}\n\n#[test]\npub fn bad_continue_in_loop_lambda_test() {\n  assert_eq!(parse_compile_and_output_err(\"(while 1 (lambda () (continue)))\"),\n             Err(PError::from(LoopPrimitiveError::continue_error(SourceOffset(20)).in_closure())));\n}\n\n#[test]\npub fn bad_continue_in_loop_lambda_class_test() {\n  assert_eq!(parse_compile_and_output_err(\"(while 1 (new Reference (defn foo () (continue))))\"),\n             Err(PError::from(LoopPrimitiveError::continue_error(SourceOffset(37)).in_closure())));\n}\n"
  }
]