diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..4d382c2
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "opt/luaminify"]
+ path = opt/luaminify
+ url = https://github.com/stravant/LuaMinify.git
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
index 5775ca1..b218928 100644
--- a/README.md
+++ b/README.md
@@ -1,52 +1,72 @@
-# Pandoc Literate Programming
+# Literate Pandoc
-[Pandoc] is world famous as a "swiss-army" document converted. However, this is
-because Pandoc is also a document parser. Thanks to this capability, this repo
-is a [Pandoc filter] for [literate programming][]: evaluate the code written in
-your documents with Pandoc and [Lua].
+[Pandoc] is a world famous "swiss-army" document converted. This is because
+Pandoc is also a document parser. Thanks to this capability, this repo is a
+[Pandoc filter] written in [Lua] for [literate] and [natural] programming
+(LNP), i.e.: ["Programming \[...\] as the process of creating works of
+literature"][1].
-> NOTE: Pandoc has [Literate Haskell support], but it doesn't support all input
-> and output formats. This filter tries to support to all of them.
+## Requirements
+
+- [Pandoc]
+
+## Install
+
+Just download this repo:
+
+ git clone https://git.cuates.net/perro/literate-pandoc.git
## Usage
-In your terminal, just execute:
+With Pandoc installed and this repo downloaded, do:
- sh tests/test.sh FORMAT
+ pandoc --lua-filter PATH/TO/literate-pandoc/src/literate.lua -t FORMAT DOC
-Pick one or more [FORMAT namespaces], e.g.:
+For example, if `DOC` is `source.md` and the output `FORMAT` is HTML, do:
+
+ pandoc --lua-filter PATH/TO/literate-pandoc/src/literate.lua -t html source.md
+
+## Manual
+
+Learn how to do LNP [here].
+
+## Test
+
+Inside this repo, do:
+
+ sh scripts/test.sh FORMAT1 FORMAT2
+
+For example, if `FORMAT1` is Markdwon and `FORMAT2` is HTML, do:
sh tests/test.sh markdown html
-You can also use this filter for your documents:[^1]
+For distribution tests, do:
- pandoc --lua-filter literate.lua -t FORMAT YOUR_DOC
+ sh scripts/test.sh --dist FORMAT1 FORMAT2
-Besides your document, you must add the class `eval` in the [fenced code
-blocks] that you want to evaluate. If you also want to replace the code block
-content with the evaluation result, you also must add the class `replace`.
-Check [this test] if you don't known what I am talking about.
+## Acknowledgments
+
+This wouldn't be possible without these projects and their collaborators:
+
+- [Pandoc][]: universal document converter and parser; handles the
+ requirements for LNP.
+- [Lua][]: programming language; enables LNP.
+- [Fennel][]: [Lisp] dialect with full Lua compatibility; allows to go from
+ LNP to Lisp or Lua.
+
+## License
+
+Literate Pandoc is under [GPLv3].
Happy hacking :)
-## TODO
-
-In order to support any markup language,[^2] the filter:
-
-- Can't rely in the built-in pandoc code blocks.
-- Requires syntaxis for custom literate programming blocks.
-
-[^1]: Right now this is only for testing, so only a subset of Lisp function are
- supported (`+`, `list`, `first`)... The objective is to support any
- programming language and more!
-
-[^2]: Some markup languages like ORG doesn't support classes for code blocks.
-
[Pandoc]: https://pandoc.org/
[Pandoc filter]: https://pandoc.org/lua-filters.html
- [literate programming]: https://en.wikipedia.org/wiki/Literate_programming
[Lua]: https://www.lua.org/
- [Literate Haskell support]: https://pandoc.org/MANUAL.html#literate-haskell-support
- [FORMAT namespaces]: https://pandoc.org/MANUAL.html#option--to
- [fenced code blocks]: https://pandoc.org/MANUAL.html#fenced-code-blocks
- [this test]: https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/tests/src/t2.md?display=source
+ [literate]: https://en.wikipedia.org/wiki/Literate_programming
+ [natural]: https://en.wikipedia.org/wiki/Natural-language_programming
+ [1]: https://web.archive.org/web/20170605163729/http://www.desy.de/user/projects/LitProg/Philosophy.html
+ [here]: https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/man/README.md
+ [Fennel]: https://fennel-lang.org
+ [Lisp]: https://en.wikipedia.org/wiki/Lisp_(programming_language)
+ [GPLv3]: https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/LICENSE.txt
diff --git a/dist/literate.min.lua b/dist/literate.min.lua
new file mode 100644
index 0000000..04b8caf
--- /dev/null
+++ b/dist/literate.min.lua
@@ -0,0 +1,2957 @@
+--[[
+Literate Pandoc & Fennel Bundle:
+ A Pandoc filter for literate and natural programming
+Fennel:
+ (C) 2016-2023 Calvin Rose and contributors
+ License: MIT License https://git.sr.ht/~technomancy/fennel/tree/main/item/LICENSE
+ Source: https://sr.ht/~technomancy/fennel or https://github.com/bakpakin/Fennel/issues
+ Website: https://fennel-lang.org
+Literate Pandoc:
+ (C) 2023 perro hi@perrotuerto.blog
+ License: GPLv3 https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/LICENSE.txt
+ Source: https://git.cuates.net/perro/literate-pandoc
+The following code is minified, check Literate Pandoc source for a readable version
+]]--
+
+package.preload["fennel.repl"]=package.preload["fennel.repl"]or
+function(...)
+local o=require("fennel.utils")local UesS=require("fennel.parser")
+local CZ=require("fennel.compiler")local iD90MK=require("fennel.specials")
+local ZuYO=require("fennel.view")local j=(table.unpack or _G.unpack)
+local function t(aBv0K6z)
+local function GI()if(0 <
+aBv0K6z["stack-size"])then return".."else return">> "end end;io.write(GI())io.flush()local nsm9=io.read()return
+(nsm9 and(nsm9 .."\n"))end;local function NChC(kO)io.write(table.concat(kO,"\9"))
+return io.write("\n")end
+local function Z4(FIHZeLmK,Gho7Jfcz,pi3tYW)
+local function nt()local R7iAD=FIHZeLmK
+if
+(R7iAD=="Lua Compile")then return
+("Bad code generated - likely a bug with the compiler:\n".."--- Generated Lua Start ---\n"..
+pi3tYW.."--- Generated Lua End ---\n")elseif(R7iAD==
+"Runtime")then return
+(CZ.traceback(tostring(Gho7Jfcz),4).."\n")elseif true then local Re6P_=R7iAD;return
+("%s error: %s\n"):format(FIHZeLmK,tostring(Gho7Jfcz))else return nil end end;return io.write(nt())end
+local function I13Z9(VNtfqHq,wB,cmQxB1bj)local aozwWK
+do local G0AXlB={}local Pv=#G0AXlB
+for HW in pairs(VNtfqHq.___replLocals___)do
+local cWzY=("local %s = ___replLocals___['%s']"):format(HW,HW)
+if(nil~=cWzY)then Pv=(Pv+1)do end(G0AXlB)[Pv]=cWzY else end end;aozwWK=G0AXlB end;local GbnuD
+do local ESO={}local DEb=#ESO
+for Uzk3,LVrOH0 in pairs(cmQxB1bj.manglings)do local YLRWb;if not
+cmQxB1bj.gensyms[LVrOH0]then
+YLRWb=("___replLocals___['%s'] = %s"):format(LVrOH0,LVrOH0)else YLRWb=nil end;if
+(nil~=YLRWb)then DEb=(DEb+1)do end(ESO)[DEb]=YLRWb else end end;GbnuD=ESO end;local CW2;if wB:find("\n")then CW2="\n"else CW2=" "end;local function vlSSXJ()
+if next(aozwWK)then return(
+table.concat(aozwWK," ")..CW2)else return""end end
+local function k()
+local u6sl,F=wB:match("^(.*)[\n ](return .*)$")
+if((nil~=u6sl)and(nil~=F))then local fgPC0lUc=u6sl;local oRfi=F;return
+(fgPC0lUc..CW2 ..
+table.concat(GbnuD," ")..CW2 ..oRfi)elseif true then local CrAGWh=u6sl;return wB else return nil end end;return(vlSSXJ()..k())end
+local function tj(d,sCjf4,y)local P=2000;local Av={}local nw={}local OVuV52eb=y:gsub(".*[%s)(]+","")local c=false
+local function Td(yqTH,gcTeY,Ce)
+local QzyH5hCR=(
+(gcTeY==d)or(gcTeY==d.___replLocals___))local hYkd7=nw;local UKZpzj=#hYkd7;local function Fniffr()
+if QzyH5hCR then return sCjf4.manglings else return gcTeY end end
+for FMt_pX,ouz in o.allpairs(Fniffr())do if
+(P<=#nw)then break end;local K1TX5
+do local BYnM1VtZ
+if QzyH5hCR then BYnM1VtZ=ouz else BYnM1VtZ=FMt_pX end
+if
+(
+(type(FMt_pX)=="string")and
+(yqTH==FMt_pX:sub(0,#yqTH))and not Av[FMt_pX]and((":"~=Ce:sub(-1))or
+("function"==type(gcTeY[BYnM1VtZ]))))then Av[FMt_pX]=true;K1TX5=(Ce..FMt_pX)else K1TX5=nil end end
+if(nil~=K1TX5)then UKZpzj=(UKZpzj+1)do end(hYkd7)[UKZpzj]=K1TX5 else end end;return hYkd7 end
+local function KlzsEvn(djzP,b_,Q1w1zKw,a5F,Ue0N)local L99;if Ue0N then L99="^([^:]+):(.*)"else L99="^([^.]+)%.(.*)"end
+local B3,PL7_Dl=djzP:match(L99)local N8V=(sCjf4.manglings[B3]or B3)
+if(
+type(b_[N8V])=="table")then c=true;if Ue0N then return
+Td(PL7_Dl,b_[N8V],(Q1w1zKw..B3 ..":"))else
+return a5F(PL7_Dl,b_[N8V],(Q1w1zKw..B3))end else return nil end end
+local function eOfvjl1(b,kCfK5,TJ5Kz5NO)local Pke1T8
+if TJ5Kz5NO then Pke1T8=(TJ5Kz5NO..".")else Pke1T8=""end
+if(not b:find("%.")and b:find(":"))then return
+KlzsEvn(b,kCfK5,Pke1T8,eOfvjl1,true)elseif not b:find("%.")then return Td(b,kCfK5,Pke1T8)else return
+KlzsEvn(b,kCfK5,Pke1T8,eOfvjl1,false)end end
+for HKyO,mx in
+ipairs({sCjf4.specials,sCjf4.macros,(d.___replLocals___ or{}),d,d._G})do if c then break end;eOfvjl1(OVuV52eb,mx)end;return nw end;local EgG={}local function YgsJ(od)return od:match("^%s*,")end
+local function aV1Rus()local iRgs
+do
+local KYSkMOxM9={}local m1=#KYSkMOxM9
+for L,uApola2 in pairs(EgG)do
+local jbLeA=(" ,%s - %s"):format(L,(
+(CZ.metadata):get(uApola2,"fnl/docstring")or"undocumented"))
+if(nil~=jbLeA)then m1=(m1+1)do end(KYSkMOxM9)[m1]=jbLeA else end end;iRgs=KYSkMOxM9 end;return table.concat(iRgs,"\n")end
+EgG.help=function(Jy,KDEqSly,w2hBXVO)
+return
+w2hBXVO({("Welcome to Fennel.\nThis is the REPL where you can enter code to be evaluated.\nYou can also run these repl commands:\n\n"..
+
+aV1Rus()..
+"\n ,exit - Leave the repl.\n\nUse ,doc something to see descriptions for individual macros and special forms.\n\nFor more information about the language, see https://fennel-lang.org/reference")})end;do end
+(CZ.metadata):set(EgG.help,"fnl/docstring","Show this message.")
+local function Gsx955(Q,c9MGuH,aOuScj,XXtE6Ji)
+local J,oyvqbnx=pcall(iD90MK["load-code"]("return require(...)",c9MGuH),Q)
+if((J==true)and(nil~=oyvqbnx))then local YdCSDvl=oyvqbnx;local Cum;package.loaded[Q]=
+nil;Cum=nil;local Je_,x=pcall(require,Q)local _nbhL;if not Je_ then aOuScj({x})
+_nbhL=YdCSDvl else _nbhL=x end
+iD90MK["macro-loaded"][Q]=nil
+if
+((type(YdCSDvl)=="table")and(type(_nbhL)=="table"))then
+for VJ_RQcYw,bgDxqiW in pairs(_nbhL)do YdCSDvl[VJ_RQcYw]=bgDxqiW end;for u in pairs(YdCSDvl)do
+if(nil== (_nbhL)[u])then YdCSDvl[u]=nil else end end;package.loaded[Q]=YdCSDvl else end;return aOuScj({"ok"})elseif
+((J==false)and(nil~=oyvqbnx))then local n_=oyvqbnx
+if n_:match("loop or previous error loading module")then package.loaded[Q]=
+nil;return Gsx955(Q,c9MGuH,aOuScj,XXtE6Ji)elseif
+(iD90MK["macro-loaded"])[Q]then iD90MK["macro-loaded"][Q]=nil;return nil else local function a()
+local xei=n_:gsub("\n.*","")return xei end;return XXtE6Ji("Runtime",a())end else return nil end end
+local function _bh_k(zHHW,H,OQYu)local k9,bRREug,R_RJfoeP=pcall(zHHW)
+if((k9 ==true)and(bRREug==true)and(nil~=
+R_RJfoeP))then
+local HuvPsV=R_RJfoeP;return OQYu(HuvPsV)elseif(k9 ==false)then
+return H("Parse","Couldn't parse input.")else return nil end end
+EgG.reload=function(pZ8,J12U,jzU,nrU)
+local function Bi7(EW4bXR)return Gsx955(tostring(EW4bXR),pZ8,jzU,nrU)end;return _bh_k(J12U,nrU,Bi7)end;do end
+(CZ.metadata):set(EgG.reload,"fnl/docstring","Reload the specified module.")
+EgG.reset=function(vL_Vj_v,fi3,hbfOHU)vL_Vj_v.___replLocals___={}return hbfOHU({"ok"})end;do end
+(CZ.metadata):set(EgG.reset,"fnl/docstring","Erase all repl-local scope.")
+EgG.complete=function(GpItItxe,O,Equz,dsN,HzKU,Zbl8)
+local function I_iWZE2I()return
+Equz(tj(GpItItxe,HzKU,string.char(j(Zbl8)):gsub(",complete +",""):sub(1,
+-2)))end;return _bh_k(O,dsN,I_iWZE2I)end;do end
+(CZ.metadata):set(EgG.complete,"fnl/docstring","Print all possible completions for a given input symbol.")
+local function w7(c5q,iXFMywMG,a,RgyIZgQm,Kz)
+for hncJ5,oMSPa in pairs(iXFMywMG)do
+if
+(("string"==type(hncJ5))and(package~=oMSPa))then local _pJ7=type(oMSPa)
+if(_pJ7 =="function")then
+if
+((a..hncJ5)):match(c5q)then table.insert(Kz,(a..hncJ5))else end elseif(_pJ7 =="table")then if not RgyIZgQm[oMSPa]then local sa6R;do local gH=RgyIZgQm
+gH[oMSPa]=true;sa6R=gH end
+w7(c5q,oMSPa,(a..hncJ5:gsub("%.","/").."."),sa6R,Kz)else end else end else end end;return Kz end
+local function drEf10P(mRsiV)local PbSET=w7(mRsiV,package.loaded,"",{},{})local hpVB={}
+local YWvn1scCO=#hpVB
+for _u9Y,_BGX in ipairs(PbSET)do local m4bfrLhD=_BGX:gsub("^_G%.","")
+if(nil~=m4bfrLhD)then YWvn1scCO=(
+YWvn1scCO+1)do end(hpVB)[YWvn1scCO]=m4bfrLhD else end end;return hpVB end
+EgG.apropos=function(phlgUmyq,y1uQkA,n_b,X5Kb,IjSJ6)
+local function nDieWGG(P)return n_b(drEf10P(tostring(P)))end;return _bh_k(y1uQkA,X5Kb,nDieWGG)end;do end
+(CZ.metadata):set(EgG.apropos,"fnl/docstring","Print all functions matching a pattern in all loaded modules.")
+local function U9bX9Qj(_li)local VgksUV
+do local egVOS={}local L7hI7N16=#egVOS;for d in _li:gmatch("[^%.]+")do local H0J=d
+if(nil~=H0J)then L7hI7N16=(
+L7hI7N16+1)do end(egVOS)[L7hI7N16]=H0J else end end;VgksUV=egVOS end;local lD8E0=package.loaded
+for oLsc,qXd in ipairs(VgksUV)do if(nil==lD8E0)then break end;local cZ8mW;do
+local U=qXd:gsub("%/",".")cZ8mW=U end;lD8E0=lD8E0[cZ8mW]end;return lD8E0 end
+local function F3vF(MA)local RmPOf3Wl={}local ROso3G=#RmPOf3Wl
+for AaZVi3,A in ipairs(drEf10P(".*"))do local X7eznY
+do
+local nui3w2g=U9bX9Qj(A)
+if("function"==type(nui3w2g))then
+local TyY1TP=(CZ.metadata):get(nui3w2g,"fnl/docstring")if(nil~=TyY1TP)then local I=TyY1TP;X7eznY=(I:match(MA)and A)else
+X7eznY=nil end else X7eznY=nil end end;if(nil~=X7eznY)then ROso3G=(ROso3G+1)do end
+(RmPOf3Wl)[ROso3G]=X7eznY else end end;return RmPOf3Wl end
+EgG["apropos-doc"]=function(p,nWwv,U5,SSRIM2dF,TQ)local function PZd(lp2390NG)
+return U5(F3vF(tostring(lp2390NG)))end;return _bh_k(nWwv,SSRIM2dF,PZd)end;do end
+(CZ.metadata):set(EgG["apropos-doc"],"fnl/docstring","Print all functions that match the pattern in their docs")
+local function l1YXXES(Vc,NV)
+for f_qSS3,jkNMk in ipairs(drEf10P(NV))do local C4yJBj5=U9bX9Qj(jkNMk)
+if
+(
+("function"==type(C4yJBj5))and(CZ.metadata):get(C4yJBj5,"fnl/docstring"))then Vc(iD90MK.doc(C4yJBj5,jkNMk))Vc()else end end;return nil end
+EgG["apropos-show-docs"]=function(Kdb,A8f,f,bvP)local function fRpw3tSl(Wj3o4LNJ)
+return l1YXXES(f,tostring(Wj3o4LNJ))end;return _bh_k(A8f,bvP,fRpw3tSl)end;do end
+(CZ.metadata):set(EgG["apropos-show-docs"],"fnl/docstring","Print all documentations matching a pattern in function name")
+local function QO9Kq(aq67Ya3,x,LJVh6)local pqvpad=x;local pA=pqvpad["___replLocals___"]local BU1O2_L=pqvpad;local uZM;local function ZcEsZ(pwz7,Kt_w)return(pA[Kt_w]or
+BU1O2_L[Kt_w])end
+uZM=setmetatable({},{__index=ZcEsZ})
+local Q56iTd,no8ruIR=pcall(CZ["compile-string"],tostring(aq67Ya3),{scope=LJVh6})
+if((Q56iTd==true)and(nil~=no8ruIR))then
+local F=no8ruIR;return iD90MK["load-code"](F,uZM)()else return nil end end
+EgG.find=function(S0,Sm,hViv5C_,jh,Pwq)
+local function q16(k1oD)local P7WPKR
+do local O=o["sym?"](k1oD)if(nil~=O)then
+local nZ0YB=QO9Kq(O,S0,Pwq)
+if(nil~=nZ0YB)then P7WPKR=debug.getinfo(nZ0YB)else P7WPKR=nZ0YB end else P7WPKR=O end end
+if
+(
+
+(_G.type(P7WPKR)=="table")and
+((P7WPKR).what=="Lua")and(nil~= (P7WPKR).source)and(nil~= (P7WPKR).linedefined)and(nil~= (P7WPKR).short_src))then local p6e=(P7WPKR).source;local po8N=(P7WPKR).linedefined
+local a7=(P7WPKR).short_src;local U6w
+do local Irt=CZ.sourcemap;if(nil~=Irt)then Irt=(Irt)[p6e]else end;if(nil~=
+Irt)then Irt=(Irt)[po8N]else end;if(nil~=Irt)then
+Irt=(Irt)[2]else end;U6w=Irt end;return
+hViv5C_({string.format("%s:%s",a7,(U6w or po8N))})elseif(P7WPKR==nil)then
+return jh("Repl","Unknown value")elseif true then local khsuDn=P7WPKR;return jh("Repl","No source info")else return nil end end;return _bh_k(Sm,jh,q16)end;do end
+(CZ.metadata):set(EgG.find,"fnl/docstring","Print the filename and line number for a given function")
+EgG.doc=function(iDQAM,dJEEzHLA,VhIhDh,rSwA,IuZ)
+local function uQBNaZ8L(amTY)local YbIZJ=tostring(amTY)
+local kVCVg=(o["multi-sym?"](YbIZJ)or{YbIZJ})local E5J5PpiD,yxld=nil,nil
+local function rn0()return
+(o["get-in"](IuZ.specials,kVCVg)or
+o["get-in"](IuZ.macros,kVCVg)or QO9Kq(YbIZJ,iDQAM,IuZ))end;E5J5PpiD,yxld=pcall(rn0)
+if E5J5PpiD then return
+VhIhDh({iD90MK.doc(yxld,YbIZJ)})else return
+rSwA("Repl","Could not resolve value for docstring lookup")end end;return _bh_k(dJEEzHLA,rSwA,uQBNaZ8L)end;do end
+(CZ.metadata):set(EgG.doc,"fnl/docstring","Print the docstring and arglist for a function, macro, or special form.")
+EgG.compile=function(Kz3Upt,FNcg,ip1Y,SNe51hiO,nbh)
+local function xyD5eM3(dvQC_Q)
+local w7pQl=iD90MK["current-global-names"](Kz3Upt)
+local F4cjGy,D5=pcall(CZ.compile,dvQC_Q,{env=Kz3Upt,scope=nbh,allowedGlobals=w7pQl})
+if F4cjGy then return ip1Y({D5})else return
+SNe51hiO("Repl",("Error compiling expression: "..D5))end end;return _bh_k(FNcg,SNe51hiO,xyD5eM3)end;do end
+(CZ.metadata):set(EgG.compile,"fnl/docstring","compiles the expression into lua and prints the result.")
+local function eG8QRxg(K46A)
+for B,DIW in ipairs((K46A or{}))do
+for mcrg,zg74G in pairs(DIW)do
+local C4Qpi_bm=mcrg:match("^repl%-command%-(.*)")if(nil~=C4Qpi_bm)then local nsBJr=C4Qpi_bm
+EgG[nsBJr]=(EgG[nsBJr]or zg74G)else end end end;return nil end
+local function YjH(y0,KkoR7,rs,l8eg,YAV,F_XBNQq,QTmbwc,Qa95Sy)local M8OIL=y0:match(",([^%s/]+)")
+do local aeH=EgG[M8OIL]
+if(nil~=aeH)then
+local LPJB=aeH;LPJB(l8eg,KkoR7,YAV,F_XBNQq,QTmbwc,Qa95Sy)elseif true then local zwqy0k=aeH
+if("exit"~=
+M8OIL)then YAV({"Unknown command",M8OIL})else end else end end;if("exit"~=M8OIL)then return rs()else return nil end end
+local function C(_5SaCc,ywU,vd2O)
+if ywU then
+if vd2O.set_readline_name then vd2O.set_readline_name("fennel")else end;vd2O.set_options({keeplines=1000,histfile=""})
+_5SaCc.readChunk=function(L)
+local hlKRv
+if(0 ")):gsub("\n$",""):gsub("\n","\n ")local BhRI=getmetatable(_qwQtun2)
+if
+(
+(type(_qwQtun2)=="function")or((type(BhRI)=="table")and
+(type(BhRI.__call)=="function")))then
+local Hnykp=table.concat(((AHA.metadata):get(_qwQtun2,"fnl/arglist")or
+{"#"})," ")local KVZGVS;if(0 <#Hnykp)then KVZGVS=" "else KVZGVS=""end;return
+string.format("(%s%s%s)\n %s",HJ,KVZGVS,Hnykp,lEZKOn)else
+return string.format("%s\n %s",HJ,lEZKOn)end end end
+local function qh0m(W,l,YtJ3Vld,gesxk)
+AHA.metadata[O11E[W]]={["fnl/arglist"]=l,["fnl/docstring"]=YtJ3Vld,["fnl/body-form?"]=gesxk}return nil end
+local function T1pL(RPBF,MA,cQiY,mi0Su5)local RSbaegr=(mi0Su5 or 2)local D_XnV=#RPBF
+local T5b=AHA["make-scope"](MA)
+for Pzvq9=RSbaegr,D_XnV do AHA.compile1(RPBF[Pzvq9],T5b,cQiY,{nval=0})end;return nil end
+O11E["do"]=function(xWDqizI,lLfybj,kfv75hSt,L,QtUzJA8W,vyg,i,iu)local AQAZ71dv=(QtUzJA8W or 2)
+local hnm7wW0=(i or AHA["make-scope"](lLfybj))local UM52=(vyg or{})local D_=#xWDqizI;local rVtCZOT={returned=true}
+local function zXaijdUvQ(RZ7Pv3B4,utllnvh,Cy)
+if
+(D_>")fB2Vks("band","0","0","&")fB2Vks("bor","0","0","|")
+fB2Vks("bxor","0","0","~")
+qh0m("lshift",{"x","n"},"Bitwise logical left shift of x by n bits.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.")
+qh0m("rshift",{"x","n"},"Bitwise logical right shift of x by n bits.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.")
+qh0m("band",{"x1","x2","..."},"Bitwise AND of any number of arguments.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.")
+qh0m("bor",{"x1","x2","..."},"Bitwise OR of any number of arguments.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.")
+qh0m("bxor",{"x1","x2","..."},"Bitwise XOR of any number of arguments.\nOnly works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.")
+qh0m("..",{"a","b","..."},"String concatenation operator; works the same as Lua but accepts more arguments.")
+local function KEar3_f(D7CGKmb8,uLyz97,BWkW1j,yZA)local WXYCK=uLyz97;local ifeZY=WXYCK[1]local hAzmuHjD=WXYCK[2]local wOpIPj=WXYCK[3]
+local ezsAVF9Y=AHA.compile1(hAzmuHjD,BWkW1j,yZA,{nval=1})local wMf1B=ezsAVF9Y[1]
+local b=AHA.compile1(wOpIPj,BWkW1j,yZA,{nval=1})local lObXzZtF=b[1]return
+string.format("(%s %s %s)",tostring(wMf1B),D7CGKmb8,tostring(lObXzZtF))end
+local function Bp(gXOgxVqi,aEAOdyb,aANaGdmD,gJ,I)local Iuy
+do local CGX={}local YOdo=#CGX
+for Ts=2,#aANaGdmD do
+local Xoxdbm=tostring((AHA.compile1(aANaGdmD[Ts],gJ,I,{nval=1}))[1])
+if(nil~=Xoxdbm)then YOdo=(YOdo+1)do end(CGX)[YOdo]=Xoxdbm else end end;Iuy=CGX end;local xpU0HjP5
+do local v={}local TpRHAsM=#v
+for O872Y=1,(#Iuy-1)do
+local Fhc=string.format("(%s %s %s)",Iuy[O872Y],gXOgxVqi,Iuy[(O872Y+1)])
+if(nil~=Fhc)then TpRHAsM=(TpRHAsM+1)do end(v)[TpRHAsM]=Fhc else end end;xpU0HjP5=v end
+local E8GAk=string.format(" %s ",(aEAOdyb or"and"))return table.concat(xpU0HjP5,E8GAk)end
+local function WRTH(Q19nu,tG,v7Zovu3S,R8Mr,E7pBdDI)local f5qj={}local bY={}local hi1fT={}
+local d=string.format(" %s ",(tG or"and"))
+for Ev=2,#v7Zovu3S do
+table.insert(f5qj,tostring(AHA.gensym(R8Mr)))
+table.insert(hi1fT,tostring((AHA.compile1(v7Zovu3S[Ev],R8Mr,E7pBdDI,{nval=1}))[1]))end
+do local PinF3=bY;local Fl=#PinF3
+for _RHbBQe_=1,(#f5qj-1)do
+local csZGJji=string.format("(%s %s %s)",f5qj[_RHbBQe_],Q19nu,f5qj[(_RHbBQe_+1)])
+if(nil~=csZGJji)then Fl=(Fl+1)do end(PinF3)[Fl]=csZGJji else end end end;return
+string.format("(function(%s) return %s end)(%s)",table.concat(f5qj,","),table.concat(bY,d),table.concat(hi1fT,","))end
+local function s(_IQM21V,c8S3fu,Z0)
+do local cAn=(c8S3fu or _IQM21V)
+local function zdq3gf(h574,nrS48GsH,VA8mi)
+AHA.assert((2 <#h574),"expected at least two arguments",h574)
+if(3 ==#h574)then return KEar3_f(cAn,h574,nrS48GsH,VA8mi)elseif
+Jzh["every?"](Jzh["idempotent-expr?"],{cI0i(h574,2)})then return Bp(cAn,Z0,h574,nrS48GsH,VA8mi)else
+return WRTH(cAn,Z0,h574,nrS48GsH,VA8mi)end end;O11E[_IQM21V]=zdq3gf end;return
+qh0m(_IQM21V,{"a","b","..."},"Comparison operator; works the same as Lua but accepts more arguments.")end;s(">")s("<")s(">=")s("<=")s("=","==")
+s("not=","~=","or")
+local function gPVgI8V(pva,orki)
+local function RqeYK(ojg0_C,mgD1,Bc)
+AHA.assert((#ojg0_C==2),"expected one argument",ojg0_C)local bJwiKR=AHA.compile1(ojg0_C[2],mgD1,Bc,{nval=1})
+return(
+(orki or pva)..tostring(bJwiKR[1]))end;O11E[pva]=RqeYK;return nil end;gPVgI8V("not","not ")
+qh0m("not",{"x"},"Logical operator; works the same as Lua.")gPVgI8V("bnot","~")
+qh0m("bnot",{"x"},"Bitwise negation; only works in Lua 5.3+ or LuaJIT with the --use-bit-lib flag.")gPVgI8V("length","#")
+qh0m("length",{"x"},"Returns the length of a table or string.")do end(O11E)["~="]=O11E["not="]O11E["#"]=O11E.length
+O11E.quote=function(fCYOnUn,z5VJbT,I8OjNIh)
+AHA.assert((
+#fCYOnUn==2),"expected one argument",fCYOnUn)local BTxuZzK,qdtphyF=true,z5VJbT
+while qdtphyF do qdtphyF=qdtphyF.parent;if
+(qdtphyF==AHA.scopes.compiler)then BTxuZzK=false else end end
+return AHA["do-quote"](fCYOnUn[2],z5VJbT,I8OjNIh,BTxuZzK)end
+qh0m("quote",{"x"},"Quasiquote the following form. Only works in macro/compiler scope.")local uA5vZEb={}
+local function ZNbFB(F)local qDcOp4_=getmetatable(F)
+assert((qDcOp4_~=getmetatable("")),"Illegal metatable access!")return qDcOp4_ end;local SE0lo2=nil
+local function GaxZqb()local Rafg8;do local N=rawget(_G,"utf8")if(nil~=N)then Rafg8=Jzh.copy(N)else
+Rafg8=N end end
+return
+{table=Jzh.copy(table),math=Jzh.copy(math),string=Jzh.copy(string),pairs=Jzh.stablepairs,ipairs=ipairs,select=select,tostring=tostring,tonumber=tonumber,bit=rawget(_G,"bit"),pcall=pcall,xpcall=xpcall,next=next,print=print,type=type,assert=assert,error=error,setmetatable=setmetatable,getmetatable=ZNbFB,require=SE0lo2,rawlen=rawget(_G,"rawlen"),rawget=rawget,rawset=rawset,rawequal=rawequal,_VERSION=_VERSION,utf8=Rafg8}end
+local function ZPz3(_5CGI6PZ)local Z={}local ipJKYa=getmetatable(_5CGI6PZ)
+local dPQffKj=ipJKYa["__index"]if("table"==type(dPQffKj))then
+for JSQ,JhH in pairs(dPQffKj)do Z[JSQ]=JhH end else end
+for uh,gWvKWBjLE in next,_5CGI6PZ,nil do Z[uh]=gWvKWBjLE end;return next,Z,nil end
+local function gwje(RsymxL,Iz,xzf,EgCB_nf)local P
+do local vQoZJVom=(EgCB_nf or Jzh.root.options)
+if((
+_G.type(vQoZJVom)=="table")and
+((vQoZJVom)["compiler-env"]=="strict"))then
+P=GaxZqb()elseif((_G.type(vQoZJVom)=="table")and
+(nil~= (vQoZJVom).compilerEnv))then
+local ftq9dIXI=(vQoZJVom).compilerEnv;P=ftq9dIXI elseif
+((_G.type(vQoZJVom)=="table")and(nil~=
+(vQoZJVom)["compiler-env"]))then local FX=(vQoZJVom)["compiler-env"]P=FX elseif true then local zGwGt3E=vQoZJVom
+P=GaxZqb(false)else P=nil end end;local kViUhgj;local function UyTq(vm7_I)return
+Jzh.sym(AHA.gensym((AHA.scopes.macro or Iz),vm7_I))end;local function p()return
+AHA.scopes.macro end;local function u6_LlQ46(L6C5NIeG)
+AHA.assert(AHA.scopes.macro,"must call from macro",RsymxL)
+return AHA.scopes.macro.manglings[tostring(L6C5NIeG)]end
+local function GAPJTX(sgg_50x)
+AHA.assert(AHA.scopes.macro,"must call from macro",RsymxL)return AHA.macroexpand(sgg_50x,AHA.scopes.macro)end
+kViUhgj={_AST=RsymxL,_CHUNK=xzf,_IS_COMPILER=true,_SCOPE=Iz,_SPECIALS=AHA.scopes.global.specials,_VARARG=Jzh.varg(),["macro-loaded"]=uA5vZEb,unpack=cI0i,["assert-compile"]=AHA.assert,view=fNDs,version=Jzh.version,metadata=AHA.metadata,["ast-source"]=Jzh["ast-source"],list=Jzh.list,["list?"]=Jzh["list?"],["table?"]=Jzh["table?"],sequence=Jzh.sequence,["sequence?"]=Jzh["sequence?"],sym=Jzh.sym,["sym?"]=Jzh["sym?"],["multi-sym?"]=Jzh["multi-sym?"],comment=Jzh.comment,["comment?"]=Jzh["comment?"],["varg?"]=Jzh["varg?"],gensym=UyTq,["get-scope"]=p,["in-scope?"]=u6_LlQ46,macroexpand=GAPJTX}kViUhgj._G=kViUhgj;return
+setmetatable(kViUhgj,{__index=P,__newindex=P,__pairs=ZPz3})end
+local function lidmlMiV(...)local bWlu={}local aS=#bWlu
+for a3KhFIf in
+string.gmatch((package.config or""),"([^\n]+)")do local sAPG6y=a3KhFIf
+if(nil~=sAPG6y)then aS=(aS+1)do end(bWlu)[aS]=sAPG6y else end end;return bWlu end;local qr0C1=lidmlMiV(...)local g=qr0C1[1]local o=qr0C1[2]local gAN1v8=qr0C1[3]
+local pL4fol={dirsep=(g or"/"),pathmark=(
+gAN1v8 or";"),pathsep=(o or"?")}
+local function nmhV(m)return string.gsub(m,"[^%w]","%%%1")end
+local function Wd1a_i(A,fd2H)local zQ5=nmhV(pL4fol.pathsep)
+local xhs=("([^%s]*)%s"):format(zQ5,zQ5)local Bd=A:gsub("%.",pL4fol.dirsep)
+local n4Rf=((fd2H or
+Jzh["fennel-module"].path)..pL4fol.pathsep)
+local function QT(vGQaEDQ)
+local HpHT=vGQaEDQ:gsub(nmhV(pL4fol.pathmark),Bd)local e=vGQaEDQ:gsub(nmhV(pL4fol.pathmark),A)local OiRV=(
+io.open(HpHT)or io.open(e))
+if(nil~=OiRV)then
+local kV=OiRV;kV:close()return HpHT elseif true then local Ieet0=OiRV
+return nil, ("no file '"..HpHT.."'")else return nil end end
+local function hjd(IZMQx0rC,BrM2hYPE)local igszykf=n4Rf:match(xhs,IZMQx0rC)
+if(nil~=igszykf)then local q_v=igszykf
+local I,H=QT(q_v)
+if(nil~=I)then local aTdQOCcy=I;return aTdQOCcy elseif((I==nil)and(nil~=H))then local zSyor=H;local function HB()local fuSiMn=(
+BrM2hYPE or{})table.insert(fuSiMn,zSyor)
+return fuSiMn end;return
+hjd((IZMQx0rC+#q_v+1),HB())else return nil end elseif true then local ZsrTm=igszykf;local function v7Q()
+local oWrDs=table.concat((BrM2hYPE or{}),"\n\9")
+if(_VERSION<"Lua 5.4")then return("\n\9"..oWrDs)else return oWrDs end end;return nil,
+v7Q()else return nil end end;return hjd(1)end
+local function ur(C9m4)
+local function dVKfuf(ulHu)local kIuvqs_z=Jzh.copy(Jzh.root.options)for WXKWwF,nGfnz9aW in
+pairs((C9m4 or{}))do kIuvqs_z[WXKWwF]=nGfnz9aW end
+kIuvqs_z["module-name"]=ulHu;local DiibeTp,VE=Wd1a_i(ulHu)
+if(nil~=DiibeTp)then local Jpkn2x2L=DiibeTp;local qIGM
+do
+local zd=Jpkn2x2L;local aIh=kIuvqs_z;local function Ox(...)
+return Jzh["fennel-module"].dofile(zd,aIh,...)end;qIGM=Ox end;return qIGM,Jpkn2x2L elseif((DiibeTp==nil)and(nil~=VE))then
+local T5K7=VE;return T5K7 else return nil end end;return dVKfuf end
+local function IAEw(VXa0K,EcGb,GFIjpl,...)
+local CXtt1l9J=(package.loaders or package.searchers or{})local MIJA=table.insert(CXtt1l9J,1,VXa0K)
+local _rbAfa=Jzh["fennel-module"].dofile(EcGb,GFIjpl,...)table.remove(CXtt1l9J,1)return _rbAfa end
+local function HkFs_(WC)local dpl5Qr8i
+do local vdrp=Jzh.copy(Jzh.root.options)do end
+(vdrp)["module-name"]=WC;vdrp["env"]="_COMPILER"vdrp["requireAsInclude"]=false;vdrp["allowedGlobals"]=
+nil;dpl5Qr8i=vdrp end
+local VhWZl_=Wd1a_i(WC,Jzh["fennel-module"]["macro-path"])
+if(nil~=VhWZl_)then local xB=VhWZl_;local qtSF2kh
+if
+(dpl5Qr8i["compiler-env"]==_G)then local Bu=HkFs_;local ja=xB;local glp=dpl5Qr8i
+local function oP(...)return IAEw(Bu,ja,glp,...)end;qtSF2kh=oP else local J92nyajw=xB;local wBBO=dpl5Qr8i;local function k3gZQQ(...)return
+Jzh["fennel-module"].dofile(J92nyajw,wBBO,...)end
+qtSF2kh=k3gZQQ end;return qtSF2kh,xB else return nil end end
+local function a6y99PLZ(TW5ElhE)local ytAdXO=Wd1a_i(TW5ElhE,package.path)
+if(nil~=ytAdXO)then
+local QzBDtoL=ytAdXO;local Wh5WXYH
+do local BIqcT3KO=io.open(QzBDtoL)
+local function Ggug(as,...)BIqcT3KO:close()if as then return...else return
+error(...,0)end end
+local function D4()return assert(BIqcT3KO:read("*a"))end
+Wh5WXYH=Ggug(_G.xpcall(D4,(package.loaded.fennel or debug).traceback))end;local _j=WpQBPz(Wh5WXYH,gwje(),QzBDtoL)return _j,QzBDtoL else return nil end end;local Maun0kCg={HkFs_,a6y99PLZ}
+local function X1okTW(bKRB,bqOmBw)local UlRBdf2T=Maun0kCg[bqOmBw]
+if
+(nil~=UlRBdf2T)then local WqG=UlRBdf2T;local jQ6ByW,hI=WqG(bKRB)
+if((nil~=jQ6ByW)and true)then
+local X=jQ6ByW;local fvOp=hI;return X,fvOp elseif true then local xIB=jQ6ByW;return X1okTW(bKRB,(bqOmBw+1))else
+return nil end else return nil end end
+local function IL(MJ)
+if
+((MJ=="fennel.macros")or
+(package and package.loaded and("table"==
+type(package.loaded[MJ]))and(
+package.loaded[MJ].metadata==AHA.metadata)))then return{metadata=AHA.metadata,view=fNDs}else return nil end end
+local function Txnp(gk4oh)
+local function drQ()local WxqsgF,B7=X1okTW(gk4oh,1)
+AHA.assert(WxqsgF,(gk4oh.." module not found."))do end(uA5vZEb)[gk4oh]=WxqsgF(gk4oh,B7)
+return uA5vZEb[gk4oh]end
+return(uA5vZEb[gk4oh]or IL(gk4oh)or drQ())end;SE0lo2=Txnp
+local function k7(DRaen,cOdno7,Q7NwnLvM)
+AHA.assert(Jzh["table?"](DRaen),"expected macros to be table",cOdno7)
+for b55dl,vv7Mbm in pairs(DRaen)do
+AHA.assert((type(vv7Mbm)=="function"),"expected each macro to be function",cOdno7)
+AHA["check-binding-valid"](Jzh.sym(b55dl),Q7NwnLvM,cOdno7,{["macro?"]=true})do end(Q7NwnLvM.macros)[b55dl]=vv7Mbm end;return nil end
+local function i4bMH(iHFG,mbY,klPjHrEL,fT)local iRHgEZ1=iHFG;local qe3O8z=iRHgEZ1["filename"]local _qk=iRHgEZ1[2]
+local WwYOV=(qe3O8z or(
+Jzh["table?"](_qk)and _qk.filename))local TzOhP=Jzh.root.options["module-name"]
+local DYbu7=AHA.compile(_qk,fT)local cW=WpQBPz(DYbu7)return cW(TzOhP,WwYOV)end
+O11E["require-macros"]=function(DzAYbnQN,v,iX,xTWLsd)
+AHA.assert((#DzAYbnQN==2),"Expected one module name argument",(xTWLsd or DzAYbnQN))local et4qFvy=i4bMH(DzAYbnQN,v,iX,{})
+AHA.assert(Jzh["string?"](et4qFvy),"module name must compile to string",(
+xTWLsd or DzAYbnQN))
+if not uA5vZEb[et4qFvy]then local j,u3d=X1okTW(et4qFvy,1)
+AHA.assert(j,(et4qFvy..
+" module not found."),DzAYbnQN)do end
+(uA5vZEb)[et4qFvy]=AHA.assert(Jzh["table?"](j(et4qFvy,u3d)),"expected macros to be table",(
+xTWLsd or DzAYbnQN))else end
+if("import-macros"==tostring(DzAYbnQN[1]))then return
+uA5vZEb[et4qFvy]else return k7(uA5vZEb[et4qFvy],DzAYbnQN,v,iX)end end
+qh0m("require-macros",{"macro-module-name"},"Load given module and use its contents as macro definitions in current scope.\nMacro module should return a table of macro functions with string keys.\nConsider using import-macros instead as it is more flexible.")
+local function o4u(Zk,IJlAF,PtNV,vGN)
+local AmjRAd75=AHA["make-scope"](Jzh.root.scope.parent)local PmbHJr={}if Jzh.root.options.requireAsInclude then
+AmjRAd75.specials.require=AHA["require-include"]else end
+for Gyrd1C,SU2WbXNN in
+S.parser(S["string-stream"](Zk),IJlAF)do table.insert(PmbHJr,SU2WbXNN)end
+for oC5=1,#PmbHJr do local ZDm
+if(oC5 ==#PmbHJr)then ZDm={tail=true}else ZDm={nval=0}end;Jzh["propagate-options"](PtNV,ZDm)
+AHA.compile1(PmbHJr[oC5],AmjRAd75,vGN,ZDm)end;return nil end
+local function XB(EauWm_Mc,hF5AiG,CaPHn8,kR,DeA)Jzh.root.scope.includes[kR]="fnl/loading"local dKC3Vzv
+do
+local cN=assert(io.open(CaPHn8))
+local function lDwx(Ki,...)cN:close()if Ki then return...else return error(...,0)end end;local function oA()
+return assert(cN:read("*all")):gsub("[\13\n]*$","")end
+dKC3Vzv=lDwx(_G.xpcall(oA,(package.loaded.fennel or debug).traceback))end
+local G=Jzh.expr(("require(\""..kR.."\")"),"statement")
+local qx6M=("package.preload[%q]"):format(kR)
+local q43=(qx6M.." = "..qx6M.." or function(...)")local CEjbsJ,XL={},{}AHA.emit(CEjbsJ,q43,EauWm_Mc)
+AHA.emit(CEjbsJ,XL)AHA.emit(CEjbsJ,"end",EauWm_Mc)for cxdelc,YxaPQgT in ipairs(CEjbsJ)do
+table.insert(Jzh.root.chunk,YxaPQgT)end
+if DeA then
+o4u(dKC3Vzv,CaPHn8,hF5AiG,XL)else AHA.emit(XL,dKC3Vzv,EauWm_Mc)end;Jzh.root.scope.includes[kR]=G;return G end
+local function l38u_I3T(lZT0q,PSOCXW8,mJ47Xs,pttAKe)
+if
+(Jzh.root.scope.includes[lZT0q]=="fnl/loading")then
+AHA.assert(mJ47Xs,"circular include detected",pttAKe)return mJ47Xs(PSOCXW8)else return nil end end
+O11E.include=function(Qpnu6Q,BS_TVtF,AsPHBFDJ,Aedxu)
+AHA.assert((#Qpnu6Q==2),"expected one argument",Qpnu6Q)local oUneh9
+do
+local Lz_usB,ljsvn5Qz=pcall(i4bMH,Qpnu6Q,BS_TVtF,AsPHBFDJ,Aedxu)
+if((Lz_usB==true)and(nil~=ljsvn5Qz))then
+local GgNU6=ljsvn5Qz
+oUneh9=Jzh.expr(string.format("%q",GgNU6),"literal")elseif true then local NPz6M=Lz_usB
+oUneh9=(AHA.compile1(Qpnu6Q[2],BS_TVtF,AsPHBFDJ,{nval=1}))[1]else oUneh9=nil end end
+if((oUneh9.type~="literal")or
+((oUneh9[1]):byte()~=34))then if Aedxu.fallback then return
+Aedxu.fallback(oUneh9)else
+return AHA.assert(false,"module name must be string literal",Qpnu6Q)end else local sY=WpQBPz(("return "..
+oUneh9[1]))()
+local eD2N=Jzh.root.options["module-name"]local lHWMtRj;Jzh.root.options["module-name"]=sY;lHWMtRj=nil
+local ooD_xGt
+local function b()local Zb2=Wd1a_i(sY)
+if(nil~=Zb2)then local NWZMp=Zb2
+return XB(Qpnu6Q,Aedxu,NWZMp,sY,true)elseif true then local pYT=Zb2;local tZED=Wd1a_i(sY,package.path)
+if tZED then return
+XB(Qpnu6Q,Aedxu,tZED,sY,false)elseif Aedxu.fallback then return Aedxu.fallback(oUneh9)else
+return AHA.assert(false,(
+"module not found "..sY),Qpnu6Q)end else return nil end end
+ooD_xGt=(
+
+
+(
+Jzh["member?"](sY,(Jzh.root.options.skipInclude or{}))and Aedxu.fallback(oUneh9,true))or l38u_I3T(sY,oUneh9,Aedxu.fallback,Qpnu6Q)or Jzh.root.scope.includes[sY]or b())Jzh.root.options["module-name"]=eD2N;return ooD_xGt end end
+qh0m("include",{"module-name-literal"},"Like require but load the target module during compilation and embed it in the\nLua output. The module must be a string literal and resolvable at compile time.")
+local function ti5b(tNb4,CUAxP,HX6Kb86)local LIG=gwje(tNb4,CUAxP,HX6Kb86)
+local oLlSHKfJ=Jzh.copy(Jzh.root.options)
+oLlSHKfJ.scope=AHA["make-scope"](AHA.scopes.compiler)oLlSHKfJ.allowedGlobals=V(LIG)return
+assert(WpQBPz(AHA.compile(tNb4,oLlSHKfJ),LhQHR(LIG)),oLlSHKfJ["module-name"],tNb4.filename)()end
+O11E.macros=function(bpr7l0,EMna5yqq,d6Ctwa)
+AHA.assert(((#bpr7l0 ==2)and Jzh["table?"](bpr7l0[2])),"Expected one table argument",bpr7l0)
+return k7(ti5b(bpr7l0[2],EMna5yqq,d6Ctwa),bpr7l0,EMna5yqq,d6Ctwa)end
+qh0m("macros",{"{:macro-name-1 (fn [...] ...) ... :macro-name-N macro-body-N}"},"Define all functions in the given table as macros local to the current scope.")
+O11E["eval-compiler"]=function(VYLNRk,_yp,k8)local l=VYLNRk[1]VYLNRk[1]=Jzh.sym("do")
+local fdIV=ti5b(VYLNRk,_yp,k8)do end(VYLNRk)[1]=l;return fdIV end
+qh0m("eval-compiler",{"..."},"Evaluate the body at compile-time. Use the macro system instead if possible.",true)
+return
+{doc=YMl,["current-global-names"]=V,["load-code"]=WpQBPz,["macro-loaded"]=uA5vZEb,["macro-searchers"]=Maun0kCg,["make-compiler-env"]=gwje,["search-module"]=Wd1a_i,["make-searcher"]=ur,["wrap-env"]=LhQHR}end
+package.preload["fennel.compiler"]=package.preload["fennel.compiler"]or
+function(...)
+local c=require("fennel.utils")local o=require("fennel.parser")
+local yK=require("fennel.friend")local MAKgn=(table.unpack or _G.unpack)local x9FArgz={}
+local function GmcCQbi(cx5uK)local uo=(cx5uK or
+x9FArgz.global)local ZAia0D6l;if uo then
+ZAia0D6l=((uo.depth or 0)+1)else ZAia0D6l=0 end
+return
+{includes=setmetatable({},{__index=(uo and uo.includes)}),macros=setmetatable({},{__index=(
+uo and uo.macros)}),manglings=setmetatable({},{__index=(uo and
+uo.manglings)}),specials=setmetatable({},{__index=(uo and uo.specials)}),symmeta=setmetatable({},{__index=(
+uo and uo.symmeta)}),unmanglings=setmetatable({},{__index=(uo and
+uo.unmanglings)}),gensyms=setmetatable({},{__index=(uo and
+uo.gensyms)}),autogensyms=setmetatable({},{__index=(uo and uo.autogensyms)}),vararg=(
+uo and uo.vararg),depth=ZAia0D6l,hashfn=(uo and uo.hashfn),refedglobals={},parent=uo}end
+local function jtHWFj7(DjicYU,aX)local griKc
+if("table"==type(DjicYU))then griKc=DjicYU else griKc={}end;local oPlKdB=getmetatable(DjicYU)
+local ithf=(
+(oPlKdB and oPlKdB.filename)or griKc.filename or"unknown")
+local EOxrAqR=((oPlKdB and oPlKdB.line)or griKc.line or"?")
+local W4MTO=((oPlKdB and oPlKdB.col)or griKc.col or"?")
+local uzkTxapi=tostring((c["sym?"](griKc[1])or griKc[1]or"()"))return
+string.format("%s:%s:%s Compile error in '%s': %s",ithf,EOxrAqR,W4MTO,uzkTxapi,aX)end
+local function NV(m2e,Id1VK,j2NNb,kiRfka8)
+if not m2e then local uteD3RU=(c.root.options or{})
+local _yAm0f0Y=uteD3RU["source"]local UF=uteD3RU["unfriendly"]
+local gV3vGZ4X=uteD3RU["error-pinpoint"]local NGrp;if next(c["ast-source"](j2NNb))then NGrp=j2NNb else
+NGrp=(kiRfka8 or{})end
+if(nil==
+c.hook("assert-compile",m2e,Id1VK,NGrp,c.root.reset))then
+c.root.reset()
+if
+(UF or not yK or not _G.io or not _G.io.read)then error(jtHWFj7(NGrp,Id1VK),0)else
+yK["assert-compile"](m2e,Id1VK,NGrp,_yAm0f0Y,{["error-pinpoint"]=gV3vGZ4X})end else end else end;return m2e end;x9FArgz.global=GmcCQbi()x9FArgz.global.vararg=true
+x9FArgz.compiler=GmcCQbi(x9FArgz.global)x9FArgz.macro=x9FArgz.global
+local tQX2JQ3q={["\7"]="\\a",["\8"]="\\b",["\9"]="\\t",["\n"]="n",["\11"]="\\v",["\12"]="\\f"}
+local function iIyPus(tQpA)local function V(x799K)return("\\"..x799K:byte())end;return
+string.gsub(string.gsub(string.format("%q",tQpA),".",tQX2JQ3q),"[\128-\255]",V)end
+local function UDoYdga(jZ)
+if c["valid-lua-identifier?"](jZ)then return jZ else local function OmVG8lf(CgZsZTK)return
+string.format("_%02x",CgZsZTK:byte())end;return("__fnl_global__"..
+jZ:gsub("[^%w]",OmVG8lf))end end
+local function u_URqAyP(lEc)local YaAvE=string.match(lEc,"^__fnl_global__(.*)$")
+if
+(nil~=YaAvE)then local yf3=YaAvE;local Bcs;local function vh77(Gq)
+return string.char(tonumber(Gq:sub(2),16))end
+Bcs=string.gsub(yf3,"_[%da-f][%da-f]",vh77)return Bcs elseif true then local IhMea=YaAvE;return lEc else return nil end end;local nxZZC=nil;local function DMZ(JfP4Aohm)return
+(not nxZZC or c["member?"](JfP4Aohm,nxZZC))end
+local function ebmBtt(F6q,K0,WSUP53i,f8pHDS)if
+(
+WSUP53i.unmanglings[K0]and not WSUP53i.gensyms[K0])then
+return ebmBtt(F6q,(F6q..f8pHDS),WSUP53i,(f8pHDS+1))else return K0 end end
+local function F6(r1hTTS,WaWBNK,e4qPh_,M1)
+NV(not c["multi-sym?"](r1hTTS),("unexpected multi symbol "..r1hTTS),e4qPh_)local Fe
+if
+((c["lua-keywords"])[r1hTTS]or r1hTTS:match("^%d"))then Fe=("_"..r1hTTS)else Fe=r1hTTS end;local RK
+local function p(eXTmm)return string.format("_%02x",eXTmm:byte())end
+RK=string.gsub(string.gsub(Fe,"-","_"),"[^%w_]",p)local P0VL2vrZ=ebmBtt(RK,RK,WaWBNK,0)do end
+(WaWBNK.unmanglings)[P0VL2vrZ]=r1hTTS;do local xy0J=(M1 or WaWBNK.manglings)do end
+(xy0J)[r1hTTS]=P0VL2vrZ end;return P0VL2vrZ end
+local function Mcv4zh(kN,Je,lbp)
+for qc,SnR in pairs(Je)do
+NV(not kN.refedglobals[SnR],("use of global "..
+qc.." is aliased by a local"),lbp)do end(kN.manglings)[qc]=SnR end;return nil end
+local function Aanl(uREm,sYd4vJv)
+local Tp=(sYd4vJv.manglings[uREm[1]]or UDoYdga(uREm[1]))
+for dL=2,#uREm do
+if c["valid-lua-identifier?"](uREm[dL])then if(
+uREm["multi-sym-method-call"]and(dL==#uREm))then Tp=(Tp..
+":"..uREm[dL])else
+Tp=(Tp.."."..uREm[dL])end else Tp=(Tp.."["..iIyPus(uREm[dL])..
+"]")end end;return Tp end
+local function VvZoG6()c.root.scope["gensym-append"]=(
+(c.root.scope["gensym-append"]or 0)+1)return
+(
+"_"..c.root.scope["gensym-append"].."_")end
+local function AjTaG(cv,YQ,f)
+local yDOTU=((YQ or"")..VvZoG6().. (f or""))while cv.unmanglings[yDOTU]do
+yDOTU=((YQ or"")..VvZoG6().. (f or""))end
+cv.unmanglings[yDOTU]=(YQ or true)do end(cv.gensyms)[yDOTU]=true;return yDOTU end
+local function oJv55vky(xs3HoM,CPt)xs3HoM[1]=CPt;local x25XhH=table.remove(xs3HoM)
+local gdJk_iMs=table.remove(xs3HoM)
+local I=((xs3HoM["multi-sym-method-call"]and":")or".")
+table.insert(xs3HoM,(gdJk_iMs..I..x25XhH))return table.concat(xs3HoM,".")end
+local function ecvFfcqw(sRldmKE8,J4S6iy)local e5Hi=c["multi-sym?"](sRldmKE8)
+if(nil~=e5Hi)then local glqD=e5Hi;return
+oJv55vky(glqD,ecvFfcqw(glqD[1],J4S6iy))elseif true then local HndLYXOs=e5Hi;local function hFfZ()
+local _jVsRVIY=AjTaG(J4S6iy,sRldmKE8:sub(1,(-2)),"auto")do end(J4S6iy.autogensyms)[sRldmKE8]=_jVsRVIY
+return _jVsRVIY end;return(
+J4S6iy.autogensyms[sRldmKE8]or hFfZ())else return nil end end
+local function giqEv(eF5jN,BMmEIl,co,tZQ)local YplB=tostring(eF5jN)local iuAKOawa
+do local Y9ajqGT8=tZQ;if(nil~=Y9ajqGT8)then
+Y9ajqGT8=(Y9ajqGT8)["macro?"]else end;iuAKOawa=Y9ajqGT8 end
+NV(not YplB:find("&"),"invalid character: &",eF5jN)
+NV(not YplB:find("^%."),"invalid character: .",eF5jN)
+NV(not(BMmEIl.specials[YplB]or
+(not iuAKOawa and BMmEIl.macros[YplB])),("local %s was overshadowed by a special form or macro"):format(YplB),co)return
+NV(not c["quoted?"](eF5jN),string.format("macro tried to bind %s without gensym",YplB),eF5jN)end
+local function DX(z,l,ed,WipOX,GepSAE)giqEv(z,ed,WipOX)local fVwL=tostring(z)
+NV(not c["multi-sym?"](fVwL),(
+"unexpected multi symbol "..fVwL),WipOX)do end(ed.symmeta)[fVwL]=l;return F6(fVwL,ed,WipOX,GepSAE)end
+local function Ymc47(Vdr5iR,kSubWX2,TJFj3cha)
+if not TJFj3cha.hashfn then return nil elseif(Vdr5iR=="$")then return"$1"elseif kSubWX2 then
+if(kSubWX2 and
+(kSubWX2[1]=="$"))then kSubWX2[1]="$1"else end;return table.concat(kSubWX2,".")else return nil end end
+local function KU1TZ(epF2,e,w2rce)c.hook("symbol-to-expression",epF2,e,w2rce)
+local SoO=epF2[1]local cvVVLDX=c["multi-sym?"](SoO)
+local adFOO=(Ymc47(SoO,cvVVLDX,e)or SoO)local hq=(cvVVLDX or{adFOO})local qhy=(
+((1 <#hq)and"expression")or"sym")
+local YqkxFq=e.manglings[hq[1]]if(YqkxFq and e.symmeta[hq[1]])then
+e.symmeta[hq[1]]["used"]=true else end
+NV(not e.macros[hq[1]],"tried to reference a macro without calling it",epF2)
+NV((not e.specials[hq[1]]or("require"==hq[1])),"tried to reference a special form without calling it",epF2)
+NV((not w2rce or YqkxFq or("_ENV"==hq[1])or DMZ(hq[1])),(
+"unknown identifier: "..tostring(hq[1])),epF2)if(nxZZC and not YqkxFq and e.parent)then
+e.parent.refedglobals[hq[1]]=true else end
+return c.expr(Aanl(hq,e),qhy)end
+local function InEPht(RC1zQfj,tw7,Ia)
+if(type(tw7)=="table")then return table.insert(RC1zQfj,tw7)else return
+table.insert(RC1zQfj,{ast=Ia,leaf=tw7})end end
+local function dAkZ7FOa(cO)
+if cO.leaf then return cO elseif
+(
+(3 <=#cO)and(cO[(#cO-2)].leaf=="do")and not cO[(#cO-1)].leaf and(
+cO[#cO].leaf=="end"))then local Yim0=dAkZ7FOa(cO[(#cO-1)])local KEgdrEJ={ast=cO.ast}
+for cvE=1,(#cO-3)
+do table.insert(KEgdrEJ,dAkZ7FOa(cO[cvE]))end
+for _ZaOK6=1,#Yim0 do table.insert(KEgdrEJ,Yim0[_ZaOK6])end;return KEgdrEJ else return c.map(cO,dAkZ7FOa)end end
+local function kUC7(cnMUYf,DAbP_w)
+local function IsHliUG5(x63,GD,tZs8,ECC)local Wj8Z7ASU=tZs8
+if x63.leaf then
+GD[Wj8Z7ASU]=((GD[Wj8Z7ASU]or"").." "..x63.leaf)else
+for DPBC0cEs,L3OyIW5 in ipairs(x63)do
+if(L3OyIW5.leaf or(0 <#L3OyIW5))then
+local IA=c["ast-source"](L3OyIW5.ast)if(ECC==IA.filename)then
+Wj8Z7ASU=math.max(Wj8Z7ASU,(IA.line or 0))else end
+Wj8Z7ASU=IsHliUG5(L3OyIW5,GD,Wj8Z7ASU,ECC)else end end end;return Wj8Z7ASU end;local onTM6={}
+local wmHyhAu=IsHliUG5(cnMUYf,onTM6,1,DAbP_w.filename)for ZXlUnI=1,wmHyhAu do
+if(onTM6[ZXlUnI]==nil)then onTM6[ZXlUnI]=""else end end
+return table.concat(onTM6,"\n")end
+local function PkKH(Yzt3,A3nycin,_AEiRFt,jA)
+if A3nycin.leaf then local DthGg=c["ast-source"](A3nycin.ast)
+local nAh1PSh=DthGg["filename"]local U3vmAAPu=DthGg["line"]
+table.insert(Yzt3,{nAh1PSh,U3vmAAPu})return A3nycin.leaf else local QSzXH
+do local CD7qMWN=_AEiRFt
+if(CD7qMWN==true)then QSzXH=" "elseif
+(CD7qMWN==false)then QSzXH=""elseif(CD7qMWN==_AEiRFt)then QSzXH=_AEiRFt elseif(CD7qMWN==nil)then QSzXH=""else
+QSzXH=nil end end
+local function ruKe(n3vXu)
+if(n3vXu.leaf or(0 <#n3vXu))then
+local dFb_c5=PkKH(Yzt3,n3vXu,QSzXH,(jA+1))if(0 ]+$")end
+local K9fi_VCl={{["min-byte"]=0,["max-byte"]=127,["min-code"]=0,["max-code"]=127,len=1},{["min-byte"]=192,["max-byte"]=223,["min-code"]=128,["max-code"]=2047,len=2},{["min-byte"]=224,["max-byte"]=239,["min-code"]=2048,["max-code"]=65535,len=3},{["min-byte"]=240,["max-byte"]=247,["min-code"]=65536,["max-code"]=1114111,len=4}}
+local function xknmF(nM1Z9cl)
+local function w(UcyAum,dYsjmqdo)local k=K9fi_VCl;local Ur_nfNU=string.byte(UcyAum,dYsjmqdo)local YF8Nredr
+do
+local tyx_Qk=nil
+for urZP,ayXNm in qvEX8(k)do if tyx_Qk then break end
+tyx_Qk=(Ur_nfNU and
+(function(Iq9D_7Yo,OMesxhmz,Q9BrppG)return(Iq9D_7Yo<=OMesxhmz)and
+(OMesxhmz<=Q9BrppG)end)(ayXNm["min-byte"],Ur_nfNU,ayXNm["max-byte"])and
+ayXNm)end;YF8Nredr=tyx_Qk end;local wFF9q_B
+local function KLNU()local D9E4D;if YF8Nredr then
+D9E4D=(Ur_nfNU-YF8Nredr["min-byte"])else D9E4D=nil end
+for hhiP24bV=(dYsjmqdo+1),(
+dYsjmqdo+YF8Nredr.len+-1)do
+local O34P1h=string.byte(UcyAum,hhiP24bV)
+D9E4D=(O34P1h and D9E4D and
+(function(M,oW2YQpc,EX1c04A)
+return(M<=oW2YQpc)and(oW2YQpc<=EX1c04A)end)(128,O34P1h,191)and((D9E4D*64)+
+(O34P1h-128)))end;return D9E4D end;wFF9q_B=(YF8Nredr and KLNU())
+if
+(wFF9q_B and
+(function(svWLk,H6V,vZece1k)return(svWLk<=H6V)and
+(H6V<=vZece1k)end)(YF8Nredr["min-code"],wFF9q_B,YF8Nredr["max-code"])and not
+(function(ZUrs,cJ9n_wGv,oNrtf_)return
+(ZUrs<=cJ9n_wGv)and(cJ9n_wGv<=oNrtf_)end)(55296,wFF9q_B,57343))then return YF8Nredr.len else return nil end end;local UTW=1;local hYYhQ={}
+while(UTW<=#nM1Z9cl)do
+local HtgpP=(
+string.find(nM1Z9cl,"[\128-\255]",UTW)or(#nM1Z9cl+1))local lyBbtwn=w(nM1Z9cl,HtgpP)
+table.insert(hYYhQ,string.sub(nM1Z9cl,UTW,(
+HtgpP+ (lyBbtwn or 0)+-1)))if(not lyBbtwn and(HtgpP<=#nM1Z9cl))then
+table.insert(hYYhQ,string.format("\\%03d",string.byte(nM1Z9cl,HtgpP)))else end;if lyBbtwn then
+UTW=(HtgpP+lyBbtwn)else UTW=(HtgpP+1)end end;return table.concat(hYYhQ)end
+local function Z69(biFy,Kp,o4A)local M9=B(biFy)
+local rzBY2CSb=((M9 <2)or
+(sF(Kp,"escape-newlines?")and(M9 <
+(Kp["line-length"]-o4A))))local eJbv;local BUVM8;if rzBY2CSb then BUVM8="\\n"else BUVM8="\n"end;local function k(Kl1YC,b8)return
+("\\%03d"):format(b8:byte())end
+eJbv=setmetatable({["\7"]="\\a",["\8"]="\\b",["\12"]="\\f",["\11"]="\\v",["\13"]="\\r",["\9"]="\\t",["\\"]="\\\\",["\""]="\\\"",["\n"]=BUVM8},{__index=k})
+local x5=("\""..biFy:gsub("[%c\\\"]",eJbv).."\"")if sF(Kp,"utf8?")then return xknmF(x5)else return x5 end end
+local function i(bGdXdZ,iIoI)local F
+do local UFS7={}
+for rsqKt,e in wXFw(Q)do local qIYmlZUV,ae8WUke=rsqKt,e;if
+((qIYmlZUV~=nil)and(ae8WUke~=nil))then UFS7[qIYmlZUV]=ae8WUke else end end;F=UFS7 end;local qRV={level=0,appearances=_KX1(bGdXdZ,{}),seen={len=0}}for EU8,B6 in wXFw((
+iIoI or{}))do F[EU8]=B6 end;for KJgE8hd,ufBK in wXFw(qRV)do
+F[KJgE8hd]=ufBK end;return F end
+local function IDjuB(cZ,maaag,Bi35M,ZjzhK)local tihCL=(Bi35M or 0)local r=(maaag or i(cZ))local ZDeC;if r.preprocess then
+ZDeC=r.preprocess(cZ,r)else ZDeC=cZ end;local HZlrFcs=type(ZDeC)
+local function O()
+local QD=getmetatable(ZDeC)if(nil~=QD)then return(QD).__fennelview else return QD end end
+if((HZlrFcs=="table")or
+((HZlrFcs=="userdata")and O()))then
+return b81Fw(ZDeC,r,tihCL)elseif(HZlrFcs=="number")then return umz(ZDeC)else
+local function hcbBcU()
+if(ZjzhK~=nil)then return ZjzhK elseif("function"==
+type(r["prefer-colon?"]))then
+return r["prefer-colon?"](ZDeC)else return sF(r,"prefer-colon?")end end
+if
+((HZlrFcs=="string")and JXCg(ZDeC)and hcbBcU())then return(":"..ZDeC)elseif(HZlrFcs=="string")then return Z69(ZDeC,r,tihCL)elseif
+((
+HZlrFcs=="boolean")or(HZlrFcs=="nil"))then return tostring(ZDeC)else return
+("#<"..tostring(ZDeC)..">")end end end;VPVZ=IDjuB
+local function xoF(OkdSp2,zO7D8g2)return VPVZ(OkdSp2,i(OkdSp2,zO7D8g2),0)end;return xoF end
+package.preload["fennel.utils"]=package.preload["fennel.utils"]or
+function(...)
+local ywRB55Y3=require("fennel.view")local uUc5YN="1.3.0"
+local function tBlx5J()return
+(
+(nil~=_G.jit)and(type(_G.jit)=="table")and(nil~=_G.jit.on)and
+(nil~=_G.jit.off)and
+(type(_G.jit.version_num)=="number"))end
+local function BJLlq()local ZfJ
+if(_G.jit.os=="OSX")then ZfJ="macOS"else ZfJ=_G.jit.os end;return
+(_G.jit.version.." "..ZfJ.."/".._G.jit.arch)end
+local function DsnW()return
+(
+
+(nil~=_G.fengari)and(type(_G.fengari)=="table")and(nil~=_G.fengari.VERSION)and
+(type(_G.fengari.VERSION_NUM)=="number"))end;local function f0qgvqD()return
+(_G.fengari.RELEASE.." (".._VERSION..")")end;local function r()
+if tBlx5J()then
+return BJLlq()elseif DsnW()then return f0qgvqD()else return("PUC ".._VERSION)end end
+local function IYCyS()return("Fennel "..uUc5YN..
+" on "..r())end;local function rgNe(kZ)
+if(_G.io and _G.io.stderr)then return
+(_G.io.stderr):write(("--WARNING: %s\n"):format(tostring(kZ)))else return nil end end
+local wMf06P3R
+do local G,UV=pcall(require,"utf8")
+if
+((G==true)and(nil~=UV))then local Vmk7fAg=UV;wMf06P3R=Vmk7fAg.len elseif true then local kDtxd5=G;wMf06P3R=string.len else wMf06P3R=nil end end
+local function dzghhciK(CR5bl6Nk,BjIQON,MLLs29h)
+for Umpt,NVyj in ipairs(getmetatable(CR5bl6Nk).keys)do if(CR5bl6Nk[NVyj]and not
+MLLs29h[NVyj])then MLLs29h[NVyj]=true
+table.insert(BjIQON,NVyj)else end end;for Nt3 in pairs(CR5bl6Nk)do
+if not MLLs29h[Nt3]then table.insert(BjIQON,Nt3)else end end;return BjIQON end
+local function PwmycU9j(UPRQV)local OuKZ9r5_b;local AE;do local ZXC=getmetatable(UPRQV)
+if(nil~=ZXC)then ZXC=(ZXC).keys else end;AE=ZXC end
+if AE then
+OuKZ9r5_b=dzghhciK(UPRQV,{},{})else local mPFtR
+do local cwH={}local L8qe=#cwH
+for w0n in pairs(UPRQV)do local Fulc=w0n;if(nil~=Fulc)then L8qe=(L8qe+1)
+do end(cwH)[L8qe]=Fulc else end end;mPFtR=cwH end;local function NK3P(ZEnxqQUU,vKS)
+return(tostring(ZEnxqQUU)* [val ...]
+ "Thread-first macro.
+ Take the first value and splice it into the second form as its first argument.
+ The value of the second form is spliced into the first arg of the third, etc."
+ (var x val)
+ (each [_ e (ipairs [...])]
+ (let [elt (if (list? e) (copy e) (list e))]
+ (table.insert elt 2 x)
+ (set x elt)))
+ x)
+
+ (fn ->>* [val ...]
+ "Thread-last macro.
+ Same as ->, except splices the value into the last position of each form
+ rather than the first."
+ (var x val)
+ (each [_ e (ipairs [...])]
+ (let [elt (if (list? e) (copy e) (list e))]
+ (table.insert elt x)
+ (set x elt)))
+ x)
+
+ (fn -?>* [val ?e ...]
+ "Nil-safe thread-first macro.
+ Same as -> except will short-circuit with nil when it encounters a nil value."
+ (if (= nil ?e)
+ val
+ (let [el (if (list? ?e) (copy ?e) (list ?e))
+ tmp (gensym)]
+ (table.insert el 2 tmp)
+ `(let [,tmp ,val]
+ (if (not= nil ,tmp)
+ (-?> ,el ,...)
+ ,tmp)))))
+
+ (fn -?>>* [val ?e ...]
+ "Nil-safe thread-last macro.
+ Same as ->> except will short-circuit with nil when it encounters a nil value."
+ (if (= nil ?e)
+ val
+ (let [el (if (list? ?e) (copy ?e) (list ?e))
+ tmp (gensym)]
+ (table.insert el tmp)
+ `(let [,tmp ,val]
+ (if (not= ,tmp nil)
+ (-?>> ,el ,...)
+ ,tmp)))))
+
+ (fn ?dot [tbl ...]
+ "Nil-safe table look up.
+ Same as . (dot), except will short-circuit with nil when it encounters
+ a nil value in any of subsequent keys."
+ (let [head (gensym :t)
+ lookups `(do
+ (var ,head ,tbl)
+ ,head)]
+ (each [_ k (ipairs [...])]
+ ;; Kinda gnarly to reassign in place like this, but it emits the best lua.
+ ;; With this impl, it emits a flat, concise, and readable set of ifs
+ (table.insert lookups (# lookups) `(if (not= nil ,head)
+ (set ,head (. ,head ,k)))))
+ lookups))
+
+ (fn doto* [val ...]
+ "Evaluate val and splice it into the first argument of subsequent forms."
+ (assert (not= val nil) "missing subject")
+ (let [rebind? (or (not (sym? val))
+ (multi-sym? val))
+ name (if rebind? (gensym) val)
+ form (if rebind? `(let [,name ,val]) `(do))]
+ (each [_ elt (ipairs [...])]
+ (let [elt (if (list? elt) (copy elt) (list elt))]
+ (table.insert elt 2 name)
+ (table.insert form elt)))
+ (table.insert form name)
+ form))
+
+ (fn when* [condition body1 ...]
+ "Evaluate body for side-effects only when condition is truthy."
+ (assert body1 "expected body")
+ `(if ,condition
+ (do
+ ,body1
+ ,...)))
+
+ (fn with-open* [closable-bindings ...]
+ "Like `let`, but invokes (v:close) on each binding after evaluating the body.
+ The body is evaluated inside `xpcall` so that bound values will be closed upon
+ encountering an error before propagating it."
+ (let [bodyfn `(fn []
+ ,...)
+ closer `(fn close-handlers# [ok# ...]
+ (if ok# ... (error ... 0)))
+ traceback `(. (or package.loaded.fennel debug) :traceback)]
+ (for [i 1 (length closable-bindings) 2]
+ (assert (sym? (. closable-bindings i))
+ "with-open only allows symbols in bindings")
+ (table.insert closer 4 `(: ,(. closable-bindings i) :close)))
+ `(let ,closable-bindings
+ ,closer
+ (close-handlers# (_G.xpcall ,bodyfn ,traceback)))))
+
+ (fn extract-into [iter-tbl]
+ (var (into iter-out found?) (values [] (copy iter-tbl)))
+ (for [i (length iter-tbl) 2 -1]
+ (let [item (. iter-tbl i)]
+ (if (or (= `&into item)
+ (= :into item))
+ (do
+ (assert (not found?) "expected only one &into clause")
+ (set found? true)
+ (set into (. iter-tbl (+ i 1)))
+ (table.remove iter-out i)
+ (table.remove iter-out i)))))
+ (assert (or (not found?) (sym? into) (table? into) (list? into))
+ "expected table, function call, or symbol in &into clause")
+ (values into iter-out))
+
+ (fn collect* [iter-tbl key-expr value-expr ...]
+ "Return a table made by running an iterator and evaluating an expression that
+ returns key-value pairs to be inserted sequentially into the table. This can
+ be thought of as a table comprehension. The body should provide two expressions
+ (used as key and value) or nil, which causes it to be omitted.
+
+ For example,
+ (collect [k v (pairs {:apple \"red\" :orange \"orange\"})]
+ (values v k))
+ returns
+ {:red \"apple\" :orange \"orange\"}
+
+ Supports an &into clause after the iterator to put results in an existing table.
+ Supports early termination with an &until clause."
+ (assert (and (sequence? iter-tbl) (<= 2 (length iter-tbl)))
+ "expected iterator binding table")
+ (assert (not= nil key-expr) "expected key and value expression")
+ (assert (= nil ...)
+ "expected 1 or 2 body expressions; wrap multiple expressions with do")
+ (let [kv-expr (if (= nil value-expr) key-expr `(values ,key-expr ,value-expr))
+ (into iter) (extract-into iter-tbl)]
+ `(let [tbl# ,into]
+ (each ,iter
+ (let [(k# v#) ,kv-expr]
+ (if (and (not= k# nil) (not= v# nil))
+ (tset tbl# k# v#))))
+ tbl#)))
+
+ (fn seq-collect [how iter-tbl value-expr ...]
+ "Common part between icollect and fcollect for producing sequential tables.
+
+ Iteration code only differs in using the for or each keyword, the rest
+ of the generated code is identical."
+ (assert (not= nil value-expr) "expected table value expression")
+ (assert (= nil ...)
+ "expected exactly one body expression. Wrap multiple expressions in do")
+ (let [(into iter) (extract-into iter-tbl)]
+ `(let [tbl# ,into]
+ ;; believe it or not, using a var here has a pretty good performance
+ ;; boost: https://p.hagelb.org/icollect-performance.html
+ (var i# (length tbl#))
+ (,how ,iter
+ (let [val# ,value-expr]
+ (when (not= nil val#)
+ (set i# (+ i# 1))
+ (tset tbl# i# val#))))
+ tbl#)))
+
+ (fn icollect* [iter-tbl value-expr ...]
+ "Return a sequential table made by running an iterator and evaluating an
+ expression that returns values to be inserted sequentially into the table.
+ This can be thought of as a table comprehension. If the body evaluates to nil
+ that element is omitted.
+
+ For example,
+ (icollect [_ v (ipairs [1 2 3 4 5])]
+ (when (not= v 3)
+ (* v v)))
+ returns
+ [1 4 16 25]
+
+ Supports an &into clause after the iterator to put results in an existing table.
+ Supports early termination with an &until clause."
+ (assert (and (sequence? iter-tbl) (<= 2 (length iter-tbl)))
+ "expected iterator binding table")
+ (seq-collect 'each iter-tbl value-expr ...))
+
+ (fn fcollect* [iter-tbl value-expr ...]
+ "Return a sequential table made by advancing a range as specified by
+ for, and evaluating an expression that returns values to be inserted
+ sequentially into the table. This can be thought of as a range
+ comprehension. If the body evaluates to nil that element is omitted.
+
+ For example,
+ (fcollect [i 1 10 2]
+ (when (not= i 3)
+ (* i i)))
+ returns
+ [1 25 49 81]
+
+ Supports an &into clause after the range to put results in an existing table.
+ Supports early termination with an &until clause."
+ (assert (and (sequence? iter-tbl) (< 2 (length iter-tbl)))
+ "expected range binding table")
+ (seq-collect 'for iter-tbl value-expr ...))
+
+ (fn accumulate-impl [for? iter-tbl body ...]
+ (assert (and (sequence? iter-tbl) (<= 4 (length iter-tbl)))
+ "expected initial value and iterator binding table")
+ (assert (not= nil body) "expected body expression")
+ (assert (= nil ...)
+ "expected exactly one body expression. Wrap multiple expressions with do")
+ (let [[accum-var accum-init] iter-tbl
+ iter (sym (if for? "for" "each"))] ; accumulate or faccumulate?
+ `(do
+ (var ,accum-var ,accum-init)
+ (,iter ,[(unpack iter-tbl 3)]
+ (set ,accum-var ,body))
+ ,(if (list? accum-var)
+ (list (sym :values) (unpack accum-var))
+ accum-var))))
+
+ (fn accumulate* [iter-tbl body ...]
+ "Accumulation macro.
+
+ It takes a binding table and an expression as its arguments. In the binding
+ table, the first form starts out bound to the second value, which is an initial
+ accumulator. The rest are an iterator binding table in the format `each` takes.
+
+ It runs through the iterator in each step of which the given expression is
+ evaluated, and the accumulator is set to the value of the expression. It
+ eventually returns the final value of the accumulator.
+
+ For example,
+ (accumulate [total 0
+ _ n (pairs {:apple 2 :orange 3})]
+ (+ total n))
+ returns 5"
+ (accumulate-impl false iter-tbl body ...))
+
+ (fn faccumulate* [iter-tbl body ...]
+ "Identical to accumulate, but after the accumulator the binding table is the
+ same as `for` instead of `each`. Like collect to fcollect, will iterate over a
+ numerical range like `for` rather than an iterator."
+ (accumulate-impl true iter-tbl body ...))
+
+ (fn double-eval-safe? [x type]
+ (or (= :number type) (= :string type) (= :boolean type)
+ (and (sym? x) (not (multi-sym? x)))))
+
+ (fn partial* [f ...]
+ "Return a function with all arguments partially applied to f."
+ (assert f "expected a function to partially apply")
+ (let [bindings []
+ args []]
+ (each [_ arg (ipairs [...])]
+ (if (double-eval-safe? arg (type arg))
+ (table.insert args arg)
+ (let [name (gensym)]
+ (table.insert bindings name)
+ (table.insert bindings arg)
+ (table.insert args name))))
+ (let [body (list f (unpack args))]
+ (table.insert body _VARARG)
+ ;; only use the extra let if we need double-eval protection
+ (if (= 0 (length bindings))
+ `(fn [,_VARARG] ,body)
+ `(let ,bindings
+ (fn [,_VARARG] ,body))))))
+
+ (fn pick-args* [n f]
+ "Create a function of arity n that applies its arguments to f.
+
+ For example,
+ (pick-args 2 func)
+ expands to
+ (fn [_0_ _1_] (func _0_ _1_))"
+ (if (and _G.io _G.io.stderr)
+ (_G.io.stderr:write
+ "-- WARNING: pick-args is deprecated and will be removed in the future.\n"))
+ (assert (and (= (type n) :number) (= n (math.floor n)) (<= 0 n))
+ (.. "Expected n to be an integer literal >= 0, got " (tostring n)))
+ (let [bindings []]
+ (for [i 1 n]
+ (tset bindings i (gensym)))
+ `(fn ,bindings
+ (,f ,(unpack bindings)))))
+
+ (fn pick-values* [n ...]
+ "Evaluate to exactly n values.
+
+ For example,
+ (pick-values 2 ...)
+ expands to
+ (let [(_0_ _1_) ...]
+ (values _0_ _1_))"
+ (assert (and (= :number (type n)) (<= 0 n) (= n (math.floor n)))
+ (.. "Expected n to be an integer >= 0, got " (tostring n)))
+ (let [let-syms (list)
+ let-values (if (= 1 (select "#" ...)) ... `(values ,...))]
+ (for [i 1 n]
+ (table.insert let-syms (gensym)))
+ (if (= n 0) `(values)
+ `(let [,let-syms ,let-values]
+ (values ,(unpack let-syms))))))
+
+ (fn lambda* [...]
+ "Function literal with nil-checked arguments.
+ Like `fn`, but will throw an exception if a declared argument is passed in as
+ nil, unless that argument's name begins with a question mark."
+ (let [args [...]
+ has-internal-name? (sym? (. args 1))
+ arglist (if has-internal-name? (. args 2) (. args 1))
+ docstring-position (if has-internal-name? 3 2)
+ has-docstring? (and (< docstring-position (length args))
+ (= :string (type (. args docstring-position))))
+ arity-check-position (- 4 (if has-internal-name? 0 1)
+ (if has-docstring? 0 1))
+ empty-body? (< (length args) arity-check-position)]
+ (fn check! [a]
+ (if (table? a)
+ (each [_ a (pairs a)]
+ (check! a))
+ (let [as (tostring a)]
+ (and (not (as:match "^?")) (not= as "&") (not= as "_")
+ (not= as "...") (not= as "&as")))
+ (table.insert args arity-check-position
+ `(_G.assert (not= nil ,a)
+ ,(: "Missing argument %s on %s:%s" :format
+ (tostring a)
+ (or a.filename :unknown)
+ (or a.line "?"))))))
+
+ (assert (= :table (type arglist)) "expected arg list")
+ (each [_ a (ipairs arglist)]
+ (check! a))
+ (if empty-body?
+ (table.insert args (sym :nil)))
+ `(fn ,(unpack args))))
+
+ (fn macro* [name ...]
+ "Define a single macro."
+ (assert (sym? name) "expected symbol for macro name")
+ (local args [...])
+ `(macros {,(tostring name) (fn ,(unpack args))}))
+
+ (fn macrodebug* [form return?]
+ "Print the resulting form after performing macroexpansion.
+ With a second argument, returns expanded form as a string instead of printing."
+ (let [handle (if return? `do `print)]
+ `(,handle ,(view (macroexpand form _SCOPE)))))
+
+ (fn import-macros* [binding1 module-name1 ...]
+ "Bind a table of macros from each macro module according to a binding form.
+ Each binding form can be either a symbol or a k/v destructuring table.
+ Example:
+ (import-macros mymacros :my-macros ; bind to symbol
+ {:macro1 alias : macro2} :proj.macros) ; import by name"
+ (assert (and binding1 module-name1 (= 0 (% (select "#" ...) 2)))
+ "expected even number of binding/modulename pairs")
+ (for [i 1 (select "#" binding1 module-name1 ...) 2]
+ ;; delegate the actual loading of the macros to the require-macros
+ ;; special which already knows how to set up the compiler env and stuff.
+ ;; this is weird because require-macros is deprecated but it works.
+ (let [(binding modname) (select i binding1 module-name1 ...)
+ scope (get-scope)
+ ;; if the module-name is an expression (and not just a string) we
+ ;; patch our expression to have the correct source filename so
+ ;; require-macros can pass it down when resolving the module-name.
+ expr `(import-macros ,modname)
+ filename (if (list? modname) (. modname 1 :filename) :unknown)
+ _ (tset expr :filename filename)
+ macros* (_SPECIALS.require-macros expr scope {} binding)]
+ (if (sym? binding)
+ ;; bind whole table of macros to table bound to symbol
+ (tset scope.macros (. binding 1) macros*)
+ ;; 1-level table destructuring for importing individual macros
+ (table? binding)
+ (each [macro-name [import-key] (pairs binding)]
+ (assert (= :function (type (. macros* macro-name)))
+ (.. "macro " macro-name " not found in module "
+ (tostring modname)))
+ (tset scope.macros import-key (. macros* macro-name))))))
+ nil)
+
+ {:-> ->*
+ :->> ->>*
+ :-?> -?>*
+ :-?>> -?>>*
+ :?. ?dot
+ :doto doto*
+ :when when*
+ :with-open with-open*
+ :collect collect*
+ :icollect icollect*
+ :fcollect fcollect*
+ :accumulate accumulate*
+ :faccumulate faccumulate*
+ :partial partial*
+ :lambda lambda*
+ :λ lambda*
+ :pick-args pick-args*
+ :pick-values pick-values*
+ :macro macro*
+ :macrodebug macrodebug*
+ :import-macros import-macros*}
+ ]===],{env=KPHc6Aeq,scope=Mm.scopes.compiler,useMetadata=true,filename="src/fennel/macros.fnl",moduleName=IGiruNd})local Z53B
+for PtID,j in pairs(kB6a)do Mm.scopes.global.macros[PtID]=j end;Z53B=nil
+local _5pPpX=K([===[;;; Pattern matching
+ ;; This is separated out so we can use the "core" macros during the
+ ;; implementation of pattern matching.
+
+ (fn copy [t] (collect [k v (pairs t)] k v))
+
+ (fn with [opts k]
+ (doto (copy opts) (tset k true)))
+
+ (fn without [opts k]
+ (doto (copy opts) (tset k nil)))
+
+ (fn case-values [vals pattern unifications case-pattern opts]
+ (let [condition `(and)
+ bindings []]
+ (each [i pat (ipairs pattern)]
+ (let [(subcondition subbindings) (case-pattern [(. vals i)] pat
+ unifications (without opts :multival?))]
+ (table.insert condition subcondition)
+ (icollect [_ b (ipairs subbindings) &into bindings] b)))
+ (values condition bindings)))
+
+ (fn case-table [val pattern unifications case-pattern opts]
+ (let [condition `(and (= (_G.type ,val) :table))
+ bindings []]
+ (each [k pat (pairs pattern)]
+ (if (= pat `&)
+ (let [rest-pat (. pattern (+ k 1))
+ rest-val `(select ,k ((or table.unpack _G.unpack) ,val))
+ subcondition (case-table `(pick-values 1 ,rest-val)
+ rest-pat unifications case-pattern
+ (without opts :multival?))]
+ (if (not (sym? rest-pat))
+ (table.insert condition subcondition))
+ (assert (= nil (. pattern (+ k 2)))
+ "expected & rest argument before last parameter")
+ (table.insert bindings rest-pat)
+ (table.insert bindings [rest-val]))
+ (= k `&as)
+ (do
+ (table.insert bindings pat)
+ (table.insert bindings val))
+ (and (= :number (type k)) (= `&as pat))
+ (do
+ (assert (= nil (. pattern (+ k 2)))
+ "expected &as argument before last parameter")
+ (table.insert bindings (. pattern (+ k 1)))
+ (table.insert bindings val))
+ ;; don't process the pattern right after &/&as; already got it
+ (or (not= :number (type k)) (and (not= `&as (. pattern (- k 1)))
+ (not= `& (. pattern (- k 1)))))
+ (let [subval `(. ,val ,k)
+ (subcondition subbindings) (case-pattern [subval] pat
+ unifications
+ (without opts :multival?))]
+ (table.insert condition subcondition)
+ (icollect [_ b (ipairs subbindings) &into bindings] b))))
+ (values condition bindings)))
+
+ (fn case-guard [vals condition guards unifications case-pattern opts]
+ (if (= 0 (length guards))
+ (case-pattern vals condition unifications opts)
+ (let [(pcondition bindings) (case-pattern vals condition unifications opts)
+ condition `(and ,(unpack guards))]
+ (values `(and ,pcondition
+ (let ,bindings
+ ,condition)) bindings))))
+
+ (fn symbols-in-pattern [pattern]
+ "gives the set of symbols inside a pattern"
+ (if (list? pattern)
+ (let [result {}]
+ (each [_ child-pattern (ipairs pattern)]
+ (collect [name symbol (pairs (symbols-in-pattern child-pattern)) &into result]
+ name symbol))
+ result)
+ (sym? pattern)
+ (if (and (not= pattern `or)
+ (not= pattern `where)
+ (not= pattern `?)
+ (not= pattern `nil))
+ {(tostring pattern) pattern}
+ {})
+ (= (type pattern) :table)
+ (let [result {}]
+ (each [key-pattern value-pattern (pairs pattern)]
+ (collect [name symbol (pairs (symbols-in-pattern key-pattern)) &into result]
+ name symbol)
+ (collect [name symbol (pairs (symbols-in-pattern value-pattern)) &into result]
+ name symbol))
+ result)
+ {}))
+
+ (fn symbols-in-every-pattern [pattern-list infer-unification?]
+ "gives a list of symbols that are present in every pattern in the list"
+ (let [?symbols (accumulate [?symbols nil
+ _ pattern (ipairs pattern-list)]
+ (let [in-pattern (symbols-in-pattern pattern)]
+ (if ?symbols
+ (do
+ (each [name symbol (pairs ?symbols)]
+ (when (not (. in-pattern name))
+ (tset ?symbols name nil)))
+ ?symbols)
+ in-pattern)))]
+ (icollect [_ symbol (pairs (or ?symbols {}))]
+ (if (not (and infer-unification?
+ (in-scope? symbol)))
+ symbol))))
+
+ (fn case-or [vals pattern guards unifications case-pattern opts]
+ (let [pattern [(unpack pattern 2)]
+ bindings (symbols-in-every-pattern pattern opts.infer-unification?)] ;; TODO opts.infer-unification instead of opts.unification?
+ (if (= 0 (length bindings))
+ ;; no bindings special case generates simple code
+ (let [condition
+ (icollect [i subpattern (ipairs pattern) &into `(or)]
+ (let [(subcondition subbindings) (case-pattern vals subpattern unifications opts)]
+ subcondition))]
+ (values
+ (if (= 0 (length guards))
+ condition
+ `(and ,condition ,(unpack guards)))
+ []))
+ ;; case with bindings is handled specially, and returns three values instead of two
+ (let [matched? (gensym :matched?)
+ bindings-mangled (icollect [_ binding (ipairs bindings)]
+ (gensym (tostring binding)))
+ pre-bindings `(if)]
+ (each [i subpattern (ipairs pattern)]
+ (let [(subcondition subbindings) (case-guard vals subpattern guards {} case-pattern opts)]
+ (table.insert pre-bindings subcondition)
+ (table.insert pre-bindings `(let ,subbindings
+ (values true ,(unpack bindings))))))
+ (values matched?
+ [`(,(unpack bindings)) `(values ,(unpack bindings-mangled))]
+ [`(,matched? ,(unpack bindings-mangled)) pre-bindings])))))
+
+ (fn case-pattern [vals pattern unifications opts top-level?]
+ "Take the AST of values and a single pattern and returns a condition
+ to determine if it matches as well as a list of bindings to
+ introduce for the duration of the body if it does match."
+
+ ;; This function returns the following values (multival):
+ ;; a "condition", which is an expression that determines whether the
+ ;; pattern should match,
+ ;; a "bindings", which bind all of the symbols used in a pattern
+ ;; an optional "pre-bindings", which is a list of bindings that happen
+ ;; before the condition and bindings are evaluated. These should only
+ ;; come from a (case-or). In this case there should be no recursion:
+ ;; the call stack should be case-condition > case-pattern > case-or
+ ;;
+ ;; Here are the expected flags in the opts table:
+ ;; :infer-unification? boolean - if the pattern should guess when to unify (ie, match -> true, case -> false)
+ ;; :multival? boolean - if the pattern can contain multivals (in order to disallow patterns like [(1 2)])
+ ;; :in-where? boolean - if the pattern is surrounded by (where) (where opts into more pattern features)
+ ;; :legacy-guard-allowed? boolean - if the pattern should allow `(a ? b) patterns
+
+ ;; we have to assume we're matching against multiple values here until we
+ ;; know we're either in a multi-valued clause (in which case we know the #
+ ;; of vals) or we're not, in which case we only care about the first one.
+ (let [[val] vals]
+ (if (and (sym? pattern)
+ (or (= pattern `nil)
+ (and opts.infer-unification?
+ (in-scope? pattern)
+ (not= pattern `_))
+ (and opts.infer-unification?
+ (multi-sym? pattern)
+ (in-scope? (. (multi-sym? pattern) 1)))))
+ (values `(= ,val ,pattern) [])
+ ;; unify a local we've seen already
+ (and (sym? pattern) (. unifications (tostring pattern)))
+ (values `(= ,(. unifications (tostring pattern)) ,val) [])
+ ;; bind a fresh local
+ (sym? pattern)
+ (let [wildcard? (: (tostring pattern) :find "^_")]
+ (if (not wildcard?) (tset unifications (tostring pattern) val))
+ (values (if (or wildcard? (string.find (tostring pattern) "^?")) true
+ `(not= ,(sym :nil) ,val)) [pattern val]))
+ ;; opt-in unify with (=)
+ (and (list? pattern)
+ (= (. pattern 1) `=)
+ (sym? (. pattern 2)))
+ (let [bind (. pattern 2)]
+ (assert-compile (= 2 (length pattern)) "(=) should take only one argument" pattern)
+ (assert-compile (not opts.infer-unification?) "(=) cannot be used inside of match" pattern)
+ (assert-compile opts.in-where? "(=) must be used in (where) patterns" pattern)
+ (assert-compile (and (sym? bind) (not= bind `nil) "= has to bind to a symbol" bind))
+ (values `(= ,val ,bind) []))
+ ;; where-or clause
+ (and (list? pattern) (= (. pattern 1) `where) (list? (. pattern 2)) (= (. pattern 2 1) `or))
+ (do
+ (assert-compile top-level? "can't nest (where) pattern" pattern)
+ (case-or vals (. pattern 2) [(unpack pattern 3)] unifications case-pattern (with opts :in-where?)))
+ ;; where clause
+ (and (list? pattern) (= (. pattern 1) `where))
+ (do
+ (assert-compile top-level? "can't nest (where) pattern" pattern)
+ (case-guard vals (. pattern 2) [(unpack pattern 3)] unifications case-pattern (with opts :in-where?)))
+ ;; or clause (not allowed on its own)
+ (and (list? pattern) (= (. pattern 1) `or))
+ (do
+ (assert-compile top-level? "can't nest (or) pattern" pattern)
+ ;; This assertion can be removed to make patterns more permissive
+ (assert-compile false "(or) must be used in (where) patterns" pattern)
+ (case-or vals pattern [] unifications case-pattern opts))
+ ;; guard clause
+ (and (list? pattern) (= (. pattern 2) `?))
+ (do
+ (assert-compile opts.legacy-guard-allowed? "legacy guard clause not supported in case" pattern)
+ (case-guard vals (. pattern 1) [(unpack pattern 3)] unifications case-pattern opts))
+ ;; multi-valued patterns (represented as lists)
+ (list? pattern)
+ (do
+ (assert-compile opts.multival? "can't nest multi-value destructuring" pattern)
+ (case-values vals pattern unifications case-pattern opts))
+ ;; table patterns
+ (= (type pattern) :table)
+ (case-table val pattern unifications case-pattern opts)
+ ;; literal value
+ (values `(= ,val ,pattern) []))))
+
+ (fn add-pre-bindings [out pre-bindings]
+ "Decide when to switch from the current `if` AST to a new one"
+ (if pre-bindings
+ ;; `out` no longer needs to grow.
+ ;; Instead, a new tail `if` AST is introduced, which is where the rest of
+ ;; the clauses will get appended. This way, all future clauses have the
+ ;; pre-bindings in scope.
+ (let [tail `(if)]
+ (table.insert out true)
+ (table.insert out `(let ,pre-bindings ,tail))
+ tail)
+ ;; otherwise, keep growing the current `if` AST.
+ out))
+
+ (fn case-condition [vals clauses match?]
+ "Construct the actual `if` AST for the given match values and clauses."
+ ;; root is the original `if` AST.
+ ;; out is the `if` AST that is currently being grown.
+ (let [root `(if)]
+ (faccumulate [out root
+ i 1 (length clauses) 2]
+ (let [pattern (. clauses i)
+ body (. clauses (+ i 1))
+ (condition bindings pre-bindings) (case-pattern vals pattern {}
+ {:multival? true
+ :infer-unification? match?
+ :legacy-guard-allowed? match?}
+ true)
+ out (add-pre-bindings out pre-bindings)]
+ ;; grow the `if` AST by one extra condition
+ (table.insert out condition)
+ (table.insert out `(let ,bindings
+ ,body))
+ out))
+ root))
+
+ (fn count-case-multival [pattern]
+ "Identify the amount of multival values that a pattern requires."
+ (if (and (list? pattern) (= (. pattern 2) `?))
+ (count-case-multival (. pattern 1))
+ (and (list? pattern) (= (. pattern 1) `where))
+ (count-case-multival (. pattern 2))
+ (and (list? pattern) (= (. pattern 1) `or))
+ (accumulate [longest 0
+ _ child-pattern (ipairs pattern)]
+ (math.max longest (count-case-multival child-pattern)))
+ (list? pattern)
+ (length pattern)
+ 1))
+
+ (fn case-val-syms [clauses]
+ "What is the length of the largest multi-valued clause? return a list of that
+ many gensyms."
+ (let [patterns (fcollect [i 1 (length clauses) 2]
+ (. clauses i))
+ sym-count (accumulate [longest 0
+ _ pattern (ipairs patterns)]
+ (math.max longest (count-case-multival pattern)))]
+ (fcollect [i 1 sym-count &into (list)]
+ (gensym))))
+
+ (fn case-impl [match? val ...]
+ "The shared implementation of case and match."
+ (assert (not= val nil) "missing subject")
+ (assert (= 0 (math.fmod (select :# ...) 2))
+ "expected even number of pattern/body pairs")
+ (assert (not= 0 (select :# ...))
+ "expected at least one pattern/body pair")
+ (let [clauses [...]
+ vals (case-val-syms clauses)]
+ ;; protect against multiple evaluation of the value, bind against as
+ ;; many values as we ever match against in the clauses.
+ (list `let [vals val] (case-condition vals clauses match?))))
+
+ (fn case* [val ...]
+ "Perform pattern matching on val. See reference for details.
+
+ Syntax:
+
+ (case data-expression
+ pattern body
+ (where pattern guards*) body
+ (or pattern patterns*) body
+ (where (or pattern patterns*) guards*) body
+ ;; legacy:
+ (pattern ? guards*) body)"
+ (case-impl false val ...))
+
+ (fn match* [val ...]
+ "Perform pattern matching on val, automatically unifying on variables in
+ local scope. See reference for details.
+
+ Syntax:
+
+ (match data-expression
+ pattern body
+ (where pattern guards*) body
+ (or pattern patterns*) body
+ (where (or pattern patterns*) guards*) body
+ ;; legacy:
+ (pattern ? guards*) body)"
+ (case-impl true val ...))
+
+ (fn case-try-step [how expr else pattern body ...]
+ (if (= nil pattern body)
+ expr
+ ;; unlike regular match, we can't know how many values the value
+ ;; might evaluate to, so we have to capture them all in ... via IIFE
+ ;; to avoid double-evaluation.
+ `((fn [...]
+ (,how ...
+ ,pattern ,(case-try-step how body else ...)
+ ,(unpack else)))
+ ,expr)))
+
+ (fn case-try-impl [how expr pattern body ...]
+ (let [clauses [pattern body ...]
+ last (. clauses (length clauses))
+ catch (if (= `catch (and (= :table (type last)) (. last 1)))
+ (let [[_ & e] (table.remove clauses)] e) ; remove `catch sym
+ [`_# `...])]
+ (assert (= 0 (math.fmod (length clauses) 2))
+ "expected every pattern to have a body")
+ (assert (= 0 (math.fmod (length catch) 2))
+ "expected every catch pattern to have a body")
+ (case-try-step how expr catch (unpack clauses))))
+
+ (fn case-try* [expr pattern body ...]
+ "Perform chained pattern matching for a sequence of steps which might fail.
+
+ The values from the initial expression are matched against the first pattern.
+ If they match, the first body is evaluated and its values are matched against
+ the second pattern, etc.
+
+ If there is a (catch pat1 body1 pat2 body2 ...) form at the end, any mismatch
+ from the steps will be tried against these patterns in sequence as a fallback
+ just like a normal match. If there is no catch, the mismatched values will be
+ returned as the value of the entire expression."
+ (case-try-impl `case expr pattern body ...))
+
+ (fn match-try* [expr pattern body ...]
+ "Perform chained pattern matching for a sequence of steps which might fail.
+
+ The values from the initial expression are matched against the first pattern.
+ If they match, the first body is evaluated and its values are matched against
+ the second pattern, etc.
+
+ If there is a (catch pat1 body1 pat2 body2 ...) form at the end, any mismatch
+ from the steps will be tried against these patterns in sequence as a fallback
+ just like a normal match. If there is no catch, the mismatched values will be
+ returned as the value of the entire expression."
+ (case-try-impl `match expr pattern body ...))
+
+ {:case case*
+ :case-try case-try*
+ :match match*
+ :match-try match-try*}
+ ]===],{env=KPHc6Aeq,scope=Mm.scopes.compiler,allowedGlobals=false,useMetadata=true,filename="src/fennel/match.fnl",moduleName=IGiruNd})
+for aP,oUuAO4X6 in pairs(_5pPpX)do Mm.scopes.global.macros[aP]=oUuAO4X6 end;package.preload[IGiruNd]=nil end;local iJYISVW=FkyzO
+local function K(q2lIy)
+is_passed,out=pcall(function()return iJYISVW.eval(q2lIy)end,function(n62)return
+n62 end)lua=""out=tostring(out)preview=out:gsub("\n.*","")if is_passed then
+lua=iJYISVW.compileString(q2lIy)end
+return{is_passed=is_passed,preview=preview,out=out,lua=lua}end
+return
+{{CodeBlock=function(_NgKSEFg)
+if _NgKSEFg.classes:includes("eval")then local ydBwQ3=_NgKSEFg.text
+print("⚙️ ",ydBwQ3)local Jo7L=K(ydBwQ3)
+print("",Jo7L["is_passed"],"→",Jo7L["preview"])if _NgKSEFg.classes:includes("replace")then return
+pandoc.CodeBlock(Jo7L["out"],{code=ydBwQ3})end end end}}
\ No newline at end of file
diff --git a/man/README.md b/man/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/man/locales/en.md b/man/locales/en.md
new file mode 100644
index 0000000..1c18169
--- /dev/null
+++ b/man/locales/en.md
@@ -0,0 +1,66 @@
+# Test 1
+
+With Literate Pandoc you can write code as you write everything else! For
+example, this is a function declaration: f() = (+ 1 2 3).
+
+Every function declaration has:
+
+1. A *function name* consisting in:
+ 1. One or more alphanumeric characters following by a opening parenthesis
+ (i.e. `%w+(`).
+ 2. Optional arguments (args).
+ 3. Closing parenthesis (`)`).
+2. A *function assignment* indicated with the equal sign (`=`).
+3. A *function body* composed by Lisp code between parentheses.
+
+Any function can be called at any time by is function name, for example now
+we call f()! This also apply for function that haven't been defined, like
+foo(1).
+
+The functions can contain args, for example:
+
+* foo(n) = (* #n #n) is a function declaration with the arg `n`; any arg can
+ be used in the function body if it is prefixed with a hashtag (`#n`).
+* They can contain more args like bar(a, b) = (- a b).
+* For a variable number of args the `...` symbol is used: baz(...) =
+ (.. #...). You can call baz() or baz('hello', ' ', 'world!').
+
+All args can be treated as keyword args (kwargs), so foo(3) can be foo(n: 3),
+or bar(2, 1) can be bar(b: 1, a: 2).
+
+With kwargs you can declare args in any order and makes args more readable. The
+trade-off is that they are verbose. Also, `...` arg can't be use as kwarg!
+
+Calling f(), foo(2), bar(4, 3), baz(':', ')') will evaluate the functions and
+will print the output during Pandoc conversion, but, what if we want to...
+
+1. ...write the evaluation result instead of the function call?
+ For example, see `6` instead of `f()`, or `:)` instead of `baz(':', ')')`.
+2. ...remove the function call? So you don't see `f()` anymore.
+3. ...avoid evaluation?
+4. ...write the evaluation result to a file?
+5. ...write the evaluated code to a file?
+6. ...write Lisp code as Lua code to a file?
+
+Well, we can do that with *reserved kwargs*. All reserved kwargs are optional
+(you don't declare them) and start with underscore (`_`):
+
+* `_action`: indicates action to perfom with the function call; can be:
+ * `'flip'`: puts its evaluation result instead;
+ * `'wipe'`: deletes it from source document but still calls it;
+ * `'skip'`: doesn't perform call;
+ * `'kill'`: doesn't perform call and deletes it from source.
+* `_eval`: saves evaluation to file; takes file path string as value.
+* `_code`: saves code to file; takes file path string as value.
+* `_lua`: converts Lisp code to Lua and saves it to file; takes file path
+ string as value.
+
+With reserved kwargs, we can finally replace f() by its evaluation result like
+this: f(_action: 'flip'); or skip its evaluation: f(_action: 'skip').
+
+We can also save different files:
+
+* The avaluation results of foo(4, _eval: 'results.txt') and bar(5, 4, _eval:
+ 'results.txt').
+* The code of foo(4, _code: 'code.lisp') and bar(5, 4, _code: 'code.lisp').
+* The Lua code of foo(4, _lua: 'code.lua') and bar (5, 4, _lua: 'code.lua').
diff --git a/man/source.md b/man/source.md
new file mode 100644
index 0000000..e69de29
diff --git a/src/fennel.lua b/opt/fennel.lua
similarity index 100%
rename from src/fennel.lua
rename to opt/fennel.lua
diff --git a/opt/luaminify b/opt/luaminify
new file mode 160000
index 0000000..cce8f5b
--- /dev/null
+++ b/opt/luaminify
@@ -0,0 +1 @@
+Subproject commit cce8f5b51bdffd1e0439956ccc308d0e5d164f90
diff --git a/scripts/get_fennel.sh b/scripts/get_fennel.sh
index 37e4151..a379399 100644
--- a/scripts/get_fennel.sh
+++ b/scripts/get_fennel.sh
@@ -4,7 +4,7 @@ ROOT=$(git rev-parse --show-toplevel)
URL="https://fennel-lang.org/downloads/$NAME.tar.gz"
# Copies Fennel from release tarball
-cd $ROOT/src
+cd $ROOT/opt
curl -O $URL
tar -xvzf $NAME.tar.gz
mv $NAME/fennel.lua .
diff --git a/scripts/make_dist.sh b/scripts/make_dist.sh
new file mode 100644
index 0000000..0e68bda
--- /dev/null
+++ b/scripts/make_dist.sh
@@ -0,0 +1,32 @@
+# Variables
+NAME="literate.min.lua"
+ROOT=$(git rev-parse --show-toplevel)
+DIST=$ROOT/dist/$NAME
+LICENSE="--[[
+Literate Pandoc & Fennel Bundle:
+ A Pandoc filter for literate and natural programming
+Fennel:
+ (C) 2016-2023 Calvin Rose and contributors
+ License: MIT License https://git.sr.ht/~technomancy/fennel/tree/main/item/LICENSE
+ Source: https://sr.ht/~technomancy/fennel or https://github.com/bakpakin/Fennel/issues
+ Website: https://fennel-lang.org
+Literate Pandoc:
+ (C) 2023 perro hi@perrotuerto.blog
+ License: GPLv3 https://git.cuates.net/perro/literate-pandoc/src/branch/no-masters/LICENSE.txt
+ Source: https://git.cuates.net/perro/literate-pandoc
+The following code is minified, check Literate Pandoc source for a readable version
+]]--"
+
+# Merges Fennel and Literate Pandoc
+head -n -1 $ROOT/opt/fennel.lua > $DIST
+echo "local fennel = mod" >> $DIST
+tail -n +11 $ROOT/src/literate.lua >> $DIST
+echo "Bundling complete"
+
+# Minifies code
+cd opt/luaminify
+lua CommandLineMinify.lua $DIST $DIST.tmp
+
+# Adds license
+(echo "$LICENSE" && cat $DIST.tmp) > $DIST
+rm $DIST.tmp
diff --git a/scripts/test.sh b/scripts/test.sh
index 84a3bb7..cedafcf 100644
--- a/scripts/test.sh
+++ b/scripts/test.sh
@@ -1,8 +1,18 @@
# Variables
-DIR=$(git rev-parse --show-toplevel)
+ROOT=$(git rev-parse --show-toplevel)
+FILTER=$ROOT/src/literate.lua
+ARGS=()
+
+# Removes unwanted args
+for arg in "$@"; do
+ case $arg in
+ "--dist") FILTER=$ROOT/dist/literate.min.lua ;;
+ *) ARGS+=($arg) ;;
+ esac
+done
# Moves to tests directory and clears the terminal
-cd $DIR/tests
+cd $ROOT/tests
clear
# Checks args
@@ -15,10 +25,10 @@ fi
# Does tests
echo "🐾 Starting tests"
-for arg in "$@"; do
+for arg in "$ARGS"; do
echo && echo "⚗️ Test in '$arg' format:"
- md=`cat *.md`
- rst=$'\n'`(pandoc -t markdown *.rst)`
- org=$'\n'`pandoc -t markdown *.org`
- echo "$md" "$rst" "$org" | pandoc --lua-filter $DIR/src/literate.lua -t $arg
+ mds=$'\n\n'`(pandoc -t markdown *.md)`
+ rst=$'\n\n'`(pandoc -t markdown *.rst)`
+ org=$'\n\n'`(pandoc -t markdown *.org)`
+ echo "$mds" "$rst" "$org" | pandoc --lua-filter $FILTER -t $arg
done
diff --git a/src/literate.lua b/src/literate.lua
index ae0a589..e390028 100644
--- a/src/literate.lua
+++ b/src/literate.lua
@@ -1,18 +1,13 @@
---[[
-literate.lua
-(C) 2023 perro tuerto
-Code under GPLv3
-]]
-
--- Initial setup
--- 1. Gets 'src' dir path
--- 2. Enables Fennel for Lisp-Lua embeded compatibility
--- Cfr. https://fennel-lang.org
+-- IMPORTANT: for distribution the first 10 lines are changed to:
+-- local fennel = mod
+-- Initial setup for development:
+-- Enables Fennel for Lisp-Lua embeded compatibility
+-- Cfr. https://fennel-lang.org
local src_root = pandoc.path.directory(PANDOC_SCRIPT_FILE)
-local fennel_lua = pandoc.path.join({src_root, "fennel.lua"})
+local fennel_lua = pandoc.path.join({src_root, "../opt/fennel.lua"})
package.path = package.path .. ";" .. fennel_lua
local fennel = require("fennel")
-
+-- IMPORTANT: code for distribution starts after this line.
--[[
-- Lua LPeg shortcuts
local P, S, R, Cf, Cc, Ct, V, Cs, Cg, Cb, B, C, Cmt =
diff --git a/tests/t1.md b/tests/t1.md
deleted file mode 100644
index 06174d4..0000000
--- a/tests/t1.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Test 1
-
-This is written in MD format.
-
-Does nothing:
-
-```
-:(){ :|:& };: # NEVER try to execute this
-```
-
-Evals:
-
-``` eval
-(+ 1 2 3)
-```
diff --git a/tests/t2.md b/tests/t2.md
deleted file mode 100644
index 98e971b..0000000
--- a/tests/t2.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Test 2
-
-This is written in MD format.
-
-Evals and replaces:
-
-``` {.eval .replace}
-(.. "hello" " world")
-```
-
-Fails and replaces with error:
-
-``` {.eval .replace}
-(FAIL! "hello" " world")
-```
diff --git a/tests/t3.rst b/tests/t3.rst
deleted file mode 100644
index 6099bfd..0000000
--- a/tests/t3.rst
+++ /dev/null
@@ -1,18 +0,0 @@
-Test 3
-======
-
-This is written in RST format.
-
-Evals:
-
-.. code::
- :class: eval
-
- (+ 4 5 6)
-
-Evals and replaces:
-
-.. code::
- :class: eval replace
-
- (+ 4 5 6)
diff --git a/tests/t4.org b/tests/t4.org
deleted file mode 100644
index 854dbb0..0000000
--- a/tests/t4.org
+++ /dev/null
@@ -1,14 +0,0 @@
-* Test 4
-This is written in ORG format.
-
-Evals:
-
-#+begin_src eval
-(+ 7 8 9)
-#+end_src
-
-Doesn't eval:
-
-#+begin_src eval replace
-(+ 7 8 9)
-#+end_src
diff --git a/tests/test.md b/tests/test.md
new file mode 100644
index 0000000..0d5b821
--- /dev/null
+++ b/tests/test.md
@@ -0,0 +1,133 @@
+# Test with Markdown
+
+## Function Declarations
+
+Valid declarations:
+
+* A declaration: f1() = (+ 1 2 3). All declarations should be print on `--verbose`.
+* f2() = (+ 4 5 6) is another declaration.
+* f3() = (+ 7 8 9)
+* Two declarations: f4() = (- 9 8) and f5() = (- 7 6).
+* Two consecutive declarations: f6() = (- 5 4) f7() = (- 3 2).
+* A declaration with one arg: f8(n) = (* #n #n).
+* A declaration with two args: f9(a, b) = (* #a #b).
+* A declaration with variable number of args: f10(...) = (.. #...).
+
+Not declarations:
+
+* \f11() = (+ 1 2); doesn't starts with `%a`
+* f-12() = (+ 1 2); doesn't continue with `%w`
+* f 13() = (+ 1 2); starts with `%d`
+* f14 () = (+ 1 2); space before `(`
+* f15) = (+ 1 2); misses `(`
+* f16() = + 1 2); misses `(`
+* f17( = (+ 1 2); misses `)`
+* f18() = (+ 1 2); misses `)`
+* f19 = (+ 1 2); misses `()`
+* f20() = + 1 2; misses `()`
+* f21() (+ 1 2); misses `=`
+* `f22() = (+ 1 2)`; inside inline code
+
+```
+f23() = (+ 1 2) inside code block
+```
+
+Overrides `f1()` with warn: f1() = (+ 2 3 4) and it should fail on `--fail-if-warnings`.
+
+## Functions Calls
+
+Valid calls:
+
+* A common call: f1(). All calls should be print on `--verbose`.
+* f2() another common call.
+* f3()
+* Two calls: f4() and f5().
+* Two consecutive calls: f6() f7().
+* A call with one arg: f8(2).
+* A call with two args: f9(2, 3).
+* A call with variable number of args: f10("The popular ", "\"Hello,", " ", "", "World!\"").
+* A call with args as kwargs: f8(n: 3) and f9(a: 4, b: 5).
+
+Valid calls and data types:
+
+* f10(); no data
+* f10(1, 1_000, 1.0); numbers
+* f10("string"); string
+* f10([]); empty array / list / sequential table
+* f10([0 1]); array / list / sequential table
+* f10({}); empty dict / table
+* f10({"k" 0}); dict / table
+
+Not calls:
+
+* \f11(); doesn't starts with `%a`
+* f-12(); doesn't continue with `%w`
+* f 13(); starts with `%d`
+* f14 (); space before `(`
+* f15); misses `(`
+* f16(a); invalid data type
+* f17(; misses `)`
+* f18(a:); misses kwarg value
+* f19(1 2); misses comma separator
+* f20( ); extra space
+* f21({); incomplete data type
+* `f22()`; inside inline code
+
+```
+f23() inside code block
+```
+
+Invalid calls:
+
+* f8(); misses arg
+* f8(a: 3); wrong kwarg
+* f9(1, 2, 3); wrong args number
+* f9(1, b: 2); mixed arg and kwarg
+* f10(...: 0); `...` can't be kwarg
+* f11(); not declared
+
+Invalid calls generate error.
+
+# Function Calls with Reserved Keyword Arguments
+
+Valid calls:
+
+* f1($action: "return"); returns result after call.
+* f2($action: "clear"); clears it from source document after call.
+* f3($action: "wipe"); wipes it and its declaration after all calls.
+* f4($action: "dump"); dumps its declaration after call.
+* f4($action: "quote"); dumps its declaration without call.
+* f5($eval: "f5.txt", $code: "f5.fnl", $lua: "f5.lua"); writes evaluation results, Lisp code and Lua code.
+* f6($eval: "f6-7.txt", $code: "f6-7.fnl", $lua: "f6-7.lua"); writes in same files than below.
+* f7($eval: "f6-7.txt", $code: "f6-7.fnl", $lua: "f6-7.lua"); writes in same files than above.
+* f8(4, $action: "return")
+* f8($action: "return", 5)
+* f9(a: 1, b: 2, $action: "return")
+* f9($action: "return", a: 1, b: 2)
+* f9(a: 1, $action: "return", b: 2)
+
+Invalid calls:
+
+* f1($act: "return"); invalid rkwarg
+* f2($action: "clearr"); invalid rkwarg value
+* f3($eval: "/path/does/not/exists"); invalid path
+* f4($code: "/path/does/not/exists"); invalid path
+* f5($lua: "/path/does/not/exists"); invalid path
+
+Invalid calls generate error.
+
+# Function Recursion
+
+Valid recursion:
+
+* A declaration that uses a call inside: f24(x) = (* f1() x), result: f24($action: "return").
+* A declaration that uses a call inside with "quote" action: f25(y, z) = (+ f2($action: "quote") y z), result: f25(9, 8, $action: "return").
+* A call with other function as arg: f8(f1()).
+* A call with other function as kwarg: f9(b: 3, a: f2()).
+
+Invalid recursion:
+
+* f26(i) = (* f11() i); \f11() not declared.
+* f27(j) = (* f27(1) f27(2)); infinite loop.
+* f8(n: f11()); \f11() not declared.
+* f8(f8(3)); infinite loop.
diff --git a/tests/test.org b/tests/test.org
new file mode 100644
index 0000000..71ee52c
--- /dev/null
+++ b/tests/test.org
@@ -0,0 +1 @@
+* Test with Org Mode
diff --git a/tests/test.rst b/tests/test.rst
new file mode 100644
index 0000000..eddab86
--- /dev/null
+++ b/tests/test.rst
@@ -0,0 +1,2 @@
+Test with reStructuredText
+==========================