From e87d4b1f43c8841cde3c61961b385c4d828fbb2b Mon Sep 17 00:00:00 2001 From: perro Date: Wed, 8 Mar 2023 19:05:42 -0800 Subject: [PATCH] Repo ordering --- .gitmodules | 3 + LICENSE.txt | 674 +++++++++ README.md | 88 +- dist/literate.min.lua | 2957 +++++++++++++++++++++++++++++++++++++++ man/README.md | 0 man/locales/en.md | 66 + man/source.md | 0 {src => opt}/fennel.lua | 0 opt/luaminify | 1 + scripts/get_fennel.sh | 2 +- scripts/make_dist.sh | 32 + scripts/test.sh | 24 +- src/literate.lua | 19 +- tests/t1.md | 15 - tests/t2.md | 15 - tests/t3.rst | 18 - tests/t4.org | 14 - tests/test.md | 133 ++ tests/test.org | 1 + tests/test.rst | 2 + 20 files changed, 3948 insertions(+), 116 deletions(-) create mode 100644 .gitmodules create mode 100644 LICENSE.txt create mode 100644 dist/literate.min.lua create mode 100644 man/README.md create mode 100644 man/locales/en.md create mode 100644 man/source.md rename {src => opt}/fennel.lua (100%) create mode 160000 opt/luaminify create mode 100644 scripts/make_dist.sh delete mode 100644 tests/t1.md delete mode 100644 tests/t2.md delete mode 100644 tests/t3.rst delete mode 100644 tests/t4.org create mode 100644 tests/test.md create mode 100644 tests/test.org create mode 100644 tests/test.rst 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 +==========================