packages feed

morloc (empty) → 0.33.0

raw patch · 46 files changed

+13038/−0 lines, 46 filesdep +QuickCheckdep +aesondep +base

Dependencies added: QuickCheck, aeson, base, bytestring, containers, directory, docopt, extra, filepath, haskell-src-meta, megaparsec, morloc, mtl, parsec, partial-order, pretty-simple, prettyprinter, prettyprinter-ansi-terminal, process, raw-strings-qq, safe, scientific, tasty, tasty-golden, tasty-hunit, tasty-quickcheck, template-haskell, text, unordered-containers, yaml

Files

+ ChangeLog.md view
@@ -0,0 +1,283 @@+1.0.0 [202x.xx.xx]+------------------++The first stable. It will be a product that I expect other people to use for+important projects, therefore backwards compatibility will be important. The+whole system needs extensive testing in real applications. Much of this will be+done in the development of the core libraries. We will also need to add+handling for several very different languages (proofs-of-concept).++ - [ ] typeclasses+ - [ ] semantic types - test in bioinformatics applications+ - [ ] constraints - refined types?+ - [ ] manifold hooks - caching, documentation, logging, effects+ - [ ] logic engine (z3?) - from typechecking to architecture design and debugging+ - [ ] ecosystem (test suite, linter, package tools, vim plugin)+ - [ ] language support (Python3, R, C++, Java, Haskell, Scheme, Prolog)+ - [ ] well tested core libraries++0.34.0 [202x.xx.xx]+-------------------++ - [ ] Remove extra space printed at the end of R JSON+ - [ ] Remove semicolon requirement++0.33.0 [2020.11.03]+-------------------++First hackage release++ * Haddock documentation+ * Update README+ * In help statements write universal, not concrete, types+ * Make default containers non-existential (probably a bad decision?)++0.32.0 [2020.11.01]+-------------------++ * Add record/table field access+ * Fix JSON handling in nexus+ * Fix nexus bug necessitated escaping quotations and braces+ * Print general types in nexus help+ * Resolve most GHC warnings++0.31.0 [2020.10.29]+-------------------++ * Fix anonymous records in C+++ * Distinguish 'record', 'object', and 'table'+ * Add object handling+ * Add table handling++0.30.0 [2020.10.23]+-------------------++ * Add `object` keyword for defining record types+ * Add full record serialization handling (C++, py, R)++0.29.0 [2020.10.21]+-------------------++ * Add AST directed (de)serialization framework+ * Add type constructors for parameterized types++0.28.0 [2020.10.12]+-------------------++ * Allow import/export of type aliases+ * Refactor with DAGs all through the parser and typechecker++0.27.0 [2020.10.04]+-------------------++ * Add systematic tests for data serialization+ * Fix bug in C++ serialization+ * Move to serialize to dedicated libraries that require no import++0.26.0 [2020.09.27]+-------------------++Add `type` keyword for defining type aliases++0.25.0 [2020.09.26]+-------------------++No explicit forall. Instead use Haskell convention of generics being lowercase+and non-generics being uppercase. ++ * no more explicit "forall"+ * generics are lowercase in type signatures+ * non-generic types are uppercase+ * normal functions are lowercase+ * class constructors are uppercase (though handling for this is not yet implemented)++0.24.0 [2020.09.22]+-------------------++Allow integration of many instances++0.23.0 [2020.05.14]++Bug fixes and code cleanup++Bug fixes / tests+ - [x] [x] github issue #7 - new Var=> typechecking rule+ - [x] [x] github issue #9 - rewire container type inference+ - [x] [x] github issue #10+ - [x] [x] github issue #11+++0.22.0 [2020.04.28]+-------------------++Implement a schema-directed composable serialization system++Major changes+ * Fully composable serialization over containers and primitives+ * Improved C++ support of generic functions+ * Record support for R and Python3 (not C++ yet)+ * Refactor generator - replace old grammar system+ * Allow arguments to be passed to general functions+   (e.g., `foo x = [x]`, where no specific language is needed) ++Minor changes+ * change default python3 interpreter from "python" to "python3"+ * add default library and tmp paths to config handler+ * test composable serialization functions in all supported languages+ * allow wrapped comments in R++Testing - grammar directed testing+ * test record handling+ * remove and replace out-of-date golden tests+ * systematic argument handling tests+ * systematic manifold form tests+ * systematic interop testing++0.21.0 [2020.03.31]+-------------------++Major - add handling and test for many many corner cases+ * Allow export of data statements+ * Allow export of non-functions+ * Allow functions with containers at the root+ * Allow export of 0-argument functions ++Minor+ * proof-of-concept composable serialization functions in C++ (cppbase)+ * add python tests+ * make the test output look pretty (no weird whitespace)++0.20.0 [2020.03.23]+-------------------++ * Add composable default types++0.19.1 [2020.02.22]+-------------------++ * bug fixes++0.19.0 [2020.02.20]+-------------------++Major changes+ * Allow currying+ * Add realization optimizations+ * Refactor generator into series of clear transformations+ * Added handling for dealing with ambiguous ASTs++Minor bug fixes and updates+ * Prettier code generation for C++, Python and R+ * More detailed comments in generated code+ * Allow tags on parenthesized types+ * Fix bug in functions with multiple parameters + * Fix bug preventing loading of package metadata ++0.18.1 [2019.11.08]+-------------------++ * Fix travis+ * Use C++11 for C++ builds+ * Make .morloc/config optional+ * Fix bug in parsing unit type: `()`++0.18.0 [2019.11.04]+-------------------++ * Add bidirectional type system+ * Allow parameterized concrete types+ * Allow higher-order functions+ * Allow properties to contain multiple terms + * Add many tests+ * Add module system+ * Allow non-primitive types in lists, tuples, and records+ * Removed arq and SPARQL dependency (very fast compilation)++0.17.4 [2019.06.29]+-------------------++ * Add C and C++ handling+ * Define Ord intance for MTypeMeta+ * Allow pools to be called as executables+ * Add type handling to generators+ * Remove redundant SPARQL queries (better performance)+ * New RDF list semantics+ * Use strings to represent concrete types (e.g. "char\*")+ * Write pretty-printed diagnostic files to `$MORLOC_HOME/tmp` + * Handling for multiple concrete type signatures (e.g., definition of+   a function in multiple languages).+ * Handling for multiple abstract type signatures+ * Handling for multiple function declarations++0.17.3 [2019.06.14]+-------------------++ * Partial C support+   - execution of sourced functions+   - no composition+   - no foreign calls++ * Partial transition to typed generators+   - bound arguments are still not typed correctly++ * Use integer IDs to identify manifolds in pools and the nexus (can to make+   calls between them) instead of long, mangled names.++ * Replace string names of languages (e.g., "python") with a sum type.++0.17.2 [2019.05.05]+-------------------++  Pycon release++0.17.1 [2019.04.26]+-------------------++ * Fix output serialization in generate code+ * Fix module linking in generated code++0.17.0 [2019.04.16]+-------------------++ * Add morloc home+ * Load modules from `$MORLOCHOME/lib`+ * Create monad stack++0.16.2 [2018.03.05]+-------------------++ * Add Zenodo badge making the project citable+ * Move to `morloc-project/morloc` github repo++0.16.1 [2018.09.24]+-------------------++Minor release consisting of internal refactoring++ * Pruned unnecessary code+ * Pruned unnecessary imports+ * Compliance with stricter compile flags++0.16.0 [2018.09.14]+-------------------++ * Write RDF bools in lowercase ("true", rather than "True"), as per specs+ * Stricter node typing (replace ad hoc names with elements from an ADT)+ * Add very rudimentary typechecking+ * Remove SPARQL server dependency (now there's a sluggish Jena dependency)++0.15.1 [2018.09.10]+-------------------++ * Add error handling and reporting to pools+ * Add type signature comments to generated pools + * Richer internal data structures++0.15.0 [2018.09.05]+-------------------++ * Generalize code generators using grammar records+ * Add Python compatibility+ * Replace unit tests with golden tests+ * Use docopt and USAGE template for argument handling+ * Report number of arguments in nexus usage statements
+ LICENSE view
@@ -0,0 +1,674 @@+                    GNU GENERAL PUBLIC LICENSE+                       Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ 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.++    {one line to give the program's name and a brief idea of what it does.}+    Copyright (C) {year}  {name of author}++    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 <http://www.gnu.org/licenses/>.++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:++    {project}  Copyright (C) {year}  {fullname}+    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+<http://www.gnu.org/licenses/>.++  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+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+ README.md view
@@ -0,0 +1,283 @@+[![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges)+[![travis build status](https://travis-ci.org/morloc-project/morloc.svg?branch=master)](https://travis-ci.org/morloc-project/morloc)+[![github release](https://img.shields.io/github/release/morloc-project/morloc.svg?label=current+release)](https://github.com/morloc-project/morloc/releases)+[![license: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)+[![DOI](https://zenodo.org/badge/75355860.svg)](https://zenodo.org/badge/latestdoi/75355860)++`morloc` is a functional programming language where functions are imported from+foreign languages and unified under a common type system. The compiler+generates the code needed to compose functions across languages and also to+direct automation of mundane tasks such as data validation, type/format+conversions, data caching, distributed computing, and file reading/writing. The+endgame is to develop `morloc` into a query language that returns optimized+programs from an infinite library of functions and compositions of functions.++See [the manual](https://morloc-project.github.io/docs) for more information.++If you want to get straight to playing with code, go through the steps in the+installation section and then go to the project in `demo/01_sequence_analysis`.++## Status++This project is under active development with no stability guarantees until the+v1.0 release. Pull requests, issue reports, and private messages are very+welcome.++## Installation++Compile and install the package (requires the Haskell utility `stack`):++```sh+git clone https://github.com/morloc-project/morloc+cd morloc+stack install --fast+```++`morloc` also depends on the `JSON::XS` perl module from CPAN, which can be+installed as follows:++```sh+export PERL_MM_USE_DEFAULT=1+export PERL_CANARY_STABILITY_NOPROMPT=1+sudo perl -MCPAN -e 'install JSON::XS' +```++For Python support, you need to download the `pymorlocinternals` library from+PyPi:++```sh+pip install pymorlocinternals+# or on Mac:+pip3 install pymorlocinternals+```++For R support, you need to install the `rmorlocinternals` library from github,+in an R session, run:++```sh+R> install.packages("devtools")+R> devtools::install_github("morloc-project/rmorlocinternals")+```++C++ support currently requires a GNU compiler that supports C++11.++`morloc` modules can be installed from the `morloc`+[library](https://github.com/morloclib) with the commands such as:++```sh+morloc install cppbase+morloc install pybase+morloc install rbase+morloc install math+```++The `morloc install` commands will install the modules in the+`$HOME/.morloc/lib` folder.++Last of all, if you are working in vim, you can install `morloc` syntax highlighting as follows:++``` sh+mkdir -p ~/.vim/syntax/+mkdir -p ~/.vim/ftdetect/+cp vim-syntax/loc.vim ~/.vim/syntax/+echo 'au BufRead,BufNewFile *.loc set filetype=loc' > ~/.vim/ftdetect/loc.vim+```++## Getting Started++```+export hello+hello = "Hello World"+```++The "export" keyword exports the variable "hello" from the module.++Paste this into a file (e.g. "hello.loc") and then it can be imported by other+`morloc` modules or directly compiled into a program where every exported term+is a subcommand.++```+morloc make hello.loc+```++This will generate a single file named "nexus.pl". The nexus is the executable+script that the user will interact with. For this simple example, it is the+only generated file. It is currently written in Perl. ++Calling "nexus.pl" with no arguemtns or with the `-h` flag, will print a help+message:++```+$ ./nexus.pl -h+The following commands are exported:+  hello+    return: Str+```++The `return: Str` phrases states that hello returns a string value.++The command `hello` can be called as shown below:++```+$ ./nexus.pl hello+Hello World+```++## Composing C++ Functions++The following code uses only C++ functions (`fold`, `map`, `add` and `mul`). ++```+import cppbase (fold, map, add, mul)++export square;+export sumOfSquares;++square x = mul x x;++sumOfSquares xs = fold add 0 (map square xs);+```++If this script is pasted into the file "example-1.loc", it can be compiled as+follows:++```sh+morloc install cppbase+morloc make example-1.loc+```++The `install` command clones the `cppbase` repo from github+[repo](https://github.com/morloclib/cppbase) into the local directory+`~/.morloc/lib`. The `morloc make` command will generate a file named+`nexus.pl`, which is an executable interface to the exported functions.++You can see typed usage information for the exported functions with the `-h` flag:++```sh+$ ./nexus.pl -h+The following commands are exported:+  square+    param 1: Num+    return: Num+  sumOfSquares+    param 1: [Num]+    return: Num+```++Then you can call the exported functions (arguments are in JSON format):++```sh+$ ./nexus.pl sumOfSquares '[1,2,3]'+14+```++The `nexus.pl` executable dispatches the command to the compiled C++ program,+`pool-cpp.out`.+++## Language interop++`morloc` can compose functions across languages. For example:++```+import math (fibonacci);+import rbase (plotVectorPDF, ints2reals);++export fibplot++fibplot n = plotVectorPDF (ints2reals (fibonacci n)) "fibonacci-plot.pdf";+```++The `fibplot` function calculates Fibonacci numbers using a C++ function and+plots it using an R function. The R function `plotPDF` is a perfectly normal R+function with no extra boilerplate:++``` R+plotPDF <- function(x, filename){+  pdf(filename)+  plot(x)+  dev.off()+}+```+++## The Morloc Type System++The first level of the `morloc` type system is basically System F extended+across languages. A given function will have a general type as well as a+specialized type for each language it is implemented in.++The map function has the types++```+map :: (a -> b) -> [a] -> [b]+map Cpp :: (a -> b) -> "std::vector<$1>" a -> "std::vector<$1>" b+map Python3 :: (a -> b) -> list a -> list b+```++The general signature looks almost the same as the Haskell equivalent (except+that `morloc` universal quantification is currently explicit). The list type+constructors for C++ are very literally "type constructors" in that they are+used to create syntactically correct C++ type strings. If the type variable `a`+is inferred to be `int`, for example, then the C++ type `std::vector<int>` will+be used in the generated code. The same occurs in the python type constructors+`list`, except here the same Python type is generated regardless of the type of+`a`.++The following example is available in `examples/rmsWithTypes.loc`:++```+import cppbase (fold, map, add, mul)++export square;+export sumOfSquares;++square x = mul x x;++sumOfSquares xs = fold add 0 (map square xs);+```++This example cannot be compiled since none of the functions are imported or+sourced, but it can be typechecked:++```+morloc typecheck examples/rmsWithTypes.loc+```++```+add :: Num -> Num -> Num;+add Cpp :: double -> double -> double;++mul :: Num -> Num -> Num;+mul Cpp :: double -> double -> double;++fold     :: (b -> a -> b) -> b -> [a] -> b;+fold Cpp :: (b -> a -> b) -> b -> "std::vector<$1>" a -> b;++map :: (a -> b) -> [a] -> [b];+map Cpp :: (a -> b) -> "std::vector<$1>" a+                    -> "std::vector<$1>" b;++square x = mul x x;+sumOfSquares xs = fold add 0 (map square xs);+```++The typechecker associates each sub-expression of the program with a set of+types. The specific type information in `mul` is sufficient to infer concrete+types for every other C++ function in the program. The inferred C++ type of+`sumOfSquares` is++```+"std::vector<$1>" double -> double+```++The general type for this expression is also inferred as:++```+List Num -> Num+```++The concrete type of `mul` is currently written as a binary function of+doubles. Ideally this function should accept any numbers (e.g., an `int` and a+`double`). I intend to add this functionallity eventually, perhaps with a+Haskell-style typeclass system.
+ executable/Main.hs view
@@ -0,0 +1,24 @@+{-# LANGUAGE QuasiQuotes #-}++module Main where++import Control.Monad (when)+import Subcommands+import System.Console.Docopt+import qualified System.Environment as SE++patterns :: Docopt+patterns = [docoptFile|USAGE|]++getArgOrExit :: Arguments -> Option -> IO String+getArgOrExit = getArgOrExitWith patterns++main :: IO ()+main = do+  args <- parseArgsOrExit patterns =<< SE.getArgs+  config <- getConfig args+  when (isPresent args (command "install")) (cmdInstall args config)+  -- do the following if we are processing Morloc code+  when (isPresent args (argument "script")) $ do+    when (isPresent args (command "make")) (cmdMake args config)+  when (isPresent args (command "typecheck")) $ cmdTypecheck args config
+ executable/Subcommands.hs view
@@ -0,0 +1,114 @@+{-# LANGUAGE OverloadedStrings #-}++{-|+Module      : Subcommands+Description : Morloc executable subcommands+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Subcommands+  ( getConfig+  , cmdInstall+  , cmdMake+  , cmdRemove+  , cmdTypecheck+  ) where++import Morloc.Namespace+import System.Console.Docopt+import qualified Morloc as M+import qualified Morloc.Config as Config+import qualified Morloc.Data.Text as MT+import qualified Morloc.Module as Mod+import qualified Morloc.Monad as MM+import qualified Morloc.Frontend.API as F++type Subcommand = Arguments -> Config.Config -> IO ()++getArgOrDie :: Arguments -> Option -> MT.Text+getArgOrDie args opt =+  case getArg args opt of+    Nothing -> error ("Invalid command: Expected option '" <> show opt)+    (Just x) -> MT.pack x++-- | read the global morloc config file or return a default one+getConfig :: Arguments -> IO Config.Config+getConfig args = do+  let givenPath = getArg args (longOption "config")+  let isVanilla = isPresent args (longOption "vanilla")+  defaultPath <- Config.getDefaultConfigFilepath+  let configPath =+        if isVanilla+          then Nothing+          else case givenPath of+                 (Just f) -> Just . Path . MT.pack $ f+                 Nothing -> Just defaultPath+  -- load the config file+  Config.loadMorlocConfig configPath++getVerbosity :: Arguments -> Int+getVerbosity args =+  if isPresent args (longOption "verbose")+    then 1+    else 0++-- | handle the code, either from a file or a raw string+readScript :: Arguments -> IO (Maybe Path, Code)+readScript args = do+  let input = getArgOrDie args (argument "script")+  if isPresent args (longOption "expression") +  then do+    let code = Code input +    return (Nothing, code)+  else do+    let filename = Path input+    code <- fmap Code $ MT.readFile (MT.unpack input)+    return (Just filename, code)++-- | install a module+cmdInstall :: Subcommand+cmdInstall args conf =+  (MM.runMorlocMonad (getVerbosity args) conf cmdInstall') >>= MM.writeMorlocReturn+  where+    cmdInstall' = do+      let name = getArgOrDie args (argument "name")+      if isPresent args (longOption "github")+        then Mod.installModule (Mod.GithubRepo name)+        else Mod.installModule (Mod.CoreGithubRepo name)++-- | remove a previously installed module (NOT YET IMPLEMENTED)+cmdRemove :: Subcommand+cmdRemove _ _ = do+  putStrLn "not removing anything"++-- | build a Morloc program, generating the nexus and pool files+cmdMake :: Subcommand+cmdMake args config = do+  (path, code) <- readScript args+  MM.runMorlocMonad (getVerbosity args) config (M.writeProgram path code) >>=+    MM.writeMorlocReturn++cmdTypecheck :: Subcommand+cmdTypecheck args config = do+  let expr = getArgOrDie args (argument "script")+  expr' <-+    if isPresent args (longOption "expression")+      then return expr+      else MT.readFile (MT.unpack expr)+  let base =+        if isPresent args (longOption "expression")+          then Nothing+          else Just (Path expr)+  let writer =+        if isPresent args (longOption "raw")+          then F.ugly+          else F.cute+  if isPresent args (longOption "type")+    then print $ F.readType expr'+    else MM.runMorlocMonad+           (getVerbosity args)+           config+           (M.typecheck base (Code expr') >>= MM.liftIO . writer) >>=+         MM.writeMorlocReturn
+ library/Morloc.hs view
@@ -0,0 +1,41 @@+module Morloc+  ( writeProgram+  , typecheck+  ) where++import Morloc.Namespace+import Morloc.Frontend.Namespace (TypedDag)++import qualified Morloc.Frontend.API as F+import Morloc.Frontend.Desugar (desugar) +import Morloc.CodeGenerator.Generate (generate)+import Morloc.ProgramBuilder.Build (buildProgram)+import Morloc.Frontend.Treeify (treeify)++typecheck :: Maybe Path -> Code -> MorlocMonad TypedDag+typecheck path code+  -- Maybe Path -> Text -> [Module]+  -- parse code into unannotated modules+  = F.parse path code+  -- [Module] -> [Module]+  -- resolve type aliases and such+  >>= desugar+  -- [Module] -> [Module]+  -- add type annotations to sub-expressions and raise type errors+  >>= F.typecheck++-- | Build a program as a local executable+writeProgram ::+     Maybe Path -- ^ source code filename (for debugging messages)+  -> Code       -- ^ source code text+  -> MorlocMonad ()+writeProgram path code+  = typecheck path code+  -- [Module] -> SAnno GMeta Many [CType]+  >>= treeify+  -- [SAnno GMeta Many [CType]] -> (Script, [Script])+  -- translate mtree into nexus and pool source code+  >>= generate+  -- (Script, [Script]) -> IO ()+  -- write the code and compile as needed+  >>= buildProgram
+ library/Morloc/CodeGenerator/Generate.hs view
@@ -0,0 +1,1040 @@+{-|+Module      : Morloc.CodeGenerator.Generate+Description : Translate AST forests into target language source code+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++The single @generate@ function wraps the entire AST forest to source code+translation process.++The input the @generate@ is of type @[SAnno GMeta Many [CType]]@. The @SAnno+GMeta Many [CType]@ elements each represent a single command exported from the+main function. The @GMeta@ type stores all general information about a given+"manifold" (a node in the function graph and all its wrappings). The term+@Many@ states that there may be one of more AST describing each expression. The+term @[CType]@ states that there may be multiple concrete, language-specific+types associated with any term.++The @generate@ function converts the @SAnno GMeta Many [CType]@ types into+@SAnno GMeta One CType@ unambiguous ASTs. This step is an important+optimization step in the morloc build pipeline. Currently the compiler uses a+flat scoring matrix for the cost of interop between languages (e.g., 0 for C+++to C++, 1000 for anything to R, 5 for R to R since there is a function call+cost, etc). Replacing this algorithm with an empirically parameterized+performance model is a major goal.++Additional manipulations of the AST can reduce the number of required foreign+calls, (de)serialization calls, and duplicate computation.++The @SAnno GMeta One CType@ expression is ultimately translated into a simple+@ExprM@ type that is then passed to a language-specific translator.++-}++module Morloc.CodeGenerator.Generate+(+  generate+) where++import Morloc.CodeGenerator.Namespace+import Morloc.CodeGenerator.Internal+import Morloc.Data.Doc+import Morloc.Pretty (prettyType)+import qualified Morloc.Config as MC+import qualified Morloc.Data.Text as MT+import qualified Morloc.Language as Lang+import qualified Morloc.Monad as MM+import Morloc.CodeGenerator.Grammars.Common+import qualified Morloc.CodeGenerator.Nexus as Nexus+import qualified Morloc.System as MS+import qualified Data.Map as Map+import qualified Data.Set as Set++import qualified Morloc.CodeGenerator.Grammars.Translator.Cpp as Cpp+import qualified Morloc.CodeGenerator.Grammars.Translator.R as R+import qualified Morloc.CodeGenerator.Grammars.Translator.Python3 as Python3++-- | Translate typed, abstract syntax forests into compilable code+generate ::+  [SAnno GMeta Many [CType]]+  -- ^ one AST forest for each command exported from main+  -> MorlocMonad (Script, [Script]) +  -- ^ the nexus code and the source code for each language pool+generate ms = do+  -- translate modules into bitrees+  (gASTs, rASTs)+    -- eliminate morloc composition abstractions+    <-  mapM rewrite ms+    -- select a single instance at each node in the tree+    >>= mapM realize   -- [Either (SAnno GMeta One CType) (SAnno GMeta One CType)]+    -- separate unrealized (general) ASTs (uASTs) from realized ASTs (rASTs)+    |>> partitionEithers++  -- Collect all call-free data+  gSerial <- mapM generalSerial gASTs++  -- build nexus+  -- -----------+  -- Each nexus subcommand calls one function from one one pool.+  -- The call passes the pool an index for the function (manifold) that will be called.+  nexus <- Nexus.generate+    gSerial+    [ (t, poolId m x, metaName m)+    | SAnno (One (x, t)) m <- rASTs+    ]++  -- find all sources files+  let srcs = unique . concat . conmap (unpackSAnno getSrcs) $ rASTs++  -- for each language, collect all functions into one "pool"+  pools+    -- thread arguments across the tree+    <- mapM parameterize rASTs+    -- convert from AST to manifold tree+    >>= mapM express+    -- rewrite lets to minimize the number of foreign calls+    >>= mapM letOptimize+    -- Separate the call trees into mono-lingual segments terminated in+    -- primitives or foreign calls.+    >>= mapM segment |>> concat+    -- Cast each call tree root as a manifold+    >>= mapM rehead+    -- Gather segments into pools, currently this entails gathering all+    -- segments from a given language into one pool. Later it may be more+    -- nuanced.+    >>= pool+    -- Generate the code for each pool+    >>= mapM (encode srcs)++  -- return the nexus script and each pool script+  return (nexus, pools)+  where+    -- map from nexus id to pool id+    -- these differ when a declared variable is exported+    poolId :: GMeta -> SExpr GMeta One TypeP -> Int+    poolId _ (LamS _ (SAnno _ meta)) = metaId meta+    poolId meta _ = metaId meta++    -- this is grossly inefficient ... but I'll deal with it later+    getSrcs :: SExpr GMeta One c -> GMeta -> c -> [Source]+    getSrcs (CallS src) g _ = src : (getSrcsFromGmeta g) +    getSrcs _ g _ = getSrcsFromGmeta g++    getSrcsFromGmeta :: GMeta -> [Source]+    getSrcsFromGmeta g+      = concat [unresolvedPackerForward p ++ unresolvedPackerReverse p+               | p <- (concat . Map.elems . metaPackers) g]+      ++ Map.elems (metaConstructors g)+++-- | Eliminate morloc function calls+-- For example:+--    foo x y = bar x (baz y)+--    bar x y = add x y+--    baz x = div x 5+-- Can be rewritten as:+--    foo x y = add x (div y 5)+-- Notice that no morloc abstractions appear on the right hand side.+rewrite+  :: SAnno GMeta Many [CType]+  -> MorlocMonad (SAnno GMeta Many [CType])+rewrite (SAnno (Many es0) g0) = do+  es0' <- fmap concat $ mapM rewriteL0 es0+  return $ SAnno (Many es0') g0+  where+    rewriteL0+      :: (SExpr GMeta Many [CType], [CType])+      -> MorlocMonad [(SExpr GMeta Many [CType], [CType])]+    rewriteL0 (AppS (SAnno (Many es1) g1) args, c1) = do+      args' <- mapM rewrite args+      -- originally es1 consists of a list of CallS and LamS constructors+      --  - CallS are irreducible source functions+      --  - LamS are Morloc abstractions that can be reduced+      -- separate LamS expressions from all others+      let (es1LamS, es1CallS) = partitionEithers (map sepLamS es1)+      -- rewrite the LamS expressions, each expression will yields 1 or more+      es1LamS' <- fmap concat $ mapM (rewriteL1 args') es1LamS+      return $ (AppS (SAnno (Many es1CallS) g1) args', c1) : es1LamS'+      where+        sepLamS+          :: (SExpr g Many c, c)+          -> Either ([EVar], SAnno g Many c)+                    (SExpr g Many c, c)+        sepLamS (LamS vs body, _) = Left (vs, body)+        sepLamS x = Right x+    rewriteL0 (AccS x k, c) = do+      x' <- rewrite x+      return [(AccS x' k, c)]+    rewriteL0 (ListS xs, c) = do+      xs' <- mapM rewrite xs+      return [(ListS xs', c)]+    rewriteL0 (TupleS xs, c) = do+      xs' <- mapM rewrite xs+      return [(TupleS xs', c)]+    rewriteL0 (RecS entries, c) = do+      xs' <- mapM rewrite (map snd entries)+      return [(RecS $ zip (map fst entries) xs', c)]+    rewriteL0 (LamS vs x, c) = do+      x' <- rewrite x+      return [(LamS vs x', c)]+    -- VarS UniS NumS LogS StrS CallS+    rewriteL0 x = return [x]++    rewriteL1+      :: [SAnno g Many c]+      -> ([EVar], SAnno g Many c) -- lambda variables and body+      -> MorlocMonad [(SExpr g Many c, c)]+    rewriteL1 args (vs, SAnno (Many es2) _)+      | length vs == length args =+          fmap concat $ mapM (substituteExprs (zip vs args)) es2+      | length vs > length args = MM.throwError . NotImplemented $+          "Partial function application not yet implemented (coming soon)"+      | length vs < length args = MM.throwError . TypeError $+          "Type error: too many arguments applied to lambda"+    rewriteL1 _ (_, SAnno (Many _) _) = error "GHC warnings tell me this is a missing case, but why?"+++    substituteExprs+      :: [(EVar, SAnno g Many c)]+      -> (SExpr g Many c, c) -- body+      -> MorlocMonad [(SExpr g Many c, c)] -- substituted bodies+    substituteExprs [] x = return [x]+    substituteExprs ((v, r):rs) x = do+      xs' <- substituteExpr v r x+      fmap concat $ mapM (substituteExprs rs) xs'++    substituteExpr+      :: EVar+      -> SAnno g Many c -- replacement+      -> (SExpr g Many c, c) -- expression+      -> MorlocMonad [(SExpr g Many c, c)]+    substituteExpr v (SAnno (Many xs) _) x@(VarS v', _)+      | v == v' = return xs+      | otherwise = return [x]+    substituteExpr v r (AccS x k, c) = do+      x' <- substituteAnno v r x+      return [(AccS x' k, c)]+    substituteExpr v r (ListS xs, c) = do+      xs' <- mapM (substituteAnno v r) xs+      return [(ListS xs', c)]+    substituteExpr v r (TupleS xs, c) = do+      xs' <- mapM (substituteAnno v r) xs+      return [(TupleS xs', c)]+    substituteExpr v r (RecS entries, c) = do+      xs' <- mapM (substituteAnno v r) (map snd entries)+      return [(RecS (zip (map fst entries) xs'), c)]+    substituteExpr v r (LamS vs x, c) = do+      x' <- substituteAnno v r x+      return [(LamS vs x', c)]+    substituteExpr v r (AppS f xs, c) = do+      f' <- substituteAnno v r f+      xs' <- mapM (substituteAnno v r) xs+      return [(AppS f' xs', c)]+    -- UniS NumS LogS StrS CallS+    substituteExpr _ _ x = return [x]++    substituteAnno+      :: EVar -- variable to replace+      -> SAnno g Many c -- replacement branch set+      -> SAnno g Many c -- search branch+      -> MorlocMonad (SAnno g Many c)+    substituteAnno v r (SAnno (Many xs) g) = do+      xs' <- fmap concat $ mapM (substituteExpr v r) xs+      return $ SAnno (Many xs') g++-- | Select a single concrete language for each sub-expression.  Store the+-- concrete type and the general type (if available).  Select pack/unpack+-- functions.+realize+  :: SAnno GMeta Many [CType]+  -> MorlocMonad (Either (SAnno GMeta One ()) (SAnno GMeta One TypeP))+realize x0 = do+  -- MM.say $ " --- realize ---"+  -- MM.say $ prettySAnnoMany x+  -- MM.say $ " ---------------"+  realizationMay <- realizeAnno 0 Nothing x0+  case realizationMay of+    Nothing -> makeGAST x0 |>> Left+    (Just (_, realization)) -> do+       mapGCM weaveTypesGCP realization >>= rewritePartials |>> Right+  where+    realizeAnno+      :: Int+      -> Maybe Lang+      -> SAnno GMeta Many [CType]+      -> MorlocMonad (Maybe (Int, SAnno GMeta One CType))+    realizeAnno depth langMay (SAnno (Many xs) m) = do+      asts <- mapM (\(x, cs) -> mapM (realizeExpr (depth+1) langMay x) cs) xs |>> concat+      case minimumOnMay (\(s,_,_) -> s) (catMaybes asts) of+        Just (i, x, c) -> do+          return $ Just (i, SAnno (One (x, c)) m)+        Nothing -> do+          return Nothing++    realizeExpr+      :: Int+      -> Maybe Lang+      -> SExpr GMeta Many [CType]+      -> CType+      -> MorlocMonad (Maybe (Int, SExpr GMeta One CType, CType))+    realizeExpr depth lang x c = do+      let lang' = if isJust lang then lang else langOf c+      realizeExpr' depth lang' x c++    realizeExpr'+      :: Int+      -> Maybe Lang+      -> SExpr GMeta Many [CType]+      -> CType+      -> MorlocMonad (Maybe (Int, SExpr GMeta One CType, CType))+    -- always choose the primitive that is in the same language as the parent+    realizeExpr' _ lang (UniS) c+      | lang == langOf c = return $ Just (0, UniS, c)+      | otherwise = return Nothing+    realizeExpr' _ lang (NumS x) c+      | lang == langOf c = return $ Just (0, NumS x, c)+      | otherwise = return Nothing+    realizeExpr' _ lang (LogS x) c+      | lang == langOf c = return $ Just (0, LogS x, c)+      | otherwise = return Nothing+    realizeExpr' _ lang (StrS x) c+      | lang == langOf c = return $ Just (0, StrS x, c)+      | otherwise = return Nothing+    -- Q: a call should also be of the same language as the parent, shouldn't it?+    -- A: not necessarily, specifically if the parent includes many child calls, say in a list+    realizeExpr' _ lang (CallS src) c+      -- FIXME: assuming function calls have 0 cost is perhaps not realistic+      | lang == langOf c = return $ Just (0, CallS src, c)+      | otherwise = return Nothing+    -- and a var?+    realizeExpr' _ lang (VarS x) c+      | lang == langOf c = return $ Just (0, VarS x, c)+      | otherwise = return Nothing+    realizeExpr' depth lang (AccS x k) c+      | lang == langOf c = do+        xMay <- realizeAnno depth lang x+        case xMay of+          Nothing -> return Nothing+          (Just (i, x')) -> return $ Just (i, AccS x' k, c)+      | otherwise = return Nothing+    -- simple recursion into ListS, TupleS, and RecS+    realizeExpr' depth lang (ListS xs) c+      | lang == langOf c = do+        xsMay <- mapM (realizeAnno depth lang) xs+        case (fmap unzip . sequence) xsMay of+          (Just (scores, xs')) -> return $ Just (sum scores, ListS xs', c)+          Nothing -> return Nothing+      | otherwise = return Nothing+    realizeExpr' depth lang (TupleS xs) c+      | lang == langOf c = do+        xsMay <- mapM (realizeAnno depth lang) xs+        case (fmap unzip . sequence) xsMay of+          (Just (scores, xs')) -> return $ Just (sum scores, TupleS xs', c)+          Nothing -> return Nothing+      | otherwise = return Nothing+    realizeExpr' depth lang (RecS entries) c+      | lang == langOf c = do+          xsMay <- mapM (realizeAnno depth lang) (map snd entries)+          case (fmap unzip . sequence) xsMay of+            (Just (scores, vals)) -> return $ Just (sum scores, RecS (zip (map fst entries) vals), c)+            Nothing -> return Nothing+      | otherwise = return Nothing+    --+    realizeExpr' depth _ (LamS vs x) c = do+      xMay <- realizeAnno depth (langOf c) x+      case xMay of+        (Just (score, x')) -> return $ Just (score, LamS vs x', c)+        Nothing -> return Nothing+    -- AppS+    realizeExpr' _ Nothing _ _ = MM.throwError . OtherError $ "Expected concrete type"+    realizeExpr' depth (Just lang) (AppS f xs) c = do+      let lang' = (fromJust . langOf) c +      fMay <- realizeAnno depth (Just lang') f+      xsMay <- mapM (realizeAnno depth (Just lang')) xs+      case (fMay, (fmap unzip . sequence) xsMay, Lang.pairwiseCost lang lang') of+        (Just (fscore, f'), Just (scores, xs'), Just interopCost) ->+          return $ Just (fscore + sum scores + interopCost, AppS f' xs', c)+        _ -> return Nothing+++-- | This function is called on trees that contain no language-specific+-- components.  "GAST" refers to General Abstract Syntax Tree. The most common+-- GAST case, and the only one that is currently supported, is a expression+-- that merely rearranges data structures without calling any functions. Here+-- are a few examples:+--+--  Constant values and containters (currently supported):+--  f1 = 5+--  f2 = [1,2,3]+--+--  Variable values and containers (coming soon):+--  f3 x = x+--+--  f4 x = [1,2,x]+--+--  Combinations of transformations on containers (possible, but not coming soon):+--  f5 :: forall a b . (a, b) -> (b, a)+--  f6 (x,y) = (y,x)+--+-- The idea could be elaborated into a full-fledged language.+makeGAST :: SAnno GMeta Many [CType] -> MorlocMonad (SAnno GMeta One ())+makeGAST (SAnno (Many []) m) = case metaGType m of+  (Just (GType t)) -> MM.throwError . CallTheMonkeys . render+    $ "Cannot build general value from type" <+> dquotes (prettyType t)+  Nothing -> MM.throwError . CallTheMonkeys . render+          $ "Cannot build general value from type."+          <+> "You probably tried to build a module that is meant to be imported."+makeGAST (SAnno (Many ((UniS, _):_)) m) = return (SAnno (One (UniS, ())) m)+makeGAST (SAnno (Many ((VarS x, _):_)) m) = return (SAnno (One (VarS x, ())) m)+makeGAST (SAnno (Many ((NumS x, _):_)) m) = return (SAnno (One (NumS x, ())) m)+makeGAST (SAnno (Many ((LogS x, _):_)) m) = return (SAnno (One (LogS x, ())) m)+makeGAST (SAnno (Many ((StrS x, _):_)) m) = return (SAnno (One (StrS x, ())) m)+makeGAST (SAnno (Many ((AccS x k, _):_)) m) = do+  x' <- makeGAST x+  return (SAnno (One (AccS x' k, ())) m)+makeGAST (SAnno (Many ((ListS ss, _):_)) m) = do+  ss' <- mapM makeGAST ss+  return $ SAnno (One (ListS ss', ())) m+makeGAST (SAnno (Many ((TupleS ss, _):_)) m) = do+  ss' <- mapM makeGAST ss+  return $ SAnno (One (TupleS ss', ())) m+makeGAST (SAnno (Many ((LamS vs s, _):_)) m) = do+  s' <- makeGAST s+  return $ SAnno (One (LamS vs s', ())) m+makeGAST (SAnno (Many ((AppS f xs, _):_)) m) = do+  f' <- makeGAST f+  xs' <- mapM makeGAST xs+  return $ SAnno (One (AppS f' xs', ())) m+makeGAST (SAnno (Many ((RecS es, _):_)) m) = do+  vs <- mapM (makeGAST . snd) es+  return $ SAnno (One (RecS (zip (map fst es) vs), ())) m+makeGAST (SAnno (Many ((CallS src, _):_)) _)+  = MM.throwError . OtherError . render+  $ "Function calls cannot be used in general code:" <+> pretty (srcName src)+++-- | Serialize a simple, general data type. This type can consists only of JSON+-- primitives and containers (lists, tuples, and records) and accessors.+generalSerial :: SAnno GMeta One () -> MorlocMonad NexusCommand+generalSerial (SAnno _ GMeta{metaName = Nothing})+  = MM.throwError . OtherError $ "No general type found for call-free function"+generalSerial (SAnno _ GMeta{metaGType = Nothing})+  = MM.throwError . OtherError $ "No name found for call-free function"+generalSerial x0@(SAnno _ GMeta{ metaName = Just subcmd+                               , metaGType = Just (GType cmdtype)}) = generalSerial' [] x0+  where+    base = NexusCommand subcmd cmdtype (dquotes "_") [] []++    generalSerial' :: JsonPath -> SAnno GMeta One () -> MorlocMonad NexusCommand+    generalSerial' _ (SAnno (One (UniS,   _)) _)+      = return $ base { commandJson = "null" }+    generalSerial' _ (SAnno (One (NumS x, _)) _)+      = return $ base { commandJson = viaShow x }+    generalSerial' _ (SAnno (One (LogS x, _)) _)+      = return $ base { commandJson = if x then "true" else "false" }+    generalSerial' _ (SAnno (One (StrS x, _)) _)+      = return $ base { commandJson = dquotes (pretty x) }+    -- if a nested accessor is observed, evaluate the nested expression and+    -- append the path +    generalSerial' ps (SAnno (One (AccS x@(SAnno (One (AccS _ _, _)) _) k, _)) _) = do+      ncmd <- generalSerial' ps x+      case commandSubs ncmd of+        [(ps1, arg, ps2)] ->+          return $ ncmd { commandSubs = [(ps1, arg, JsonKey (unEVar k) : ps2)] }+        _ -> error "Bad record access"+    -- record the path to and from a record access, leave the value as null, it+    -- will be set in the nexus+    generalSerial' ps (SAnno (One (AccS (SAnno (One (VarS v, _)) g) k, _)) _) =+      case g of+        (metaGType->(Just (GType (NamT _ _ _ _)))) ->+          return $ base { commandSubs = [(ps, unEVar v, [JsonKey (unEVar k)])] }+        _ -> error "Attempted to use key access to non-record"+    generalSerial' ps (SAnno (One (ListS xs, _)) _) = do+      ncmds <- zipWithM generalSerial'+                        [ps ++ [JsonIndex i] | i <- [0..]] xs+      return $ base +        { commandJson = list (map commandJson ncmds)+        , commandSubs = conmap commandSubs ncmds+        }+    generalSerial' ps (SAnno (One (TupleS xs, _)) _) = do+      ncmds <- zipWithM generalSerial'+                        [ps ++ [JsonIndex i] | i <- [0..]] xs+      return $ base+        { commandJson = list (map commandJson ncmds)+        , commandSubs = conmap commandSubs ncmds+        }+    generalSerial' ps (SAnno (One (RecS es, _)) _) = do+      ncmds <- zipWithM generalSerial'+                        [ps ++ [JsonKey (unEVar k)] | k <- (map fst es)]+                        (map snd es)+      let entries = zip (map fst es) (map commandJson ncmds)+          obj = encloseSep "{" "}" ","+                (map (\(k, v) -> dquotes (pretty k) <> ":" <> v) entries)+      return $ base+        { commandJson = obj+        , commandSubs = conmap commandSubs ncmds+        }+    generalSerial' ps (SAnno (One (LamS vs x, _)) _) = do+      ncmd <- generalSerial' ps x+      return $ ncmd { commandArgs = vs }+    generalSerial' ps (SAnno (One (VarS (EVar v), _)) _) =+      return $ base { commandSubs = [(ps, v, [])] }+    generalSerial' _ (SAnno (One _) m) = do+      MM.throwError . OtherError . render $+        "Cannot serialize general type:" <+> prettyType (fromJust $ metaGType m)++rewritePartials+  :: SAnno GMeta One TypeP+  -> MorlocMonad (SAnno GMeta One TypeP)+rewritePartials (SAnno (One (AppS f xs, ftype@(FunP _ _))) m) = do+  let gTypeArgs = maybe (repeat Nothing) (map Just . decomposeFull) (metaGType m)+  f' <- rewritePartials f+  xs' <- mapM rewritePartials xs+  lamGType <- makeGType $ [metaGType g | (SAnno _ g) <- xs'] ++ gTypeArgs+  let vs = map EVar . take (nargs ftype) $ freshVarsAZ [] -- TODO: exclude existing arguments+      ys = zipWith3 makeVar vs (decomposeFull ftype) gTypeArgs+      -- unsafe, but should not fail for well-typed input+      appType = last . decomposeFull $ ftype+      appMeta = m {metaGType = metaGType m >>= (last . map Just . decomposeFull)}+      lamMeta = m {metaGType = Just lamGType}+      lamCType = ftype++  return $ SAnno (One (LamS vs (SAnno (One (AppS f' (xs' ++ ys), appType)) appMeta), lamCType)) lamMeta+  where+    makeGType :: [Maybe GType] -> MorlocMonad GType+    makeGType ts = fmap GType . makeType . map unGType $ (map fromJust ts)++    -- make an sanno variable from variable name and type info+    makeVar :: EVar -> TypeP -> Maybe GType -> SAnno GMeta One TypeP+    makeVar v c g = SAnno (One (VarS v, c))+      ( m { metaGType = g+          , metaName = Nothing+          , metaProperties = Set.empty+          , metaConstraints = Set.empty+          }+      )+-- apply the pattern above down the AST+rewritePartials (SAnno (One (AppS f xs, t)) m) = do+  xs' <- mapM rewritePartials xs+  f' <- rewritePartials f+  return $ SAnno (One (AppS f' xs', t)) m+rewritePartials (SAnno (One (LamS vs x, t)) m) = do+  x' <- rewritePartials x+  return $ SAnno (One (LamS vs x', t)) m+rewritePartials (SAnno (One (AccS x k, t)) m) = do+  x' <- rewritePartials x+  return $ SAnno (One (AccS x' k, t)) m+rewritePartials (SAnno (One (ListS xs, t)) m) = do+  xs' <- mapM rewritePartials xs+  return $ SAnno (One (ListS xs', t)) m+rewritePartials (SAnno (One (TupleS xs, t)) m) = do+  xs' <- mapM rewritePartials xs+  return $ SAnno (One (TupleS xs', t)) m+rewritePartials (SAnno (One (RecS entries, t)) m) = do+  let keys = map fst entries+  vals <- mapM rewritePartials (map snd entries)+  return $ SAnno (One (RecS (zip keys vals), t)) m+rewritePartials x = return x++-- | Add arguments that are required for each term. Unneeded arguments are+-- removed at each step.+parameterize+  :: SAnno GMeta One TypeP+  -> MorlocMonad (SAnno GMeta One (TypeP, [(EVar, Argument)]))+parameterize (SAnno (One (LamS vs x, t)) m) = do+  let args0 = zip vs $ zipWith makeArgument [0..] (decomposeFull t)+  x' <- parameterize' args0 x+  return $ SAnno (One (LamS vs x', (t, args0))) m+parameterize (SAnno (One (CallS src, t)) m) = do+  let ts = init . decomposeFull $ t+      vs = map EVar (freshVarsAZ [])+      args0 = zipWith makeArgument [0..] ts+  return $ SAnno (One (CallS src, (t, zip vs args0))) m+parameterize x = parameterize' [] x++-- TODO: the arguments coupled to every term should be the arguments USED+-- (not inherited) by the term. I need to ensure the argument threading+-- leads to correct passing of serialized/unserialized arguments. AppS should+-- "know" that it needs to deserialize functions that are passed to a foreign+-- call, for instance.+parameterize'+  :: [(EVar, Argument)] -- arguments in parental scope (child needn't retain them)+  -> SAnno GMeta One TypeP+  -> MorlocMonad (SAnno GMeta One (TypeP, [(EVar, Argument)]))+-- primitives, no arguments are required for a primitive, so empty lists+parameterize' _ (SAnno (One (UniS, c)) m) = return $ SAnno (One (UniS, (c, []))) m+parameterize' _ (SAnno (One (NumS x, c)) m) = return $ SAnno (One (NumS x, (c, []))) m+parameterize' _ (SAnno (One (LogS x, c)) m) = return $ SAnno (One (LogS x, (c, []))) m+parameterize' _ (SAnno (One (StrS x, c)) m) = return $ SAnno (One (StrS x, (c, []))) m+-- VarS EVar+parameterize' args (SAnno (One (VarS v, c)) m) = do+  let args' = [(v', r) | (v', r) <- args, v' == v]+  return $ SAnno (One (VarS v, (c, args'))) m+-- CallS Source+parameterize' _ (SAnno (One (CallS src, c)) m) = do+  return $ SAnno (One (CallS src, (c, []))) m+-- record access+parameterize' args (SAnno (One (AccS x k, c)) m) = do+  x' <- parameterize' args x+  return $ SAnno (One (AccS x' k, (c, args))) m+-- containers+parameterize' args (SAnno (One (ListS xs, c)) m) = do+  xs' <- mapM (parameterize' args) xs+  let usedArgs = map fst . unique . concat . map sannoSnd $ xs'+      args' = [(v, r) | (v, r) <- args, elem v usedArgs] +  return $ SAnno (One (ListS xs', (c, args'))) m+parameterize' args (SAnno (One (TupleS xs, c)) m) = do+  xs' <- mapM (parameterize' args) xs+  let usedArgs = map fst . unique . concat . map sannoSnd $ xs'+      args' = [(v, r) | (v, r) <- args, elem v usedArgs] +  return $ SAnno (One (TupleS xs', (c, args'))) m+parameterize' args (SAnno (One (RecS entries, c)) m) = do+  vs' <- mapM (parameterize' args) (map snd entries)+  let usedArgs = map fst . unique . concat . map sannoSnd $ vs'+      args' = [(v, r) | (v, r) <- args, elem v usedArgs] +  return $ SAnno (One (RecS (zip (map fst entries) vs'), (c, args'))) m+parameterize' args (SAnno (One (LamS vs x, c)) m) = do+  let args' = [(v, r) | (v, r) <- args, not (elem v vs)]+      startId = maximum (map (argId . snd) args) + 1+      args0 = zip vs $ map unpackArgument $ zipWith makeArgument [startId..] (decomposeFull c)+  x' <- parameterize' (args' ++ args0) x+  return $ SAnno (One (LamS vs x', (c, args'))) m+parameterize' args (SAnno (One (AppS x xs, c)) m) = do+  x' <- parameterize' args x+  xs' <- mapM (parameterize' args) xs+  let usedArgs = map fst $ (sannoSnd x' ++ (unique . concat . map sannoSnd $ xs'))+      args' = [(v, r) | (v, r) <- args, elem v usedArgs] +  return $ SAnno (One (AppS x' xs', (c, args'))) m++makeArgument :: Int -> TypeP -> Argument+makeArgument i (UnkP _) = PassThroughArgument i+makeArgument i t = SerialArgument i t+++-- convert from unambiguous tree to non-segmented ExprM+express :: SAnno GMeta One (TypeP, [(EVar, Argument)]) -> MorlocMonad (ExprM Many)+express s0@(SAnno (One (_, (c0, _))) _) = express' True c0 s0 where++  express' :: Bool -> TypeP -> SAnno GMeta One (TypeP, [(EVar, Argument)]) -> MorlocMonad (ExprM Many)++  -- primitives+  express' _ _ (SAnno (One (NumS x, (c, _))) _) = return $ NumM (Native c) x+  express' _ _ (SAnno (One (LogS x, (c, _))) _) = return $ LogM (Native c) x+  express' _ _ (SAnno (One (StrS x, (c, _))) _) = return $ StrM (Native c) x+  express' _ _ (SAnno (One (UniS, (c, _))) _) = return $ NullM (Native c)++  -- record access+  express' isTop pc (SAnno (One (AccS x k, _)) m) = do+    x' <- express' isTop pc x >>= unpackExprM m+    return (AccM x' k)++  -- containers+  express' isTop _ (SAnno (One (ListS xs, (c@(ArrP _ [t]), args))) m) = do+    xs' <- mapM (express' False t) xs >>= mapM (unpackExprM m)+    let x = (ListM (Native c) xs')+    if isTop+      then do+        x' <- packExprM m x+        return $ ManifoldM m (map snd args) (ReturnM x')+      else return x+  express' _ _ (SAnno (One (ListS _, _)) _) = MM.throwError . CallTheMonkeys $ "ListS can only be ArrP type"++  express' isTop _ (SAnno (One (TupleS xs, (c@(ArrP _ ts), args))) m) = do+    xs' <- zipWithM (express' False) ts xs >>= mapM (unpackExprM m)+    let x = (TupleM (Native c) xs')+    if isTop+      then do+        x' <- packExprM m x+        return $ ManifoldM m (map snd args) (ReturnM x')+      else return x++  express' isTop _ (SAnno (One (RecS entries, (c@(NamP _ _ _ rs), args))) m) = do+    xs' <- zipWithM (express' False) (map snd rs) (map snd entries) >>= mapM (unpackExprM m)+    let x = RecordM (Native c) (zip (map fst entries) xs')+    if isTop+      then do+        x' <- packExprM m x+        return $ ManifoldM m (map snd args) (ReturnM x')+      else return x++  -- lambda+  express' isTop _ (SAnno (One (LamS _ x@(SAnno (One (_, (c,_))) _), _)) _) = express' isTop c x++  -- var+  express' _ _ (SAnno (One (VarS v, (c, rs))) _) =+    case [r | (v', r) <- rs, v == v'] of+      [r] -> case r of+        (SerialArgument i _) -> return $ BndVarM (Serial c) i+        (NativeArgument i _) -> return $ BndVarM (Native c) i+        -- NOT passthrough, since it doesn't+        -- After segmentation, this type will be used to resolve passthroughs everywhere+        (PassThroughArgument i) -> return $ BndVarM (Serial c) i+      _ -> MM.throwError . OtherError $ "Expected VarS to match exactly one argument"++  -- Apply arguments to a sourced function+  -- The CallS object may be in a foreign language. These inter-language+  -- connections will be snapped apart in the segment step.+  express' _ pc (SAnno (One (AppS (SAnno (One (CallS src, (fc, _))) _) xs, (_, args))) m)+    -- case #1+    | sameLanguage && fullyApplied = do+        xs' <- zipWithM (express' False) inputs xs >>= mapM (unpackExprM m)+        return . ManifoldM m (map snd args) $+          ReturnM (AppM f xs')++    -- case #2+    | sameLanguage && not fullyApplied = do+        xs' <- zipWithM (express' False) inputs xs >>= mapM (unpackExprM m)+        let startId = maximum (map (argId . snd) args) + 1+            lambdaTypes = drop (length xs) (map typeP2typeM inputs)+            lambdaArgs = zipWith NativeArgument [startId ..] inputs+            lambdaVals = zipWith BndVarM          lambdaTypes [startId ..]+        return . ManifoldM m (map snd args) $+          ReturnM (LamM lambdaArgs (AppM f (xs' ++ lambdaVals)))++    -- case #3+    | not sameLanguage && fullyApplied = do+          xs' <- zipWithM (express' False) inputs xs >>= mapM (unpackExprM m)+          return . ForeignInterfaceM (packTypeM (typeP2typeM pc)) . ManifoldM m (map snd args) $+            ReturnM (AppM f xs')++    -- case #4+    | not sameLanguage && not fullyApplied = do+        xs' <- zipWithM (express' False) inputs xs >>= mapM (unpackExprM m)+        let startId = maximum (map (argId . snd) args) + 1+            lambdaTypes = drop (length xs) (map typeP2typeM inputs)+            lambdaArgs = zipWith NativeArgument [startId ..] inputs+            lambdaVals = zipWith BndVarM lambdaTypes [startId ..]+        return . ForeignInterfaceM (packTypeM (typeP2typeM pc))+               . ManifoldM m (map snd args)+               $ ReturnM (LamM lambdaArgs (AppM f (xs' ++ lambdaVals)))+    where+      (inputs, _) = decompose fc+      sameLanguage = langOf pc == langOf fc+      fullyApplied = length inputs == length xs+      f = SrcM (typeP2typeM fc) src++  -- CallS - direct export of a sourced function, e.g.:+  express' True _ (SAnno (One (CallS src, (c, _))) m) = do+    let (inputs, _) = decompose c+        lambdaArgs = zipWith SerialArgument [0 ..] inputs+        lambdaTypes = map (packTypeM . typeP2typeM) inputs+        f = SrcM (typeP2typeM c) src+    lambdaVals <- mapM (unpackExprM m) $ zipWith BndVarM lambdaTypes [0 ..]+    return $ ManifoldM m lambdaArgs (ReturnM $ AppM f lambdaVals)++  -- An un-applied source call+  express' False pc (SAnno (One (CallS src, (c, _))) m) = do+    let (inputs, _) = decompose c+        lambdaTypes = map typeP2typeM inputs+        lambdaArgs = zipWith NativeArgument [0 ..] inputs+        lambdaVals = zipWith BndVarM lambdaTypes [0 ..]+        f = SrcM (typeP2typeM c) src+        manifold = ManifoldM m lambdaArgs (ReturnM $ AppM f lambdaVals)++    if langOf pc == langOf c+      then return manifold+      else return $ ForeignInterfaceM (typeP2typeM pc) manifold++  express' _ _ (SAnno (One (_, (t, _))) m) = MM.throwError . CallTheMonkeys . render $+    "Invalid input to express' in module (" <> viaShow (metaName m) <> ") - type: " <> prettyTypeP t++-- | Move let assignments to minimize number of foreign calls.  This step+-- should be integrated with the optimizations performed in the realize step.+-- FIXME: replace stub+letOptimize :: ExprM Many -> MorlocMonad (ExprM Many)+letOptimize e = return e++segment :: ExprM Many -> MorlocMonad [ExprM Many]+segment e0+  = segment' (gmetaOf e0) (argsOf e0) e0+  |>> (\(ms,e) -> e:ms)+  |>> map reparameterize where++  -- This is where segmentation happens, every other match is just traversal+  segment' _ args (ForeignInterfaceM t e@(ManifoldM m args' _)) = do+    (ms, e') <- segment' m args' e+    config <- MM.ask+    case MC.buildPoolCallBase config (langOf e') (metaId m) of+      (Just cmds) -> return (e':ms, PoolCallM (packTypeM t) (metaId m) cmds args)+      Nothing -> MM.throwError . OtherError $ "Unsupported language: " <> MT.show' (langOf e')++  segment' m args (SerializeM _ (AppM e@(ForeignInterfaceM _ _) es)) = do+    (ms, e') <- segment' m args e+    (mss, es') <- mapM (segment' m args) es |>> unzip+    es'' <- mapM (packExprM m) es'+    return (ms ++ concat mss, AppM e' es'')++  segment' _ _ (ManifoldM m args e) = do+    (ms, e') <- segment' m args e+    return (ms, ManifoldM m args e')++  segment' m args (AppM e es) = do+    (ms, e') <- segment' m args e+    (mss, es') <- mapM (segment' m args) es |>> unzip+    return (ms ++ concat mss, AppM e' es')++  segment' m args0 (LamM args1 e) = do+    (ms, e') <- (segment' m (args0 ++ args1)) e+    return (ms, LamM args1 e')++  segment' m args (LetM i e1 e2) = do+    (ms1, e1') <- segment' m args e1+    (ms2, e2') <- segment' m args e2+    return (ms1 ++ ms2, LetM i e1' e2')++  segment' m args (AccM e k) = do+    (ms, e') <- segment' m args e+    return (ms, AccM e' k)++  segment' m args (ListM t es) = do+    (mss, es') <- mapM (segment' m args) es |>> unzip+    return (concat mss, ListM t es')++  segment' m args (TupleM t es) = do+    (mss, es') <- mapM (segment' m args) es |>> unzip+    return (concat mss, TupleM t es')++  segment' m args (RecordM t entries) = do+    (mss, es') <- mapM (segment' m args) (map snd entries) |>> unzip+    return (concat mss, RecordM t (zip (map fst entries) es'))++  segment' m args (SerializeM s e) = do+    (ms, e') <- segment' m args e+    return (ms, SerializeM s e')++  segment' m args (DeserializeM s e) = do+    (ms, e') <- segment' m args e+    return (ms, DeserializeM s e')++  segment' m args (ReturnM e) = do+    (ms, e') <- segment' m args e+    return (ms, ReturnM e')++  segment' _ _ e = return ([], e)++-- Now that the AST is segmented by language, we can resolve passed-through+-- arguments where possible.+reparameterize :: (ExprM Many) -> (ExprM Many)+reparameterize e0 = snd (substituteBndArgs e0) where +  substituteBndArgs :: (ExprM Many) -> ([(Int, TypeM)], ExprM Many) +  substituteBndArgs (ForeignInterfaceM i e) =+    let (vs, e') = substituteBndArgs e+    in (vs, ForeignInterfaceM i (snd $ substituteBndArgs e'))+  substituteBndArgs (ManifoldM m args e) =+    let (vs, e') = substituteBndArgs e+    in (vs, ManifoldM m (map (sub vs) args) e')+  substituteBndArgs (AppM e es) =+    let (vs, e') = substituteBndArgs e+        (vss, es') = unzip $ map substituteBndArgs es+    in (vs ++ concat vss, AppM e' es')+  substituteBndArgs (LamM args e) =+    let (vs, e') = substituteBndArgs e+    in (vs, LamM (map (sub vs) args) e')+  substituteBndArgs (LetM i e1 e2) =+    let (vs1, e1') = substituteBndArgs e1+        (vs2, e2') = substituteBndArgs e2+    in (vs1 ++ vs2, LetM i e1' e2')+  substituteBndArgs (AccM e k) =+    let (vs, e') = substituteBndArgs e+    in (vs, AccM e' k)+  substituteBndArgs (ListM t es) =+    let (vss, es') = unzip $ map substituteBndArgs es+    in (concat vss, ListM t es')+  substituteBndArgs (TupleM t es) =+    let (vss, es') = unzip $ map substituteBndArgs es+    in (concat vss, TupleM t es')+  substituteBndArgs (RecordM t entries) =+    let (vss, es') = unzip $ map substituteBndArgs (map snd entries)+    in (concat vss, RecordM t (zip (map fst entries) es'))+  substituteBndArgs (SerializeM s e) =+    let (vs, e') = substituteBndArgs e+    in (vs, SerializeM s e')+  substituteBndArgs (DeserializeM s e) =+    let (vs, e') = substituteBndArgs e+    in (vs, DeserializeM s e')+  substituteBndArgs (ReturnM e) =+    let (vs, e') = substituteBndArgs e+    in (vs, ReturnM e')+  substituteBndArgs e@(BndVarM t i) = ([(i, t)], e)+  substituteBndArgs e = ([], e)++  sub :: [(Int, TypeM)] -> Argument -> Argument+  sub bnds r@(PassThroughArgument i) = case [t | (i', t) <- bnds, i == i'] of+    ((Serial t):_) -> SerialArgument i t +    ((Native t):_) -> NativeArgument i t +    ((Function _ _):_) -> error "You don't need to pass functions as manifold arguments"+    (Passthrough : _) -> error "What about 'Passthrough' do you not understand?"+    _ -> r +  sub _ r = r++rehead :: ExprM Many -> MorlocMonad (ExprM Many)+rehead (LamM _ e) = rehead e+rehead (ManifoldM m args (ReturnM e)) = do+  e' <- packExprM m e+  return $ ManifoldM m args (ReturnM e')+rehead _ = MM.throwError $ CallTheMonkeys "Bad Head"++-- Sort manifolds into pools. Within pools, group manifolds into call sets.+pool :: [ExprM Many] -> MorlocMonad [(Lang, [ExprM Many])]+pool = return . groupSort . map (\e -> (fromJust $ langOf e, e))++encode+  :: [Source]+  -> (Lang, [ExprM Many])+  -> MorlocMonad Script+encode srcs (lang, xs) = do+  state <- MM.get++  let srcs' = unique [s | s <- srcs, srcLang s == lang]++  xs' <- mapM (preprocess lang) xs >>= chooseSerializer+  -- translate each node in the AST to code+  code <- translate lang srcs' xs'++  return $ Script+    { scriptBase = "pool"+    , scriptLang = lang+    , scriptCode = Code . render $ code+    , scriptCompilerFlags =+        filter (/= "") . map packageGccFlags $ statePackageMeta state+    , scriptInclude = unique . map MS.takeDirectory $+        (unique . catMaybes) (map srcPath srcs')+    }++preprocess :: Lang -> ExprM Many -> MorlocMonad (ExprM Many)+preprocess CppLang es = Cpp.preprocess es+preprocess RLang es = R.preprocess es+preprocess Python3Lang es = Python3.preprocess es+preprocess l _ = MM.throwError . PoolBuildError . render+               $ "Language '" <> viaShow l <> "' has no translator"++chooseSerializer :: [ExprM Many] -> MorlocMonad [ExprM One]+chooseSerializer xs = mapM chooseSerializer' xs where+  chooseSerializer' :: ExprM Many -> MorlocMonad (ExprM One)+  -- This is where the magic happens, the rest is just plumbing+  chooseSerializer' (SerializeM s e) = SerializeM <$> oneSerial s <*> chooseSerializer' e+  chooseSerializer' (DeserializeM s e) = DeserializeM <$> oneSerial s <*> chooseSerializer' e+  -- plumbing+  chooseSerializer' (ManifoldM g args e) = ManifoldM g args <$> chooseSerializer' e+  chooseSerializer' (ForeignInterfaceM t e) = ForeignInterfaceM t <$> chooseSerializer' e+  chooseSerializer' (LetM i e1 e2) = LetM i <$> chooseSerializer' e1 <*> chooseSerializer' e2+  chooseSerializer' (AppM e es) = AppM <$> chooseSerializer' e <*> mapM chooseSerializer' es+  chooseSerializer' (LamM args e) = LamM args <$> chooseSerializer' e+  chooseSerializer' (AccM e k) = AccM <$> chooseSerializer' e <*> pure k+  chooseSerializer' (ListM t es) = ListM t <$> mapM chooseSerializer' es+  chooseSerializer' (TupleM t es) = TupleM t <$> mapM chooseSerializer' es+  chooseSerializer' (RecordM t rs) = do+    ts <- mapM (chooseSerializer' . snd) rs+    return $ RecordM t (zip (map fst rs) ts)+  chooseSerializer' (ReturnM e ) = ReturnM <$> chooseSerializer' e+  chooseSerializer' (SrcM t s) = return $ SrcM t s+  chooseSerializer' (PoolCallM t i d args) = return $ PoolCallM t i d args+  chooseSerializer' (BndVarM t i ) = return $ BndVarM t i+  chooseSerializer' (LetVarM t i) = return $ LetVarM t i+  chooseSerializer' (LogM t x) = return $ LogM t x+  chooseSerializer' (NumM t x) = return $ NumM t x+  chooseSerializer' (StrM t x) = return $ StrM t x+  chooseSerializer' (NullM t) = return $ NullM t++  oneSerial :: SerialAST Many -> MorlocMonad (SerialAST One)+  oneSerial (SerialPack _ (Many [])) = MM.throwError . SerializationError $ "No valid serializer found"+  oneSerial (SerialPack v (Many ((p,s):_))) = do+    s' <- oneSerial s+    return $ SerialPack v (One (p, s'))+  oneSerial (SerialList s) = SerialList <$> oneSerial s+  oneSerial (SerialTuple ss) = SerialTuple <$> mapM oneSerial ss+  oneSerial (SerialObject r v ps rs) = do+    ts <- mapM (oneSerial . snd) rs+    return $ SerialObject r v ps (zip (map fst rs) ts)+  oneSerial (SerialNum t) = return $ SerialNum t+  oneSerial (SerialBool t) = return $ SerialBool t+  oneSerial (SerialString t) = return $ SerialString t+  oneSerial (SerialNull t) = return $ SerialNull t+  oneSerial (SerialUnknown t) = return $ SerialUnknown t++translate :: Lang -> [Source] -> [ExprM One] -> MorlocMonad MDoc+translate lang srcs es = do+  case lang of+    CppLang -> Cpp.translate srcs es+    RLang -> R.translate srcs es+    Python3Lang -> Python3.translate srcs es+    x -> MM.throwError . PoolBuildError . render+      $ "Language '" <> viaShow x <> "' has no translator"+++-------- Utility and lookup functions ----------------------------------------++unpackSAnno :: (SExpr g One c -> g -> c -> a) -> SAnno g One c -> [a]+unpackSAnno f (SAnno (One (e@(AccS x _),     c)) g) = f e g c : unpackSAnno f x+unpackSAnno f (SAnno (One (e@(ListS xs),     c)) g) = f e g c : conmap (unpackSAnno f) xs+unpackSAnno f (SAnno (One (e@(TupleS xs),    c)) g) = f e g c : conmap (unpackSAnno f) xs+unpackSAnno f (SAnno (One (e@(RecS entries), c)) g) = f e g c : conmap (unpackSAnno f) (map snd entries)+unpackSAnno f (SAnno (One (e@(LamS _ x),     c)) g) = f e g c : unpackSAnno f x+unpackSAnno f (SAnno (One (e@(AppS x xs),    c)) g) = f e g c : conmap (unpackSAnno f) (x:xs)+unpackSAnno f (SAnno (One (e, c)) g)                = [f e g c]++mapGCM :: (g -> c -> MorlocMonad c') -> SAnno g One c -> MorlocMonad (SAnno g One c')+mapGCM f (SAnno (One (AccS x k, c)) g) = do+  x' <- mapGCM f x+  c' <- f g c+  return $ SAnno (One (AccS x' k, c')) g+mapGCM f (SAnno (One (ListS xs, c)) g) = do+  xs' <- mapM (mapGCM f) xs+  c' <- f g c+  return $ SAnno (One (ListS xs', c')) g+mapGCM f (SAnno (One (TupleS xs, c)) g) = do+  xs' <- mapM (mapGCM f) xs+  c' <- f g c+  return $ SAnno (One (TupleS xs', c')) g+mapGCM f (SAnno (One (RecS entries, c)) g) = do+  xs' <- mapM (mapGCM f) (map snd entries)+  c' <- f g c+  return $ SAnno (One (RecS (zip (map fst entries) xs'), c')) g+mapGCM f (SAnno (One (LamS vs x, c)) g) = do+  x' <- mapGCM f x+  c' <- f g c+  return $ SAnno (One (LamS vs x', c')) g+mapGCM f (SAnno (One (AppS x xs, c)) g) = do+  x' <- mapGCM f x+  xs' <- mapM (mapGCM f) xs+  c' <- f g c+  return $ SAnno (One (AppS x' xs', c')) g+mapGCM f (SAnno (One (VarS x, c)) g) = do+  c' <- f g c+  return $ SAnno (One (VarS x, c')) g+mapGCM f (SAnno (One (CallS src, c)) g) = do+  c' <- f g c+  return $ SAnno (One (CallS src, c')) g+mapGCM f (SAnno (One (UniS, c)) g) = do+  c' <- f g c+  return $ SAnno (One (UniS, c')) g+mapGCM f (SAnno (One (NumS x, c)) g) = do+  c' <- f g c+  return $ SAnno (One (NumS x, c')) g+mapGCM f (SAnno (One (LogS x, c)) g) = do+  c' <- f g c+  return $ SAnno (One (LogS x, c')) g+mapGCM f (SAnno (One (StrS x, c)) g) = do+  c' <- f g c+  return $ SAnno (One (StrS x, c')) g++sannoSnd :: SAnno g One (a, b) -> b+sannoSnd (SAnno (One (_, (_, x))) _) = x++-- generate infinite list of fresh variables of form+-- ['a','b',...,'z','aa','ab',...,'zz',...]+freshVarsAZ+  :: [MT.Text] -- variables to exclude+  -> [MT.Text]+freshVarsAZ exclude =+  filter+    (\x -> not (elem x exclude))+    ([1 ..] >>= flip replicateM ['a' .. 'z'] |>> MT.pack)++-- turn type list into a function+makeType :: [Type] -> MorlocMonad Type+makeType [] = MM.throwError . TypeError $ "empty type"+makeType [t] = return t+makeType (t:ts) = FunT <$> pure t <*> makeType ts
+ library/Morloc/CodeGenerator/Grammars/Common.hs view
@@ -0,0 +1,341 @@+{-|+Module      : Morloc.CodeGenerator.Grammars.Common+Description : A common set of utility functions for language templates+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.CodeGenerator.Grammars.Common+  ( argType+  , unpackArgument+  , argId+  , typeOfExprM+  , gmetaOf+  , argsOf+  , typeOfTypeM+  , invertExprM+  , packTypeM+  , packExprM+  , unpackExprM+  , unpackTypeM+  , nargsTypeM+  , arg2typeM+  , type2jsontype+  , jsontype2json+  , prettyArgument+  , prettyExprM+  , prettyTypeM+  , prettyTypeP+  , splitArgs+  ) where++import Morloc.Data.Doc+import Morloc.CodeGenerator.Namespace+import qualified Morloc.Data.Text as MT+import qualified Morloc.Monad as MM+import qualified Morloc.CodeGenerator.Serial as MCS+++prettyArgument :: Argument -> MDoc+prettyArgument (SerialArgument i c) =+  "Serial" <+> "x" <> pretty i <+> parens (prettyTypeP c)+prettyArgument (NativeArgument i c) =+  "Native" <+> "x" <> pretty i <+> parens (prettyTypeP c)+prettyArgument (PassThroughArgument i) =+  "PassThrough" <+> "x" <> pretty i++argId :: Argument -> Int+argId (SerialArgument i _) = i+argId (NativeArgument i _) = i+argId (PassThroughArgument i ) = i++argType :: Argument -> Maybe TypeP+argType (SerialArgument _ t) = Just t+argType (NativeArgument _ t) = Just t+argType (PassThroughArgument _) = Nothing++unpackArgument :: Argument -> Argument+unpackArgument (SerialArgument i t) = NativeArgument i t+unpackArgument x = x++nargsTypeM :: TypeM -> Int+nargsTypeM (Function ts _) = length ts+nargsTypeM _ = 0++prettyExprM :: ExprM f -> MDoc+prettyExprM e0 = (vsep . punctuate line . fst $ f e0) <> line where+  manNamer :: Int -> MDoc+  manNamer i = "m" <> pretty i++  f :: ExprM f -> ([MDoc], MDoc)+  f (ManifoldM m args e) =+    let (ms', body) = f e+        decl = manNamer (metaId m) <> tupled (map prettyArgument args)+        mdoc = block 4 decl body+    in (mdoc : ms', manNamer (metaId m))+  f (PoolCallM t _ cmds args) =+    let poolArgs = cmds ++ map prettyArgument args+    in ([], "PoolCallM" <> list (poolArgs) <+> "::" <+> prettyTypeM t) +  f (ForeignInterfaceM t e) =+    let (ms, _) = f e+    in (ms, "ForeignInterface :: " <> prettyTypeM t)+  f (LetM v e1 e2) =+    let (ms1', e1') = f e1+        (ms2', e2') = f e2+    in (ms1' ++ ms2', "a" <> pretty v <+> "=" <+> e1' <> line <> e2')+  f (AppM fun xs) =+    let (ms', fun') = f fun+        (mss', xs') = unzip $ map f xs+    in (ms' ++ concat mss', fun' <> tupled xs')+  f (SrcM _ src) = ([], pretty (srcName src))+  f (LamM args e) =+    let (ms', e') = f e+        vsFull = map prettyArgument args+        vsNames = map (\r -> "x" <> pretty (argId r)) args+    in (ms', "\\ " <+> hsep (punctuate "," vsFull) <> "->" <+> e' <> tupled vsNames)+  f (BndVarM _ i) = ([], "x" <> pretty i)+  f (LetVarM _ i) = ([], "a" <> pretty i)+  f (AccM e k) =+    let (ms, e') = f e+    in (ms, parens e' <> "@" <> pretty k)+  f (ListM _ es) =+    let (mss', es') = unzip $ map f es+    in (concat mss', list es')+  f (TupleM _ es) =+    let (mss', es') = unzip $ map f es+    in (concat mss', tupled es')+  f (RecordM c entries) =+    let (mss', es') = unzip $ map (f . snd) entries+        entries' = zipWith (\k v -> pretty k <> "=" <> v) (map fst entries) es'+    in (concat mss', prettyRecordPVar c <+> "{" <> tupled entries' <> "}")+  f (LogM _ x) = ([], if x then "true" else "false")+  f (NumM _ x) = ([], viaShow x)+  f (StrM _ x) = ([], dquotes $ pretty x)+  f (NullM _) = ([], "null")+  f (SerializeM _ e) =+    let (ms, e') = f e+    in (ms, "PACK" <> tupled [e'])+  f (DeserializeM _ e) =+    let (ms, e') = f e+    in (ms, "UNPACK" <> tupled [e'])+  f (ReturnM e) =+    let (ms, e') = f e+    in (ms, "RETURN(" <> e' <> ")")++prettyRecordPVar :: TypeM -> MDoc+prettyRecordPVar (Serial (NamP _ v _ _)) = prettyPVar v+prettyRecordPVar (Native (NamP _ v _ _)) = prettyPVar v+prettyRecordPVar _ = "<UNKNOWN RECORD>"++prettyPVar :: PVar -> MDoc+prettyPVar (PV _ (Just g) t) = parens (pretty g <+> pretty t)+prettyPVar (PV _ Nothing t) = parens ("*" <+> pretty t)++prettyTypeP :: TypeP -> MDoc+prettyTypeP (UnkP v) = prettyPVar v +prettyTypeP (VarP v) = prettyPVar v +prettyTypeP (FunP t1 t2) = parens (prettyTypeP t1 <+> "->" <+> prettyTypeP t2)+prettyTypeP (ArrP v ts) = prettyPVar v <+> hsep (map prettyTypeP ts)+prettyTypeP (NamP r v _ rs)+  = viaShow r <+> prettyPVar v <+> encloseSep "{" "}" ","+    (zipWith (\key val -> key <+> "=" <+> val)+             (map (prettyPVar . fst) rs)+             (map (prettyTypeP . snd) rs))++prettyTypeM :: TypeM -> MDoc+prettyTypeM Passthrough = "Passthrough"+prettyTypeM (Serial c) = "Serial<" <> prettyTypeP c <> ">"+prettyTypeM (Native c) = "Native<" <> prettyTypeP c <> ">"+prettyTypeM (Function ts t) =+  "Function<" <> hsep (punctuate "->" (map prettyTypeM (ts ++ [t]))) <> ">"++-- see page 112 of my super-secret notes ...+-- example:+-- > f [g x, 42] (h 1 [1,2])+-- converts to:+-- > let a0 = g x+-- > in let a1 = [a0, 42]+-- >    in let a2 = [1,2]+-- >       in let a3 = h 1 a2+-- >          in f a1 a3+-- expression inversion will not alter expression type+invertExprM :: (ExprM f) -> MorlocMonad (ExprM f)+invertExprM (ManifoldM m args e) = do+  MM.startCounter+  e' <- invertExprM e+  return $ ManifoldM m args e'+invertExprM (LetM v e1 e2) = do+  e2' <- invertExprM e2+  return $ LetM v e1 e2'+invertExprM e@(AppM f es) = do+  f' <- invertExprM f+  es' <- mapM invertExprM es+  v <- MM.getCounter+  let t = typeOfExprM e+      appM' = LetM v (AppM (terminalOf f') (map terminalOf es')) (LetVarM t v)+  return $ foldl dependsOn appM' (f':es')+-- you can't pull the body of the lambda out into a let statement+invertExprM f@(LamM _ _) = return f+invertExprM (AccM e k) = do+  e' <- invertExprM e+  return $ dependsOn (AccM (terminalOf e') k) e'+invertExprM (ListM c es) = do+  es' <- mapM invertExprM es+  v <- MM.getCounter+  let e = LetM v (ListM c (map terminalOf es')) (LetVarM c v)+      e' = foldl (\x y -> dependsOn x y) e es'+  return e'+invertExprM (TupleM c es) = do+  es' <- mapM invertExprM es+  v <- MM.getCounter+  let e = LetM v (TupleM c (map terminalOf es')) (LetVarM c v)+      e' = foldl (\x y -> dependsOn x y) e es'+  return e'+invertExprM (RecordM c entries) = do+  es' <- mapM invertExprM (map snd entries)+  v <- MM.getCounter+  let entries' = zip (map fst entries) (map terminalOf es')+      e = LetM v (RecordM c entries') (LetVarM c v)+      e' = foldl (\x y -> dependsOn x y) e es'+  return e'+invertExprM (SerializeM p e) = do+  e' <- invertExprM e+  v <- MM.getCounter+  let t' = packTypeM $ typeOfExprM e+  return $ dependsOn (LetM v (SerializeM p (terminalOf e')) (LetVarM t' v)) e'+invertExprM (DeserializeM p e) = do+  e' <- invertExprM e+  v <- MM.getCounter+  let t' = unpackTypeM $ typeOfExprM e+  return $ dependsOn (LetM v (DeserializeM p (terminalOf e')) (LetVarM t' v)) e'+invertExprM (ReturnM e) = do+  e' <- invertExprM e+  return $ dependsOn (ReturnM (terminalOf e')) e'+invertExprM (PoolCallM t i cmds args) = do+  v <- MM.getCounter+  return $ LetM v (PoolCallM t i cmds args) (LetVarM t v)+invertExprM e = return e++-- transfer all let-dependencies from y to x+--+-- Technically, I should check for variable reuse in the let-chain and+-- resolve conflicts be substituting in fresh variable names. However, for+-- now, I will trust that my name generator created names that are unique+-- within the manifold.+dependsOn :: ExprM f -> ExprM f -> ExprM f+dependsOn x (LetM v e y) = LetM v e (dependsOn x y)+dependsOn x _ = x++-- get the rightmost expression in a let-tree+terminalOf :: ExprM f -> ExprM f+terminalOf (LetM _ _ e) = terminalOf e+terminalOf e = e++typeOfTypeM :: TypeM -> Maybe TypeP +typeOfTypeM Passthrough = Nothing+typeOfTypeM (Serial c) = Just c+typeOfTypeM (Native c) = Just c+typeOfTypeM (Function [] t) = typeOfTypeM t+typeOfTypeM (Function (ti:ts) to)+  = FunP <$> typeOfTypeM ti <*> typeOfTypeM (Function ts to)  ++arg2typeM :: Argument -> TypeM+arg2typeM (SerialArgument _ c) = Serial c+arg2typeM (NativeArgument _ c) = Native c+arg2typeM (PassThroughArgument _) = Passthrough++-- | Get the manifold type of an expression+--+-- The ExprM must have exactly enough type information to infer the type of any+-- element without reference to the element's parent.+typeOfExprM :: ExprM f -> TypeM+typeOfExprM (ManifoldM _ args e) = Function (map arg2typeM args) (typeOfExprM e)+typeOfExprM (ForeignInterfaceM t _) = t+typeOfExprM (PoolCallM t _ _ _) = t+typeOfExprM (LetM _ _ e2) = typeOfExprM e2+typeOfExprM (AppM f xs) = case typeOfExprM f of+  (Function inputs output) -> case drop (length xs) inputs of+    [] -> output+    inputs' -> Function inputs' output+  _ -> error . MT.unpack . render $ "COMPILER BUG: application of non-function" <+> parens (prettyTypeM $ typeOfExprM f)+typeOfExprM (SrcM t _) = t+typeOfExprM (LamM args x) = Function (map arg2typeM args) (typeOfExprM x)+typeOfExprM (BndVarM t _) = t+typeOfExprM (LetVarM t _) = t+typeOfExprM (AccM e _) = typeOfExprM e+typeOfExprM (ListM t _) = t+typeOfExprM (TupleM t _) = t+typeOfExprM (RecordM t _) = t+typeOfExprM (LogM t _) = t+typeOfExprM (NumM t _) = t+typeOfExprM (StrM t _) = t+typeOfExprM (NullM t) = t+typeOfExprM (SerializeM _ e) = packTypeM (typeOfExprM e)+typeOfExprM (DeserializeM _ e) = unpackTypeM (typeOfExprM e)+typeOfExprM (ReturnM e) = typeOfExprM e++packTypeM :: TypeM -> TypeM+packTypeM (Native t) = Serial t+packTypeM (Function _ _) = error $ "BUG: Cannot pack a function"+packTypeM t = t++unpackTypeM :: TypeM -> TypeM+unpackTypeM (Serial t) = Native t+unpackTypeM Passthrough = error $ "BUG: Cannot unpack a passthrough type"+unpackTypeM t = t ++unpackExprM :: GMeta -> ExprM Many -> MorlocMonad (ExprM Many) +unpackExprM m e = case typeOfExprM e of+  (Serial t) -> DeserializeM <$> MCS.makeSerialAST m t <*> pure e+  (Passthrough) -> MM.throwError . SerializationError $ "Cannot unpack a passthrough typed expression"+  _ -> return e++packExprM :: GMeta -> ExprM Many -> MorlocMonad (ExprM Many)+packExprM m e = case typeOfExprM e of+  (Native t) -> SerializeM <$> MCS.makeSerialAST m t <*> pure e+  -- (Function _ _) -> error "Cannot pack a function"+  _ -> return e++type2jsontype :: TypeP -> MorlocMonad JsonType+type2jsontype (UnkP _) = MM.throwError . SerializationError $ "Invalid JSON type: UnkT"+type2jsontype (VarP (PV _ _ v)) = return $ VarJ v+type2jsontype (ArrP (PV _ _ v) ts) = ArrJ v <$> mapM type2jsontype ts+type2jsontype (FunP _ _) = MM.throwError . SerializationError $ "Invalid JSON type: FunT"+type2jsontype (NamP namType (PV _ _ v) _ rs) = do+  vs <- mapM type2jsontype (map snd rs)+  return $ NamJ jsontype (zip [val | (PV _ _ val, _) <- rs] vs)+  where+    jsontype = case namType of+      NamRecord -> "record"+      NamObject -> v+      NamTable -> v++jsontype2json :: JsonType -> MDoc+jsontype2json (VarJ v) = dquotes (pretty v)+jsontype2json (ArrJ v ts) = "{" <> key <> ":" <> val <> "}" where+  key = dquotes (pretty v)+  val = encloseSep "[" "]" "," (map jsontype2json ts)+jsontype2json (NamJ v rs) = "{" <> dquotes (pretty v) <> ":" <> encloseSep "{" "}" "," rs' <> "}" where+  keys = map (dquotes . pretty) (map fst rs) +  vals = map jsontype2json (map snd rs)+  rs' = zipWith (\key val -> key <> ":" <> val) keys vals++argsOf :: ExprM f -> [Argument]+argsOf (LamM args _) = args+argsOf (ManifoldM _ args _) = args+argsOf _ = []++gmetaOf :: ExprM f -> GMeta+gmetaOf (ManifoldM m _ _) = m+gmetaOf (LamM _ e) = gmetaOf e+gmetaOf _ = error "Malformed top-expression"++-- divide a list of arguments based on wheither they are in a second list+splitArgs :: [Argument] -> [Argument] -> ([Argument], [Argument])+splitArgs args1 args2 = partitionEithers $ map splitOne args1 where+  splitOne :: Argument -> Either Argument Argument+  splitOne r = if elem r args2+               then Left r+               else Right r+
+ library/Morloc/CodeGenerator/Grammars/Macro.hs view
@@ -0,0 +1,72 @@+{-|+Module      : Morloc.CodeGenerator.Grammars.Macro+Description : Expand parameters in concrete types+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.CodeGenerator.Grammars.Macro+(   expandMacro+  , expandType+) where++import Morloc.CodeGenerator.Namespace+import Morloc.Data.Doc+import qualified Morloc.Data.Text as MT+import qualified Control.Monad.State as CMS+import Text.Megaparsec+import Text.Megaparsec.Char+import Data.Void (Void)+import qualified Text.Megaparsec.Char.Lexer as L++type Parser a = CMS.StateT ParserState (Parsec Void MT.Text) a++data ParserState = ParserState {+    stateParameters :: [MT.Text]+}++expandType+  :: (MDoc -> [MDoc] -> MDoc) -- ^ make function type+  -> (PVar -> [(PVar, MDoc)] -> MDoc) -- ^ make record type+  -> TypeP+  -> MDoc+expandType mkfun mkrec t0 = f t0 where+  f :: TypeP -> MDoc+  f (VarP (PV _ _ v)) = pretty v+  f t@(FunP t1 _) = mkfun (f t1) (map f (decomposeFull t))+  f (ArrP (PV _ _ v) ts) = pretty $ expandMacro v (map (render . f) ts)+  f (NamP _ v _ entries) = mkrec v [(k, f t) | (k, t) <- entries]+  f (UnkP _) = error "Cannot build unsolved type"++expandMacro :: MT.Text -> [MT.Text] -> MT.Text+expandMacro t [] = t+expandMacro t ps =+  case runParser+         (CMS.runStateT (pBase <* eof) (ParserState ps))+         "typemacro"+         t of+    Left err -> error (show err)+    Right (es, _) -> es++many1 :: Parser a -> Parser [a]+many1 p = do+  x <- p+  xs <- many p+  return (x : xs)++pBase :: Parser MT.Text+pBase = MT.concat <$> many1 (pChar <|> pMacro)++pChar :: Parser MT.Text+pChar = MT.pack <$> many1 (noneOf ['$'])++pMacro :: Parser MT.Text+pMacro = do+  xs <- CMS.gets stateParameters+  _ <- string "$"+  n <- L.decimal+  -- index is 1-based+  let i = n - 1+  return (xs !! i)
+ library/Morloc/CodeGenerator/Grammars/Translator/Cpp.hs view
@@ -0,0 +1,775 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++{-|+Module      : Morloc.CodeGenerator.Grammars.Translator.Cpp+Description : C++ translator+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.CodeGenerator.Grammars.Translator.Cpp+  ( +    translate+  , preprocess+  ) where++import Morloc.CodeGenerator.Namespace+import Morloc.CodeGenerator.Internal (typeP2typeM)+import Morloc.CodeGenerator.Serial ( isSerializable+                                   , prettySerialOne+                                   , serialAstToType+                                   , serialAstToType'+                                   , shallowType+                                   )+import Morloc.CodeGenerator.Grammars.Common+import qualified Morloc.CodeGenerator.Grammars.Translator.Source.CppInternals as Src+import Morloc.Data.Doc+import Morloc.Quasi+import qualified Morloc.System as MS+import Morloc.CodeGenerator.Grammars.Macro (expandMacro)+import qualified Morloc.Monad as MM+import qualified Data.Map as Map++-- | @RecEntry@ stores the common name, keys, and types of records that are not+-- imported from C++ source. These records are generated as structs in the C+++-- pool. @unifyRecords@ takes all such records and "unifies" ones with the same+-- name and keys. The unified records may have different types, but they will+-- all be instances of the same generic struct. That is, any fields that differ+-- between instances will be made generic.+data RecEntry = RecEntry {+    recName :: MDoc -- ^ the automatically generated name for this anonymous type+  , recFields :: [( PVar -- The field key+                  , Maybe TypeP -- The field type if not generic+                  )]+}++-- | @RecMap@ is used to lookup up the struct name shared by all records that+-- are not imported from C++ source.+type RecMap = [((PVar, [PVar]), RecEntry)]++-- tree rewrites+preprocess :: ExprM Many -> MorlocMonad (ExprM Many)+preprocess = invertExprM++translate :: [Source] -> [ExprM One] -> MorlocMonad MDoc+translate srcs es = do+  -- translate sources+  includeDocs <- mapM+    translateSource+    (unique . catMaybes . map srcPath $ srcs)++  -- diagnostics+  liftIO . putDoc . vsep $ "-- C++ translation --" : map prettyExprM es++  let recmap = unifyRecords . conmap collectRecords $ es+      (autoDecl, autoSerial) = generateAnonymousStructs recmap+      (srcDecl, srcSerial) = generateSourcedSerializers es+      dispatch = makeDispatch es+      signatures = map (makeSignature recmap) es+      serializationCode = autoDecl ++ srcDecl ++ autoSerial ++ srcSerial++  -- translate each manifold tree, rooted on a call from nexus or another pool+  mDocs <- mapM (translateManifold recmap) es++  -- create and return complete pool script+  return $ makeMain includeDocs signatures serializationCode mDocs dispatch++letNamer :: Int -> MDoc+letNamer i = "a" <> viaShow i++manNamer :: Int -> MDoc+manNamer i = "m" <> viaShow i++bndNamer :: Int -> MDoc+bndNamer i = "x" <> viaShow i++serialType :: MDoc+serialType = "std::string"++makeSignature :: RecMap -> ExprM One -> MDoc+makeSignature recmap e0@(ManifoldM _ _ _) = vsep (f e0) where+  f :: ExprM One -> [MDoc]+  f (ManifoldM (metaId->i) args e) =+    let t = typeOfExprM e+        sig = showTypeM recmap t <+> manNamer i <> tupled (map (makeArg recmap) args) <> ";"+    in sig : f e+  f (LetM _ e1 e2) = f e1 ++ f e2+  f (AppM e es) = f e ++ conmap f es+  f (LamM _ e) = f e+  f (AccM e _) = f e+  f (ListM _ es) = conmap f es+  f (TupleM _ es) = conmap f es+  f (RecordM _ entries) = conmap f (map snd entries)+  f (SerializeM _ e) = f e+  f (DeserializeM _ e) = f e+  f (ReturnM e) = f e+  f _ = []+makeSignature _ _ = error "Expected ManifoldM"++makeArg :: RecMap -> Argument -> MDoc+makeArg _ (SerialArgument i _) = serialType <+> bndNamer i+makeArg recmap (NativeArgument i c) = showTypeM recmap (Native c) <+> bndNamer i+makeArg _ (PassThroughArgument i) = serialType <+> bndNamer i++argName :: Argument -> MDoc+argName (SerialArgument i _) = bndNamer i+argName (NativeArgument i _) = bndNamer i+argName (PassThroughArgument i) = bndNamer i++tupleKey :: Int -> MDoc -> MDoc+tupleKey i v = [idoc|std::get<#{pretty i}>(#{v})|]++recordAccess :: MDoc -> MDoc -> MDoc+recordAccess record field = record <> "." <> field++-- TLDR: Use `#include "foo.h"` rather than `#include <foo.h>`+-- Include statements in C can be either wrapped in angle brackets (e.g.,+-- `<stdio.h>`) or in quotes (e.g., `"myfile.h"`). The difference between these+-- is implementation specific. I currently use the GCC compiler. For quoted+-- strings, it first searches relative to the working directory and then, if+-- nothing is found, searches system files. For angle brackets, it searches+-- only system files: <https://gcc.gnu.org/onlinedocs/cpp/Search-Path.html>. So+-- quoting seems more reasonable, for now. This might change only if I start+-- loading the morloc libraries into the system directories (which might be+-- reasonable), though still, quotes would work.+--+-- UPDATE: The build system will now read the source paths from the Script+-- object and write an `-I${MORLOC_HOME}/lib/${MORLOC_PACKAGE}` argument for+-- g++. This will tell g++ where to look for headers. So now in the generated+-- source code I can just write the basename. This makes the generated code+-- neater (no hard-coded local paths), but now the g++ compiler will search+-- through all the module paths for each file, which introduces the possibility+-- of name conflicts.+translateSource+  :: Path -- ^ Path to a header (e.g., `$MORLOC_HOME/lib/foo.h`)+  -> MorlocMonad MDoc+translateSource path = return $+  "#include" <+> (dquotes . pretty . MS.takeFileName) path+++serialize+  :: RecMap+  -> Int -- The let index `i`+  -> MDoc -- A variable name pointing to e1+  -> SerialAST One+  -> MorlocMonad [MDoc]+serialize recmap letIndex datavar0 s0 = do+  (x, before) <- serialize' datavar0 s0+  t0 <- (showTypeM recmap . Native) <$> serialAstToType s0+  let schemaName = [idoc|#{letNamer letIndex}_schema|]+      schema = [idoc|#{t0} #{schemaName};|]+      final = [idoc|#{serialType} #{letNamer letIndex} = serialize(#{x}, #{schemaName});|]+  return (before ++ [schema, final])++  where+    serialize'+      :: MDoc -- a variable name that stores the data described by the SerialAST object+      -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    serialize' v s+      | isSerializable s = return (v, [])+      | otherwise = construct v s++    construct :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    construct v (SerialPack _ (One (p, s))) = do+      unpacker <- case typePackerReverse p of+        [] -> MM.throwError . SerializationError $ "No unpacker found"+        (src:_) -> return . pretty . srcName $ src+      serialize' [idoc|#{unpacker}(#{v})|] s++    construct v lst@(SerialList s) = do+      idx <- fmap pretty $ MM.getCounter+      t <- serialAstToType lst+      let v' = "s" <> idx +          decl = [idoc|#{showType recmap t} #{v'};|]+      (x, before) <- serialize' [idoc|#{v}[i#{idx}]|] s+      let push = [idoc|#{v'}.push_back(#{x});|]+          loop  = block 4 [idoc|for(size_t i#{idx} = 0; i#{idx} < #{v}.size(); i#{idx}++)|] +                         (vsep (before ++ [push]))+      return (v', [decl, loop])++    construct v tup@(SerialTuple ss) = do+      (ss', befores) <- fmap unzip $ zipWithM (\i s -> serialize' (tupleKey i v) s) [0..] ss+      idx <- fmap pretty $ MM.getCounter+      t <- serialAstToType tup+      let v' = "s" <> idx+          x = [idoc|#{showType recmap t} #{v'} = std::make_tuple#{tupled ss'};|]+      return (v', concat befores ++ [x]);++    construct v rec@(SerialObject NamRecord _ _ rs) = do+      (ss', befores) <- fmap unzip $ mapM (\(PV _ _ k,s) -> serialize' (recordAccess v (pretty k)) s) rs+      idx <- fmap pretty $ MM.getCounter+      t <- (showType recmap) <$> serialAstToType rec+      let v' = "s" <> idx+          decl = encloseSep "{" "}" "," ss'+          x = [idoc|#{t} #{v'} = #{decl};|]+      return (v', concat befores ++ [x]);++    construct _ s = MM.throwError . SerializationError . render+      $ "construct: " <> prettySerialOne s++-- reverse of serialize, parameters are the same+deserialize :: RecMap -> Int -> MDoc -> MDoc -> SerialAST One -> MorlocMonad [MDoc]+deserialize recmap letIndex typestr0 varname0 s0+  | isSerializable s0 = do +      let schemaName = [idoc|#{letNamer letIndex}_schema|]+          schema = [idoc|#{typestr0} #{schemaName};|]+          deserializing = [idoc|#{typestr0} #{letNamer letIndex} = deserialize(#{varname0}, #{schemaName});|]+      return [schema, deserializing]+  | otherwise = do+      idx <- fmap pretty $ MM.getCounter+      t <- serialAstToType s0+      let rawtype = showType recmap $ t+          schemaName = [idoc|#{letNamer letIndex}_schema|]+          rawvar = "s" <> idx+          schema = [idoc|#{rawtype} #{schemaName};|]+          deserializing = [idoc|#{rawtype} #{rawvar} = deserialize(#{varname0}, #{schemaName});|]+      (x, before) <- construct rawvar s0+      let final = [idoc|#{typestr0} #{letNamer letIndex} = #{x};|]+      return ([schema, deserializing] ++ before ++ [final])++  where+    check :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    check v s+      | isSerializable s = return (v, [])+      | otherwise = construct v s++    construct :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    construct v (SerialPack _ (One (p, s'))) = do+      packer <- case typePackerForward p of+        [] -> MM.throwError . SerializationError $ "No packer found"+        (x:_) -> return . pretty . srcName $ x+      (x, before) <- check v s'+      let deserialized = [idoc|#{packer}(#{x})|]+      return (deserialized, before)++    construct v lst@(SerialList s) = do+      idx <- fmap pretty $ MM.getCounter+      t <- fmap (showType recmap) $ shallowType lst+      let v' = "s" <> idx +          decl = [idoc|#{t} #{v'};|]+      (x, before) <- check [idoc|#{v}[i#{idx}]|] s+      let push = [idoc|#{v'}.push_back(#{x});|]+          loop = block 4 [idoc|for(size_t i#{idx} = 0; i#{idx} < #{v}.size(); i#{idx}++)|] +                         (vsep (before ++ [push]))+      return (v', [decl, loop])++    construct v tup@(SerialTuple ss) = do+      idx <- fmap pretty $ MM.getCounter+      (ss', befores) <- fmap unzip $ zipWithM (\i s -> check (tupleKey i v) s) [0..] ss+      t <- shallowType tup+      let v' = "s" <> idx+          x = [idoc|#{showType recmap $ t} #{v'} = std::make_tuple#{tupled ss'};|]+      return (v', concat befores ++ [x]);++    construct v rec@(SerialObject NamRecord _ _ rs) = do+      idx <- fmap pretty $ MM.getCounter+      (ss', befores) <- fmap unzip $ mapM (\(PV _ _ k,s) -> check (recordAccess v (pretty k)) s) rs+      t <- fmap (showType recmap) $ shallowType rec+      let v' = "s" <> idx+          decl = encloseSep "{" "}" "," ss'+          x = [idoc|#{t} #{v'} = #{decl};|]+      return (v', concat befores ++ [x]);++    construct _ s = MM.throwError . SerializationError . render+      $ "deserializeDescend: " <> prettySerialOne s++translateManifold :: RecMap -> ExprM One -> MorlocMonad MDoc+translateManifold recmap m0@(ManifoldM _ args0 _) = do+  MM.startCounter+  (vsep . punctuate line . (\(x,_,_)->x)) <$> f args0 m0+  where++  f :: [Argument]+    -> ExprM One+    -> MorlocMonad+       ( [MDoc] -- the collection of final manifolds+       , MDoc -- a call tag for this expression+       , [MDoc] -- a list of statements that should precede this assignment+       )++  f args (LetM i (SerializeM s e1) e2) = do+    (ms1, e1', ps1) <- f args e1+    (ms2, e2', ps2) <- f args e2+    serialized <- serialize recmap i e1' s+    return (ms1 ++ ms2, vsep $ ps1 ++ ps2 ++ serialized ++ [e2'], [])++  f args (LetM i (DeserializeM s e1) e2) = do+    (ms1, e1', ps1) <- f args e1+    (ms2, e2', ps2) <- f args e2+    t <- showNativeTypeM recmap (typeOfExprM e1)+    deserialized <- deserialize recmap i t e1' s+    return (ms1 ++ ms2, vsep $ ps1 ++ ps2 ++ deserialized ++ [e2'], [])++  f _ (SerializeM _ _) = MM.throwError . SerializationError+    $ "SerializeM should only appear in an assignment"++  f _ (DeserializeM _ _) = MM.throwError . SerializationError+    $ "DeserializeM should only appear in an assignment"++  f args (LetM i e1 e2) = do+    (ms1', e1', ps1) <- (f args) e1+    (ms2', e2', ps2) <- (f args) e2+    let t = showTypeM recmap (typeOfExprM e1)+        ps = ps1 ++ ps2 ++ [[idoc|#{t} #{letNamer i} = #{e1'};|], e2']+    return (ms1' ++ ms2', vsep ps, [])++  f args (AppM (SrcM (Function inputs output) src) xs) = do+    (mss', xs', pss) <- mapM (f args) xs |>> unzip3+    let+        name = pretty $ srcName src+        mangledName = name <> "_fun"+        inputBlock = cat (punctuate "," (map (showTypeM recmap) inputs))+        sig = [idoc|#{showTypeM recmap output}(*#{mangledName})(#{inputBlock}) = &#{name};|]+    return (concat mss', mangledName <> tupled xs', sig : concat pss)++  f _ (AppM _ _) = error "Can only apply functions"++  f _ (SrcM _ src) = return ([], pretty $ srcName src, [])++  f pargs (ManifoldM (metaId->i) args e) = do+    (ms', body, ps1) <- f args e+    let t = typeOfExprM e+        decl = showTypeM recmap t <+> manNamer i <> tupled (map (makeArg recmap) args)+        mdoc = block 4 decl body+        mname = manNamer i+    (call, ps2) <- case (splitArgs args pargs, nargsTypeM t) of+      ((rs, []), _) -> return (mname <> tupled (map (bndNamer . argId) rs), [])+      (([], _ ), _) -> return (mname, [])+      ((rs, vs), _) -> do+        let v = mname <> "_fun"+        lhs <- stdFunction recmap t vs |>> (\x -> x <+> v)+        castFunction <- staticCast recmap t args mname+        let vs' = take+                  (length vs)+                  (map (\j -> "std::placeholders::_" <> viaShow j) ([1..] :: [Int]))+            rs' = map (bndNamer . argId) rs+            rhs = stdBind $ castFunction : (rs' ++ vs')+            sig = nest 4 (vsep [lhs <+> "=", rhs]) <> ";"+        return (v, [sig])+    return (mdoc : ms', call, ps1 ++ ps2)++  f _ (PoolCallM _ _ cmds args) = do+    let bufDef = "std::ostringstream s;"+        callArgs = map dquotes cmds ++ map argName args+        cmd = "s << " <> cat (punctuate " << \" \" << " callArgs) <> ";"+        call = [idoc|foreign_call(s.str())|] +    return ([], call, [bufDef, cmd])++  f _ (ForeignInterfaceM _ _) = MM.throwError . CallTheMonkeys $+    "Foreign interfaces should have been resolved before passed to the translators"++  f _ (LamM _ _) = undefined++  f args (AccM e k) = do+    (ms, e', ps) <- f args e+    return (ms, e' <> "." <> pretty k, ps)++  f args (ListM _ es) = do+    (mss', es', pss) <- mapM (f args) es |>> unzip3+    let x' = encloseSep "{" "}" "," es'+    return (concat mss', x', concat pss)++  f args (TupleM _ es) = do+    (mss', es', pss) <- mapM (f args) es |>> unzip3+    return (concat mss', "std::make_tuple" <> tupled es', concat pss)++  f args (RecordM c entries) = do+    (mss', es', pss) <- mapM (f args . snd) entries |>> unzip3+    idx <- fmap pretty $ MM.getCounter+    let t = showTypeM recmap c+        v' = "a" <> idx+        decl = encloseSep "{" "}" "," es'+        x = [idoc|#{t} #{v'} = #{decl};|]+    return (concat mss', v', concat pss ++ [x])++  f _ (BndVarM _ i) = return ([], bndNamer i, [])+  f _ (LetVarM _ i) = return ([], letNamer i, [])+  f _ (LogM _ x) = return ([], if x then "true" else "false", [])+  f _ (NumM _ x) = return ([], viaShow x, [])+  f _ (StrM _ x) = return ([], dquotes $ pretty x, [])+  f _ (NullM _) = return ([], "null", [])++  f args (ReturnM e) = do+    (ms, e', ps) <- f args e+    return (ms, "return(" <> e' <> ");", ps)+translateManifold _ _ = error "Every ExprM object must start with a Manifold term"++stdFunction :: RecMap -> TypeM -> [Argument] -> MorlocMonad MDoc+stdFunction recmap t args = +  let argList = cat (punctuate "," (map (argTypeM recmap) args))+  in return [idoc|std::function<#{showTypeM recmap t}(#{argList})>|]++stdBind :: [MDoc] -> MDoc+stdBind xs = [idoc|std::bind(#{args})|] where+  args = cat (punctuate "," xs)++staticCast :: RecMap -> TypeM -> [Argument] -> MDoc -> MorlocMonad MDoc+staticCast recmap t args name = do+  let output = showTypeM recmap t+      inputs = map (argTypeM recmap) args+      argList = cat (punctuate "," inputs)+  return $ [idoc|static_cast<#{output}(*)(#{argList})>(&#{name})|]++argTypeM :: RecMap -> Argument -> MDoc+argTypeM _ (SerialArgument _ _) = serialType+argTypeM recmap (NativeArgument _ c) = showType recmap c+argTypeM _ (PassThroughArgument _) = serialType++makeDispatch :: [ExprM One] -> MDoc+makeDispatch ms = block 4 "switch(cmdID)" (vsep (map makeCase ms))+  where+    makeCase :: ExprM One -> MDoc+    makeCase (ManifoldM (metaId->i) args _) =+      let args' = take (length args) $ map (\j -> "argv[" <> viaShow j <> "]") ([2..] :: [Int])+      in+        (nest 4 . vsep)+          [ "case" <+> viaShow i <> ":"+          , "result = " <> manNamer i <> tupled args' <> ";"+          , "break;"+          ]+    makeCase _ = error "Every ExprM must start with a manifold object"++showType :: RecMap -> TypeP -> MDoc+showType _ (UnkP _) = serialType+showType _ (VarP (PV _ _ v)) = pretty v +showType recmap t@(FunP _ _) = showTypeM recmap (typeP2typeM t)+showType recmap (ArrP (PV _ _ v) ts) = pretty $ expandMacro v (map (render . showType recmap) ts)+showType recmap (NamP _ v@(PV _ _ "struct") _ rs) =+  -- handle autogenerated structs+  case lookup (v, map fst rs) recmap of+    (Just rec) -> recName rec <> typeParams recmap (zip (map snd (recFields rec)) (map snd rs))+    Nothing -> error "Should not happen"+showType recmap (NamP _ (PV _ _ s) ps _) =+    pretty s <>  encloseSep "<" ">" "," (map (showType recmap) ps)++typeParams :: RecMap -> [(Maybe TypeP, TypeP)] -> MDoc+typeParams recmap ts+  = case [showTypeM recmap (Native t) | (Nothing, t) <- ts] of+      [] -> ""+      ds -> encloseSep "<" ">" "," ds++showTypeM :: RecMap -> TypeM -> MDoc+showTypeM _ Passthrough = serialType+showTypeM _ (Serial _) = serialType+showTypeM recmap (Native t) = showType recmap t+showTypeM recmap (Function ts t)+  = "std::function<" <> showTypeM recmap t+  <> "(" <> cat (punctuate "," (map (showTypeM recmap) ts)) <> ")>"++-- for use in making schema, where the native type is needed+showNativeTypeM :: RecMap -> TypeM -> MorlocMonad MDoc+showNativeTypeM recmap (Serial t) = return $ showTypeM recmap (Native t)+showNativeTypeM recmap (Native t) = return $ showTypeM recmap (Native t)+showNativeTypeM _ _ = MM.throwError . OtherError $ "Expected a native or serialized type"+++collectRecords :: ExprM One -> [(PVar, GMeta, [(PVar, TypeP)])]+collectRecords e0 = f (gmetaOf e0) e0 where+  f _ (ManifoldM m _ e) = f m e+  f m (ForeignInterfaceM t e) = cleanRecord m t ++ f m e+  f m (PoolCallM t _ _ _) = cleanRecord m t+  f m (LetM _ e1 e2) = f m e1 ++ f m e2+  f m (AppM e es) = f m e ++ conmap (f m) es+  f m (LamM _ e) = f m e+  f m (AccM e _) = f m e+  f m (ListM t es) = cleanRecord m t ++ conmap (f m) es+  f m (TupleM t es) = cleanRecord m t ++ conmap (f m) es+  f m (RecordM t rs) = cleanRecord m t ++ conmap (f m . snd) rs+  f m (SerializeM s e)+    = cleanRecord m (Native (serialAstToType' s)) ++ f m e+  f m (DeserializeM s e)+    = cleanRecord m (Serial (serialAstToType' s)) ++ f m e+  f m (ReturnM e) = f m e+  f m (BndVarM t _) = cleanRecord m t+  f m (LetVarM t _) = cleanRecord m t+  f _ _ = []++cleanRecord :: GMeta -> TypeM -> [(PVar, GMeta, [(PVar, TypeP)])]+cleanRecord m tm = case typeOfTypeM tm of+  (Just t) -> toRecord t+  Nothing -> []+  where+    toRecord :: TypeP -> [(PVar, GMeta, [(PVar, TypeP)])]+    toRecord (UnkP _) = []+    toRecord (VarP _) = []+    toRecord (FunP t1 t2) = toRecord t1 ++ toRecord t2+    toRecord (ArrP _ ts) = conmap toRecord ts+    toRecord (NamP _ v@(PV _ _ "struct") _ rs) = (v, m, rs) : conmap toRecord (map snd rs)+    toRecord (NamP _ _ _ rs) = conmap toRecord (map snd rs)++-- unify records with the same name/keys+unifyRecords+  :: [(PVar -- The "v" in (NamP _ v@(PV _ _ "struct") _ rs)+     , GMeta -- The GMeta object stored in the records ManifoldM term+     , [(PVar, TypeP)]) -- key/type terms for this record+     ] -> RecMap+unifyRecords xs+  = zipWith (\i ((v,ks),es) -> ((v,ks), RecEntry (structName i v) es)) [1..]+  . map (\((v,m,ks), rss) -> ((v,ks), [unifyField m fs | fs <- transpose rss]))+  . map (\((v,ks), rss) -> ((v, fst (head rss),ks), map snd rss))+  -- [((record_name, record_keys), [(GMeta, [(key,type)])])]+  -- associate unique pairs of record name and keys with their edge types+  . groupSort+  . unique+  $ [((v, map fst es), (m, es)) | (v, m, es) <- xs]++structName :: Int -> PVar -> MDoc+structName i (PV _ (Just v1) "struct") = "mlc_" <> pretty v1 <> "_" <> pretty i +structName _ (PV _ _ v) = pretty v++unifyField :: GMeta -> [(PVar, TypeP)] -> (PVar, Maybe TypeP)+unifyField _ [] = error "Empty field"+unifyField _ rs@((v,_):_)+  | not (all ((==) v) (map fst rs))+      = error $ "Bad record - unequal fields: " <> show (unique rs)+  | otherwise = case unique (map snd rs) of+      [t] -> (v, Just t)+      _ -> (v, Nothing)++generateAnonymousStructs :: RecMap -> ([MDoc],[MDoc])+generateAnonymousStructs recmap+  = (\xs -> (conmap fst xs, conmap snd xs))+  . map (makeSerializers recmap)+  . reverse+  . map snd+  $ recmap++makeSerializers :: RecMap -> RecEntry -> ([MDoc],[MDoc])+makeSerializers recmap rec+  = ([structDecl, serialDecl, deserialDecl], [serializer, deserializer])+  where+    templateTerms = zipWith (<>) (repeat "T") (map pretty ([1..] :: [Int]))+    rs' = zip templateTerms (recFields rec)++    params = [t | (t, (_, Nothing)) <- rs']+    rname = recName rec+    rtype = rname <> recordTemplate [v | (v, (_, Nothing)) <- rs']+    fields = [(pretty k, maybe t (showType recmap) v') | (t, (PV _ _ k, v')) <- rs']++    structDecl = structTypedefTemplate params rname fields+    serialDecl = serialHeaderTemplate params rtype+    deserialDecl = deserialHeaderTemplate params rtype++    serializer = serializerTemplate params rtype fields+    deserializer = deserializerTemplate False params rtype fields++++generateSourcedSerializers :: [ExprM One] -> ([MDoc],[MDoc])+generateSourcedSerializers+  = foldl groupQuad ([],[])+  . Map.elems+  . Map.mapMaybeWithKey makeSerial+  . foldl collect' Map.empty+  where+    collect'+      :: Map.Map TVar (Type, [TVar])+      -> ExprM One+      -> Map.Map TVar (Type, [TVar])+    collect' m (ManifoldM g _ e) = collect' (Map.union m (metaTypedefs g)) e+    collect' m (ForeignInterfaceM _ e) = collect' m e+    collect' m (LetM _ e1 e2) = Map.union (collect' m e1) (collect' m e2)+    collect' m (AppM e es) = Map.unions $ collect' m e : map (collect' m) es+    collect' m (LamM _ e) = collect' m e+    collect' m (AccM e _) = collect' m e+    collect' m (ListM _ es) = Map.unions $ map (collect' m) es+    collect' m (TupleM _ es) = Map.unions $ map (collect' m) es+    collect' m (RecordM _ entries) = Map.unions $ map (collect' m) (map snd entries)+    collect' m (SerializeM _ e) = collect' m e+    collect' m (DeserializeM _ e) = collect' m e+    collect' m (ReturnM e) = collect' m e+    collect' m _ = m++    groupQuad :: ([a],[a]) -> (a, a, a, a) -> ([a],[a])+    groupQuad (xs,ys) (x1, y1, x2, y2) = (x1:x2:xs, y1:y2:ys)++    makeSerial :: TVar -> (Type, [TVar]) -> Maybe (MDoc, MDoc, MDoc, MDoc)+    makeSerial _ (NamT _ (TV _ "struct") _ _, _) = Nothing+    makeSerial (TV (Just CppLang) _) (NamT r (TV _ v) _ rs, ps)+      = Just (serialDecl, serializer, deserialDecl, deserializer) where++        templateTerms = ["T" <> pretty p | (TV _ p) <- ps]++        params = map (\p -> "T" <> pretty (unTVar p)) ps+        rtype = pretty v <> recordTemplate templateTerms+        fields = [(pretty k, showDefType ps t) | (k, t) <- rs]++        serialDecl = serialHeaderTemplate params rtype+        deserialDecl = deserialHeaderTemplate params rtype++        serializer = serializerTemplate params rtype fields++        deserializer = deserializerTemplate (r == NamObject) params rtype fields+    makeSerial _ _ = Nothing++    showDefType :: [TVar] -> Type -> MDoc +    showDefType ps (UnkT v@(TV _ s))+      | elem v ps = "T" <> pretty s+      | otherwise = pretty s+    showDefType ps (VarT v@(TV _ s))+      | elem v ps = "T" <> pretty s+      | otherwise = pretty s+    showDefType _ (FunT _ _) = error "Cannot serialize functions"+    showDefType ps (ArrT (TV _ v) ts) = pretty $ expandMacro v (map (render . showDefType ps) ts)+    showDefType ps (NamT _ (TV _ v) ts _)+      = pretty v <> encloseSep "<" ">" "," (map (showDefType ps) ts)+++makeTemplateHeader :: [MDoc] -> MDoc+makeTemplateHeader [] = ""+makeTemplateHeader ts = "template" <+> encloseSep "<" ">" "," ["class" <+> t | t <- ts]++recordTemplate :: [MDoc] -> MDoc+recordTemplate [] = ""+recordTemplate ts = encloseSep "<" ">" "," ts++++-- Example+-- > template <class T>+-- > struct Person+-- > {+-- >     std::vector<std::string> name;+-- >     std::vector<T> info;+-- > };+structTypedefTemplate+  :: [MDoc] -- template parameters (e.g., ["T"])+  -> MDoc -- the name of the structure (e.g., "Person")+  -> [(MDoc, MDoc)] -- key and type for all fields+  -> MDoc -- structure definition+structTypedefTemplate params rname fields = vsep [template, struct] where+  template = makeTemplateHeader params+  struct = block 4 ("struct" <+> rname)+                   (vsep [t <+> k <> ";" | (k,t) <- fields]) <> ";"++++-- Example+-- > template <class T>+-- > std::string serialize(person<T> x, person<T> schema);+serialHeaderTemplate :: [MDoc] -> MDoc -> MDoc+serialHeaderTemplate params rtype = vsep [template, prototype]+  where+  template = makeTemplateHeader params+  prototype = [idoc|std::string serialize(#{rtype} x, #{rtype} schema);|]++++-- Example:+-- > template <class T>+-- > bool deserialize(const std::string json, size_t &i, person<T> &x);+deserialHeaderTemplate :: [MDoc] -> MDoc -> MDoc+deserialHeaderTemplate params rtype = vsep [template, prototype]+  where+  template = makeTemplateHeader params+  prototype = [idoc|bool deserialize(const std::string json, size_t &i, #{rtype} &x);|]++++serializerTemplate+  :: [MDoc] -- template parameters+  -> MDoc -- type of thing being serialized+  -> [(MDoc, MDoc)] -- key and type for all fields+  -> MDoc -- output serializer function+serializerTemplate params rtype fields = [idoc|+#{makeTemplateHeader params}+std::string serialize(#{rtype} x, #{rtype} schema){+    #{schemata}+    std::ostringstream json;+    json << "{" << #{align $ vsep (punctuate " << ',' <<" writers)} << "}";+    return json.str();+}+|] where+  schemata = align $ vsep (map (\(k,t) -> t <+> k <> "_" <> ";") fields)+  writers = map (\(k,_) -> dquotes ("\\\"" <> k <> "\\\"" <> ":")+          <+> "<<" <+> [idoc|serialize(x.#{k}, #{k}_)|] ) fields++++deserializerTemplate+  :: Bool -- build object with constructor+  -> [MDoc] -- ^ template parameters+  -> MDoc -- ^ type of thing being deserialized+  -> [(MDoc, MDoc)] -- ^ key and type for all fields+  -> MDoc -- ^ output deserializer function+deserializerTemplate isObj params rtype fields+  = [idoc|+#{makeTemplateHeader params}+bool deserialize(const std::string json, size_t &i, #{rtype} &x){+    #{schemata}+    try {+        whitespace(json, i);+        if(! match(json, "{", i))+            throw 1;+        whitespace(json, i);+        #{fieldParsers}+        if(! match(json, "}", i))+            throw 1;+        whitespace(json, i);+    } catch (int e) {+        return false;+    }+    #{assign}+    return true;+}+|] where+  schemata = align $ vsep (map (\(k,t) -> t <+> k <> "_" <> ";") fields)+  fieldParsers = align $ vsep (punctuate parseComma (map (makeParseField . fst) fields))+  values = [k <> "_" | (k,_) <- fields]+  assign = if isObj+           then [idoc|#{rtype} y#{tupled values}; x = y;|]+           else let obj = encloseSep "{" "}" "," values+                in [idoc|#{rtype} y = #{obj}; x = y;|]++parseComma = [idoc|+if(! match(json, ",", i))+    throw 800;+whitespace(json, i);|]++makeParseField :: MDoc -> MDoc+makeParseField field = [idoc|+if(! match(json, "\"#{field}\"", i))+    throw 1;+whitespace(json, i);+if(! match(json, ":", i))+    throw 1;+whitespace(json, i);+if(! deserialize(json, i, #{field}_))+    throw 1;+whitespace(json, i);|]++++makeMain :: [MDoc] -> [MDoc] -> [MDoc] -> [MDoc] -> MDoc -> MDoc+makeMain includes signatures serialization manifolds dispatch = [idoc|#include <string>+#include <iostream>+#include <sstream>+#include <functional>+#include <vector>+#include <string>+#include <algorithm> // for std::transform++#{Src.foreignCallFunction}++#{Src.serializationHandling}++#{vsep includes}++#{vsep signatures}++#{vsep serialization}++#{vsep manifolds}++int main(int argc, char * argv[])+{+    int cmdID;+    #{serialType} result;+    cmdID = std::stoi(argv[1]);+    #{dispatch}+    std::cout << result << std::endl;+    return 0;+}+|]
+ library/Morloc/CodeGenerator/Grammars/Translator/Python3.hs view
@@ -0,0 +1,387 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++{-|+Module      : Morloc.CodeGenerator.Grammars.Translator.Python3+Description : Python3 translator+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.CodeGenerator.Grammars.Translator.Python3+  (+    translate+  , preprocess+  ) where++import Morloc.CodeGenerator.Namespace+import Morloc.CodeGenerator.Serial (isSerializable, prettySerialOne, serialAstToType)+import Morloc.CodeGenerator.Grammars.Common+import Morloc.Data.Doc+import Morloc.Quasi+import qualified Morloc.Config as MC+import qualified Morloc.Monad as MM+import qualified Morloc.Data.Text as MT+import qualified System.FilePath as SF+import qualified Data.Char as DC++-- tree rewrites+preprocess :: ExprM Many -> MorlocMonad (ExprM Many)+preprocess = invertExprM++translate :: [Source] -> [ExprM One] -> MorlocMonad MDoc+translate srcs es = do+  -- setup library paths+  lib <- fmap pretty $ MM.asks MC.configLibrary++  -- translate sources+  includeDocs <- mapM+    translateSource+    (unique . catMaybes . map srcPath $ srcs)++  -- diagnostics+  liftIO . putDoc $ (vsep $ map prettyExprM es)++  -- translate each manifold tree, rooted on a call from nexus or another pool+  mDocs <- mapM translateManifold es++  -- make code for dispatching to manifolds+  let dispatch = makeDispatch es++  return $ makePool lib includeDocs mDocs dispatch++-- create an internal variable based on a unique id+letNamer :: Int -> MDoc+letNamer i = "a" <> viaShow i++-- create namer for manifold positional arguments+bndNamer :: Int -> MDoc+bndNamer i = "x" <> viaShow i++-- create a name for a manifold based on a unique id+manNamer :: Int -> MDoc+manNamer i = "m" <> viaShow i++-- FIXME: should definitely use namespaces here, not `import *`+translateSource :: Path -> MorlocMonad MDoc+translateSource (Path s) = do+  (Path lib) <- MM.asks configLibrary+  let moduleStr = pretty+                . MT.liftToText (map DC.toLower)+                . MT.replace "/" "."+                . MT.stripPrefixIfPresent "/" -- strip the leading slash (if present)+                . MT.stripPrefixIfPresent "./" -- no path if relative to here+                . MT.stripPrefixIfPresent lib  -- make the path relative to the library+                . MT.liftToText SF.dropExtensions+                $ s+  return $ "from" <+> moduleStr <+> "import *"++tupleKey :: Int -> MDoc -> MDoc+tupleKey i v = [idoc|#{v}[#{pretty i}]|]++selectAccessor :: NamType -> MT.Text -> MorlocMonad (MDoc -> MDoc -> MDoc)+selectAccessor NamTable  "dict" = return recordAccess+selectAccessor NamRecord _      = return recordAccess+selectAccessor NamTable  _      = return objectAccess+selectAccessor NamObject _      = return objectAccess++recordAccess :: MDoc -> MDoc -> MDoc+recordAccess record field = record <> "[" <> dquotes field <> "]"++objectAccess :: MDoc -> MDoc -> MDoc+objectAccess object field = object <> "." <> field++serialize :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+serialize v0 s0 = do+  (ms, v1) <- serialize' v0 s0+  t <- serialAstToType s0+  schema <- typeSchema t+  let v2 = "mlc_serialize" <> tupled [v1, schema]+  return (v2, ms)+  where+    serialize' :: MDoc -> SerialAST One -> MorlocMonad ([MDoc], MDoc)+    serialize' v s+      | isSerializable s = return ([], v)+      | otherwise = construct v s++    construct :: MDoc -> SerialAST One -> MorlocMonad ([MDoc], MDoc)+    construct v (SerialPack _ (One (p, s))) = do+      unpacker <- case typePackerReverse p of+        [] -> MM.throwError . SerializationError $ "No unpacker found"+        (src:_) -> return . pretty . srcName $ src+      serialize' [idoc|#{unpacker}(#{v})|] s++    construct v (SerialList s) = do+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+      (before, x) <- serialize' [idoc|i#{idx}|] s+      let push = [idoc|#{v'}.append(#{x})|]+          lst  = vsep [ [idoc|#{v'} = []|]+                      , nest 4 (vsep ([idoc|for i#{idx} in #{v}:|] : before ++ [push]))+                      ]+      return ([lst], v')++    construct v (SerialTuple ss) = do+      (befores, ss') <- fmap unzip $ zipWithM (\i s -> construct (tupleKey i v) s) [0..] ss+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+          x = [idoc|#{v'} = #{tupled ss'}|]+      return (concat befores ++ [x], v');++    construct v (SerialObject namType (PV _ _ constructor) _ rs) = do+      accessField <- selectAccessor namType constructor+      (befores, ss') <- fmap unzip $ mapM (\(PV _ _ k,s) -> serialize' (accessField v (pretty k)) s) rs+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+          entries = zipWith (\(PV _ _ key) val -> pretty key <> "=" <> val)+                            (map fst rs) ss'+          decl = [idoc|#{v'} = dict#{tupled (entries)};|]+      return (concat befores ++ [decl], v');++    construct _ s = MM.throwError . SerializationError . render+      $ "construct: " <> prettySerialOne s++deserialize :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+deserialize v0 s0+  | isSerializable s0 = do+      t <- serialAstToType s0+      schema <- typeSchema t+      let deserializing = [idoc|mlc_deserialize(#{v0}, #{schema});|]+      return (deserializing, [])+  | otherwise = do+      idx <- fmap pretty $ MM.getCounter+      t <- serialAstToType s0+      schema <- typeSchema t+      let rawvar = "s" <> idx+          deserializing = [idoc|#{rawvar} = mlc_deserialize(#{v0}, #{schema});|]+      (x, befores) <- check rawvar s0+      return (x, deserializing:befores)+  where+    check :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    check v s+      | isSerializable s = return (v, [])+      | otherwise = construct v s++    construct :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    construct v (SerialPack _ (One (p, s'))) = do+      packer <- case typePackerForward p of+        [] -> MM.throwError . SerializationError $ "No packer found"+        (x:_) -> return . pretty . srcName $ x+      (x, before) <- check v s'+      let deserialized = [idoc|#{packer}(#{x})|]+      return (deserialized, before)++    construct v (SerialList s) = do+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+      (x, before) <- check [idoc|i#{idx}|] s+      let push = [idoc|#{v'}.append(#{x});|]+          lst = vsep [ [idoc|#{v'} = [];|]+                     , nest 4 (vsep ([idoc|for i#{idx} in #{v}:|] : before ++ [push]))+                     ]+      return (v', [lst])++    construct v (SerialTuple ss) = do+      (ss', befores) <- fmap unzip $ zipWithM (\i s -> check (tupleKey i v) s) [0..] ss+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+          x = [idoc|#{v'} = #{tupled ss'};|]+      return (v', concat befores ++ [x]);++    construct v (SerialObject namType (PV _ _ constructor) _ rs) = do+      idx <- fmap pretty $ MM.getCounter+      accessField <- selectAccessor namType constructor+      (ss', befores) <- fmap unzip $ mapM (\(PV _ _ k,s) -> check (accessField v (pretty k)) s) rs+      let v' = "s" <> idx+          entries = zipWith (\(PV _ _ key) val -> pretty key <> "=" <> val)+                            (map fst rs) ss'+          decl = [idoc|#{v'} = #{pretty constructor}#{tupled entries};|]+      return (v', concat befores ++ [decl]);++    construct _ s = MM.throwError . SerializationError . render+      $ "deserializeDescend: " <> prettySerialOne s++++-- break a call tree into manifolds+translateManifold :: ExprM One -> MorlocMonad MDoc+translateManifold m0@(ManifoldM _ args0 _) = do+  MM.startCounter+  (vsep . punctuate line . (\(x,_,_)->x)) <$> f args0 m0+  where++  f :: [Argument]+    -> ExprM One+    -> MorlocMonad+        ( [MDoc] -- completely generated manifolds+        , MDoc   -- a tag for the returned expression+        , [MDoc] -- lines to precede the returned expression+        )+  f pargs m@(ManifoldM (metaId->i) args e) = do+    (ms', e', rs') <- f args e+    let mname = manNamer i+        def   = "def" <+> mname <> tupled (map makeArgument args) <> ":"+        mdoc = nest 4 (vsep $ def:rs' ++ [e'])+    call <- return $ case (splitArgs args pargs, nargsTypeM (typeOfExprM m)) of+      ((rs, []), _) -> mname <> tupled (map makeArgument rs) -- covers #1, #2 and #4+      (([], _ ), _) -> mname+      ((rs, vs), _) -> makeLambda vs (mname <> tupled (map makeArgument (rs ++ vs))) -- covers #5+    return (mdoc : ms', call, [])++  f _ (PoolCallM _ _ cmds args) = do+    let call = "_morloc_foreign_call(" <> list(map dquotes cmds ++ map makeArgument args) <> ")"+    return ([], call, [])++  f _ (ForeignInterfaceM _ _) = MM.throwError . CallTheMonkeys $+    "Foreign interfaces should have been resolved before passed to the translators"++  f args (LetM i e1 e2) = do+    (ms1', e1', rs1) <- (f args) e1+    (ms2', e2', rs2) <- (f args) e2+    let rs = rs1 ++ [ letNamer i <+> "=" <+> e1' ] ++ rs2+    return (ms1' ++ ms2', e2', rs)++  f args (AppM (SrcM _ src) xs) = do+    (mss', xs', rss') <- mapM (f args) xs |>> unzip3+    return (concat mss', pretty (srcName src) <> tupled xs', concat rss')++  f _ (AppM _ _) = error "Can only apply functions"++  f _ (SrcM _ src) = return ([], pretty (srcName src), [])++  f _ (LamM _ _) = undefined -- FIXME: this is defined in R++  f _ (BndVarM _ i) = return ([], bndNamer i, [])++  f _ (LetVarM _ i) = return ([], letNamer i, [])++  f args (AccM e k) = do+    (ms, e', ps) <- f args e+    x <- case typeOfTypeM (typeOfExprM e) of+      (Just (NamP r (PV _ _ v) _ _)) -> selectAccessor r v <*> pure e' <*> pure (pretty k)+      _ -> MM.throwError . CallTheMonkeys $ "Bad record access"+    return (ms, x, ps)++  f args (ListM _ es) = do+    (mss', es', rss') <- mapM (f args) es |>> unzip3+    return (concat mss', list es', concat rss')++  f args (TupleM _ es) = do+    (mss', es', rss') <- mapM (f args) es |>> unzip3+    return (concat mss', tupled es', concat rss')++  f args (RecordM _ entries) = do+    (mss', es', rss') <- mapM (f args . snd) entries |>> unzip3+    let entries' = zipWith (\k v -> pretty k <> "=" <> v) (map fst entries) es'+    return (concat mss', "OrderedDict" <> tupled entries', concat rss')++  f _ (LogM _ x) = return ([], if x then "True" else "False", [])++  f _ (NumM _ x) = return ([], viaShow x, [])++  f _ (StrM _ x) = return ([], dquotes $ pretty x, [])++  f _ (NullM _) = return ([], "None", [])++  f args (SerializeM s e) = do+    (ms, e', rs1) <- f args e+    (serialized, rs2) <- serialize e' s+    return (ms, serialized, rs1 ++ rs2)++  f args (DeserializeM s e) = do+    (ms, e', rs1) <- f args e+    (deserialized, rs2) <- deserialize e' s+    return (ms, deserialized, rs1 ++ rs2)++  f args (ReturnM e) = do+    (ms, e', rs) <- f args e+    return (ms, "return(" <> e' <> ")", rs)+translateManifold _ = error "Every ExprM object must start with a Manifold term"++++makeLambda :: [Argument] -> MDoc -> MDoc+makeLambda args body = "lambda" <+> hsep (punctuate "," (map makeArgument args)) <> ":" <+> body++makeArgument :: Argument -> MDoc+makeArgument (SerialArgument v _) = bndNamer v+makeArgument (NativeArgument v _) = bndNamer v+makeArgument (PassThroughArgument v) = bndNamer v++makeDispatch :: [ExprM One] -> MDoc+makeDispatch ms = align . vsep $+  [ align . vsep $ ["dispatch = {", indent 4 (vsep $ map entry ms), "}"]+  , "f = dispatch[cmdID]"+  ]+  where+    entry :: ExprM One -> MDoc+    entry (ManifoldM (metaId->i) _ _)+      = pretty i <> ":" <+> manNamer i <> ","+    entry _ = error "Expected ManifoldM"++typeSchema :: TypeP -> MorlocMonad MDoc+typeSchema t0 = f <$> type2jsontype t0+  where+    f :: JsonType -> MDoc+    f (VarJ v) = lst [var v, "None"]+    f (ArrJ v ts) = lst [var v, lst (map f ts)]+    f (NamJ "dict" es) = lst [dquotes "dict", dict (map entry es)]+    f (NamJ "record" es) = lst [dquotes "record", dict (map entry es)]+    f (NamJ v es) = lst [pretty v, dict (map entry es)]++    entry :: (MT.Text, JsonType) -> MDoc+    entry (v, t) = pretty v <> "=" <> f t++    dict :: [MDoc] -> MDoc+    dict xs = "OrderedDict" <> lst xs++    lst :: [MDoc] -> MDoc+    lst xs = encloseSep "(" ")" "," xs++    var :: MT.Text -> MDoc+    var v = dquotes (pretty v)++makePool :: MDoc -> [MDoc] -> [MDoc] -> MDoc -> MDoc+makePool lib includeDocs manifolds dispatch = [idoc|#!/usr/bin/env python++import sys+import subprocess+import json+from pymorlocinternals import (mlc_serialize, mlc_deserialize)+from collections import OrderedDict++sys.path = ["#{lib}"] + sys.path++#{vsep includeDocs}++def _morloc_foreign_call(args):+    try:+        sysObj = subprocess.run(+            args,+            stdout=subprocess.PIPE,+            check=True+        )+    except subprocess.CalledProcessError as e:+        sys.exit(str(e))++    return(sysObj.stdout.decode("ascii"))++#{vsep manifolds}++if __name__ == '__main__':+    try:+        cmdID = int(sys.argv[1])+    except IndexError:+        sys.exit("Internal error in {}: no manifold id found".format(sys.argv[0]))+    except ValueError:+        sys.exit("Internal error in {}: expected integer manifold id".format(sys.argv[0]))+    try:+        #{dispatch}+    except KeyError:+        sys.exit("Internal error in {}: no manifold found with id={}".format(sys.argv[0], cmdID))++    result = f(*sys.argv[2:])++    print(result)+|]
+ library/Morloc/CodeGenerator/Grammars/Translator/R.hs view
@@ -0,0 +1,382 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++{-|+Module      : Morloc.CodeGenerator.Grammars.Translator.R+Description : R translator+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.CodeGenerator.Grammars.Translator.R+  ( +    translate+  , preprocess+  ) where++import Morloc.CodeGenerator.Namespace+import Morloc.CodeGenerator.Serial (isSerializable, prettySerialOne, serialAstToType)+import Morloc.CodeGenerator.Grammars.Common+import Morloc.Data.Doc+import Morloc.Quasi+import qualified Morloc.Monad as MM+import qualified Morloc.Data.Text as MT++-- tree rewrites+preprocess :: ExprM Many -> MorlocMonad (ExprM Many)+preprocess = invertExprM++translate :: [Source] -> [ExprM One] -> MorlocMonad MDoc+translate srcs es = do+  -- translate sources+  includeDocs <- mapM+    translateSource+    (unique . catMaybes . map srcPath $ srcs)++  -- diagnostics+  liftIO . putDoc $ (vsep $ map prettyExprM es)++  -- translate each manifold tree, rooted on a call from nexus or another pool+  mDocs <- mapM translateManifold es++  return $ makePool includeDocs mDocs++letNamer :: Int -> MDoc +letNamer i = "a" <> viaShow i++bndNamer :: Int -> MDoc+bndNamer i = "x" <> viaShow i++manNamer :: Int -> MDoc+manNamer i = "m" <> viaShow i++translateSource :: Path -> MorlocMonad MDoc+translateSource (Path p) = do+  let p' = MT.stripPrefixIfPresent "./" p+  return $ "source(" <> dquotes (pretty p') <> ")"++tupleKey :: Int -> MDoc -> MDoc+tupleKey i v = [idoc|#{v}[[#{pretty i}]]|]++recordAccess :: MDoc -> MDoc -> MDoc+recordAccess record field = record <> "$" <> field++serialize :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+serialize v0 s0 = do+  (ms, v1) <- serialize' v0 s0+  t <- serialAstToType s0+  schema <- typeSchema t+  let v2 = "rmorlocinternals::mlc_serialize" <> tupled [v1, schema]+  return (v2, ms)+  where+    serialize' :: MDoc -> SerialAST One -> MorlocMonad ([MDoc], MDoc)+    serialize' v s+      | isSerializable s = return ([], v)+      | otherwise = construct v s++    construct :: MDoc -> SerialAST One -> MorlocMonad ([MDoc], MDoc)+    construct v (SerialPack _ (One (p, s))) = do+      unpacker <- case typePackerReverse p of+        [] -> MM.throwError . SerializationError $ "No unpacker found"+        (src:_) -> return . pretty . srcName $ src+      serialize' [idoc|#{unpacker}(#{v})|] s++    construct v (SerialList s) = do+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+      (before, x) <- serialize' [idoc|i#{idx}|] s+      let lst = block 4 [idoc|#{v'} <- lapply(#{v}, function(i#{idx})|] (vsep (before ++ [x])) <> ")"+      return ([lst], v')++    construct v (SerialTuple ss) = do+      (befores, ss') <- fmap unzip $ zipWithM (\i s -> construct (tupleKey i v) s) [1..] ss+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+          x = [idoc|#{v'} <- list#{tupled ss'}|]+      return (concat befores ++ [x], v');++    construct v (SerialObject _ _ _ rs) = do+      (befores, ss') <- fmap unzip $ mapM (\(PV _ _ k,s) -> serialize' (recordAccess v (pretty k)) s) rs+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+          entries = zipWith (\(PV _ _ key) val -> pretty key <> "=" <> val) (map fst rs) ss'+          decl = [idoc|#{v'} <- list#{tupled entries};|]+      return (concat befores ++ [decl], v');++    construct _ s = MM.throwError . SerializationError . render+      $ "construct: " <> prettySerialOne s+++deserialize :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+deserialize v0 s0+  | isSerializable s0 = do+      t <- serialAstToType s0+      schema <- typeSchema t+      let deserializing = [idoc|rmorlocinternals::mlc_deserialize(#{v0}, #{schema});|]+      return (deserializing, [])+  | otherwise = do+      idx <- fmap pretty $ MM.getCounter+      t <- serialAstToType s0+      schema <- typeSchema t+      let rawvar = "s" <> idx+          deserializing = [idoc|#{rawvar} <- rmorlocinternals::mlc_deserialize(#{v0}, #{schema});|]+      (x, befores) <- check rawvar s0+      return (x, deserializing:befores)+  where+    check :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    check v s+      | isSerializable s = return (v, [])+      | otherwise = construct v s++    construct :: MDoc -> SerialAST One -> MorlocMonad (MDoc, [MDoc])+    construct v (SerialPack _ (One (p, s'))) = do+      packer <- case typePackerForward p of+        [] -> MM.throwError . SerializationError $ "No packer found"+        (x:_) -> return . pretty . srcName $ x+      (x, before) <- check v s'+      let deserialized = [idoc|#{packer}(#{x})|]+      return (deserialized, before)++    construct v (SerialList s) = do+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+      (x, before) <- check [idoc|i#{idx}|] s+      let lst = block 4 [idoc|#{v'} <- lapply(#{v}, function(i#{idx})|] (vsep (before ++ [x])) <> ")"+      return (v', [lst])++    construct v (SerialTuple ss) = do+      (ss', befores) <- fmap unzip $ zipWithM (\i s -> check (tupleKey i v) s) [1..] ss+      idx <- fmap pretty $ MM.getCounter+      let v' = "s" <> idx+          x = [idoc|#{v'} <- list#{tupled ss'};|]+      return (v', concat befores ++ [x]);++    construct v (SerialObject _ (PV _ _ constructor) _ rs) = do+      idx <- fmap pretty $ MM.getCounter+      (ss', befores) <- fmap unzip $ mapM (\(PV _ _ k,s) -> check (recordAccess v (pretty k)) s) rs+      let v' = "s" <> idx+          entries = zipWith (\(PV _ _ key) val -> pretty key <> "=" <> val) (map fst rs) ss'+          decl = [idoc|#{v'} <- #{pretty constructor}#{tupled entries};|]+      return (v', concat befores ++ [decl]);++    construct _ s = MM.throwError . SerializationError . render+      $ "deserializeDescend: " <> prettySerialOne s++++-- break a call tree into manifolds+translateManifold :: ExprM One -> MorlocMonad MDoc+translateManifold m0@(ManifoldM _ args0 _) = do+  MM.startCounter+  (vsep . punctuate line . (\(x,_,_)->x)) <$> f args0 m0+  where+++  f :: [Argument] -> ExprM One -> MorlocMonad ([MDoc], MDoc, [MDoc])+  f pargs m@(ManifoldM (metaId->i) args e) = do+    (ms', body, rs') <- f args e+    let decl = manNamer i <+> "<- function" <> tupled (map makeArgument args)+        mdoc = block 4 decl (vsep $ rs' ++ [body])+        mname = manNamer i+    -- TODO: handle partials BEFORE translation+    call <- return $ case (splitArgs args pargs, nargsTypeM (typeOfExprM m)) of+      ((rs, []), _) -> mname <> tupled (map makeArgument rs) -- covers #1, #2 and #4+      (([], _ ), _) -> mname+      ((rs, vs), _) -> makeLambda vs (mname <> tupled (map makeArgument (rs ++ vs))) -- covers #5+    return (mdoc : ms', call, [])++  f _ (PoolCallM _ _ cmds args) = do+    let quotedCmds = map dquotes cmds+        callArgs = "list(" <> hsep (punctuate "," (drop 1 quotedCmds ++ map makeArgument args)) <> ")"+        call = ".morloc_foreign_call" <> tupled([head quotedCmds, callArgs, dquotes "_", dquotes "_"])+    return ([], call, [])++  f _ (ForeignInterfaceM _ _) = MM.throwError . CallTheMonkeys $+    "Foreign interfaces should have been resolved before passed to the translators"++  f args (LetM i e1 e2) = do+    (ms1', e1', rs1) <- (f args) e1+    (ms2', e2', rs2) <- (f args) e2+    let rs = rs1 ++ [ letNamer i <+> "<-" <+> e1' ] ++ rs2+    return (ms1' ++ ms2', e2', rs)++  f args (AppM (SrcM _ src) xs) = do+    (mss', xs', rss') <- mapM (f args) xs |>> unzip3+    return (concat mss', pretty (srcName src) <> tupled xs', concat rss')++  f _ (AppM _ _) = error "Can only apply functions"++  f _ (SrcM _ src) = return ([], pretty (srcName src), [])++  f args (LamM labmdaArgs e) = do+    (ms', e', rs) <- f args e+    let vs = map (bndNamer . argId) labmdaArgs+    return (ms', "function" <> tupled vs <> "{" <+> e' <> "}", rs)++  f _ (BndVarM _ i) = return ([], bndNamer i, [])++  f _ (LetVarM _ i) = return ([], letNamer i, [])++  f args (AccM e k) = do+    (ms, e', ps) <- f args e+    return (ms, e' <> "$" <> pretty k, ps)++  f args (ListM t es) = do+    (mss', es', rss) <- mapM (f args) es |>> unzip3+    x' <- return $ case t of+      (Native (ArrP _ [VarP et])) -> case et of+        (PV _ _ "numeric") -> "c" <> tupled es'+        (PV _ _ "logical") -> "c" <> tupled es'+        (PV _ _ "character") -> "c" <> tupled es'+        _ -> "list" <> tupled es'+      _ -> "list" <> tupled es'+    return (concat mss', x', concat rss)++  f args (TupleM _ es) = do+    (mss', es', rss) <- mapM (f args) es |>> unzip3+    return (concat mss', "list" <> tupled es', concat rss)++  f args (RecordM _ entries) = do+    (mss', es', rss) <- mapM (f args . snd) entries |>> unzip3+    let entries' = zipWith (\k v -> pretty k <> "=" <> v) (map fst entries) es'+    return (concat mss', "list" <> tupled entries', concat rss)++  f _ (LogM _ x) = return ([], if x then "TRUE" else "FALSE", [])++  f _ (NumM _ x) = return ([], viaShow x, [])++  f _ (StrM _ x) = return ([], dquotes $ pretty x, [])++  f _ (NullM _) = return ([], "NULL", [])++  f args (SerializeM s e) = do+    (ms, e', rs1) <- f args e+    (serialized, rs2) <- serialize e' s+    return (ms, serialized, rs1 ++ rs2)++  f args (DeserializeM s e) = do+    (ms, e', rs1) <- f args e+    (deserialized, rs2) <- deserialize e' s+    return (ms, deserialized, rs1 ++ rs2)++  f args (ReturnM e) = do+    (ms, e', rs) <- f args e+    return (ms, e', rs)+translateManifold _ = error "Every ExprM object must start with a Manifold term"++makeLambda :: [Argument] -> MDoc -> MDoc+makeLambda args body = "function" <+> tupled (map makeArgument args) <> "{" <> body <> "}"++makeArgument :: Argument -> MDoc+makeArgument (SerialArgument v _) = bndNamer v+makeArgument (NativeArgument v _) = bndNamer v+makeArgument (PassThroughArgument v) = bndNamer v++-- For R, the type schema is the JSON representation of the type+typeSchema :: TypeP -> MorlocMonad MDoc+typeSchema t = do+  json <- jsontype2rjson <$> type2jsontype t+  -- FIXME: Need to support single quotes inside strings+  return $ "'" <> json <> "'"++jsontype2rjson :: JsonType -> MDoc+jsontype2rjson (VarJ v) = dquotes (pretty v)+jsontype2rjson (ArrJ v ts) = "{" <> key <> ":" <> val <> "}" where+  key = dquotes (pretty v)+  val = encloseSep "[" "]" "," (map jsontype2rjson ts)+jsontype2rjson (NamJ objType rs) =+  case objType of+    "data.frame" -> "{" <> dquotes "data.frame" <> ":" <> encloseSep "{" "}" "," rs' <> "}"+    "record" -> "{" <> dquotes "record" <> ":" <> encloseSep "{" "}" "," rs' <> "}"+    _ -> encloseSep "{" "}" "," rs'+  where+  keys = map (dquotes . pretty) (map fst rs) +  vals = map jsontype2rjson (map snd rs)+  rs' = zipWith (\key val -> key <> ":" <> val) keys vals++makePool :: [MDoc] -> [MDoc] -> MDoc+makePool sources manifolds = [idoc|#!/usr/bin/env Rscript++#{vsep sources}++.morloc_run <- function(f, args){+  fails <- ""+  isOK <- TRUE+  warns <- list()+  notes <- capture.output(+    {+      value <- withCallingHandlers(+        tryCatch(+          do.call(f, args),+          error = function(e) {+            fails <<- e$message;+            isOK <<- FALSE+          }+        ),+        warning = function(w){+          warns <<- append(warns, w$message)+          invokeRestart("muffleWarning")+        }+      )+    },+    type="message"+  )+  list(+    value = value,+    isOK  = isOK,+    fails = fails,+    warns = warns,+    notes = notes+  )+}++# dies on error, ignores warnings and messages+.morloc_try <- function(f, args, .log=stderr(), .pool="_", .name="_"){+  x <- .morloc_run(f=f, args=args)+  location <- sprintf("%s::%s", .pool, .name)+  if(! x$isOK){+    cat("** R errors in ", location, "\n", file=stderr())+    cat(x$fails, "\n", file=stderr())+    stop(1)+  }+  if(! is.null(.log)){+    lines = c()+    if(length(x$warns) > 0){+      cat("** R warnings in ", location, "\n", file=stderr())+      cat(paste(unlist(x$warns), sep="\n"), file=stderr())+    }+    if(length(x$notes) > 0){+      cat("** R messages in ", location, "\n", file=stderr())+      cat(paste(unlist(x$notes), sep="\n"), file=stderr())+    }+  }+  x$value+}++.morloc_unpack <- function(unpacker, x, .pool, .name){+  x <- .morloc_try(f=unpacker, args=list(as.character(x)), .pool=.pool, .name=.name)+  return(x)+}++.morloc_foreign_call <- function(cmd, args, .pool, .name){+  .morloc_try(f=system2, args=list(cmd, args=args, stdout=TRUE), .pool=.pool, .name=.name)+}++#{vsep manifolds}++args <- as.list(commandArgs(trailingOnly=TRUE))+if(length(args) == 0){+  stop("Expected 1 or more arguments")+} else {+  cmdID <- args[[1]]+  f_str <- paste0("m", cmdID)+  if(exists(f_str)){+    f <- eval(parse(text=paste0("m", cmdID)))+    result <- do.call(f, args[-1])+    cat(result, "\n")+  } else {+    cat("Could not find manifold '", cmdID, "'\n", file=stderr())+  }+}+|]
+ library/Morloc/CodeGenerator/Grammars/Translator/Source/CppInternals.hs view
@@ -0,0 +1,438 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++{-|+Module      : Morloc.CodeGenerator.Grammars.Translator.Source.CppInternals+Description : C++ serialization source code+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++The @serializationHandling@ code is copy-and-pasted from+@morloc-project/cppmorlocinternals/serial.hpp@. This is dreadful, I know, and I+will find an alternative solution soon.++-}++++module Morloc.CodeGenerator.Grammars.Translator.Source.CppInternals+  ( foreignCallFunction+  , serializationHandling+  ) where++import Morloc.Quasi++foreignCallFunction = [idoc|+// Handle foreign calls. This function is used inside of C++ manifolds. Any+// changes in the name will require a mirrored change in the morloc code. +std::string foreign_call(std::string cmd){+    char buffer[256];+    std::string result = "";+    FILE* pipe = popen(cmd.c_str(), "r");+    while (fgets(buffer, sizeof buffer, pipe) != NULL) {+        result += buffer;+    }+    pclose(pipe);+    return(result);+}+|]++serializationHandling = [idoc|+#include <iostream>+#include <sstream>+#include <string>+#include <stdexcept>+#include <stdio.h>+#include <vector>+#include <iomanip>+#include <limits>+#include <tuple>+#include <utility> +++std::string serialize(bool x, bool schema);+std::string serialize(int x, int schema);+std::string serialize(int x, size_t schema);+std::string serialize(int x, long schema);+std::string serialize(double x, double schema);+std::string serialize(std::string x, std::string schema);++template <class A> std::string serialize(A x);++template<std::size_t I = 0, class... Rs>+inline typename std::enable_if<I == sizeof...(Rs), std::string>::type+  _serialize_tuple(std::tuple<Rs...> x);++template<std::size_t I = 0, class... Rs>+inline typename std::enable_if<I < sizeof...(Rs), std::string>::type+  _serialize_tuple(std::tuple<Rs...> x);++template <class... A>+std::string serialize(std::tuple<A...> x, std::tuple<A...> schema);++template <class A>+std::string serialize(std::vector<A> x, std::vector<A> schema);++bool match(const std::string json, const std::string pattern, size_t &i);+void whitespace(const std::string json, size_t &i);+std::string digit_str(const std::string json, size_t &i);+double read_double(std::string json);++// attempt a run a parser, on failure, consume no input+template <class A>+bool try_parse(std::string json, size_t &i, A &x, bool (*f)(std::string, size_t &, A &));++bool deserialize(const std::string json, size_t &i, bool &x);+bool deserialize(const std::string json, size_t &i, double &x);+bool deserialize(const std::string json, size_t &i, std::string &x);++template <class A>+bool integer_deserialize(const std::string json, size_t &i, A &x);+bool deserialize(const std::string json, size_t &i, int &x);+bool deserialize(const std::string json, size_t &i, size_t &x);+bool deserialize(const std::string json, size_t &i, long &x);++template <class A>+bool deserialize(const std::string json, size_t &i, std::vector<A> &x);++template <class A>+bool _deserialize_tuple(const std::string json, size_t &i, std::tuple<A> &x);++template <class A, class... Rest>+bool _deserialize_tuple(const std::string json, size_t &i, std::tuple<A, Rest...> &x);++template <class... Rest>+bool deserialize(const std::string json, size_t &i, std::tuple<Rest...> &x);++template <class A>+A deserialize(const std::string json, A output);+++++/* ---------------------------------------------------------------------- */+/*                       S E R I A L I Z A T I O N                        */+/* ---------------------------------------------------------------------- */++std::string serialize(bool x, bool schema){+    return(x? "true" : "false");+}++std::string serialize(int x, int schema){+    std::ostringstream s;+    s << x;+    return(s.str());+}+std::string serialize(int x, size_t schema){+    std::ostringstream s;+    s << x;+    return(s.str());+}+std::string serialize(int x, long schema){+    std::ostringstream s;+    s << x;+    return(s.str());+}++std::string serialize(double x, double schema){+    std::ostringstream s;+    s << std::setprecision(std::numeric_limits<double>::digits10 + 2) << x;+    return(s.str());+}++std::string serialize(std::string x, std::string schema){+    std::ostringstream s;+    s << '"' << x << '"';+    return(s.str());+}++template <class A>+std::string serialize(std::vector<A> x, std::vector<A> schema){+    A element_schema;+    std::ostringstream s;+    s << "[";+    for(size_t i = 0; i < x.size(); i++){+        s << serialize(x[i], element_schema);+        if((i+1) < x.size()){+            s << ',';+        }+    }+    s << "]";+    return (s.str());+}++template <class A>+std::string serialize(A x){+    return serialize(x, x);+}++// adapted from stackoverflow #1198260 answer from emsr+template<std::size_t I = 0, class... Rs>+inline typename std::enable_if<I == sizeof...(Rs), std::string>::type+  _serialize_tuple(std::tuple<Rs...> x)+  { return ""; }++template<std::size_t I = 0, class... Rs>+inline typename std::enable_if<I < sizeof...(Rs), std::string>::type+  _serialize_tuple(std::tuple<Rs...> x)+  {+    return serialize(std::get<I>(x)) + "," + _serialize_tuple<I + 1, Rs...>(x);+  }++template <class... A>+std::string serialize(std::tuple<A...> x, std::tuple<A...> schema){+    std::ostringstream ss;+    ss << "[";+    ss << _serialize_tuple(x);+    std::string json = ss.str();+    // _serialize_tuple adds a terminal comma, replaced here with the end bracket+    json[json.size() - 1] = ']';+    return json;+}+++/* ---------------------------------------------------------------------- */+/*                             P A R S E R S                              */+/* ---------------------------------------------------------------------- */++// match a constant string, nothing is consumed on failure+bool match(const std::string json, const std::string pattern, size_t &i){+    for(size_t j = 0; j < pattern.size(); j++){+        if(j + i >= json.size()){+            return false;+        }+        if(json[j + i] != pattern[j]){+            return false;+        }+    }+    i += pattern.size();+    return true;+}++void whitespace(const std::string json, size_t &i){+    while(json[i] == ' ' || json[i] == '\n' || json[i] == '\t'){+        i++;+    }+}++// parse sequences of digits from a larger string+// used as part of a larger number parser+std::string digit_str(const std::string json, size_t &i){+    std::string num = "";+    while(json[i] >= '0' && json[i] <= '9'){+        num += json[i];+        i++;+    }+    return num;+}++double read_double(std::string json){+    return std::stod(json.c_str());+}++// attempt a run a parser, on failure, consume no input+template <class A>+bool try_parse(std::string json, size_t &i, A &x, bool (*f)(std::string, size_t &, A &)){+    size_t j = i;+    if(f(json, i, x)){+        return true;+    } else {+        i = j;+        return false;+    }+}++/* ---------------------------------------------------------------------- */+/*                      D E S E R I A L I Z A T I O N                     */+/* ---------------------------------------------------------------------- */++// All combinator functions have the following general signature:+//+//   template <class A>+//   bool deserialize(const std::string json, size_t &i, A &x)++// The return value represents parse success.+// The index may be incremented even on failure.++// combinator parser for bool+bool deserialize(const std::string json, size_t &i, bool &x){+    if(match(json, "true", i)){+        x = true;+    }+    else if(match(json, "false", i)){+        x = false;+    }+    else {+        return false;+    }+    return true;+}++// combinator parser for doubles+bool deserialize(const std::string json, size_t &i, double &x){+    std::string lhs = "";+    std::string rhs = "";+    char sign = '+';+    +    if(json[i] == '-'){+        sign = '-';+        i++;+    }+    lhs = digit_str(json, i);+    if(json[i] == '.'){+        i++;+        rhs = digit_str(json, i);+    } else {+        rhs = "0";+    }++    if(lhs.size() > 0){+        x = read_double(sign + lhs + '.' + rhs);  +        return true;+    } else {+        return false;+    }+}++// combinator parser for double-quoted strings+bool deserialize(const std::string json, size_t &i, std::string &x){+    try {+        x = "";+        if(! match(json, "\"", i)){+            throw 1;+        }+        // TODO: add full JSON specification support (escapes, magic chars, etc)+        while(i < json.size() && json[i] != '"'){+            x += json[i];+            i++;+        }+        if(! match(json, "\"", i)){+            throw 1;+        }+    } catch (int e) {+        return false;+    }+    return true;+}++template <class A>+bool integer_deserialize(const std::string json, size_t &i, A &x){+    char sign = '+';+    if(json[i] == '-'){+        sign = '-';+        i++;+    }+    std::string x_str = digit_str(json, i);+    if(x_str.size() > 0){+        std::stringstream sstream(sign + x_str);+        sstream >> x;+        return true;+    }+    return false; +}+bool deserialize(const std::string json, size_t &i, int &x){+    return integer_deserialize(json, i, x);+}+bool deserialize(const std::string json, size_t &i, size_t &x){+    return integer_deserialize(json, i, x);+}+bool deserialize(const std::string json, size_t &i, long &x){+    return integer_deserialize(json, i, x);+}++// parser for vectors+template <class A>+bool deserialize(const std::string json, size_t &i, std::vector<A> &x){+    x = {};+    try {+        if(! match(json, "[", i)){+            throw 1;+        }+        whitespace(json, i);+        while(true){+            A element;+            if(deserialize(json, i, element)){+                x.push_back(element);+                whitespace(json, i);+                match(json, ",", i);+                whitespace(json, i);+            } else {+                break;+            }+        }+        whitespace(json, i);+        if(! match(json, "]", i)){+            throw 1;+        }+    } catch (int e) {+        return false;+    }+    return true;+}++template <class A>+bool _deserialize_tuple(const std::string json, size_t &i, std::tuple<A> &x){+    A a;+    if(! deserialize(json, i, a)){+        return false;+    }+    x = std::make_tuple(a);+    return true;+}+template <class A, class... Rest>+bool _deserialize_tuple(const std::string json, size_t &i, std::tuple<A, Rest...> &x){+    A a;+    // parse the next element+    if(! deserialize(json, i, a)){+        return false;+    }+    // skip whitespace and the comma+    whitespace(json, i);+    if(! match(json, ",", i)){+        return false;+    }+    whitespace(json, i);+    // parse the rest of the elements+    std::tuple<Rest...> rs;+    if(! _deserialize_tuple(json, i, rs)){+        return false;+    }+    // cons+    x = std::tuple_cat(std::make_tuple(a), rs);+    return true;+}+template <class... Rest>+bool deserialize(const std::string json, size_t &i, std::tuple<Rest...> &x){+    try {+        if(! match(json, "[", i)){+            throw 1;+        }+        whitespace(json, i);+        if(! _deserialize_tuple(json, i, x)){+            throw 1;+        }+        whitespace(json, i);+        if(! match(json, "]", i)){+            throw 1;+        }+    } catch (int e) {+        return false;+    }+    return true;+}++template <class A>+A deserialize(const std::string json, A output){+    size_t i = 0;+    deserialize(json, i, output);+    return output;+}++template <class... Rest>+std::tuple<Rest...> deserialize(const std::string json, std::tuple<Rest...> output){+    size_t i = 0;+    deserialize(json, i, output);+    return output;+}+|]
+ library/Morloc/CodeGenerator/Internal.hs view
@@ -0,0 +1,69 @@+{-|+Module      : Morloc.CodeGenerator.Internal+Description : Miscellaneous backend utilities+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.CodeGenerator.Internal+(+    weaveTypes+  , weaveTypesGCP+  , weaveTypesGCM+  , typeP2typeM+) where++import Morloc.CodeGenerator.Namespace+import qualified Morloc.Monad as MM++weaveTypes :: Maybe Type -> Type -> MorlocMonad TypeP+weaveTypes g0 t0 = case (g0 >>= langOf, langOf t0) of+  (_, Nothing) -> MM.throwError . CallTheMonkeys+    $ "Expected a language-specific type as the second argument"+  (Just _, _) -> MM.throwError . CallTheMonkeys+    $ "Expected a general type as the first argument"+  (_, Just lang) -> return $ f lang g0 t0+  where+    f :: Lang -> Maybe Type -> Type -> TypeP++    f lang (Just (UnkT (TV _ v1))) (UnkT (TV _ v2)) = UnkP (PV lang (Just v1) v2)+    f lang _ (UnkT (TV _ v)) = UnkP (PV lang Nothing v)++    f lang (Just (VarT (TV _ v1))) (VarT (TV _ v2)) = VarP (PV lang (Just v1) v2)+    f lang _ (VarT (TV _ v2)) = VarP (PV lang Nothing v2)++    f lang (Just (FunT t11 t12)) (FunT t21 t22)+      = FunP (f lang (Just t11) t21) (f lang (Just t12) t22)+    f lang _ (FunT t1 t2)+      = FunP (f lang Nothing t1) (f lang Nothing t2)++    f lang (Just (ArrT (TV _ v1) ts1)) (ArrT (TV _ v2) ts2)+      = ArrP (PV lang (Just v1) v2) (zipWith (f lang) (map Just ts1) ts2)+    f lang _ (ArrT (TV _ v) ts)+      = ArrP (PV lang Nothing v) (map (f lang Nothing) ts)++    f lang (Just (NamT _ (TV _ v1) ts1 rs1)) (NamT r2 (TV _ v2) ts2 rs2)+      = NamP r2 (PV lang (Just v1) v2) (zipWith (f lang) (map Just ts1) ts2)+      $ zip+        (zipWith (PV lang) (map (Just . fst) rs1) (map fst rs2))+        (zipWith (f lang) (map (Just . snd) rs1) (map snd rs2))+    f lang _ (NamT r (TV _ v) ts rs)+      = NamP r (PV lang Nothing v) (map (f lang Nothing) ts)+      $ zip+        (map (PV lang Nothing) (map fst rs))+        (map (f lang Nothing) (map snd rs))+++weaveTypesGCP :: GMeta -> CType -> MorlocMonad TypeP+weaveTypesGCP g (CType t) = weaveTypes (unGType <$> metaGType g) t++weaveTypesGCM :: GMeta -> CType -> MorlocMonad TypeM+weaveTypesGCM g (CType t) = typeP2typeM <$> weaveTypes (unGType <$> metaGType g) t++typeP2typeM :: TypeP -> TypeM+typeP2typeM f@(FunP _ _) = case decompose f of+  (inputs, output) -> Function (map typeP2typeM inputs) (typeP2typeM output)+typeP2typeM (UnkP _) = Passthrough+typeP2typeM t = Native t
+ library/Morloc/CodeGenerator/Namespace.hs view
@@ -0,0 +1,255 @@+{-|+Module      : Morloc.CodeGenerator.Namespace+Description : All code generator types and datastructures+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.CodeGenerator.Namespace+  ( module Morloc.Namespace+  -- ** Types used in final translations+  , TypeM(..)+  , ExprM(..)+  , Argument(..)+  , JsonType(..)+  , PVar(..)+  , TypeP(..)+  , JsonPath+  , JsonAccessor(..)+  , NexusCommand(..)+  -- ** Serialization AST+  , SerialAST(..)+  , TypePacker(..)+  ) where++import Morloc.Namespace+import Data.Scientific (Scientific)+import Data.Text (Text)++-- | Stores the language, general name and concrete name for a type expression+data PVar+  = PV+    Lang+    (Maybe Text)+    Text+  deriving (Show, Eq, Ord)++-- | A solved type coupling a language specific form to an optional general form+data TypeP+  = UnkP PVar+  | VarP PVar+  | FunP TypeP TypeP+  | ArrP PVar [TypeP]+  | NamP NamType PVar [TypeP] [(PVar, TypeP)]+  deriving (Show, Ord, Eq)++type JsonPath = [JsonAccessor]+data JsonAccessor+  = JsonIndex Int+  | JsonKey Text++data NexusCommand = NexusCommand+  { commandName :: EVar -- ^ user-exposed subcommand name in the nexus+  , commandType :: Type -- ^ the general type of the expression+  , commandJson :: MDoc -- ^ JSON output with null's where values will be replaced+  , commandArgs :: [EVar] -- ^ list of function arguments+  , commandSubs :: [(JsonPath, Text, JsonPath)]+  -- ^ list of tuples with values 1) path in JSON to value needs to be replaced+  -- 2) the function argument from which to pull replacement value and 3) the+  -- path to the replacement value+  }++instance Typelike TypeP where+  typeOf (UnkP (PV lang _ t)) = UnkT (TV (Just lang) t)+  typeOf (VarP (PV lang _ t)) = VarT (TV (Just lang) t)+  typeOf (FunP t1 t2) = FunT (typeOf t1) (typeOf t2)+  typeOf (ArrP (PV lang _ v) ts) = ArrT (TV (Just lang) v) (map typeOf ts)+  typeOf (NamP r (PV lang _ t) ps es)+    = NamT r (TV (Just lang) t)+             (map typeOf ps)+             (zip [v | (PV _ _ v, _) <- es] (map (typeOf . snd) es))++  decompose (FunP t1 t2) = case decompose t2 of +    (ts, finalType) -> (t1:ts, finalType) +  decompose t = ([], t)++-- | A tree describing how to (de)serialize an object+data SerialAST f+  = SerialPack PVar (f (TypePacker, SerialAST f)) -- ^ use an (un)pack function to simplify an object+  | SerialList (SerialAST f)+  | SerialTuple [SerialAST f]+  | SerialObject NamType PVar [TypeP] [(PVar, SerialAST f)] -- ^ make a record, table, or object+  | SerialNum PVar+  | SerialBool PVar+  | SerialString PVar+  | SerialNull PVar+  | SerialUnknown PVar+  -- ^ depending on the language, this may or may not raise an error down the+  -- line, the parameter contains the variable name, which is useful only for+  -- source code comments.++data TypePacker = TypePacker+  { typePackerType    :: TypeP+  , typePackerFrom    :: TypeP+  , typePackerForward :: [Source]+  , typePackerReverse :: [Source]+  } deriving (Show, Ord, Eq)++-- | A simplified subset of the Type record+-- functions, existential, and universal types are removed+-- language-specific info is removed+data JsonType+  = VarJ Text+  -- ^ {"int"}+  | ArrJ Text [JsonType]+  -- ^ {"list":["int"]}+  | NamJ Text [(Text, JsonType)]+  -- ^ {"Foo":{"bar":"A","baz":"B"}}+  deriving (Show, Ord, Eq)++-- | An argument that is passed to a manifold+data Argument+  = SerialArgument Int TypeP+  -- ^ A serialized (e.g., JSON string) argument.  The parameters are 1)+  -- argument name (e.g., x), and 2) argument type (e.g., double). Some types+  -- may not be serializable. This is OK, so long as they are only used in+  -- functions of the same language.+  | NativeArgument Int TypeP+  -- ^ A native argument with the same parameters as above+  | PassThroughArgument Int+  -- ^ A serialized argument that is untyped in the current language. It cannot+  -- be deserialized, but will be passed eventually to a foreign argument where it+  -- does have a concrete type.+  deriving (Show, Ord, Eq)++data TypeM+  = Passthrough -- ^ serialized data that cannot be deserialized in this language+  | Serial TypeP -- ^ serialized data that may be deserialized in this language+  | Native TypeP -- ^ an unserialized native data type+  | Function [TypeM] TypeM -- ^ a function of n inputs and one output (cannot be serialized)+  deriving(Show, Eq, Ord)+++-- | A grammar that describes the implementation of the pools. Expressions in+-- this grammar will be directly translated into concrete code.+data ExprM f+  = ManifoldM GMeta [Argument] (ExprM f)+  -- ^ A wrapper around a single source call or (in some cases) a container.++  | ForeignInterfaceM+      TypeM -- required type in the calling language+      (ExprM f) -- expression in the foreign language+  -- ^ A generic interface to an expression in another language. Currently it+  -- will be resolved only to the specfic pool call interface type, where+  -- system calls pass serialized information between pools in different+  -- languages. Eventually, better methods will be added for certain pairs of+  -- languages.++  | PoolCallM+      TypeM -- serialized return data+      Int -- foreign manifold id+      [MDoc] -- shell command components that preceed the passed data+      [Argument] -- argument passed to the foreign function (must be serialized)+  -- ^ Make a system call to another language++  | LetM Int (ExprM f) (ExprM f)+  -- ^ let syntax allows fine control over order of operations in the generated+  -- code. The Int is an index for a LetVarM. It is also important in languages+  -- such as C++ where values need to be declared with explicit types and+  -- special constructors.++  | AppM+      (ExprM f) -- ManifoldM | SrcM | LamM+      [(ExprM f)]++  | SrcM TypeM Source+  -- ^ a within pool function call (cis)++  | LamM [Argument] (ExprM f)+  -- ^ Nothing Evar will be auto generated++  | BndVarM TypeM Int+  -- ^ A lambda-bound variable. BndVarM only describes variables bound as positional+  -- arguments in a manifold. The are represented as integers since the name+  -- will be language-specific.+  --+  -- In the rewrite step, morloc declarations are removed. So the expression:+  --   x = 5+  --   foo y = mul x y+  -- Is rewritten as:+  --   \y -> mul 5 y+  -- So BndVarM does NOT include variables defined in the morloc script. It only+  -- includes lambda-bound variables. The only BndVarM is `y` (`mul` is SrcM). The+  -- literal name "y" is replaced, though, with the integer 1. This is required in+  -- order to avoid name conflicts in concrete languages, for example consider+  -- the following (perfectly legal) morloc function:+  --   foo for = mul for 2+  -- If the string "for" were retained as the variable name, this would fail in+  -- many language where "for" is a keyword.++  | AccM (ExprM f) EVar +  -- ^ Access a field in record ExprM++  | LetVarM TypeM Int+  -- ^ An internally generated variable id used in let assignments. When+  -- translated into a language, the integer will be used to generate a unique+  -- variable name (e.g. [a0,a1,...] or [a,b,c,...]).++  -- containers+  | ListM TypeM [(ExprM f)]+  | TupleM TypeM [(ExprM f)]+  | RecordM TypeM [(EVar, (ExprM f))]++  -- primitives+  | LogM TypeM Bool+  | NumM TypeM Scientific+  | StrM TypeM Text+  | NullM TypeM++  -- serialization+  | SerializeM (SerialAST f) (ExprM f)+  | DeserializeM (SerialAST f) (ExprM f)++  | ReturnM (ExprM f)+  -- ^ The return value of a manifold. I need this to distinguish between the+  -- values assigned in let expressions and the final return value. In some+  -- languages, this may not be necessary (e.g., R).+++instance HasOneLanguage (TypeP) where+  langOf' (UnkP (PV lang _ _)) = lang+  langOf' (VarP (PV lang _ _)) = lang+  langOf' (FunP t _) = langOf' t+  langOf' (ArrP (PV lang _ _) _) = lang+  langOf' (NamP _ (PV lang _ _) _ _) = lang++instance HasOneLanguage (TypeM) where+  langOf Passthrough = Nothing +  langOf (Serial t) = langOf t+  langOf (Native t) = langOf t+  langOf (Function _ t) = langOf t++instance HasOneLanguage (ExprM f) where+  -- langOf :: a -> Maybe Lang+  langOf' (ManifoldM _ _ e) = langOf' e+  langOf' (ForeignInterfaceM t _) = langOf' t+  langOf' (PoolCallM t _ _ _) = langOf' t+  langOf' (LetM _ _ e2) = langOf' e2+  langOf' (AppM e _) = langOf' e+  langOf' (SrcM _ src) = srcLang src+  langOf' (LamM _ e) = langOf' e+  langOf' (BndVarM t _) = langOf' t+  langOf' (LetVarM t _) = langOf' t+  langOf' (AccM e _) = langOf' e+  langOf' (ListM t _) = langOf' t+  langOf' (TupleM t _) = langOf' t+  langOf' (RecordM t _) = langOf' t+  langOf' (LogM t _) = langOf' t+  langOf' (NumM t _) = langOf' t+  langOf' (StrM t _) = langOf' t+  langOf' (NullM t) = langOf' t+  langOf' (SerializeM _ e) = langOf' e+  langOf' (DeserializeM _ e) = langOf' e+  langOf' (ReturnM e) = langOf' e
+ library/Morloc/CodeGenerator/Nexus.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++{-|+Module      : Morloc.CodeGenerator.Nexus+Description : Templates for generating a Perl nexus+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.CodeGenerator.Nexus+  ( generate+  ) where++import Morloc.Data.Doc+import Morloc.CodeGenerator.Namespace+import Morloc.Quasi+import Morloc.Pretty (prettyType)+import qualified Morloc.Data.Text as MT+import qualified Control.Monad as CM+import qualified Morloc.Config as MC+import qualified Morloc.Language as ML+import qualified Morloc.Monad as MM++type FData =+  ( MDoc -- pool call command, (e.g., "RScript pool.R 4 --")+  , MDoc -- subcommand name+  , TypeP -- argument type+  )++generate :: [NexusCommand] -> [(TypeP, Int, Maybe EVar)] -> MorlocMonad Script+generate cs xs = do+  let names = [pretty name | (_, _, Just name) <- xs] ++ map (pretty . commandName) cs+  fdata <- CM.mapM getFData [(t, i, n) | (t, i, Just n) <- xs] -- [FData]+  return $+    Script+      { scriptBase = "nexus"+      , scriptLang = ML.PerlLang+      , scriptCode = Code . render $ main names fdata cs+      , scriptCompilerFlags = []+      , scriptInclude = []+      }++getFData :: (TypeP, Int, EVar) -> MorlocMonad FData+getFData (t, i, n) = do+  config <- MM.ask+  let lang = langOf t+  case MC.buildPoolCallBase config lang i of+    (Just cmds) -> return (hsep cmds, pretty n, t)+    Nothing ->+      MM.throwError . GeneratorError $+      "No execution method found for language: " <> ML.showLangName (fromJust lang)++main :: [MDoc] -> [FData] -> [NexusCommand] -> MDoc+main names fdata cdata =+  [idoc|#!/usr/bin/env perl++use strict;+use warnings;++use JSON::XS;++my $json = JSON::XS->new->canonical;++&printResult(&dispatch(@ARGV));++sub printResult {+    my $result = shift;+    print "$result";+}++sub dispatch {+    if(scalar(@_) == 0){+        &usage();+    }++    my $cmd = shift;+    my $result = undef;++    #{mapT names}++    if($cmd eq '-h' || $cmd eq '-?' || $cmd eq '--help' || $cmd eq '?'){+        &usage();+    }++    if(exists($cmds{$cmd})){+        $result = $cmds{$cmd}(@_);+    } else {+        print STDERR "Command '$cmd' not found\n";+        &usage();+    }++    return $result;+}++#{usageT fdata cdata}++#{vsep (map functionCT cdata ++ map functionT fdata)}++|]++mapT names = [idoc|my %cmds = #{tupled (map mapEntryT names)};|]++mapEntryT n = [idoc|#{n} => \&call_#{n}|]++usageT :: [FData] -> [NexusCommand] -> MDoc+usageT fdata cdata =+  [idoc|+sub usage{+    print STDERR "The following commands are exported:\n";+    #{align $ vsep (map usageLineT fdata ++ map usageLineConst cdata)}+    exit 0;+}+|]++usageLineT :: FData -> MDoc+usageLineT (_, name, t) = vsep+  ( [idoc|print STDERR "  #{name}\n";|]+  : writeTypes (gtypeOf t)+  )++gtypeOf (UnkP (PV _ (Just v) _)) = UnkT (TV Nothing v)+gtypeOf (VarP (PV _ (Just v) _)) = VarT (TV Nothing v)+gtypeOf (FunP t1 t2) = FunT (gtypeOf t1) (gtypeOf t2)+gtypeOf (ArrP (PV _ (Just v) _) ts) = ArrT (TV Nothing v) (map gtypeOf ts)+gtypeOf (NamP r (PV _ (Just v) _) ps es)+  = NamT r (TV Nothing v)+           (map gtypeOf ps)+           (zip [k | (PV _ (Just k) _, _) <- es] (map (gtypeOf . snd) es))+gtypeOf _ = UnkT (TV Nothing "?") -- this shouldn't happen++usageLineConst :: NexusCommand -> MDoc+usageLineConst cmd = vsep+  ( [idoc|print STDERR "  #{pretty (commandName cmd)}\n";|]+  : writeTypes (commandType cmd) +  )++writeTypes :: Type -> [MDoc]+writeTypes t =+  let (inputs, output) = decompose t+  in zipWith writeType [Just i | i <- [1..]] inputs ++ [writeType Nothing output]++writeType :: Maybe Int -> Type -> MDoc+writeType (Just i) t  = [idoc|print STDERR q{    param #{pretty i}: #{prettyType t}}, "\n";|]+writeType (Nothing) t = [idoc|print STDERR q{    return: #{prettyType t}}, "\n";|]+++functionT :: FData -> MDoc+functionT (cmd, name, t) =+  [idoc|+sub call_#{name}{+    if(scalar(@_) != #{pretty n}){+        print STDERR "Expected #{pretty n} arguments to '#{name}', given " . +        scalar(@_) . "\n";+        exit 1;+    }+    return `#{poolcall}`;+}+|]+  where+    n = nargs t+    poolcall = hsep $ cmd : map argT [0 .. (n - 1)]++functionCT :: NexusCommand -> MDoc+functionCT (NexusCommand cmd _ json_str args subs) =+  [idoc|+sub call_#{pretty cmd}{+    if(scalar(@_) != #{pretty $ length args}){+        print STDERR "Expected #{pretty $ length args} arguments to '#{pretty cmd}', given " . scalar(@_) . "\n";+        exit 1;+    }+    my $json_obj = $json->decode(q{#{json_str}});+    #{align . vsep $ readArguments ++ replacements}+    return ($json->encode($json_obj) . "\n");+}+|]+  where+    readArguments = zipWith readJsonArg args [1..]+    replacements = map (uncurry3 replaceJson) subs++replaceJson :: JsonPath -> MT.Text -> JsonPath -> MDoc+replaceJson pathTo v pathFrom+  = (access "$json_obj" pathTo)+  <+> "="+  <+> (access ([idoc|$json_#{pretty v}|]) pathFrom)+  <> ";"++access :: MDoc -> JsonPath -> MDoc+access v ps = cat $ punctuate "->" (v : map pathElement ps)  ++pathElement :: JsonAccessor -> MDoc+pathElement (JsonIndex i) = brackets (pretty i)+pathElement (JsonKey key) = braces (pretty key)++readJsonArg ::EVar -> Int -> MDoc+readJsonArg v i = [idoc|my $json_#{pretty v} = $json->decode($ARGV[#{pretty i}]); |]++argT :: Int -> MDoc+argT i = "'$_[" <> pretty i <> "]'" 
+ library/Morloc/CodeGenerator/Serial.hs view
@@ -0,0 +1,283 @@+{-|+Module      : Morloc.CodeGenerator.Serial+Description : Process serialization trees+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.CodeGenerator.Serial+  ( makeSerialAST +  , findSerializationCycles +  , chooseSerializationCycle+  , isSerializable+  , prettySerialOne+  , serialAstToType+  , serialAstToType'+  , shallowType+  ) where++import Morloc.CodeGenerator.Namespace+import Morloc.CodeGenerator.Internal+import Morloc.Frontend.Namespace (resolve)+import qualified Morloc.Monad as MM+import qualified Data.Map as Map+import qualified Morloc.Frontend.Lang.DefaultTypes as Def+import Morloc.Pretty (prettyPackMap)+import Morloc.Data.Doc++pv2tv :: PVar -> TVar+pv2tv (PV lang _ v) = TV (Just lang) v++defaultListFirst :: TypeP -> TypeP+defaultListFirst t = defaultListAll t !! 0++defaultTupleFirst :: [TypeP] -> TypeP+defaultTupleFirst ts = defaultTupleAll ts !! 0++-- | A metaphor for America+dummies :: Maybe Lang -> [UnresolvedType]+dummies lang = repeat $ VarU (TV lang "dummy")++defaultListAll :: TypeP -> [TypeP]+defaultListAll t =+  [ ArrP (PV lang gtype v) [t]+  | (ArrU (TV (Just lang) v) _) <- Def.defaultList (langOf t) (head (dummies (langOf t)))+  ]+  where+    gtype = case Def.defaultList Nothing (head (dummies Nothing)) of+      ((ArrU (TV _ v1) _):_) -> Just v1+      _ -> Nothing++isList :: TypeP -> Bool+isList (ArrP (PV lang _ v) [_]) =+  let ds = Def.defaultList (Just lang) (head (dummies (Just lang)))+  in length [v' | (ArrU (TV _ v') _) <- ds, v == v'] > 0+isList _ = False++defaultTupleAll :: [TypeP] -> [TypeP]+defaultTupleAll [] = error $ "Illegal empty tuple"+defaultTupleAll ts@(t:_) =+    [ ArrP (PV lang gtype v) ts+    | (ArrU (TV (Just lang) v) _) <- Def.defaultTuple (langOf t) (take (length ts) (dummies (langOf t)))+    ]+  where+    gtype = case Def.defaultTuple Nothing (take (length ts) (dummies Nothing)) of+      ((ArrU (TV _ v1) _):_) -> Just v1+      _ -> Nothing++isTuple :: TypeP -> Bool+isTuple (ArrP (PV lang _ v) ts) =+  let ds = Def.defaultTuple (Just lang) (take (length ts) (dummies (Just lang)))+  in length [v' | (ArrU (TV _ v') _) <- ds, v == v'] > 0+isTuple _ = False++isPrimitiveType :: (Maybe Lang -> [UnresolvedType]) -> TypeP -> Bool+isPrimitiveType lookupDefault t =+  let xs = filter (typeEqual t)+         $ [ VarP (PV lang gtype v)+           | (VarU (TV (Just lang) v)) <- lookupDefault (langOf t)]+  in length xs > 0+  where+    gtype = case lookupDefault Nothing of+      ((VarU (TV _ g)):_) -> Just g+      _ -> Nothing++-- | recurse all the way to a serializable type+serialAstToType :: SerialAST One -> MorlocMonad TypeP+serialAstToType (SerialPack _ (One (_, s))) = serialAstToType s+serialAstToType (SerialList s) = serialAstToType s |>> defaultListFirst+serialAstToType (SerialTuple ss) = mapM serialAstToType ss |>> defaultTupleFirst+serialAstToType (SerialObject r v ps rs) = do+  rs' <- mapM (serialAstToType . snd) rs+  return $ NamP r v ps (zip (map fst rs) rs')+serialAstToType (SerialNum    x) = return $ VarP x+serialAstToType (SerialBool   x) = return $ VarP x+serialAstToType (SerialString x) = return $ VarP x+serialAstToType (SerialNull   x) = return $ VarP x+serialAstToType (SerialUnknown x)+  = MM.throwError . SerializationError . render+  $ "Cannot guess serialization type:" <+> viaShow x++-- | recurse all the way to a serializable type, unsafe+serialAstToType' :: SerialAST One -> TypeP+serialAstToType' (SerialPack _ (One (_, s))) = serialAstToType' s+serialAstToType' (SerialList s) = defaultListFirst $ serialAstToType' s+serialAstToType' (SerialTuple ss) = defaultTupleFirst $ map serialAstToType' ss+serialAstToType' (SerialObject r v ps rs) = NamP r v ps (zip (map fst rs) (map (serialAstToType' . snd) rs))+serialAstToType' (SerialNum    x) = VarP x+serialAstToType' (SerialBool   x) = VarP x+serialAstToType' (SerialString x) = VarP x+serialAstToType' (SerialNull   x) = VarP x+serialAstToType' (SerialUnknown _) = error "Cannot guess serialization type"+++-- | get only the toplevel type+shallowType :: SerialAST One -> MorlocMonad TypeP+shallowType (SerialPack _ (One (p, _))) = return (typePackerFrom p)+shallowType (SerialList s) = shallowType s |>> defaultListFirst+shallowType (SerialTuple ss) = mapM shallowType ss |>> defaultTupleFirst+shallowType (SerialObject r v ps rs) = do+  ts <- mapM shallowType (map snd rs)+  return $ NamP r v ps (zip (map fst rs) ts)+shallowType (SerialNum    x) = return $ VarP x+shallowType (SerialBool   x) = return $ VarP x+shallowType (SerialString x) = return $ VarP x+shallowType (SerialNull   x) = return $ VarP x+shallowType (SerialUnknown _) = MM.throwError . SerializationError+                                       $ "Cannot guess serialization type"++makeSerialAST+  :: GMeta+  -> TypeP+  -> MorlocMonad (SerialAST Many)+makeSerialAST _ (UnkP v) = return $ SerialUnknown v+makeSerialAST m t@(VarP v@(PV _ _ _))+  | isPrimitiveType Def.defaultNull   t = return $ SerialNull   v+  | isPrimitiveType Def.defaultBool   t = return $ SerialBool   v+  | isPrimitiveType Def.defaultString t = return $ SerialString v+  | isPrimitiveType Def.defaultNumber t = return $ SerialNum    v+  | otherwise = makeSerialAST m (ArrP v [])+makeSerialAST _ (FunP _ _)+  = MM.throwError . SerializationError+  $ "Cannot serialize functions"+makeSerialAST m t@(ArrP v@(PV _ _ s) ts)+  | isList t = SerialList <$> makeSerialAST m (ts !! 0)+  | isTuple t = SerialTuple <$> mapM (makeSerialAST m) ts+  | otherwise = case Map.lookup (pv2tv v, length ts) (metaPackers m) of+      (Just ps) -> do+        ps' <- mapM (resolvePacker t ts) ps+        ts' <- mapM (makeSerialAST m) (map typePackerType ps')+        return $ SerialPack v (Many (zip ps' ts'))+      Nothing -> MM.throwError . SerializationError . render+        $ "Cannot find constructor" <+> dquotes (pretty s)+        <> "<" <> pretty (length ts) <> ">"+        <+> "in packmap:\n" <> prettyPackMap (metaPackers m)+makeSerialAST m (NamP r v ps rs) = do+  ts <- mapM (makeSerialAST m) (map snd rs)+  return $ SerialObject r v ps (zip (map fst rs) ts)++pvarEqual :: PVar -> PVar -> Bool+pvarEqual (PV lang1 _ v1) (PV lang2 _ v2) = lang1 == lang2 && v1 == v2 ++typeEqual :: TypeP -> TypeP -> Bool+typeEqual (VarP v1) (VarP v2) = pvarEqual v1 v2+typeEqual (ArrP v1 ts1) (ArrP v2 ts2)+  | length ts1 /= length ts2 = False+  | otherwise = foldl (&&) (pvarEqual v1 v2) (zipWith typeEqual ts1 ts2 )+typeEqual (NamP _ v1 _ rs1) (NamP _ v2 _ rs2)+  =  (pvarEqual v1 v2)+  && map fst rs1 == map fst rs2+  && foldl (&&) True (zipWith typeEqual (map snd rs1) (map snd rs2))+typeEqual _ _ = False+++resolvePacker :: TypeP -> [TypeP] -> UnresolvedPacker -> MorlocMonad TypePacker+resolvePacker packedType ts u = do +  t <- resolveType ts (unresolvedPackerCType u) +  return $ TypePacker+    { typePackerType = t+    , typePackerFrom = packedType+    , typePackerForward = unresolvedPackerForward u+    , typePackerReverse = unresolvedPackerReverse u+    }++resolveType :: [TypeP] -> UnresolvedType -> MorlocMonad TypeP+resolveType [] (ForallU _ _) = MM.throwError . SerializationError $ "Packer parity error"+resolveType [] u = weaveTypes Nothing (resolve u)+resolveType (t:ts) (ForallU v u) = substituteP v t <$> resolveType ts u+resolveType (_:_) _ = MM.throwError . SerializationError $ "Packer parity error"++-- | substitute all appearances of a given variable with a given new type+substituteP :: TVar -> TypeP -> TypeP -> TypeP+substituteP v0 r0 t0 = sub t0+  where+    sub :: TypeP -> TypeP+    sub t'@(UnkP _) = t'+    sub t'@(VarP (PV lang _ v'))+      | v0 == (TV (Just lang) v') = r0+      | otherwise = t'+    sub (FunP t1 t2) = FunP (sub t1) (sub t2)+    sub (ArrP v' ts) = ArrP v' (map sub ts)+    sub (NamP r v' ps rs) = NamP r v' (map sub ps) [(x, sub t') | (x, t') <- rs]++-- | Given serialization trees for two languages, where each serialization tree+-- may contain, try to find+findSerializationCycles+  :: ([(SerialAST One, SerialAST One)] -> Maybe (SerialAST One, SerialAST One))+  -- ^ pruning function+  -> SerialAST Many+  -> SerialAST Many+  -> Maybe (SerialAST One, SerialAST One)+findSerializationCycles choose x0 y0 = f x0 y0 where+  f :: SerialAST Many -> SerialAST Many -> Maybe (SerialAST One, SerialAST One) +  -- reduce constructs until we get down to something that has general meaning+  f (SerialPack v (Many ss1)) s2+    = choose+    . catMaybes+    $ [ fmap (\(x,y)->(SerialPack v (One (p1,x)),y)) (f s1 s2)+      | (p1,s1) <- ss1]+  -- same as above, just swap the arguments+  f s1 s2@(SerialPack _ _) = case f s2 s1 of +    (Just (x,y)) -> Just (y,x)+    Nothing -> Nothing+  f (SerialList s1) (SerialList s2) = case f s1 s2 of +      (Just (s1', s2')) -> Just (SerialList s1', SerialList s2')+      Nothing -> Nothing+  f (SerialTuple ts1) (SerialTuple ts2)+    | length ts1 /= length ts1 = Nothing+    | otherwise = case fmap unzip . sequence $ zipWith f ts1 ts2 of+        (Just (xs,ys)) -> Just (SerialTuple xs, SerialTuple ys)+        Nothing -> Nothing+  f (SerialObject r1 v1 ps1 rs1) (SerialObject r2 v2 ps2 rs2)+    | map fst rs1 /= map fst rs2 = Nothing +    | otherwise = case fmap unzip . sequence $ zipWith f ts1 ts2 of+        Nothing -> Nothing+        Just (rs1', rs2') -> Just ( SerialObject r1 v1 ps1 (zip (map fst rs1) rs1')+                                  , SerialObject r2 v2 ps2 (zip (map fst rs2) rs2'))+      where+        ts1 = map snd rs1+        ts2 = map snd rs1+  f (SerialNum    x1) (SerialNum    x2) = Just (SerialNum    x1, SerialNum    x2)+  f (SerialBool   x1) (SerialBool   x2) = Just (SerialBool   x1, SerialBool   x2)+  f (SerialString x1) (SerialString x2) = Just (SerialString x1, SerialString x2)+  f (SerialNull   x1) (SerialNull   x2) = Just (SerialNull   x1, SerialNull   x2)+  f (SerialUnknown v1) (SerialUnknown v2) = Just (SerialUnknown v1, SerialUnknown v2)+  f _ _ = Nothing++-- | Given a list of possible ways to (de)serialize data between two languages,+-- choose one (or none if the list is empty). Currently I just take the first+-- in the list, but different cycles may have very different performance, so+-- this will be an important optimization step later on.+chooseSerializationCycle+  :: [(SerialAST One, SerialAST One)]+  -> Maybe (SerialAST One, SerialAST One)+chooseSerializationCycle [] = Nothing+chooseSerializationCycle (x:_) = Just x++-- | Determine if a SerialAST can be directly translated to JSON, if not it+-- will need to be further reduced.+isSerializable :: Functor f => SerialAST f -> Bool+isSerializable (SerialPack _ _) = False+isSerializable (SerialList x) = isSerializable x+isSerializable (SerialTuple xs) = all isSerializable xs +isSerializable (SerialObject _ _ _ rs) = all isSerializable (map snd rs) +isSerializable (SerialNum    _) = True+isSerializable (SerialBool   _) = True+isSerializable (SerialString _) = True+isSerializable (SerialNull   _) = True+isSerializable (SerialUnknown _) = True -- are you feeling lucky?++prettySerialOne :: SerialAST One -> MDoc+prettySerialOne (SerialPack _ _) = "SerialPack"+prettySerialOne (SerialList x) = "SerialList" <> parens (prettySerialOne x)+prettySerialOne (SerialTuple xs) = "SerialTuple" <> tupled (map prettySerialOne xs)+prettySerialOne (SerialObject r _ _ rs)+  = block 4 ("SerialObject@" <> viaShow r)+  $ vsep (map (\(k,v) -> parens (viaShow k) <> "=" <> prettySerialOne v) rs)+prettySerialOne (SerialNum    _) = "SerialNum"+prettySerialOne (SerialBool   _) = "SerialBool"+prettySerialOne (SerialString _) = "SerialString"+prettySerialOne (SerialNull   _) = "SerialNull"+prettySerialOne (SerialUnknown _) = "SerialUnknown"
+ library/Morloc/Config.hs view
@@ -0,0 +1,135 @@+{-|+Module      : Morloc.Config+Description : Handle local configuration+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.Config+  ( Config(..)+  , loadMorlocConfig+  , loadDefaultMorlocConfig+  , buildPoolCallBase+  , getDefaultConfigFilepath+  , getDefaultMorlocTmpDir+  , makeLibSourceString+  ) where++import Data.Aeson (FromJSON(..), (.!=), (.:?), withObject)+import Morloc.Data.Doc+import Morloc.Namespace+import qualified Morloc.Language as ML+import qualified Data.HashMap.Strict as H+import qualified Data.Yaml.Config as YC+import qualified Morloc.Data.Text as MT+import qualified Morloc.System as MS+import Morloc.Pretty ()+++getDefaultConfigFilepath :: IO Path+getDefaultConfigFilepath =+  MS.getHomeDirectory |>> MS.appendPath (Path ".morloc/config")++instance FromJSON Path where+  parseJSON = fmap Path . parseJSON++-- FIXME: remove this chronic multiplication+instance FromJSON Config where+  parseJSON =+    withObject "object" $ \o ->+      Config+        <$> fmap Path (o .:? "home" .!= "$HOME/.morloc")+        <*> fmap Path (o .:? "library" .!= "$HOME/.morloc/lib")+        <*> fmap Path (o .:? "tmpdir" .!= "$HOME/.morloc/tmp" )+        <*> fmap Path (o .:? "lang_python3" .!= "python3")+        <*> fmap Path (o .:? "lang_R" .!= "Rscript")+        <*> fmap Path (o .:? "lang_perl" .!= "perl")++-- | Load the default Morloc configuration, ignoring any local configurations.+loadDefaultMorlocConfig :: IO Config+loadDefaultMorlocConfig = do+  defaults <- defaultFields+  return $+    Config+      (Path $ defaults H.! "home")+      (Path $ defaults H.! "library")+      (Path $ defaults H.! "tmpdir")+      (Path "python") -- lang_python3+      (Path "Rscript") -- lang_R+      (Path "perl") -- lang_perl++-- | Load a Morloc config file. If no file is given (i.e., Nothing), then the+-- default configuration will be used.+loadMorlocConfig :: Maybe Path -> IO Config+loadMorlocConfig Nothing = do+  defaults <- defaultFields+  MS.loadYamlConfig+    Nothing +    (YC.useCustomEnv defaults)+    loadDefaultMorlocConfig+loadMorlocConfig (Just configFile) = do+  configExists <- MS.fileExists configFile+  defaults <- defaultFields+  if configExists+    then+      MS.loadYamlConfig+        (Just [configFile])+        (YC.useCustomEnv defaults)+        loadDefaultMorlocConfig+    else+      loadMorlocConfig Nothing ++-- | Create the base call to a pool (without arguments)+-- For example:+--   ./pool.R 1 --+--   ./pool.py 1 --+--   ./pool-cpp.out 1 --+--   ./pool.R 1 [1,2,3] true+buildPoolCallBase+  :: Config+  -> Maybe Lang+  -> Int+  -> Maybe [MDoc]+buildPoolCallBase _ (Just CLang) i =+  Just ["./" <> pretty (ML.makeExecutableName CLang "pool"), pretty i]+buildPoolCallBase _ (Just CppLang) i =+  Just ["./" <> pretty (ML.makeExecutableName CppLang "pool"), pretty i]+buildPoolCallBase c (Just RLang) i =+  Just [pretty (configLangR c), pretty (ML.makeExecutableName RLang "pool"), pretty i]+buildPoolCallBase c (Just Python3Lang) i =+  Just [pretty (configLangPython3 c), pretty (ML.makeExecutableName Python3Lang "pool"), pretty i]+buildPoolCallBase _ _ _ = Nothing -- FIXME: add error handling++-- A key value map+defaultFields :: IO (H.HashMap MT.Text MT.Text)+defaultFields = do+  home <- fmap unPath getDefaultMorlocHome+  lib <- fmap unPath getDefaultMorlocLibrary+  tmp <- fmap unPath getDefaultMorlocTmpDir+  return $ H.fromList [("home", home), ("library", lib), ("tmpdir", tmp)]++-- | Get the Morloc home directory (absolute path)+getDefaultMorlocHome :: IO Path+getDefaultMorlocHome = MS.getHomeDirectory |>> MS.appendPath (Path ".morloc")++-- | Get the Morloc library directory (absolute path). Usually this will be a+-- folder inside the home directory.+getDefaultMorlocLibrary :: IO Path+getDefaultMorlocLibrary = MS.getHomeDirectory |>> MS.appendPath (Path ".morloc/lib")++-- | Get the Morloc default temporary directory. This will store generated+-- SPARQL queries and rdf dumps that can be used in debugging.+getDefaultMorlocTmpDir :: IO Path+getDefaultMorlocTmpDir = MS.getHomeDirectory |>> MS.appendPath (Path ".morloc/tmp")++-- | Get a source string for a library module. This will 1) remove the+-- user-specific home directory and 2) replace '/' separators with '.'. An+-- input of Nothing indicates the input is a local file or STDIN.+makeLibSourceString :: Maybe Path -> MorlocMonad (Maybe Path)+makeLibSourceString (Just (Path x)) = do+  homedir <- liftIO getDefaultMorlocLibrary+  let x' = maybe x id (MT.stripPrefix (unPath homedir) x)+  let x'' = maybe x' id (MT.stripPrefix "/" x')+  return . Just . Path . MT.replace "/" "__" . MT.replace "." "_" $ x''+makeLibSourceString Nothing = return Nothing
+ library/Morloc/Data/DAG.hs view
@@ -0,0 +1,242 @@+{-|+Module      : Morloc.Data.DAG+Description : Functions for working with directed acyclic graphs+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Data.DAG+  ( edgelist+  , insertEdge+  , edges+  , nodes+  , lookupNode+  , lookupEdge+  , lookupEdgeTriple+  , local+  , roots+  , leafs+  , findCycle+  , mapNode+  , mapNodeM+  , mapNodeWithKey+  , mapNodeWithKeyM+  , mapEdge+  , mapEdgeWithNode+  , mapNodeWithEdge+  , mapEdgeWithNodeM+  , lookupAliasedTerm+  , lookupAliasedTermM+  , synthesizeDAG+  ) where++import Morloc.Namespace+import qualified Morloc.Monad as MM+import qualified Data.Map as Map +import qualified Data.Set as Set ++edgelist :: DAG k e n -> [(k,k)]+edgelist d = concat [[(k,j) | (j,_) <- xs] | (k, (_, xs)) <- Map.toList d ]++insertEdge :: Ord k => k -> k -> e -> DAG k e n -> DAG k e n+insertEdge k1 k2 e d = Map.alter f k1 d+  where  +    -- f :: Maybe [(k, e)] -> Maybe [(k, e)]+    f Nothing = error "Cannot add edge to non-existant node"+    f (Just (n,xs)) = Just $ (n,(k2,e):xs)++-- | Get all edges+edges :: DAG k e n -> [e]+edges = map snd . concat . map snd . Map.elems++-- | Get all nodes+nodes :: DAG k e n -> [n]+nodes = map fst . Map.elems++lookupNode :: Ord k => k -> DAG k e n -> Maybe n+lookupNode k d = case Map.lookup k d of+  (Just (n,_)) -> Just n+  Nothing -> Nothing++lookupEdge :: (Ord k) => k -> k -> DAG k e n -> Maybe e+lookupEdge k1 k2 d = case Map.lookup k1 d of+  (Just (_,xs)) -> lookup k2 xs+  Nothing -> Nothing++lookupEdgeTriple :: (Ord k) => k -> k -> DAG k e n -> Maybe (n, e, n)+lookupEdgeTriple k1 k2 d = do+  (n1, xs) <- Map.lookup k1 d+  e <- lookup k2 xs+  (n2, _) <- Map.lookup k2 d+  return (n1, e, n2)++local :: Ord k => k -> DAG k e n -> Maybe (n, [(k, e, n)])+local k d = do+  (n1, xs) <- Map.lookup k d+  ns <- mapM (flip lookupNode $ d) (map fst xs)+  return $ (n1, [(k',e,n2) | (n2, (k', e)) <- zip ns xs])++-- | Get all roots+roots :: Ord k => DAG k e n -> [k]+roots d = Set.toList $ Set.difference parents children+  where+    g = edgelist d+    parents = Map.keysSet d+    children = Set.fromList (map snd g)++-- | Get all leaves that have no children+leafs :: DAG k e n -> [k]+leafs d = [k | (k, (_, [])) <- Map.toList d]++-- | Searches a DAG for a cycle, stops on the first observed cycle and returns+-- the path.+findCycle :: Ord k => DAG k e n -> Maybe [k]+findCycle d = case mapMaybe (findCycle' []) (roots d) of +  [] -> Nothing+  (x:_) -> Just x+  where+    -- findCycle' :: [k] -> k -> Maybe [k]+    findCycle' seen k+      | elem k seen = Just seen+      | otherwise = case Map.lookup k d of+          Nothing -> Nothing -- we have reached a leaf+          (Just (_,xs)) -> case mapMaybe (findCycle' (k:seen)) (map fst xs) of+            [] -> Nothing+            (x:_) -> Just x++-- Map function over nodes independent of the edge data+mapNode :: (n1 -> n2) -> DAG k e n1 -> DAG k e n2+mapNode f d = Map.map (\(n, xs) -> (f n, xs)) d++mapNodeWithKey :: (k -> n1 -> n2) -> DAG k e n1 -> DAG k e n2+mapNodeWithKey f d = Map.mapWithKey (\k (n, xs) -> (f k n, xs)) d++-- Map function over nodes independent of the edge data+mapNodeM :: Ord k => (n1 -> MorlocMonad n2) -> DAG k e n1 -> MorlocMonad (DAG k e n2)+mapNodeM f d+  = mapM (\(k,(n,xs)) -> f n >>= (\n' -> return (k, (n',xs)))) (Map.toList d)+  |>> Map.fromList++-- Map function over nodes independent of the edge data+mapNodeWithKeyM :: Ord k => (k -> n1 -> MorlocMonad n2) -> DAG k e n1 -> MorlocMonad (DAG k e n2)+mapNodeWithKeyM f d+  = mapM (\(k,(n,xs)) -> f k n >>= (\n' -> return (k, (n',xs)))) (Map.toList d)+  |>> Map.fromList++-- Map function over edges independent of the node data+mapEdge :: (e1 -> e2) -> DAG k e1 n -> DAG k e2 n+mapEdge f = Map.map (\(n, xs) -> (n, [(k, f e) | (k,e) <- xs]))++-- | map over edges given the nodes the edge connects+mapEdgeWithNode+  :: Ord k+  => (n -> e1 -> n -> e2)+  -> DAG k e1 n -> DAG k e2 n+mapEdgeWithNode f d = Map.mapWithKey runit d where+  runit k _ = case local k d of+    (Just (n1, xs)) -> (n1, [(k2, f n1 e n2) | (k2, e, n2) <- xs])+    Nothing -> error "Bad DAG"++-- | Map node data given edges and child data+mapNodeWithEdge+  :: Ord k+  => (n1 -> [(k, e, n1)] -> n2)+  -> DAG k e n1 -> DAG k e n2+mapNodeWithEdge f d = Map.mapWithKey fkey d where+  fkey k1 (_, xs0) = case local k1 d of+    (Just (n1, xs1)) -> (f n1 xs1, xs0)+    Nothing -> error "Bad DAG"++-- | map over edges given the nodes the edge connects+mapEdgeWithNodeM+  :: Ord k+  => (n -> e1 -> n -> MorlocMonad e2)+  -> DAG k e1 n -> MorlocMonad (DAG k e2 n)+mapEdgeWithNodeM f d = mapM runit (Map.toList d) |>> Map.fromList+  where+    runit (k1, _) = case local k1 d of +      (Just (n1, xs)) -> do+        e2s <- mapM (\(_, e, n2) -> f n1 e n2) xs+        return (k1, (n1, zip (map (\(x,_,_)->x) xs) e2s))+      Nothing -> MM.throwError . CallTheMonkeys $ "Incomplete DAG, missing object"++-- | Map a monadic function over a DAG yielding a new DAG with the same+-- topology but a new node values. If the DAG contains cycles, Nothing is+-- returned.+synthesizeDAG+  :: (Ord k, Monad m)+  => (k -> n1 -> [(k, e, n2)] -> m n2)+  -> DAG k e n1+  -> m (Maybe (DAG k e n2))+synthesizeDAG f d0 = synthesizeDAG' (Just Map.empty) where+  -- iteratively synthesize all nodes that have met dependencies+  synthesizeDAG' Nothing = return Nothing+  synthesizeDAG' (Just dn)+    -- stop, we have completed the mapping. Jubilation.+    | Map.size d0 == Map.size dn = return (Just dn) +    | otherwise = do+        -- traverse the original making any nodes that now have met dependencies+        dn' <- foldlM addIfPossible dn (Map.toList d0)+        if Map.size dn' == Map.size dn+          -- if map size hasn't changed, then nothing was added and we are stuck+          then return Nothing+          -- otherwise move on to the next iteration+          else synthesizeDAG' (Just dn')++  -- add leaves+  addIfPossible dn (k1, (n1, []))+    | Map.member k1 dn = return dn+    | otherwise = do+        n2 <- f k1 n1 []+        return $ Map.insert k1 (n2, []) dn+  -- add nodes where all children have been processed+  addIfPossible dn (k1, (n1, xs))+    | Map.member k1 dn = return dn+    | otherwise = case mapM (\k -> Map.lookup k dn) (map fst xs) of+        Nothing -> return dn+        (Just children) -> do+          let augmented = [(k,e,n2) | ((k, e), (n2, _)) <- zip xs children]+          n2 <- f k1 n1 augmented+          return $ Map.insert k1 (n2, xs) dn+++lookupAliasedTerm+  :: (Ord k, Eq v)+  => v+  -- ^ look up this term+  -> k+  -- ^ starting from this node+  -> (n -> a)+  -- ^ extract the desired data with this function+  -> DAG k [(v,v)] n+  -- ^ original DAG where edges are "import as" statements+  -> DAG k None (v,a)+  -- ^ The final DAG with no edge attribute and the +lookupAliasedTerm v0 k0 f d0 = fromJust $ lookupAliasedTermM v0 k0 (Just . f) d0++lookupAliasedTermM+  :: (Monad m, Ord k, Eq v)+  => v+  -- ^ look up this term+  -> k+  -- ^ starting from this node+  -> (n -> m a)+  -- ^ extract the desired data with this function+  -> DAG k [(v,v)] n+  -> m (DAG k None (v,a))+lookupAliasedTermM v0 k0 f d0 = lookupAliasedTerm' v0 k0 mempty where+  lookupAliasedTerm' v k d+    | Map.member k d = return d+    | otherwise = case Map.lookup k d0 of+        Nothing -> error "Could not find module"+        (Just (n, xs)) -> do+          let xs' = [ (k', [(v1,v2) | (v1,v2) <- vs, v2 == v])+                    | (k', vs) <- xs+                    , elem v (map snd vs)]+              edges' = map (\(k', _) -> (k', None)) xs'+          n' <- f n+          foldlM (\d2 (k2,v2) -> lookupAliasedTerm' v2 k2 d2)+                (Map.insert k ((v, n'), edges') d)+                (concat [zip (repeat k') (map fst vs) | (k', vs) <- xs'])
+ library/Morloc/Data/Doc.hs view
@@ -0,0 +1,58 @@+{-|+Module      : Morloc.Data.Doc+Description : A wrapper around prettyprint+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++This module re-exports Leijen's text builder along with a few other utilities.+-}+module Morloc.Data.Doc+  ( module Data.Text.Prettyprint.Doc+  , module Data.Text.Prettyprint.Doc.Render.Text+  , render+  , render'+  , textEsc'+  , tupledNoFold+    -- ** These are not strictly necessary, since @pretty@ could be used, but+    -- they avoid the requirements of an explicity type signature.+  , int+  , integer+  , block+  ) where++import Data.Monoid ((<>))+import qualified Data.Text as DT+import Data.Text.Prettyprint.Doc hiding ((<>))+import Data.Text.Prettyprint.Doc.Render.Text++render :: Doc ann -> DT.Text+render = renderStrict . layoutPretty defaultLayoutOptions++render' :: Doc ann -> String+render' = show -- NOTE: This ignores layouts++int :: Int -> Doc ann+int = pretty++integer :: Integer -> Doc ann+integer = pretty++block :: Int -> Doc ann -> Doc ann -> Doc ann+block level header body = align . vsep $ [header, "{", indent level body, "}"]++-- | a tupled function that does not fold long lines (folding breaks commenting)+tupledNoFold :: [Doc ann] -> Doc ann+tupledNoFold [] = ""+tupledNoFold (x:xs) = parens (foldl (\l r -> l <> "," <+> r) x xs)++textEsc' :: DT.Text -> Doc ann+textEsc' lit = (dquotes . pretty) $ DT.concatMap escapeChar lit+  where+    escapeChar '\n' = "\\n"+    escapeChar '\t' = "\\t"+    escapeChar '\r' = "\\r"+    escapeChar '"' = "\\\""+    escapeChar '\\' = "\\\\"+    escapeChar c = DT.singleton c
+ library/Morloc/Data/Text.hs view
@@ -0,0 +1,91 @@+{-|+Module      : Morloc.Data.Text+Description : All things text+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++This is a general wrapper around all textual representations in Morloc.+-}+module Morloc.Data.Text+  ( module Data.Text+  , module Data.Text.IO+  , module Data.Text.Encoding+  , show'+  , pretty+  , read'+  , readMay'+  , parseTSV+  , unparseTSV+  , unenclose+  , unangle+  , unquote+  , undquote+  , stripPrefixIfPresent+  , liftToText+  ) where++import Data.Text hiding (map)+import Data.Text.Encoding+import Data.Text.IO+import qualified Data.Text.Lazy as DL+import Prelude hiding (concat, length, lines, unlines)+import qualified Safe+import qualified Text.Pretty.Simple as Pretty++show' :: Show a => a -> Text+show' = pack . show++read' :: Read a => Text -> a+read' = read . unpack++readMay' :: Read a => Text -> Maybe a+readMay' = Safe.readMay . unpack++stripPrefixIfPresent :: Text -> Text -> Text+stripPrefixIfPresent prefix text =+  case stripPrefix prefix text of+    (Just x) -> x+    Nothing -> text++pretty :: Show a => a -> Text+pretty = DL.toStrict . Pretty.pShowNoColor++-- | Parse a TSV, ignore first line (header). Cells are also unquoted and+-- wrapping angles are removed.+parseTSV :: Text -> [[Maybe Text]]+parseTSV =+  map (map (nonZero . undquote . unangle)) .+  map (split ((==) '\t')) . Prelude.tail . lines++liftToText :: (String -> String) -> Text -> Text+liftToText f = pack . f . unpack++-- | Make a TSV text+unparseTSV :: [[Maybe Text]] -> Text+unparseTSV = unlines . map renderRow+  where+    renderRow :: [Maybe Text] -> Text+    renderRow = intercalate "\t" . map renderCell+    renderCell :: Maybe Text -> Text+    renderCell (Nothing) = "-"+    renderCell (Just x) = x++nonZero :: Text -> Maybe Text+nonZero s =+  if length s == 0+    then Nothing+    else Just s++unenclose :: Text -> Text -> Text -> Text+unenclose a b x = maybe x id (stripPrefix a x >>= stripSuffix b)++unangle :: Text -> Text+unangle = unenclose "<" ">"++unquote :: Text -> Text+unquote = unenclose "'" "'"++undquote :: Text -> Text+undquote = unenclose "\"" "\""
+ library/Morloc/Error.hs view
@@ -0,0 +1,106 @@+{-|+Module      : Morloc.Error+Description : Prepare error messages from MorlocError types+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++MorlocError is the type used within morloc to store data related to any errors+that are encountered. Data constructors in the MorlocError type may associates+data with the error. This data may be an arbitrary message or any other type.+The @errmsg@ function in this module defines how these errors will be printed+to the user.+-}+module Morloc.Error () where++import Morloc.Namespace+import Morloc.Pretty (prettyType)+import Morloc.Data.Doc (render)+import qualified Morloc.Data.Text as MT++-- TODO: fix this orphan instance+instance Show MorlocError where+  show = MT.unpack . errmsg++errmsg :: MorlocError -> MT.Text+errmsg UnknownError = "UnknownError"+errmsg (InvalidRDF msg) = "Invalid RDF: " <> msg+errmsg (NotImplemented msg) = "Not yet implemented: " <> msg+errmsg (NotSupported msg) = "NotSupported: " <> msg+errmsg (UnknownLanguage lang) =+  "'" <> lang <> "' is not recognized as a supported language"+errmsg (SyntaxError err) = "SyntaxError: " <> MT.show' err+errmsg (SerializationError t) = "SerializationError: " <> t+errmsg (TypeConflict t1 t2) = "TypeConflict: cannot cast " <> t1 <> " as " <> t2+errmsg (TypeError msg) = "TypeError: " <> msg+errmsg (CannotLoadModule t) = "CannotLoadModule: " <> t+errmsg (SystemCallError cmd loc msg) =+  "System call failed at (" <>+  loc <> "):\n" <> " cmd> " <> cmd <> "\n" <> " msg>\n" <> msg+errmsg (PoolBuildError msg) = "PoolBuildError: " <> msg+errmsg (SelfRecursiveTypeAlias v) = "SelfRecursiveTypeAlias: " <> MT.show' v+errmsg (MutuallyRecursiveTypeAlias vs) = "MutuallyRecursiveTypeAlias: " <> MT.unwords (map MT.show' vs)+errmsg (BadTypeAliasParameters (TV _ v) exp' obs)+  =  "BadTypeAliasParameters: for type alias " <> MT.show' v+  <> " expected " <> MT.show' exp'+  <> " parameters but found " <> MT.show' obs+errmsg (ConflictingTypeAliases t1 t2)+  = "ConflictingTypeAliases: (" <> MT.show' t1 <> ", " <> MT.show' t2 <> ")" +errmsg NoBenefits =+  "Manifolds in this context need to be fully resolved. " <>+  "This is probably due to a bug in the code."+errmsg (CallTheMonkeys msg) =+  "There is a bug in the code, send this message to the maintainer: " <> msg+errmsg (GeneratorError msg) = "GeneratorError: " <> msg+errmsg MissingGeneralType = "MissingGeneralType"+errmsg AmbiguousGeneralType = "AmbiguousGeneralType"+errmsg (SubtypeError t1 t2) = "SubtypeError: (" <> MT.show' t1 <> ") <: (" <> MT.show' t2 <> ")"+errmsg ExistentialError = "ExistentialError"+errmsg UnsolvedExistentialTerm = "UnsolvedExistentialTerm"+errmsg BadExistentialCast = "BadExistentialCast"+errmsg (AccessError _) = "AccessError"+errmsg NonFunctionDerive = "NonFunctionDerive"+errmsg (UnboundVariable v) = "UnboundVariable: " <> unEVar v+errmsg OccursCheckFail = "OccursCheckFail"+errmsg EmptyCut = "EmptyCut"+errmsg TypeMismatch = "TypeMismatch"+errmsg ToplevelRedefinition = "ToplevelRedefinition"+errmsg BadRecordAccess = "BadRecordAccess" +errmsg NoAnnotationFound = "NoAnnotationFound"+errmsg (OtherError msg) = "OtherError: " <> msg+-- container errors+errmsg EmptyTuple = "EmptyTuple"+errmsg TupleSingleton = "TupleSingleton"+errmsg EmptyRecord = "EmptyRecord"+-- module errors+errmsg (MultipleModuleDeclarations mv) = "MultipleModuleDeclarations: " <> MT.unwords (map unMVar mv) +errmsg (BadImport mv ev) = "BadImport: " <> unMVar mv <> "::" <> unEVar ev+errmsg (CannotFindModule name) = "Cannot find morloc module '" <> unMVar name <> "'"+errmsg CyclicDependency = "CyclicDependency"+errmsg (SelfImport _) = "SelfImport"+errmsg BadRealization = "BadRealization"+errmsg MissingSource = "MissingSource"+-- serialization errors+errmsg (MissingPacker place t)+  = "SerializationError: no packer found for type ("+  <> render (prettyType (unCType t)) <> ") at " <> place +errmsg (MissingUnpacker place t)+  = "SerializationError: no unpacker found for type ("+  <> render (prettyType (unCType t)) <> ") at " <> place+-- type extension errors+errmsg (AmbiguousPacker _) = "AmbiguousPacker"+errmsg (AmbiguousUnpacker _) = "AmbiguousUnpacker"+errmsg (AmbiguousCast _ _) = "AmbiguousCast"+errmsg (IncompatibleRealization _) = "IncompatibleRealization"+errmsg MissingAbstractType = "MissingAbstractType"+errmsg ExpectedAbstractType = "ExpectedAbstractType"+errmsg CannotInferConcretePrimitiveType = "CannotInferConcretePrimitiveType"+errmsg ToplevelStatementsHaveNoLanguage = "ToplevelStatementsHaveNoLanguage"+errmsg InconsistentWithinTypeLanguage = "InconsistentWithinTypeLanguage"+errmsg CannotInferLanguageOfEmptyRecord = "CannotInferLanguageOfEmptyRecord"+errmsg ConflictingSignatures = "ConflictingSignatures: currently a given term can have only one type per language"+errmsg CompositionsMustBeGeneral = "CompositionsMustBeGeneral"+errmsg IllegalConcreteAnnotation = "IllegalConcreteAnnotation"+errmsg (DagMissingKey msg) = "DagMissingKey: " <> msg+errmsg TooManyRealizations = "TooManyRealizations"
+ library/Morloc/Frontend/API.hs view
@@ -0,0 +1,87 @@+{-|+Module      : Morloc.Frontend.API+Description : Morloc frontend API+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.Frontend.API+  ( parse+  , typecheck+  , runStack+  , Parser.readType+  , Pretty.ugly+  , Pretty.cute+  ) where++import Morloc.Frontend.Namespace+import qualified Control.Monad.Except as ME+import qualified Control.Monad.Reader as MR+import qualified Control.Monad.State as MS+import qualified Control.Monad.Writer as MW+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Morloc.Data.DAG as MDD+import qualified Morloc.Data.Text as MT+import qualified Morloc.Module as Mod+import qualified Morloc.Monad as MM+import qualified Morloc.Frontend.Parser as Parser+import qualified Morloc.Frontend.Infer as Infer+import qualified Morloc.Frontend.Pretty as Pretty++parse ::+     Maybe Path+  -> Code -- ^ code of the current module+  -> MorlocMonad (DAG MVar Import ParserNode)+parse f (Code code) = parseImports (Parser.readProgram f code mempty)+  where+    parseImports+      :: DAG MVar Import ParserNode+      -> MorlocMonad (DAG MVar Import ParserNode)+    parseImports d+      | length unimported == 0 = return d+      | otherwise = do+          importPath <- Mod.findModule (head unimported)+          Mod.loadModuleMetadata importPath+          (path', code') <- openLocalModule importPath+          parseImports (Parser.readProgram path' code' d)+      where+        g = MDD.edgelist d+        parents = Map.keysSet d+        children = Set.fromList (map snd g)+        unimported = Set.toList $ Set.difference children parents++-- | assume @t@ is a filename and open it, return file name and contents+openLocalModule :: Path -> MorlocMonad (Maybe Path, MT.Text)+openLocalModule filename = do+  code <- liftIO $ MT.readFile (MT.unpack . unPath $ filename)+  return (Just filename, code)+++typecheck+  :: DAG MVar [(EVar, EVar)] PreparedNode+  -> MorlocMonad (DAG MVar [(EVar, EVar)] TypedNode)+typecheck d = do+  verbosity <- MS.gets stateVerbosity+  x <- liftIO $ runStack verbosity (Infer.typecheck d)+  case x of+    ((Right result, _), _) -> return result+    ((Left err, _), _) -> MM.throwError err++-- | currently I do nothing with the Reader and Writer monads, but I'm leaving+-- them in for now since I will need them when I plug this all into Morloc.+runStack :: Int -> Stack a -> IO ((Either MorlocError a, [MT.Text]), StackState)+runStack verbosity e+  = flip MS.runStateT emptyState+  . MW.runWriterT+  . ME.runExceptT+  . MR.runReaderT e+  $ StackConfig verbosity++emptyState = StackState+  { stateVar = 0+  , stateQul = 0+  , stateSer = []+  , stateDepth = 0+  }
+ library/Morloc/Frontend/Desugar.hs view
@@ -0,0 +1,337 @@+{-|+Module      : Morloc.Frontend.Desugar+Description : Write Module objects to resolve type aliases and such+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Frontend.Desugar (desugar, desugarType) where++import Morloc.Frontend.Namespace+import Morloc.Pretty ()+import Morloc.Data.Doc+import qualified Morloc.Monad as MM+import qualified Morloc.Data.DAG as MDD+import qualified Morloc.Data.Text as MT+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Morloc.Frontend.PartialOrder as MTP++desugar+  :: DAG MVar Import ParserNode+  -> MorlocMonad (DAG MVar [(EVar, EVar)] PreparedNode)+desugar s+  -- DAG MVar Import ParserNode+  = resolveImports s+  -- DAG MVar (Map EVar EVar) ParserNode+  >>= desugarDag+  -- DAG MVar (Map EVar EVar) PreparedNode+  >>= simplify+  -- Add packer map+  >>= addPackerMap+++-- | Consider export/import information to determine which terms are imported+-- into each module. This step reduces the Import edge type to an m-to-n source+-- name to alias map.+resolveImports+  :: DAG MVar Import ParserNode+  -> MorlocMonad (DAG MVar [(EVar, EVar)] ParserNode)+resolveImports = MDD.mapEdgeWithNodeM resolveImport where+  resolveImport+    :: ParserNode+    -> Import+    -> ParserNode+    -> MorlocMonad [(EVar, EVar)]+  resolveImport _ (Import _ Nothing exc _) n2+    = return+    . map (\x -> (x,x)) -- alias is identical+    . Set.toList+    $ Set.difference (parserNodeExports n2) (Set.fromList exc)+  resolveImport _ (Import _ (Just inc) exc _) n2+    | length contradict > 0+        = MM.throwError . CallTheMonkeys+        $ "Error: The following terms are both included and excluded: " <>+          render (tupledNoFold $ map pretty contradict)+    | length missing > 0+        = MM.throwError . CallTheMonkeys+        $ "Error: The following terms are not exported: " <>+          render (tupledNoFold $ map pretty missing)+    | otherwise = return inc+    where+      missing = [n | (n, _) <- inc, not $ Set.member n (parserNodeExports n2)]+      contradict = [n | (n, _) <- inc, elem n exc]++desugarDag+  :: DAG MVar [(EVar, EVar)] ParserNode+  -> MorlocMonad (DAG MVar [(EVar, EVar)] ParserNode)+desugarDag m = do+  mapM_ checkForSelfRecursion (map parserNodeTypedefs (MDD.nodes m))+  MDD.mapNodeWithKeyM (desugarParserNode m) m++simplify+  :: (DAG MVar [(EVar, EVar)] ParserNode)+  -> MorlocMonad (DAG MVar [(EVar, EVar)] PreparedNode)+simplify = return . MDD.mapNode prepare where+  prepare :: ParserNode -> PreparedNode+  prepare n1 = PreparedNode+    { preparedNodePath = parserNodePath n1+    , preparedNodeBody = parserNodeBody n1+    , preparedNodeSourceMap = parserNodeSourceMap n1+    , preparedNodeExports = parserNodeExports n1+    , preparedNodePackers = Map.empty -- This will be filled in in `addPackerMap`+    , preparedNodeTypedefs = parserNodeTypedefs n1+    }++checkForSelfRecursion :: Map.Map TVar (UnresolvedType, [TVar]) -> MorlocMonad ()+checkForSelfRecursion h = mapM_ (uncurry f) [(v,t) | (v,(t,_)) <- Map.toList h] where+  f :: TVar -> UnresolvedType -> MorlocMonad ()+  f v (VarU v')+    | v == v' = MM.throwError . SelfRecursiveTypeAlias $ v+    | otherwise = return ()+  f _ (ExistU _ _ _) = MM.throwError $ CallTheMonkeys "existential crisis"+  f v (ForallU _ t) = f v t+  f v (FunU t1 t2) = f v t1 >> f v t2+  f v (ArrU v0 ts)+    | v == v0 = MM.throwError . SelfRecursiveTypeAlias $ v+    | otherwise = mapM_ (f v) ts+  f v (NamU _ _ _ rs) = mapM_ (f v) (map snd rs)++desugarParserNode+  :: DAG MVar [(EVar, EVar)] ParserNode+  -> MVar+  -> ParserNode+  -> MorlocMonad ParserNode+desugarParserNode d k n = do+  nodeBody <- mapM (desugarExpr d k) (parserNodeBody n)+  return $ n { parserNodeBody = nodeBody }++desugarExpr+  :: DAG MVar [(EVar, EVar)] ParserNode+  -> MVar+  -> Expr+  -> MorlocMonad Expr+desugarExpr _ _ e@(SrcE _) = return e+desugarExpr d k (Signature v t) = Signature v <$> desugarEType d k t+desugarExpr d k (Declaration v e) = Declaration v <$> desugarExpr d k e+desugarExpr _ _ UniE = return UniE+desugarExpr _ _ e@(VarE _) = return e+desugarExpr d k (AccE e key) = AccE <$> desugarExpr d k e <*> pure key+desugarExpr d k (ListE xs) = ListE <$> mapM (desugarExpr d k) xs+desugarExpr d k (TupleE xs) = TupleE <$> mapM (desugarExpr d k) xs+desugarExpr d k (LamE v e) = LamE v <$> desugarExpr d k e+desugarExpr d k (AppE e1 e2) = AppE <$> desugarExpr d k e1 <*> desugarExpr d k e2+desugarExpr d k (AnnE e ts) = AnnE <$> desugarExpr d k e <*> mapM (desugarType d k) ts+desugarExpr _ _ e@(NumE _) = return e+desugarExpr _ _ e@(LogE _) = return e+desugarExpr _ _ e@(StrE _) = return e+desugarExpr d k (RecE rs) = do+  es <- mapM (desugarExpr d k) (map snd rs)+  return (RecE (zip (map fst rs) es))++desugarEType :: DAG MVar [(EVar, EVar)] ParserNode -> MVar -> EType -> MorlocMonad EType+desugarEType d k (EType t ps cs) = EType <$> desugarType d k t <*> pure ps <*> pure cs++desugarType+  :: DAG MVar [(EVar, EVar)] ParserNode+  -> MVar+  -> UnresolvedType+  -> MorlocMonad UnresolvedType+desugarType d k t0@(VarU v) =+  case lookupTypedefs v k d of+    [] -> return t0+    ts'@(t':_) -> do+      (t, _) <- foldlM (mergeAliases v 0) t' ts'+      desugarType d k t+desugarType d k (ExistU v ts ds) = do+  ts' <- mapM (desugarType d k) ts+  ds' <- mapM (desugarType d k) ds+  return $ ExistU v ts' ds'+desugarType d k (ForallU v t) = ForallU v <$> desugarType d k t+desugarType d k (FunU t1 t2) = FunU <$> desugarType d k t1 <*> desugarType d k t2+desugarType d k (ArrU v ts) =+  case lookupTypedefs v k d of+    [] -> ArrU v <$> mapM (desugarType d k) ts+    (t':ts') -> do+      (t, vs) <- foldlM (mergeAliases v (length ts)) t' ts'+      if length ts == length vs+        -- substitute parameters into alias+        then desugarType d k (foldr parsub (choiceExistential t) (zip vs (map choiceExistential ts)))+        else MM.throwError $ BadTypeAliasParameters v (length vs) (length ts)+desugarType d k (NamU r v ts rs) = do+  let keys = map fst rs+  vals <- mapM (desugarType d k) (map snd rs)+  return (NamU r v ts (zip keys vals))++lookupTypedefs+  :: TVar+  -> MVar+  -> DAG MVar [(EVar, EVar)] ParserNode+  -> [(UnresolvedType, [TVar])]+lookupTypedefs (TV lang v) k h+  = catMaybes+  . MDD.nodes+  . MDD.mapNode (\(EVar v', typemap) -> Map.lookup (TV lang v') typemap)+  $ MDD.lookupAliasedTerm (EVar v) k parserNodeTypedefs h+++-- When a type alias is imported from two places, this function reconciles them, if possible+mergeAliases+  :: TVar+  -> Int+  -> (UnresolvedType, [TVar])+  -> (UnresolvedType, [TVar])+  -> MorlocMonad (UnresolvedType, [TVar])+mergeAliases v i t@(t1, ts1) (t2, ts2)+  | i /= length ts1 = MM.throwError $ BadTypeAliasParameters v i (length ts1)+  |    MTP.isSubtypeOf t1' t2'+    && MTP.isSubtypeOf t2' t1'+    && length ts1 == length ts2 = return t+  | otherwise = MM.throwError (ConflictingTypeAliases (unresolvedType2type t1) (unresolvedType2type t2))+  where+    t1' = foldl (\t' v' -> ForallU v' t') t1 ts1+    t2' = foldl (\t' v' -> ForallU v' t') t2 ts2+++parsub :: (TVar, UnresolvedType) -> UnresolvedType -> UnresolvedType+parsub (v, t2) t1@(VarU v0)+  | v0 == v = t2 -- substitute+  | otherwise = t1 -- keep the original+parsub _ (ExistU _ _ _) = error "What the bloody hell is an existential doing down here?"+parsub pair (ForallU v t1) = ForallU v (parsub pair t1)+parsub pair (FunU a b) = FunU (parsub pair a) (parsub pair b)+parsub pair (ArrU v ts) = ArrU v (map (parsub pair) ts)+parsub pair (NamU r v ts rs) = NamU r v (map (parsub pair) ts) (zip (map fst rs) (map (parsub pair . snd) rs))++++addPackerMap+  :: (DAG MVar [(EVar, EVar)] PreparedNode)+  -> MorlocMonad (DAG MVar [(EVar, EVar)] PreparedNode)+addPackerMap d = do+  maybeDAG <- MDD.synthesizeDAG gatherPackers d+  case maybeDAG of+    Nothing -> MM.throwError CyclicDependency+    (Just d') -> return d'++gatherPackers+  :: MVar -- the importing module name (currently unused)+  -> PreparedNode -- data about the importing module+  -> [( MVar -- the name of an imported module+        , [(EVar -- the name of a term in the imported module+          , EVar -- the alias in the importing module+          )]+        , PreparedNode -- data about the imported module+     )]+  -> MorlocMonad PreparedNode+gatherPackers _ n1 es = do+  let packers   = starpack n1 Pack+      unpackers = starpack n1 Unpack+  nodepackers <- makeNodePackers packers unpackers n1+  let m = Map.unionsWith (<>) $ map (\(_, e, n2) -> inheritPackers e n2) es+      m' = Map.unionWith (<>) nodepackers m+  return $ n1 { preparedNodePackers = m' }++starpack :: PreparedNode -> Property -> [(EVar, UnresolvedType, [Source])]+starpack n pro+  = [ (v, t, maybeToList $ lookupSource v t (preparedNodeSourceMap n))+    | (Signature v e@(EType t p _)) <- preparedNodeBody n+    , isJust (langOf e)+    , Set.member pro p]+  where+    lookupSource :: EVar -> UnresolvedType -> Map.Map (EVar, Lang) Source -> Maybe Source+    lookupSource v t m = langOf t >>= (\lang -> Map.lookup (v, lang) m)++makeNodePackers+  :: [(EVar, UnresolvedType, [Source])]+  -> [(EVar, UnresolvedType, [Source])]+  -> PreparedNode+  -> MorlocMonad (Map.Map (TVar, Int) [UnresolvedPacker])+makeNodePackers xs ys n =+  let xs' = map (\(x,y,z)->(x, choiceExistential y, z)) xs+      ys' = map (\(x,y,z)->(x, choiceExistential y, z)) ys+      items = [ ( packerKey t2+                , [UnresolvedPacker (packerTerm v2 n) (packerType t1) ss1 ss2])+              | (_ , t1, ss1) <- xs'+              , (v2, t2, ss2) <- ys'+              , packerTypesMatch t1 t2+              ]+  in return $ Map.fromList items++packerTerm :: EVar -> PreparedNode -> Maybe EVar+packerTerm v n = listToMaybe . catMaybes $+  [ termOf e+  | (Signature v' e) <- preparedNodeBody n+  , v == v'+  , isNothing (langOf e)+  ]+  where+    termOf :: EType -> Maybe EVar+    termOf e = case splitArgs (etype e) of+      (_, [VarU (TV _ term), _]) -> Just $ EVar term+      (_, [ArrU (TV _ term) _, _]) -> Just $ EVar term+      _ -> Nothing++choiceExistential :: UnresolvedType -> UnresolvedType+choiceExistential (VarU v) = VarU v+choiceExistential (ExistU _ _ (t:_)) = (choiceExistential t)+choiceExistential (ExistU _ _ []) = error "Existential with no default value"+choiceExistential (ForallU v t) = ForallU v (choiceExistential t)+choiceExistential (FunU t1 t2) = FunU (choiceExistential t1) (choiceExistential t2)+choiceExistential (ArrU v ts) = ArrU v (map choiceExistential ts)+choiceExistential (NamU r v ts recs) = NamU r v (map choiceExistential ts) (zip (map fst recs) (map (choiceExistential . snd) recs))++packerTypesMatch :: UnresolvedType -> UnresolvedType -> Bool+packerTypesMatch t1 t2 = case (splitArgs t1, splitArgs t2) of+  ((vs1@[_,_], [t11, t12]), (vs2@[_,_], [t21, t22]))+    -> MTP.equivalent (qualify vs1 t11) (qualify vs2 t22)+    && MTP.equivalent (qualify vs1 t12) (qualify vs2 t21)+  _ -> False++packerType :: UnresolvedType -> UnresolvedType+packerType t = case splitArgs t of+  (params, [t1, _]) -> qualify params t1+  _ -> error "bad packer"++packerKey :: UnresolvedType -> (TVar, Int)+packerKey t = case splitArgs t of+  (params, [VarU v, _])   -> (v, length params)+  (params, [ArrU v _, _]) -> (v, length params)+  (params, [NamU _ v _ _, _]) -> (v, length params)+  _ -> error "bad packer"++qualify :: [TVar] -> UnresolvedType -> UnresolvedType+qualify [] t = t+qualify (v:vs) t = ForallU v (qualify vs t)++splitArgs :: UnresolvedType -> ([TVar], [UnresolvedType])+splitArgs (ForallU v u) =+  let (vs, ts) = splitArgs u+  in (v:vs, ts)+splitArgs (FunU t1 t2) =+  let (vs, ts) = splitArgs t2+  in (vs, t1:ts)+splitArgs t = ([], [t])++inheritPackers+  :: [( EVar -- key in THIS module descrived in the PreparedNode argument+      , EVar -- alias used in the importing module+      )]+  -> PreparedNode+  -> Map.Map (TVar, Int) [UnresolvedPacker]+inheritPackers es n =+  -- names of terms exported from this module+  let names = Set.fromList (map (unEVar . fst) es)+  in   Map.map (map toAlias)+     $ Map.filter (isImported names) (preparedNodePackers n)+  where+    toAlias :: UnresolvedPacker -> UnresolvedPacker+    toAlias n' = n' { unresolvedPackerTerm = unresolvedPackerTerm n' >>= (flip lookup) es }++    isImported :: Set.Set MT.Text -> [UnresolvedPacker] -> Bool+    isImported _ [] = False+    isImported names' (n0:_) = case unresolvedPackerTerm n0 of+      (Just (EVar v)) -> Set.member v names'+      _ -> False
+ library/Morloc/Frontend/Infer.hs view
@@ -0,0 +1,1109 @@+{-|+Module      : Morloc.Frontend.Infer+Description : Core inference module+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.Frontend.Infer+  (+  -- * The main type checker+    typecheck+  -- * Internal functions used in testing+  , subtype+  , substitute+  , apply+  , infer+  , rename+  , unrename+  , fromType+  ) where++import Morloc.Frontend.Namespace+import Morloc.Frontend.Internal+import qualified Morloc.Frontend.PartialOrder as P+import qualified Morloc.Frontend.Lang.DefaultTypes as MLD+import qualified Morloc.Data.DAG as MDD+import qualified Data.Map as Map+import qualified Data.Set as Set+import qualified Morloc.Data.Text as MT+import qualified Control.Monad.Reader as R++import Morloc.Data.Doc hiding (putDoc)+import Morloc.Frontend.Pretty+import Data.Text.Prettyprint.Doc.Render.Terminal (putDoc, AnsiStyle)++typecheck+  :: DAG MVar [(EVar, EVar)] PreparedNode+  -> Stack (DAG MVar [(EVar, EVar)] TypedNode)+typecheck d = do+  maybeDAG <- MDD.synthesizeDAG typecheck' d+  case maybeDAG of+    Nothing -> throwError CyclicDependency+    (Just d') -> do+      d'' <- MDD.synthesizeDAG propagateConstructors d'+      case d'' of+        (Just d''') -> return d'''+        Nothing -> throwError CyclicDependency+  where+    typecheck'+      :: MVar+      -> PreparedNode+      -> [(MVar, [(EVar, EVar)], TypedNode)]+      -> Stack TypedNode+    typecheck' k n xs = do+      enter $ "entering module '" <> viaShow k <> "'"+      g0 <- importTypes xs+      (g1, es) <- typecheckExpr g0 (preparedNodeBody n)+      leave $ "module"+      return $ TypedNode+        { typedNodeModuleName = k+        , typedNodePath = preparedNodePath n+        , typedNodeBody = es+          -- the typemap is really only used when typchecking modules that+          -- import this module, so it technically could be removed deleted for+          -- being passed to the downstream generators.+        , typedNodeTypeMap = nodeTypeMapFromGamma g1+        , typedNodeSourceMap = preparedNodeSourceMap n+        , typedNodeExports = preparedNodeExports n+        , typedNodePackers = preparedNodePackers n+        , typedNodeConstructors+            = Map.fromList+            . map (\src@(Source _ lang _ alias) -> (TV (Just lang) (unEVar alias), src))+            . catMaybes+            . map ((flip Map.lookup) (preparedNodeSourceMap n))+            $ [ (EVar v, lang)+              | (TV (Just lang) v) <- unique (conmap collectConstructors es)]++        , typedNodeTypedefs = Map.map (\(t,ps) -> (resolve t, ps)) (preparedNodeTypedefs n)+        }+++    collectConstructors :: Expr -> [TVar] +    collectConstructors (AnnE e ts) = collectConstructors e ++ (conmap findTVar ts)+    collectConstructors (Declaration _ e) = collectConstructors e+    collectConstructors (ListE es) = conmap collectConstructors es+    collectConstructors (TupleE es) = conmap collectConstructors es+    collectConstructors (LamE _ e) = collectConstructors e+    collectConstructors (AppE e1 e2) = collectConstructors e1 ++ collectConstructors e2+    collectConstructors (RecE rs) = conmap (collectConstructors . snd) rs+    collectConstructors _ = []++    findTVar :: UnresolvedType -> [TVar]+    findTVar (VarU _) = []+    findTVar (ExistU _ _ _) = []+    findTVar (ForallU _ t) = findTVar t+    findTVar (FunU t1 t2) = findTVar t1 ++ findTVar t2+    findTVar (ArrU _ ts) = conmap findTVar ts+    findTVar (NamU _ v _ rs) = v : conmap (findTVar . snd) rs++    propagateConstructors+      :: MVar -- the importing module name (currently unused)+      -> TypedNode -- data about the importing module+      -> [(   MVar -- the name of an imported module+            , [(EVar -- the name of a term in the imported module+              , EVar -- the alias in the importing module+              )]+            , TypedNode -- data about the imported module+         )]+      -> Stack TypedNode+    propagateConstructors _ n1 es = do+      let constructor = Map.union (typedNodeConstructors n1)+                      $ (Map.fromList . concat)+                        [inherit n2 ps | (_, ps, n2) <- es] +      return $ n1 { typedNodeConstructors = constructor }++    inherit :: TypedNode -> [(EVar, EVar)] -> [(TVar, Source)]+    inherit ((Map.toList . typedNodeConstructors) -> ms) es =+      [ (TV lang (unEVar n'), Source n l p n')+      | (TV lang _, Source n l p a) <- ms -- information from parent+      , (a', n') <- es -- edge: a' imported term name+      , a == a']++    nodeTypeMapFromGamma :: Gamma -> Map.Map EVar TypeSet+    nodeTypeMapFromGamma g+      = Map.fromList+      $ [(e,t) | AnnG (VarE e) t <- g] ++ [(v,t) | AnnG (Declaration v _) t <- g]++    importTypes :: [(MVar, [(EVar, EVar)], TypedNode)] -> Stack Gamma+    importTypes xs+      -- [(EVar, [TypeSet])]+      = (return . groupSort . concat . map importTypes') xs+      -- [(EVar, TypeSet)]+      >>= mapM mergeManyTypeSets+      -- [GammaIndex]+      |>> map (\(v, t) -> AnnG (VarE v) t)++    importTypes' :: (MVar, [(EVar, EVar)], TypedNode) -> [(EVar, TypeSet)]+    importTypes' (_, xs, n) = mapMaybe (lookupOne (typedNodeTypeMap n)) xs++    lookupOne :: Map.Map EVar TypeSet -> (EVar, EVar) -> Maybe (EVar, TypeSet)+    lookupOne m (name, _) = case Map.lookup name m of+      (Just t) -> return (name, t)+      Nothing -> Nothing++    -- Typecheck a set of expressions within a given context (i.e., one module).+    -- Return the modified context and a list of annotated expressions.+    typecheckExpr :: Gamma -> [Expr] -> Stack (Gamma, [Expr])+    typecheckExpr g1 e1 = do+      es <- mapM rename e1+      (g', es') <- typecheckExpr' g1 [] es+      let es'' = concat [toExpr v t | (AnnG (VarE v) t) <- g'] ++ reverse es'+      return $ (g', map (generalizeE . unrename . apply g') es'')+      where+        toExpr :: EVar -> TypeSet -> [Expr]+        toExpr v (TypeSet (Just e) es) = [Signature v t | t <- (e : es)]+        toExpr v (TypeSet Nothing es) = [Signature v t | t <- es]++        typecheckExpr' :: Gamma -> [Expr] -> [Expr] -> Stack (Gamma, [Expr])+        typecheckExpr' g es [] = return (g, es)+        typecheckExpr' g es (x:xs) = do+          (g', _, e') <- infer Nothing g x+          case e' of+            (Signature _ _) -> typecheckExpr' g' es xs+            _ -> typecheckExpr' g' (e' : es) xs++    mergeManyTypeSets :: (EVar, [TypeSet]) -> Stack (EVar, TypeSet)+    mergeManyTypeSets (v, ts) = do+      gtype <- mergeGeneral $ catMaybes [gt | (TypeSet gt _) <- ts]+      let concreteTypes = concat [cs | (TypeSet _ cs) <- ts]+      return $ (v, TypeSet gtype concreteTypes)++    mergeGeneral :: [EType] -> Stack (Maybe EType)+    mergeGeneral [] = return Nothing+    mergeGeneral [e] = return (Just e)+    mergeGeneral [e1, e2] = fmap Just $ mergeGeneralTwo e1 e2+    mergeGeneral (e1:es) = do+      e2' <- mergeGeneral es+      case e2' of+        (Just e2) -> fmap Just $ mergeGeneralTwo e1 e2+        Nothing -> return Nothing++    mergeGeneralTwo :: EType -> EType -> Stack EType+    mergeGeneralTwo (EType t1 ps1 cs1) (EType t2 ps2 cs2) = do+      subtype t1 t2 []+      subtype t2 t1 []+      -- FIXME: implement better behavior here for joining properties+      return $ EType t1 (Set.union ps1 ps2) (Set.union cs1 cs2)+++-- | type 1 is more polymorphic than type 2 (Dunfield Figure 9)+subtype :: UnresolvedType -> UnresolvedType -> Gamma -> Stack Gamma+subtype t1 t2 g = do+  enter $ prettyGreenUnresolvedType t1 <+> "<:" <+> prettyGreenUnresolvedType t2+  seeGamma g+  g' <- subtype' t1 t2 g+  leave "subtype"+  return g'++-- VarU vs VarT+subtype' t1@(VarU (TV lang1 a1)) t2@(VarU (TV lang2 a2)) g+  -- If everything is the same, do nothing+  --+  -- ----------------------------------------- <:Var+  --  G[a] |- a_l <: a_l -| G[a]+  | lang1 == lang2 && a1 == a2 = return g+  -- If languages are different, do nothing+  --  l1 != l2    b_l2 ~~> a_l1+  -- ----------------------------------------- <:Var+  --  G[a] |- a_l1 <: b_l2 -| G[a]+  | lang1 /= lang2 = serialConstraint t1 t2 >> return g+  -- If languages are same, but types are different, raise error+  | lang1 == lang2 && a1 /= a2 = throwError $ SubtypeError (unresolvedType2type t1) (unresolvedType2type t2)++subtype' a@(ExistU (TV l1 _) _ _) b@(ExistU (TV l2 _) _ _) g+  --+  -- ----------------------------------------- <:Exvar+  --  G[E.a] |- E.a <: E.a -| G[E.a]+  | a == b = return g+  --  l1 == l2+  -- ----------------------------------------- <:AlienExvar+  --  G[E.a,E.b] |- E.a <: E.b -| G[E.a,E.b], E.a ~~> E.b+  | l1 /= l2 = return $ g +> UnsolvedConstraint a b+  --+  -- ----------------------------------------- <:InstantiateL/<:InstantiateR+  --  G[E.a] |- Ea <: Ea -| G[E.a]+  | otherwise+      -- formally, an `Ea notin FV(G)` check should be done here, but since the+      -- types involved are all existentials, it will always pass, so I omit+      -- it.+   = instantiate a b g++--  g1 |- B1 <: A1 -| g2+--  g2 |- [g2]A2 <: [g2]B2 -| g3+-- ----------------------------------------- <:-->+--  g1 |- A1 -> A2 <: B1 -> B2 -| g3+subtype' (FunU a1 a2) (FunU b1 b2) g1+  -- function subtypes are *contravariant* with respect to the input, that is,+  -- the subtypes are reversed so we have b1<:a1 instead of a1<:b1.+ = do+  g2 <- subtype b1 a1 g1+  subtype (apply g2 a2) (apply g2 b2) g2++--  g1 |- A1 <: B1+-- ----------------------------------------- <:App+--  g1 |- A1 A2 <: B1 B2 -| g2+--  unparameterized types are the same as VarT, so subtype on that instead+subtype' (ArrU v1 []) (ArrU v2 []) g+  | langOf v1 == langOf v2 = subtype (VarU v1) (VarU v2) g+  | otherwise = throwError . OtherError $ "Cannot compare types between languages"+subtype' t1@(ArrU v1@(TV l1 _) vs1) t2@(ArrU v2@(TV l2 _) vs2) g+  | length vs1 /= length vs2 = throwError . OtherError+    $ "Cannot subtype types with unequal parameter count" +  | l1 /= l2 = serialConstraint t1 t2 >> return g+  | v1 == v2 = compareArr vs1 vs2 g+  | otherwise = throwError . OtherError $ "Shit happens" +  where+    compareArr :: [UnresolvedType] -> [UnresolvedType] -> Gamma -> Stack Gamma+    compareArr [] [] g' = return g'+    compareArr (t1':ts1') (t2':ts2') g' = do+      g'' <- subtype t1' t2' g'+      compareArr ts1' ts2' g''+    compareArr _ _ _ = throwError TypeMismatch++-- subtype unordered records+subtype' (NamU _ v1 _ rs1) (NamU _ v2 _ rs2) g = do+  g' <- subtype (VarU v1) (VarU v2) g+  compareEntry (sort rs1) (sort rs2) g'+  where+    compareEntry :: [(MT.Text, UnresolvedType)] -> [(MT.Text, UnresolvedType)] -> Gamma -> Stack Gamma+    compareEntry [] [] g2 = return g2+    compareEntry ((k1, t1):rs1') ((k2, t2):rs2') g2+      | l1 == l2 = do+          g3 <- subtype (VarU (TV l1 k1)) (VarU (TV l2 k2)) g2+          g4 <- subtype t1 t2 g3+          compareEntry rs1' rs2' g4+      | otherwise = serialConstraint t1 t2 >> return g+      where+        l1 = langOf t1+        l2 = langOf t2+    compareEntry _ _ _ = throwError TypeMismatch++--  Ea not in FV(a)+--  g1[Ea] |- A <=: Ea -| g2+-- ----------------------------------------- <:InstantiateR+--  g1[Ea] |- A <: Ea -| g2+subtype' a b@(ExistU _ [] _) g+  | langOf a /= langOf b = return g -- incomparable+  | otherwise = occursCheck a b >> instantiate a b g+--  Ea not in FV(a)+--  g1[Ea] |- Ea <=: A -| g2+-- ----------------------------------------- <:InstantiateL+--  g1[Ea] |- Ea <: A -| g2+subtype' a@(ExistU _ [] _) b g+  | langOf a /= langOf b = return g -- incomparable+  | otherwise = occursCheck b a >> instantiate a b g++subtype' a@(ArrU v1 ps1) b@(ExistU v2 ps2 _) g+  | langOf a /= langOf b = return g -- incomparable+  | otherwise = subtype' (ArrU v1 ps1) (ExistU v2 ps2 []) g+subtype' (ExistU v1 ps1 _) t@(ArrU v2 ps2) g1+  | langOf v1 /= langOf v2 = return g1 -- incomparable+  | length ps1 /= length ps2 = throwError . OtherError . render $ +      "Expected equal number of type paramters, found:"+        <+> list (map prettyGreenUnresolvedType ps1)+        <+> list (map prettyGreenUnresolvedType ps2)+  | otherwise = do+    g2 <- foldM (\g (p1, p2) -> subtype p1 p2 g) g1 (zip ps1 ps2)+    case access1 v1 g2 of+      Just (rs, _, ls) ->+        return $ rs ++ [SolvedG v1 t] ++ ls+      Nothing -> return g2 -- it is already solved, so do nothing++--  g1,>Ea,Ea |- [Ea/x]A <: B -| g2,>Ea,g3+-- ----------------------------------------- <:ForallL+--  g1 |- Forall x . A <: B -| g2+--+subtype' (ForallU v@(TV lang _) a) b g+  | lang /= langOf b = return g+  | otherwise = do+      a' <- newvar lang+      g' <- subtype (P.substitute v a' a) b (g +> MarkG v +> a')+      cut (MarkG v) g'++--  g1,a |- A <: B -| g2,a,g3+-- ----------------------------------------- <:ForallR+--  g1 |- A <: Forall a. B -| g2+subtype' a (ForallU v@(TV lang _) b) g+  | lang /= langOf a = return g+  | otherwise = subtype a b (g +> VarG v) >>= cut (VarG v)+subtype' a b _ = throwError $ SubtypeError (unresolvedType2type a) (unresolvedType2type b)++++-- | Dunfield Figure 10 -- type-level structural recursion+instantiate :: UnresolvedType -> UnresolvedType -> Gamma -> Stack Gamma+instantiate t1 t2 g1 = do+  say $ prettyGreenUnresolvedType t1 <+> "<=:" <+> prettyGreenUnresolvedType t2+  g2 <- instantiate' t1 t2 g1 +  say $ "instantiate done"+  seeGamma g2+  return g2++--  g1[Ea2, Ea1, Ea=Ea1->Ea2] |- A1 <=: Ea1 -| g2+--  g2 |- Ea2 <=: [g2]A2 -| g3+-- ----------------------------------------- InstLArr+--  g1[Ea] |- Ea <=: A1 -> A2 -| g3+instantiate' (ExistU v@(TV lang _) [] _) (FunU t1 t2) g1 = do+  ea1 <- newvar lang+  ea2 <- newvar lang+  g2 <-+    case access1 v g1 of+      Just (rs, _, ls) ->+        return $ rs ++ [SolvedG v (FunU ea1 ea2), index ea1, index ea2] ++ ls+      Nothing -> throwError $ OtherError "Bad thing #2"+  g3 <- instantiate t1 ea1 g2+  g4 <- instantiate ea2 (apply g3 t2) g3+  return g4+--  g1[Ea2,Ea1,Ea=Ea1->Ea2] |- Ea1 <=: A1 -| g2+--  g2 |- [g2]A2 <=: Ea2 -| g3+-- ----------------------------------------- InstRArr+--  g1[Ea] |- A1 -> A2 <=: Ea -| g3+instantiate' (FunU t1 t2) (ExistU v@(TV lang _) [] _) g1 = do+  ea1 <- newvar lang+  ea2 <- newvar lang+  g2 <-+    case access1 v g1 of+      Just (rs, _, ls) ->+        return $ rs ++ [SolvedG v (FunU ea1 ea2), index ea1, index ea2] ++ ls+      Nothing -> throwError $ OtherError "Bad thing #3"+  g3 <- instantiate t1 ea1 g2+  g4 <- instantiate ea2 (apply g3 t2) g3+  return g4+--+-- ----------------------------------------- InstLAllR+--+instantiate' ta@(ExistU _ _ _) tb@(ForallU v2 t2) g1+  | langOf ta /= langOf tb = return g1+  | otherwise = instantiate ta t2 (g1 +> VarG v2) >>= cut (VarG v2)+-- InstLReach or instRReach -- each rule eliminates an existential+-- Replace the rightmost with leftmost (G[a][b] --> L,a,M,b=a,R)+-- WARNING: be careful here, since the implementation adds to the front and the+-- formal syntax adds to the back. Don't change anything in the function unless+-- you really know what you are doing and have tests to confirm it.+instantiate' ta@(ExistU v1 ps1 []) tb@(ExistU v2 ps2 []) g1 = do+  g2 <- foldM (\g (t1, t2) -> subtype t1 t2 g) g1 (zip ps1 ps2)+  g3 <- case access2 v1 v2 g2 of+    -- InstLReach+    (Just (ls, _, ms, x, rs)) -> return $ ls <> (SolvedG v1 tb : ms) <> (x : rs)+    Nothing ->+      case access2 v2 v1 g2 of+      -- InstRReach+        (Just (ls, _, ms, x, rs)) ->+          return $ ls <> (SolvedG v2 ta : ms) <> (x : rs)+        Nothing -> return g2+  return g3+--  g1[Ea],>Eb,Eb |- [Eb/x]B <=: Ea -| g2,>Eb,g3+-- ----------------------------------------- InstRAllL+--  g1[Ea] |- Forall x. B <=: Ea -| g2+instantiate' ta@(ForallU x b) tb@(ExistU _ [] _) g1+  | langOf ta /= langOf tb = return g1+  | otherwise =+      instantiate+        (substitute x b) -- [Eb/x]B+        tb -- Ea+        (g1 +> MarkG x +> ExistG x [] []) -- g1[Ea],>Eb,Eb+      >>= cut (MarkG x)+--  g1 |- t+-- ----------------------------------------- InstRSolve+--  g1,Ea,g2 |- t <=: Ea -| g1,Ea=t,g2+instantiate' ta tb@(ExistU v [] []) g1+  | langOf ta /= langOf tb = return g1+  | otherwise =+      case access1 v g1 of+        (Just (ls, _, rs)) -> return $ ls ++ (SolvedG v ta) : rs+        Nothing ->+          case lookupU v g1 of+            (Just _) -> return g1+            Nothing ->+              throwError . OtherError $+              "Error in InstRSolve: ta=(" <>+              MT.show' ta <> ") tb=(" <> MT.show' tb <> ") g1=(" <> MT.show' g1 <> ")"+--  g1 |- t+-- ----------------------------------------- instLSolve+--  g1,Ea,g2 |- Ea <=: t -| g1,Ea=t,g2+instantiate' ta@(ExistU v [] []) tb g1+  | langOf ta /= langOf tb = return g1+  | otherwise =+      case access1 v g1 of+        (Just (ls, _, rs)) -> return $ ls ++ (SolvedG v tb) : rs+        Nothing ->+          case lookupU v g1 of+            (Just _) -> return g1+            Nothing -> error "error in InstLSolve"++-- if defaults are involved, no solving is done, but the subtypes of parameters+-- and defaults needs to be checked. +instantiate' (ExistU _ ps1 ds1) (ExistU _ ps2 ds2) g1 = do+  g2 <- foldM (\g (t1, t2) -> subtype t1 t2 g) g1 (zip ps1 ps2)+  g3 <- foldM (\g d1 -> foldM (\g' d2 -> subtype d1 d2 g') g ds2) g2 ds1+  return g3++-- bad+instantiate' _ _ g = return g++++infer ::+     Maybe Lang+  -> Gamma+  -> Expr -- ^ A subexpression from the original expression+  -> Stack ( Gamma+           , [UnresolvedType] -- The return types+           , Expr -- The annotated expression+           )+infer l g e = do+  enter $ "infer" <+> maybe "MLang" (viaShow . id) l <+> parens (prettyExpr e)+  seeGamma g+  o@(_, ts, _) <- infer' l g e+  leave $ "infer |-" <+> encloseSep "(" ")" ", " (map prettyGreenUnresolvedType ts)+  return o++--+-- ----------------------------------------- <primitive>+--  g |- <primitive expr> => <primitive type> -| g+--+-- Uni=>+infer' Nothing g UniE = do+  let t = head $ MLD.defaultNull Nothing+  return (g, [t], ann UniE t)+infer' lang g UniE = do+  t <- newvarRich [] [head $ MLD.defaultNull lang] lang+  return (g +> t, [t], ann UniE t)++-- Num=>+infer' Nothing g e@(NumE _) = do+  let t = head $ MLD.defaultNumber Nothing+  return (g, [t], ann e t)+infer' lang g e@(NumE _) = do+  t <- newvarRich [] [head $ MLD.defaultNumber lang] lang+  return (g +> t, [t], ann e t)++-- Str=>+infer' Nothing g e@(StrE _) = do+  let t = head $ MLD.defaultString Nothing+  return (g, [t], ann e t)+infer' lang g e@(StrE _) = do+  t <- newvarRich [] [head $ MLD.defaultString lang] lang+  return (g +> t, [t], ann e t)++-- Log=>+infer' Nothing g e@(LogE _) = do+  let t = head $ MLD.defaultBool Nothing+  return (g, [t], ann e t)+infer' lang g e@(LogE _) = do+  t <- newvarRich [] [head $ MLD.defaultBool lang] lang+  return (g +> t, [t], ann e t)++-- Src=>+-- -- FIXME: the expressions are now NOT sorted ... need to fix+-- Since the expressions in a Morloc script are sorted before being+-- evaluated, the SrcE expressions will be considered before the Signature+-- and Declaration expressions. Thus every term that originates in source+-- code will be initialized here and elaborated upon with deeper type+-- information as the signatures and declarations are parsed. +-- -- NOTE: Keeping SrcE as an expression, rather than pulling it out of the+-- body, as is done with imports and exports, is justified since the type+-- system should know that a given term is from a given language since it may+-- be possible, in cases, to infer a type signature for the given language from+-- the general type signature.+infer' (Just _) _ (SrcE _) = throwError ToplevelStatementsHaveNoLanguage+infer' Nothing g1 s1@(SrcE srcs) = do+  let g3 = map SrcG srcs ++ g1+  return (g3, [], s1)++-- Signature=>+infer' (Just _) _ (Signature _ _) = throwError ToplevelStatementsHaveNoLanguage+infer' Nothing g1 (Signature v1 e1) = do+  g2 <- accessWith1 isAnnG (append' e1) (ifNotFound e1) g1+  return (g2, [], Signature v1 e1)+  where++    -- find a typeset+    isAnnG :: GammaIndex -> Bool+    isAnnG (AnnG (VarE e) _)+      | v1 == e = True+      | otherwise = False+    isAnnG _ = False++    -- update the found typeset+    append' :: EType -> GammaIndex -> Stack GammaIndex+    append' e (AnnG x@(VarE _) r2) = AnnG <$> pure x <*> appendTypeSet r2 e+    append' _ _ = throwError $ OtherError "Bad Gamma"++    -- create a new typeset if none was found+    ifNotFound :: EType -> Gamma -> Stack Gamma+    ifNotFound e g' = case (langOf . etype) e of+        (Just _) -> return $ AnnG (VarE v1) (TypeSet Nothing [e]) : g'+        Nothing  -> return $ AnnG (VarE v1) (TypeSet (Just e) []) : g'++-- Declaration=>+infer' (Just _) _ (Declaration _ _) = throwError ToplevelStatementsHaveNoLanguage+infer' Nothing g1 e0@(Declaration v e1) = do+  (typeset3, g4, es4) <- case lookupE v g1 of+    -- CheckDeclaration+    (Just (_, typeset@(TypeSet t ts))) -> do+      let xs1 = map etype (maybeToList t ++ ts)+          tlangs = langsOf g1 typeset+          langs = [lang | lang <- langsOf g1 e1, not (elem lang tlangs)]+      -- Check each of the signatures against the expression.+      (g2, ts2, es2) <- foldM (foldCheck e1) (g1, [], []) xs1+      (g3, ts3, es3) <- mapM newvar langs+                     >>= foldM (foldCheckExist v e1) (g2, ts2, es2)+      typeset2 <- foldM appendTypeSet typeset (map (toEType g3) ts3)+      return (generalizeTypeSet typeset2, g3, es3)+    -- InferDeclaration+    Nothing -> do+      (g3, ts3, es3) <- foldM (foldInfer v e1) (g1, [], []) (langsOf g1 e1)+      let ts4 = unique ts3+      typeset2 <- typesetFromList (map generalize ts4)+      return (typeset2, g3, es3)++  e2 <- collate es4++  let e5 = Declaration v (generalizeE e2)++  return (g4 +> AnnG e0 typeset3, [], e5)+  where++    foldInfer+      :: EVar+      -> Expr+      -> (Gamma, [UnresolvedType], [Expr])+      -> Maybe Lang+      -> Stack (Gamma, [UnresolvedType], [Expr])+    foldInfer v' e' (g1', ts1, es) lang = do+      (g2', ts2, e2) <- infer lang (g1' +> MarkEG v') e'+      g3' <- cut (MarkEG v') g2'+      return (g3', ts1 ++ ts2, e2:es)++    foldCheckExist+      :: EVar+      -> Expr+      -> (Gamma, [UnresolvedType], [Expr])+      -> UnresolvedType+      -> Stack (Gamma, [UnresolvedType], [Expr])+    foldCheckExist v' e' (g1', ts, es) t' = do+      (g2', t2', e2') <- check (g1' +> MarkEG v' +> t') e' t'+      g3' <- cut (MarkEG v') g2'+      return (g3', t2':ts, e2':es)++    foldCheck ::+         Expr+      -> (Gamma, [UnresolvedType], [Expr])+      -> UnresolvedType+      -> Stack (Gamma, [UnresolvedType], [Expr])+    foldCheck e' (g1', ts, es) t' = do+      (g2', t2', e2') <- check g1' e' t'+      say (prettyExpr e2')+      return (g2', t2':ts, e2':es)++    toEType _ t = EType+      { etype = t+      , eprop = Set.empty+      , econs = Set.empty+      }++infer' lang g e@(VarE v) = do+  say $ "----------------------------------"+  say $ pretty v+  case (lang, lookupE v g) of+    (Just _, Just (VarE v', t@(TypeSet _ []))) -> +      if v' == v+      then return (g, mapTS etype t, AnnE (VarE v') (mapTS etype t))+      else infer' lang g (VarE v')+    --  forall M . (x:A_m) not_in +    -- ------------------------------------------- Var=>+    --  g |- x => A -| g+    (Just _, Just (e', TypeSet _ [])) -> infer lang g e'+    --  (x:A) in g+    -- ------------------------------------------- Var+    --  g |- x => A -| g+    (_, Just (_, typeset)) ->+      let ts = mapTS etype typeset+      in return (g, ts, AnnE e ts)+    (_, Nothing) -> throwError (UnboundVariable v)+  where+    mapTS :: (EType -> a) -> TypeSet -> [a]+    mapTS f (TypeSet (Just a) es) = map f (a:es)+    mapTS f (TypeSet Nothing es) = map f es++infer' lang g (AccE e k) = do+  (g', record_ts, e') <- infer lang g e+  ts <- mapM (accessRecord k) record_ts |>> catMaybes+  return (g', ts, AnnE (AccE e' k) ts)+  where+    accessRecord :: EVar -> UnresolvedType -> Stack (Maybe UnresolvedType)+    accessRecord (EVar key) (NamU _ _ _ rs) = return $ lookup key rs+    accessRecord _ _ = throwError  BadRecordAccess++--  g1,Ea,Eb,x:Ea |- e <= Eb -| g2,x:Ea,g3+-- ----------------------------------------- -->I=>+--  g1 |- \x.e => Ea -> Eb -| g2+-- | type 1 is more polymorphic than type 2 (Dunfield Figure 9)+infer' lang g1 (LamE v e2) = do+  a <- newvar lang+  b <- newvar lang+  let anng = AnnG (VarE v) (fromType lang a)+      g2 = g1 +> a +> b +> anng+  (g3, t1, e2') <- check g2 e2 b+  case fmap snd (lookupE v g3) >>= toType lang of+    (Just t2) -> do+      let t3 = FunU (apply g3 t2) t1+      g4 <- cut anng g3+      return (g4, [t3], ann (LamE v e2') t3)+    Nothing -> throwError $ OtherError "Bad thing #4"++{-  g |- e1 => A* -| d_1+ -  { d_i |- [d_i]A_i o e2 =>> C_i -| d_{i+1} } forall i in (1,2 ... k)+ - ----------------------------------------- -->E+ -  g |- e1 e2 =>> C -| d_k+ -}+infer' lang g1 (AppE e1 e2) = do+  -- Anonymous lambda functions are currently not supported. So e1 currently will+  -- be a VarE, an AppE, or an AnnE annotating a VarE or AppE. Anonymous lambdas+  -- would roughly correspond to DeclareInfer statements while adding annotated+  -- lambdas would correspond to DeclareAnnot.++  -- @as1@ will include one entry consisting of the general type `(Nothing,t)`+  -- and one or more realizatoins `(Just lang, t)`+  (d1, as1, e1') <- infer lang g1 e1++  -- Map derive over every type observed for e1, the functional element. The+  -- result is a list of the types and expressions derived from e2+  (g2, fs, es2') <- foldM deriveF (d1, [], []) as1++  e2' <- collate es2' ++  -- e1' - e1 with type annotations+  -- e2' - e2 with type annotations (after being applied to e2)+  (as2, ek') <- applyConcrete e1' e2' fs++  return (g2, as2, ek')+  where+    -- pair input and output types by language and construct the function type+    applyConcrete :: Expr -> Expr -> [UnresolvedType] ->  Stack ([UnresolvedType], Expr)+    applyConcrete (AnnE e1' _) e2' fs' = do+      let (tas, tcs) = unzip [ (FunU a c, c) | (FunU a c) <- fs' ]+      return (tcs, AnnE (AppE (AnnE e1' tas) e2') tcs)+    applyConcrete e _ _ = do+      say $ prettyScream "ERROR!!!"+      say $ "e =" <+> prettyExpr e+      throwError . OtherError $ "bad concrete"++    deriveF ::+         (Gamma, [UnresolvedType], [Expr])+      -> UnresolvedType+      -> Stack (Gamma, [UnresolvedType], [Expr])+    deriveF (g', ts, es) t' = do+      (g'', t'', e'') <- derive g' e2 t'+      return (g'', t'':ts, e'':es)++--  g1 |- A+--  g1 |- e <= A -| g2+-- ----------------------------------------- Anno+--  g1 |- (e:A) => A -| g2+infer' _ g e1@(AnnE e@(VarE v) [t]) = do+  -- FIXME - I need to distinguish between the two types of annotations. There+  -- are annotations that the user writes; these need to be checked. There are+  -- annotations that are generated by the typechecker; these are basically+  -- cached results that do not need to be checked.+  --+  -- Currently I am checking the general cases, since that is the only kind of+  -- annotation the user can make, but this still runs some unnecessary checks.+  if langOf t == Nothing+    then+      case lookupE v g of+        (Just _) -> checkup g e t+        Nothing -> return (g, [t], e1)+    else+        return (g, [t], e1)+infer' _ g (AnnE e [t]) =+  if langOf t == Nothing+    then checkup g e t+    else return (g, [t], e)+infer' _ g (AnnE e ts) = return (g, ts, e)++-- List=>+infer' lang g1 (ListE xs1) = do+  (g2, pairs) <- chainInfer lang g1 xs1+  elementType <- case (P.mostSpecific . catMaybes)  (map fst pairs) of+    [] -> newvar lang+    (t:_) -> return t+  (g3, _, xs3) <- chainCheck (zip (repeat elementType) xs1) g2+  let dts = MLD.defaultList lang elementType+  containerType <-+    if lang == Nothing+    then return (head dts)+    else newvarRich [elementType] dts lang+  return (g3, [containerType], ann (ListE xs3) containerType)++-- Tuple=>+infer' _ _ (TupleE []) = throwError EmptyTuple+infer' _ _ (TupleE [_]) = throwError TupleSingleton+infer' lang g1 (TupleE xs1) = do+  (g2, pairs) <- chainInfer lang g1 xs1+  let (ts2may, xs2) = unzip pairs+  ts2 <- case sequence ts2may of+    Nothing -> throwError . OtherError $ "Could not infer tuple type"+    (Just ts2') -> return ts2' +  let dts = MLD.defaultTuple lang ts2+  containerType <-+    if lang == Nothing+    then return (head dts)+    else newvarRich ts2 dts lang+  return (g2, [containerType], ann (TupleE xs2) containerType)++-- Record=>+infer' _ _ (RecE []) = throwError EmptyRecord+infer' lang g1 (RecE rs) = do+  (g2, pairs) <- chainInfer lang g1 (map snd rs)+  let (ts2may, xs2) = unzip pairs+      keys = map fst rs+  entries <- case sequence ts2may of+    (Just ts2) -> return $ zip (map unEVar keys) ts2+    Nothing -> throwError . OtherError $ "Could not infer record type"+  let dts = MLD.defaultRecord lang entries+  containerType <-+    if lang == Nothing+    then return (head dts)+    else newvarRich [NamU NamRecord (TV lang "__RECORD__") [] entries] dts lang -- see entry in Parser.hs+  return (g2, [containerType], ann (RecE (zip keys xs2)) containerType)++++-- | Pattern matches against each type+check ::+     Gamma+  -> Expr -- ^ An expression which should be of the type given+  -> UnresolvedType -- ^ The expected type of the expression+  -> Stack ( Gamma+           , UnresolvedType -- The inferred type of the expression+           , Expr -- The annotated expression+           )+check g e t = do+  enter $ "check" <+> parens (prettyExpr e) <> "  " <> prettyGreenUnresolvedType t+  seeGamma g+  (g', t', e') <- check' g e t+  leave $ "check |-" <+> prettyGreenUnresolvedType t'+  return (g', t', e')++--  g1,x:A |- e <= B -| g2,x:A,g3+-- ----------------------------------------- -->I+--  g1 |- \x.e <= A -> B -| g2+check' g1 (LamE v e1) t1@(FunU a b) = do+  -- define x:A+  let anng = AnnG (VarE v) (fromType (langOf t1) a)+  -- check that e has the expected output type+  (g2, t2, e2) <- check (g1 +> anng) e1 b+  -- ignore the trailing context and (x:A), since it is out of scope+  g3 <- cut anng g2+  let t3 = FunU a t2+  return (g3, t3, ann (LamE v e2) t3)++--  g1,x |- e <= A -| g2,x,g3+-- ----------------------------------------- Forall.I+--  g1 |- e <= Forall x.A -| g2+check' g1 e1 t2@(ForallU x a) = do+  (g2, _, e2) <- check (g1 +> VarG x) e1 a+  g3 <- cut (VarG x) g2+  let t3 = apply g3 t2+  return (g3, t3, ann e2 t3)++--  g1 |- e => A -| g2+--  g2 |- [g2]A <: [g2]B -| g3+-- ----------------------------------------- Sub+--  g1 |- e <= B -| g3+check' g1 e1 b = do+  (g2, ts, e2) <- infer (langOf b) g1 e1+  g3 <- foldM (\g t -> subtype (apply g t) (apply g b) g) g2 ts+  return (g3, apply g3 b, anns (apply g3 e2) (map (apply g3) ts))++++derive ::+     Gamma+  -> Expr -- the expression that is passed to the function+  -> UnresolvedType -- the function type+  -> Stack ( Gamma+           , UnresolvedType -- output function type+           , Expr -- @e@, with type annotation+            )+derive g e f = do+  enter $ "derive" <+> prettyExpr e <> "  " <> prettyGreenUnresolvedType f+  seeGamma g+  (g', t', e') <- derive' g e f+  leave $ "derive |-" <+> prettyGreenUnresolvedType t'+  return (g', t', e')++--  g1 |- e <= A -| g2+-- ----------------------------------------- -->App+--  g1 |- A->C o e =>> C -| g2+derive' g e (FunU a b) = do+  (g', a', e') <- check g e a+  let b' = apply g' b+  return (g', FunU a' b', apply g' e')++--  g1,Ea |- [Ea/a]A o e =>> C -| g2+-- ----------------------------------------- Forall App+--  g1 |- Forall x.A o e =>> C -| g2+derive' g e (ForallU x s) = derive (g +> ExistG x [] []) e (substitute x s)++--  g1[Ea2, Ea1, Ea=Ea1->Ea2] |- e <= Ea1 -| g2+-- ----------------------------------------- EaApp+--  g1[Ea] |- Ea o e =>> Ea2 -| g2+derive' g e (ExistU v@(TV lang _) [] _) =+  case access1 v g of+    -- replace <t0> with <t0>:<ea1> -> <ea2>+    Just (rs, _, ls) -> do+      ea1 <- newvar lang+      ea2 <- newvar lang+      let t' = FunU ea1 ea2+          g2 = rs ++ [SolvedG v t', index ea1, index ea2] ++ ls+      (g3, a', e2) <- check g2 e ea1+      let f' = FunU a' (apply g3 ea2)+      return (g3, f', e2)+    -- if the variable has already been solved, use solved value+    Nothing -> case lookupU v g of+      (Just (FunU t1 t2)) -> do+        (g2, _, e2) <- check g e t1+        return (g2, FunU t1 t2, e2)+      _ -> throwError . OtherError $ "Expected a function"++derive' _ e t = do+  say $ prettyScream "ERROR!!!"+  say $ "e: " <> prettyExpr e+  say $ "t: " <> prettyGreenUnresolvedType t+  throwError NonFunctionDerive++++-- ----- H E L P E R S --------------------------------------------------++-- | substitute all appearances of a given variable with an existential+-- [t/v]A+substitute :: TVar -> UnresolvedType -> UnresolvedType+substitute v t = P.substitute v (ExistU v [] []) t++occursCheck :: UnresolvedType -> UnresolvedType -> Stack ()+occursCheck t1 t2 = do+  -- say $ "occursCheck:" <+> prettyGreenUnresolvedType t1 <+> prettyGreenUnresolvedType t2+  case Set.member t1 (P.free t2) of+    True -> throwError OccursCheckFail+    False -> return ()+++-- | fold a list of annotated expressions into one, preserving annotations+collate :: [Expr] -> Stack Expr+collate [] = throwError . OtherError $ "Nothing to collate"+collate [e] = return e+collate (e:es) = do+  say $ "collating" <+> (align . vsep . map prettyExpr) (e:es)+  e' <- foldM collateOne e es+  say $ "collated to:" <+> prettyExpr e'+  return e'++-- | Merge two annotated expressions into one, fail if the expressions are not+-- equivalent.+collateOne :: Expr -> Expr -> Stack Expr+collateOne (AnnE e1 ts1) (AnnE e2 ts2) = AnnE <$> collateOne e1 e2 <*> collateTypes ts1 ts2+-- +collateOne (AppE e11 e12) (AppE e21 e22) = AppE <$> collateOne e11 e21 <*> collateOne e12 e22+collateOne (LamE v1 e1) (LamE v2 e2)+  | v1 == v2 = LamE <$> pure v1 <*> collateOne e1 e2+  | otherwise = throwError $ OtherError "collate error #1"+collateOne e@(VarE v1) (VarE v2)+  | v1 == v2 = return e+  | otherwise = throwError $ OtherError "collate error #2"+-- primitives+collateOne e@UniE UniE = return e+collateOne e@(LogE _) (LogE _) = return e+collateOne e@(NumE _) (NumE _) = return e+collateOne e@(StrE _) (StrE _) = return e+-- accessors+collateOne (AccE e1 k1) (AccE e2 k2)+  | k1 == k2 = AccE <$> collateOne e1 e2 <*> pure k1+  | otherwise = throwError $ OtherError "collate error: unequal access keys"+-- containers+collateOne (ListE es1) (ListE es2)+  | length es1 == length es2 = ListE <$> zipWithM collateOne es1 es2+  | otherwise = throwError $ OtherError "collate error: unequal list length"+collateOne (TupleE es1) (TupleE es2)+  | length es1 == length es2 = TupleE <$> zipWithM collateOne es1 es2+  | otherwise = throwError $ OtherError "collate error: unequal tuple length"+collateOne (RecE es1) (RecE es2)+  | length es1 == length es2 =+    RecE <$> (+          zip+      <$> zipWithM returnIfEqual (map fst es1) (map fst es2)+      <*> zipWithM collateOne (map snd es1) (map snd es2)+    )+  | otherwise = throwError $ OtherError "collate error: unequal record length"+  where+    returnIfEqual :: Eq a => a -> a -> Stack a+    returnIfEqual x y+      | x == y = return x+      | otherwise = throwError $ OtherError "expected them to be equal"+-- variable expansion+collateOne (VarE _) x = return x+collateOne x (VarE _) = return x+-- illegal+collateOne (Signature _ _) (Signature _ _) = error "the hell's a toplevel doing down here?"+collateOne (Declaration _ _) (Declaration _ _) = error "the hell's is a toplevel doing down here?"+collateOne (SrcE _) (SrcE _) = error "the hell's is a toplevel doing down here?"+collateOne e1 e2 = throwError . OtherError . render $+  nest 2 . vsep $ ["collation failure - unequal expressions:", viaShow e1, viaShow e2]++collateTypes :: [UnresolvedType] -> [UnresolvedType] -> Stack [UnresolvedType]+collateTypes xs ys+  = mapM (collateByLang . snd)+  . groupSort+  $ [(langOf t, t) | t <- unique (xs ++ ys)]+  where+    collateByLang :: [UnresolvedType] -> Stack UnresolvedType+    collateByLang [] = throwError . OtherError $ "This should be impossible"+    collateByLang [t] = return t+    collateByLang (t1:ts) = foldM moreSpecific t1 ts++    moreSpecific :: UnresolvedType -> UnresolvedType -> Stack UnresolvedType+    moreSpecific (FunU t11 t12) (FunU t21 t22) = FunU <$> moreSpecific t11 t21 <*> moreSpecific t12 t22+    moreSpecific (ArrU v1 ts1) (ArrU _ ts2) = ArrU v1 <$> zipWithM moreSpecific ts1 ts2+    moreSpecific (NamU r1 v1 ps rs1) (NamU r2 v2 _ rs2)+      | v1 == v2 && r1 == r2 = NamU r1 <$> pure v1 <*> pure ps <*> zipWithM mergeEntry (sort rs1) (sort rs2)+      | otherwise = throwError . OtherError $ "Cannot collate records with unequal names/langs"+      where+      mergeEntry (k1, t1) (k2, t2)+        | k1 == k2 = (,) <$> pure k1 <*> moreSpecific t1 t2+        | otherwise = throwError . OtherError $ "Cannot collate records with unequal keys"+    moreSpecific (ExistU _ _ []) t = return t+    moreSpecific t (ExistU _ _ []) = return t+    moreSpecific (ForallU _ _) t = return t+    moreSpecific t (ForallU _ _) = return t+    moreSpecific t _ = return t+++-- | merge the new data from a signature with any prior type data+appendTypeSet :: TypeSet -> EType -> Stack TypeSet+appendTypeSet s e1 =+  case ((langOf . etype) e1, s) of+  -- if e is a general type, and there is no conflicting type, then set e+    (Nothing, TypeSet Nothing rs) -> do+      mapM_ (checkRealization e1) rs+      return $ TypeSet (Just e1) rs+  -- if e is a realization, and no general type is set, just add e to the list+    (Just _, TypeSet Nothing rs) -> do+      return $ TypeSet Nothing (e1 : [r | r <- rs, r /= e1])+  -- if e is a realization, and a general type exists, append it and check+    (Just _, TypeSet (Just e2) rs) -> do+      checkRealization e2 e1+      return $ TypeSet (Just e2) (e1 : [r | r <- rs, r /= e1])+  -- if e is general, and a general type exists, merge the general types+    (Nothing, TypeSet (Just e2) rs) -> do+      let e3 =+            EType+              { etype = etype e2+              , eprop = Set.union (eprop e1) (eprop e2)+              , econs = Set.union (econs e1) (econs e2)+              }+      return $ TypeSet (Just e3) rs++checkRealization :: EType -> EType -> Stack ()+checkRealization e1 e2 = f' (etype e1) (etype e2)+  where+    f' :: UnresolvedType -> UnresolvedType -> Stack ()+    f' (FunU x1 y1) (FunU x2 y2) = f' x1 x2 >> f' y1 y2+    f' (ForallU _ x) (ForallU _ y) = f' x y+    f' (ForallU _ x) y = f' x y+    f' x (ForallU _ y) = f' x y+    f' (ExistU _ [] _) (ExistU _ [] _) = return ()+    f' (ExistU v (_:xs) ds1) (ExistU w (_:ys) ds2) = f' (ExistU v xs ds1) (ExistU w ys ds2)+    f' (ExistU _ _ _) (ExistU _ _ _) = throwError . OtherError $+      "BadRealization: unequal number of parameters"+    f' (ExistU _ _ _) _ = return ()+    f' _ (ExistU _ _ _) = return ()+    f' t1@(FunU _ _) t2 = throwError . OtherError $+      "BadRealization: Cannot compare types '" <> MT.show' t1 <> "' to '" <> MT.show' t2 <> "'"+    f' t1 t2@(FunU _ _) = throwError . OtherError $+      "BadRealization: Cannot compare types '" <> MT.show' t1 <> "' to '" <> MT.show' t2 <> "'"+    f' _ _ = return ()++checkup :: Gamma -> Expr -> UnresolvedType -> Stack (Gamma, [UnresolvedType], Expr)+checkup g e t = do+  say "checkup"+  (g', t', e') <- check g e t+  return (g', [t'], e')++typesetFromList :: [UnresolvedType] -> Stack TypeSet+typesetFromList ts = do +  say "typesetFromList"+  let gentype = [makeEType t | t <- ts, (isNothing . langOf) t]+      contype = [makeEType t | t <- ts, (isJust . langOf) t]+  case (gentype, contype) of+    ([x], cs) -> return $ TypeSet (Just x) cs+    ([], cs) -> return $ TypeSet Nothing cs+    _ -> throwError $ OtherError "ambiguous general type"+  where+    makeEType :: UnresolvedType -> EType+    makeEType t = EType+      { etype = t+      , eprop = Set.empty+      , econs = Set.empty+      }++-- Synthesize types for a list of expressions. Each expression is synthesized+-- independently, though context is passed along. The returned "Maybe Type" is+-- the type of the paired expression in the given language.+chainInfer :: Maybe Lang -> Gamma -> [Expr] -> Stack (Gamma, [(Maybe UnresolvedType, Expr)])+chainInfer lang g0 es0 = do+  say "chainInfer"+  chainInfer' g0 (reverse es0) []+  where+    chainInfer' ::+         Gamma -> [Expr] -> [(Maybe UnresolvedType,Expr)] -> Stack (Gamma, [(Maybe UnresolvedType, Expr)])+    chainInfer' g [] xs = return (g, xs)+    chainInfer' g (e:es) xs = do+      (g', ts, e') <- infer lang g e+      let t' = listToMaybe $ filter (\t -> langOf t == lang) ts+      chainInfer' g' es ((t', e'):xs)++chainCheck :: [(UnresolvedType, Expr)] -> Gamma -> Stack (Gamma, [UnresolvedType], [Expr])+chainCheck xs g0 = do+  (g, ts, es) <- foldM f (g0, [], []) xs+  return (g, reverse ts, reverse es)+  where+    f :: (Gamma, [UnresolvedType], [Expr])+      -> (UnresolvedType, Expr)+      -> Stack (Gamma, [UnresolvedType], [Expr])+    f (g', ts, es) (t', e') = do +      (g'', t'', e'') <- check g' e' t'+      return (g'', t'':ts, e'':es)++++-- ----- U T I L I T I E S ----------------------------------------------++enter :: Doc AnsiStyle -> Stack ()+enter d = do+  depth <- incDepth+  debugLog $ pretty (take depth (repeat '-')) <> ">" <+> align d <> "\n"++say :: Doc AnsiStyle -> Stack ()+say d = do+  depth <- getDepth+  debugLog $ pretty (take depth (repeat ' ')) <> ":" <+> align d <> "\n"++seeGamma :: Gamma -> Stack ()+seeGamma g = say $ nest 4 $ "Gamma:" <> line <> (vsep (map prettyGammaIndex g))++leave :: Doc AnsiStyle -> Stack ()+leave d = do+  depth <- decDepth+  debugLog $ "<" <> pretty (take depth (repeat '-')) <+> align d <> "\n"++debugLog :: Doc AnsiStyle -> Stack ()+debugLog d = do+  verbosity <- R.asks stackConfigVerbosity +  if verbosity > 0+    then (liftIO . putDoc) d+    else return ()
+ library/Morloc/Frontend/Internal.hs view
@@ -0,0 +1,378 @@+{-|+Module      : Morloc.Frontend.Internal+Description : Utilities for type checking+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Frontend.Internal+  ( (+>)+  , (++>)+  , Renameable(..)+  , Applicable(..)+  , Typed(..) +  , access1+  , access2+  , accessWith1+  , ann+  , anns+  , cut+  , generalize+  , generalizeE+  , generalizeTypeSet+  , index+  , lookupE+  , lookupU+  , mapU+  , mapU'+  , newqul+  , newvar+  , newvarRich+  , throwError+  , serialConstraint+  , incDepth+  , decDepth+  , getDepth+  , langsOf+  ) where++import Control.Monad.Except (throwError)+import Morloc.Frontend.Namespace+import qualified Control.Monad.State as CMS+import qualified Data.Set as Set+import qualified Morloc.Data.Text as MT+import qualified Morloc.Frontend.PartialOrder as P++class HasManyLanguages a where+  langsOf :: Gamma -> a -> [Maybe Lang]++instance HasManyLanguages TypeSet where+  langsOf _ (TypeSet Nothing es) = map langOf es+  langsOf _ (TypeSet (Just e) es) = langOf e : map langOf es++instance HasManyLanguages Expr where+  langsOf g0 e0 = unique $ Nothing : langsOf' g0 e0 where+    langsOf' _ (SrcE srcs) = map (Just . srcLang) srcs+    langsOf' _ (Signature _ t) = [langOf t] +    langsOf' g (Declaration _ e) = langsOf' g e+    langsOf' _ UniE = [] +    langsOf' g (VarE v) = case lookupE v g of  +      (Just (_, ts)) -> langsOf g ts+      Nothing -> []+    langsOf' g (AccE e _) = langsOf' g e+    langsOf' g (ListE es) = concat . map (langsOf' g) $ es+    langsOf' g (TupleE es) = concat . map (langsOf' g) $ es+    langsOf' g (LamE _ e) = langsOf' g e +    langsOf' g (AppE e1 e2) = langsOf' g e1 ++ langsOf' g e2 +    langsOf' _ (AnnE _ ts) = map langOf ts+    langsOf' _ (NumE _) = []+    langsOf' _ (LogE _) = [] +    langsOf' _ (StrE _) = []+    langsOf' g (RecE entries) = concat . map (langsOf' g . snd) $ entries++class Renameable a where+  rename :: a -> Stack a+  unrename :: a -> a++instance Renameable Expr where+  rename = mapU' rename+  unrename = mapU unrename++instance Renameable UnresolvedType where+  rename t@(VarU _) = return t+  rename (ExistU v ts ds) = ExistU <$> pure v <*> (mapM rename ts) <*> (mapM rename ds)+  rename (ForallU v t) = do+    v' <- rename v+    t' <- rename (P.substitute v (VarU v') t)+    return $ ForallU v' t'+  rename (FunU t1 t2) = FunU <$> rename t1 <*> rename t2+  rename (ArrU v ts) = ArrU <$> pure v <*> mapM rename ts+  rename (NamU r v ts rs) =+    NamU r <$> pure v <*> mapM rename ts <*> mapM (\(x, t) -> (,) <$> pure x <*> rename t) rs++  unrename (VarU v) = VarU (unrename v)+  unrename (ExistU v ts ds) = ExistU v (map unrename ts) (map unrename ds)+  unrename (ForallU v t) = ForallU (unrename v) (unrename t)+  unrename (FunU t1 t2) = FunU (unrename t1) (unrename t2)+  unrename (ArrU v ts) = ArrU v (map unrename ts)+  unrename (NamU r v ts rs) = NamU r v (map unrename ts) [(x, unrename t) | (x, t) <- rs]++instance Renameable TVar where+  unrename (TV l t) = TV l . head $ MT.splitOn "." t+  rename = newqul+++class Applicable a where+  apply :: Gamma -> a -> a++-- | Apply a context to a type (See Dunfield Figure 8).+instance Applicable UnresolvedType where+  -- [G]a = a+  apply _ a@(VarU _) = a+  -- [G](A->B) = ([G]A -> [G]B)+  apply g (FunU a b) = FunU (apply g a) (apply g b)+  -- [G]ForallU a.a = forall a. [G]a+  apply g (ForallU x a) = ForallU x (apply g a)+  -- [G[a=t]]a = [G[a=t]]t+  apply g (ExistU v ts ds) =+    case lookupU v g of+      -- FIXME: this seems problematic - do I keep the previous parameters or the new ones?+      (Just t') -> apply g t' -- reduce an existential; strictly smaller term+      Nothing -> ExistU v (map (apply g) ts) (map (apply g) ds)+  apply g (ArrU v ts) = ArrU v (map (apply g) ts)+  apply g (NamU r v ts rs) = NamU r v (map (apply g) ts) (map (\(n, t) -> (n, apply g t)) rs)++instance Applicable Expr where+  apply g e = mapU (apply g) e++instance Applicable EType where+  apply g e = e { etype = apply g (etype e) }+++class Typed a where+  toType :: Maybe Lang -> a -> Maybe UnresolvedType+  fromType :: Maybe Lang -> UnresolvedType -> a++instance Typed EType where+  toType lang e+    | (langOf . etype) e == lang = Just (etype e)+    | otherwise = Nothing+  fromType _ t =+    EType+      { etype = t+      , eprop = Set.empty+      , econs = Set.empty+      }+++instance Typed TypeSet where+  toType Nothing (TypeSet e _) = e >>= toType Nothing+  toType lang (TypeSet _ ts) = case filter (\e -> (langOf . etype) e == lang) ts of +    [ ] -> Nothing+    [e] -> Just (etype e)+    _ -> error "a typeset can contain only one instance of each language"++  fromType Nothing t = TypeSet (Just (fromType Nothing t)) []+  fromType lang t = TypeSet Nothing [fromType lang t]++serialConstraint :: UnresolvedType -> UnresolvedType -> Stack ()+serialConstraint t1 t2 = do+  s <- CMS.get+  CMS.put (s {stateSer = (t1, t2):stateSer s})++incDepth :: Stack Int+incDepth = do+  s <- CMS.get +  let depth = stateDepth s + 1+  CMS.put (s {stateDepth = depth})+  return depth++decDepth :: Stack Int+decDepth = do+  s <- CMS.get +  let depth = stateDepth s - 1+  CMS.put (s {stateDepth = depth})+  return depth++getDepth :: Stack Int+getDepth = CMS.gets stateDepth++mapU :: (UnresolvedType -> UnresolvedType) -> Expr -> Expr+mapU f (LamE v e) = LamE v (mapU f e)+mapU f (ListE es) = ListE (map (mapU f) es)+mapU f (TupleE es) = TupleE (map (mapU f) es)+mapU f (RecE rs) = RecE (zip (map fst rs) (map (mapU f . snd) rs))+mapU f (AppE e1 e2) = AppE (mapU f e1) (mapU f e2)+mapU f (AnnE e ts) = AnnE (mapU f e) (map f ts)+mapU f (Declaration v e) = Declaration v (mapU f e)+mapU f (Signature v e) = Signature v $ e {etype = f (etype e)}+mapU _ e = e++mapU' :: Monad m => (UnresolvedType -> m UnresolvedType) -> Expr -> m Expr+mapU' f (LamE v e) = LamE <$> pure v <*> mapU' f e+mapU' f (ListE es) = ListE <$> mapM (mapU' f) es+mapU' f (RecE rs) = do+  es' <- mapM (mapU' f . snd) rs+  return $ RecE (zip (map fst rs) es')+mapU' f (TupleE es) = TupleE <$> mapM (mapU' f) es+mapU' f (AppE e1 e2) = AppE <$> mapU' f e1 <*> mapU' f e2+mapU' f (AnnE e ts) = AnnE <$> mapU' f e <*> mapM f ts+mapU' f (Declaration v e) = Declaration <$> pure v <*> mapU' f e+mapU' f (Signature v e) = do+  t' <- f (etype e)+  return $ Signature v (e {etype = t'})+mapU' _ e = return e++(+>) :: Indexable a => Gamma -> a -> Gamma+(+>) xs x = (index x) : xs++(++>) :: Indexable a => Gamma -> [a] -> Gamma+(++>) g xs = map index (reverse xs) ++ g ++-- | remove context up to a marker+cut :: GammaIndex -> Gamma -> Stack Gamma+cut _ [] = throwError EmptyCut+cut i (x:xs)+  | i == x = return xs+  | otherwise = cut i xs++-- | Look up a type annotated expression+lookupE :: EVar -> Gamma -> Maybe (Expr, TypeSet)+lookupE _ [] = Nothing+lookupE v ((AnnG (Declaration v' e) t):gs)+  | v == v' = Just (e, t)+  | otherwise = lookupE v gs+lookupE v ((AnnG e@(VarE v') t):gs)+  | v == v' = Just (e, t)+  | otherwise = lookupE v gs+lookupE v (_:gs) = lookupE v gs++-- | Look up a solved existential type variable+lookupU :: TVar -> Gamma -> Maybe UnresolvedType+lookupU _ [] = Nothing+lookupU v ((SolvedG v' t):gs)+  | v == v' = Just t+  | otherwise = lookupU v gs+lookupU v (_:gs) = lookupU v gs++access1 :: TVar -> Gamma -> Maybe (Gamma, GammaIndex, Gamma)+access1 v gs =+  case findIndex (exists v) gs of+    (Just 0) -> Just ([], head gs, tail gs)+    (Just i) -> Just (take i gs, gs !! i, drop (i + 1) gs)+    _ -> Nothing+  where+    exists :: TVar -> GammaIndex -> Bool+    exists v1 (ExistG v2 _ _) = v1 == v2+    exists _ _ = False++accessWith1 :: Monad m =>+     (GammaIndex -> Bool) -- ^ method for finding the index+  -> (GammaIndex -> m GammaIndex) -- ^ alter GammaIndex+  -> (Gamma -> m Gamma) -- ^ default action if the index is not found+  -> Gamma -- ^ context that is searched+  -> m Gamma+accessWith1 select make def g =+  case findIndex select g of+    (Just i) ->+      case (i, g !! i) of+        (0, x) -> make x >>= (\y -> return ([] <> (y : tail g)))+        (_, x) -> make x >>= (\y -> return (take i g <> (y : drop (i + 1) g)))+    Nothing -> def g++access2 ::+     TVar -> TVar+  -> Gamma -> Maybe (Gamma, GammaIndex, Gamma, GammaIndex, Gamma)+access2 lv rv gs =+  case access1 lv gs of+    Just (ls, x, rs) ->+      case access1 rv rs of+        Just (ls', y, rs') -> Just (ls, x, ls', y, rs')+        _ -> Nothing+    _ -> Nothing++ann :: Expr -> UnresolvedType -> Expr+ann (AnnE e _) t = AnnE e [t] +ann e@(Declaration _ _) _ = e+ann e@(Signature _ _) _ = e+ann e t = AnnE e [t]++anns :: Expr -> [UnresolvedType] -> Expr+anns (AnnE e _) ts = AnnE e ts +anns e@(Declaration _ _) _ = e+anns e@(Signature _ _) _ = e+anns e ts = AnnE e ts++-- | Deal with existentials.+-- This function is used to resolve remaining existentials when no further+-- inferences about their type can be made. If the existentials have a default+-- type, then that type can be used to replace the existential. Otherwise, the+-- existential can be cast as generic (ForallU).+generalize :: UnresolvedType -> UnresolvedType+generalize = (\t -> generalize' (existentialMap t) t) . setDefaults where+  generalize' :: [(TVar, Name)] -> UnresolvedType -> UnresolvedType+  generalize' [] t = t+  generalize' ((e, r):xs) t = generalize' xs (generalizeOne e r t)++  setDefaults :: UnresolvedType -> UnresolvedType+  setDefaults (ExistU v ps []) = ExistU v (map setDefaults ps) []+  setDefaults (ExistU _ _ (d:_)) = setDefaults d+  setDefaults t@(VarU _) = t+  setDefaults (ForallU v t) = ForallU v (setDefaults t)+  setDefaults (FunU t1 t2) = FunU (setDefaults t1) (setDefaults t2)+  setDefaults (ArrU v ts) = ArrU v (map setDefaults ts)+  setDefaults (NamU r v ts es)+    = NamU r v (map setDefaults ts) (zip (map fst es) (map (setDefaults . snd) es))++  variables = [1 ..] >>= flip replicateM ['a' .. 'z']++  existentialMap t =+    zip (Set.toList (findExistentials t)) (map (Name . MT.pack) variables)++  findExistentials :: UnresolvedType -> Set.Set TVar+  findExistentials (VarU _) = Set.empty+  findExistentials (ExistU v ts ds) =+    Set.unions+      $ [Set.singleton v]+      ++ map findExistentials ts+      ++ map findExistentials ds+  findExistentials (ForallU v t) = Set.delete v (findExistentials t)+  findExistentials (FunU t1 t2) =+    Set.union (findExistentials t1) (findExistentials t2)+  findExistentials (ArrU _ ts) = Set.unions (map findExistentials ts)+  findExistentials (NamU _ _ ts rs)+    = Set.unions (map findExistentials ts ++ map (findExistentials . snd) rs)++  generalizeOne :: TVar -> Name -> UnresolvedType -> UnresolvedType+  generalizeOne v0@(TV lang0 _) r0 t0 = ForallU (TV lang0 (unName r0)) (f v0 t0)+    where+      f :: TVar -> UnresolvedType -> UnresolvedType+      f v t1@(ExistU v' [] _)+        | v == v' = VarU (TV lang0 (unName r0))+        | otherwise = t1+      f v (ExistU v' ts _)+        | v == v' = ArrU (TV lang0 (unName r0)) (map (f v) ts)+        | otherwise = ArrU v (map (f v) ts)+      f v (FunU t1 t2) = FunU (f v t1) (f v t2)+      f v t1@(ForallU x t2)+        | v /= x = ForallU x (f v t2)+        | otherwise = t1+      f v (ArrU v' xs) = ArrU v' (map (f v) xs)+      f v (NamU r v' ts xs) = NamU r v' (map (f v) ts) [(k, f v t) | (k, t) <- xs]+      f _ t1 = t1++generalizeE :: Expr -> Expr+generalizeE = mapU generalize++generalizeEType :: EType -> EType+generalizeEType e = e {etype = generalize (etype e)}++generalizeTypeSet :: TypeSet -> TypeSet+generalizeTypeSet (TypeSet t ts) =+  TypeSet (fmap generalizeEType t) (map generalizeEType ts)++newvar :: Maybe Lang -> Stack UnresolvedType+newvar = newvarRich [] []++newvarRich+  :: [UnresolvedType]+  -> [UnresolvedType] -- ^ default types+  -> Maybe Lang+  -> Stack UnresolvedType+newvarRich ps ds lang = do+  s <- CMS.get+  let v = newvars !! stateVar s+  CMS.put $ s {stateVar = stateVar s + 1}+  return (ExistU (TV lang v) ps ds)+  where+    newvars =+      zipWith (\x y -> MT.pack (x ++ show y)) (repeat "t") ([0 ..] :: [Integer])++newqul :: TVar -> Stack TVar+newqul (TV l v) = do+  s <- CMS.get+  let v' = TV l (v <> "." <> (MT.pack . show $ stateQul s)) -- create a new variable such as "a.0"+  CMS.put $ s {stateQul = stateQul s + 1}+  return v'
+ library/Morloc/Frontend/Lang/DefaultTypes.hs view
@@ -0,0 +1,92 @@+{-|+Module      : Morloc.Frontend.Lang.DefaultTypes+Description : Define default types for each language+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++Concrete types are usually inferred from concrete function types. But in some+cases, it is necessary to guess the type. For example, in the statement @f x =+[x]@, a concrete list container is needed. When @f@ is called from another+function, the concrete list type can be inferred. But if @f@ is directly+exported, the concrete list type is unknown. Providing a concrete signature for+@f@, however, limits the use of @f@ in other functions. Thus a default type is+needed.+-}++module Morloc.Frontend.Lang.DefaultTypes+  ( defaultBool+  , defaultList+  , defaultNull+  , defaultNumber+  , defaultRecord+  , defaultString+  , defaultTuple+  ) where++import Morloc.Frontend.Namespace+import qualified Morloc.Data.Text as MT++defaultList :: Maybe Lang -> UnresolvedType -> [UnresolvedType]+defaultList lang@Nothing t = [ArrU (TV lang "List") [t]]+defaultList lang@(Just Python3Lang) t = [ArrU (TV lang "list") [t]]+defaultList lang@(Just RLang) t = [ArrU (TV lang "list") [t]]+defaultList lang@(Just CLang) t = [ArrU (TV lang "$1*") [t]]+defaultList lang@(Just CppLang) t = [ArrU (TV lang "std::vector<$1>") [t]]+defaultList lang@(Just PerlLang) t = [ArrU (TV lang "array") [t]]++defaultTuple :: Maybe Lang -> [UnresolvedType] -> [UnresolvedType]+defaultTuple lang@Nothing ts = [ArrU (TV lang (MT.pack $ "Tuple" ++ show (length ts))) ts]+defaultTuple lang@(Just Python3Lang) ts = [ArrU (TV lang "tuple") ts]+defaultTuple lang@(Just RLang) ts = [ArrU (TV lang "tuple") ts]+defaultTuple      (Just CLang) _ = []+defaultTuple lang@(Just CppLang) ts = [ArrU (TV lang t) ts] where+  vars = ["$" <> MT.show' i | i <- [1 .. length ts]]+  t = "std::tuple<" <> MT.intercalate "," vars <> ">"+defaultTuple lang@(Just PerlLang) ts = [ArrU (TV lang "array") ts]++defaultRecord :: Maybe Lang -> [(MT.Text, UnresolvedType)] -> [UnresolvedType]+defaultRecord lang@Nothing entries = [NamU NamRecord (TV lang "Record") [] entries]+defaultRecord lang@(Just Python3Lang) entries = [NamU NamRecord (TV lang "dict") [] entries]+defaultRecord lang@(Just RLang) entries = [NamU NamRecord (TV lang "list") [] entries]+defaultRecord      (Just CLang) _ = []+defaultRecord lang@(Just CppLang) entries = [NamU NamRecord (TV lang "struct") [] entries]+defaultRecord lang@(Just PerlLang) entries = [NamU NamRecord (TV lang "hash") [] entries]++defaultNull :: Maybe Lang -> [UnresolvedType]+defaultNull lang@Nothing = [VarU (TV lang "Unit")]+defaultNull lang@(Just Python3Lang) = [VarU (TV lang "None")]+defaultNull lang@(Just RLang) = [VarU (TV lang "NULL")]+defaultNull lang@(Just CLang) = [VarU (TV lang "null")]+defaultNull lang@(Just CppLang) = [VarU (TV lang "null")]+defaultNull lang@(Just PerlLang) = [VarU (TV lang "NULL")]++defaultBool :: Maybe Lang -> [UnresolvedType]+defaultBool lang@Nothing = [VarU (TV lang "Bool")]+defaultBool lang@(Just Python3Lang) = [VarU (TV lang "bool")]+defaultBool lang@(Just RLang) = [VarU (TV lang "logical" )]+defaultBool lang@(Just CLang) = [VarU (TV lang "bool")]+defaultBool lang@(Just CppLang) = [VarU (TV lang "bool")]+defaultBool lang@(Just PerlLang) = [VarU (TV lang "bool")]++defaultString :: Maybe Lang -> [UnresolvedType]+defaultString lang@Nothing = [VarU (TV lang "Str")]+defaultString lang@(Just Python3Lang) = [VarU (TV lang "str")]+defaultString lang@(Just RLang) = [VarU (TV lang "character")]+defaultString lang@(Just CLang) = [VarU (TV lang "char*")]+defaultString lang@(Just CppLang) = [VarU (TV lang "std::string")]+defaultString lang@(Just PerlLang) = [VarU (TV lang "str")]++defaultNumber :: Maybe Lang -> [UnresolvedType]+defaultNumber lang@Nothing = [VarU (TV lang "Num"), VarU (TV lang "Int")]+defaultNumber lang@(Just Python3Lang) = [VarU (TV lang "float"), VarU (TV lang "int")]+defaultNumber lang@(Just RLang) = [VarU (TV lang "numeric"), VarU (TV lang "integer")]+defaultNumber lang@(Just CLang) = [VarU (TV lang "double"), VarU (TV lang "int")]+defaultNumber lang@(Just CppLang) =+  [ VarU (TV lang "double")+  , VarU (TV lang "int")+  , VarU (TV lang "long")+  , VarU (TV lang "size_t")+  ]+defaultNumber lang@(Just PerlLang) = [VarU (TV lang "double")]
+ library/Morloc/Frontend/Namespace.hs view
@@ -0,0 +1,235 @@+{-|+Module      : Morloc.Frontend.Namespace+Description : All frontend types and datastructures+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Frontend.Namespace+  ( module Morloc.Namespace+  , Expr(..)+  , Import(..)+  , Stack+  , StackState(..)+  , StackConfig(..)+  -- ** DAG and associated types+  , ParserNode(..)+  , ParserDag+  , PreparedNode(..)+  , PreparedDag+  , TypedNode(..)+  , TypedDag+  -- ** Typechecking+  , Gamma+  , GammaIndex(..)+  , EType(..)+  , TypeSet(..)+  , Indexable(..)+  -- ** ModuleGamma paraphernalia+  , ModularGamma+  -- rifraf+  , resolve+  , substituteT+  ) where++import Morloc.Namespace+import Data.Set (Set)+import Data.Map.Strict (Map)+import Control.Monad.Except (ExceptT)+import Control.Monad.Reader (ReaderT)+import Control.Monad.State (StateT)+import Control.Monad.Writer (WriterT)+import Data.Scientific (Scientific)+import Data.Text (Text)++++-- This functions removes qualified and existential types.+--  * all qualified terms are replaced with UnkT+--  * all existentials are replaced with default values if a possible+--    FIXME: should I really just take the first in the list???+resolve :: UnresolvedType -> Type+resolve (VarU v) = VarT v+resolve (FunU t1 t2) = FunT (resolve t1) (resolve t2)+resolve (ArrU v ts) = ArrT v (map resolve ts)+resolve (NamU r v ps rs) =+  let ts' = map (resolve . snd) rs+      ps' = map resolve ps +  in NamT r v ps' (zip (map fst rs) ts')+resolve (ExistU _ _ []) = error "UnsolvedExistentialTerm"+resolve (ExistU _ _ (t:_)) = resolve t+resolve (ForallU v t) = substituteT v (UnkT v) (resolve t)++-- | substitute all appearances of a given variable with a given new type+substituteT :: TVar -> Type -> Type -> Type+substituteT v0 r0 t0 = sub t0+  where+    sub :: Type -> Type+    sub t@(UnkT _) = t+    sub t@(VarT v)+      | v0 == v = r0+      | otherwise = t+    sub (FunT t1 t2) = FunT (sub t1) (sub t2)+    sub (ArrT v ts) = ArrT v (map sub ts)+    sub (NamT r v ts rs) = NamT r v (map sub ts) [(x, sub t) | (x, t) <- rs]+++-- | Terms, see Dunfield Figure 1+data Expr+  = SrcE [Source]+  -- ^ import "c" from "foo.c" ("f" as yolo)+  | Signature EVar EType+  -- ^ x :: A; e+  | Declaration EVar Expr+  -- ^ x=e1; e2+  | UniE+  -- ^ (())+  | VarE EVar+  -- ^ (x)+  | AccE Expr EVar+  -- ^ person@age - access a field in a record+  | ListE [Expr]+  -- ^ [e]+  | TupleE [Expr]+  -- ^ (e1), (e1,e2), ... (e1,e2,...,en)+  | LamE EVar Expr+  -- ^ (\x -> e)+  | AppE Expr Expr+  -- ^ (e e)+  | AnnE Expr [UnresolvedType]+  -- ^ (e : A)+  | NumE Scientific+  -- ^ number of arbitrary size and precision+  | LogE Bool+  -- ^ boolean primitive+  | StrE Text+  -- ^ literal string+  | RecE [(EVar, Expr)]+  deriving (Show, Ord, Eq)++-- | Extended Type that may represent a language specific type as well as sets+-- of properties and constrains.+data EType =+  EType+    { etype :: UnresolvedType+    , eprop :: Set Property+    , econs :: Set Constraint+    }+  deriving (Show, Eq, Ord)++instance HasOneLanguage EType where+  langOf e = langOf (etype e) ++data Import =+  Import+    { importModuleName :: MVar+    , importInclude :: Maybe [(EVar, EVar)]+    , importExclude :: [EVar]+    , importNamespace :: Maybe EVar -- currently not used+    }+  deriving (Ord, Eq, Show)++-- | A context, see Dunfield Figure 6+data GammaIndex+  = VarG TVar+  -- ^ (G,a)+  | AnnG Expr TypeSet+  -- ^ (G,x:A) looked up in the (Var) and cut in (-->I)+  | ExistG TVar [UnresolvedType] [UnresolvedType]+  -- ^ (G,a^) unsolved existential variable+  | SolvedG TVar UnresolvedType+  -- ^ (G,a^=t) Store a solved existential variable+  | MarkG TVar+  -- ^ (G,>a^) Store a type variable marker bound under a forall+  | MarkEG EVar+  -- ^ ...+  | SrcG Source+  -- ^ source+  | UnsolvedConstraint UnresolvedType UnresolvedType+  -- ^ Store an unsolved serialization constraint containing one or more+  -- existential variables. When the existential variables are solved, the+  -- constraint will be written into the Stack state.+  deriving (Ord, Eq, Show)++type Gamma = [GammaIndex]++data TypeSet =+  TypeSet (Maybe EType) [EType]+  deriving (Show, Eq, Ord)++type ModularGamma = Map MVar (Map EVar TypeSet)++class Indexable a where+  index :: a -> GammaIndex++instance Indexable GammaIndex where+  index = id++instance Indexable UnresolvedType where+  index (ExistU t ts ds) = ExistG t ts ds+  index t = error $ "Can only index ExistT, found: " <> show t++++type GeneralStack c e l s a+   = ReaderT c (ExceptT e (WriterT l (StateT s IO))) a++type Stack a = GeneralStack StackConfig MorlocError [Text] StackState a++data StackConfig =+  StackConfig+    { stackConfigVerbosity :: Int+    }++data StackState =+  StackState+    { stateVar :: Int+    , stateQul :: Int+    , stateSer :: [(UnresolvedType, UnresolvedType)]+    , stateDepth :: Int+    }+  deriving (Ord, Eq, Show)++++-- | The type returned from the Parser. It contains all the information in a+-- single module but knows NOTHING about other modules.+data ParserNode = ParserNode  {+    parserNodePath :: Maybe Path+  , parserNodeBody :: [Expr]+  , parserNodeSourceMap :: Map (EVar, Lang) Source+  , parserNodeTypedefs :: Map TVar (UnresolvedType, [TVar])+  , parserNodeExports :: Set EVar+} deriving (Show, Ord, Eq)+type ParserDag = DAG MVar Import ParserNode++-- | Node description after desugaring (substitute type aliases and resolve+-- imports/exports)+data PreparedNode = PreparedNode {+    preparedNodePath :: Maybe Path+  , preparedNodeBody :: [Expr]+  , preparedNodeSourceMap :: Map (EVar, Lang) Source+  , preparedNodeExports :: Set EVar+  , preparedNodeTypedefs :: Map TVar (UnresolvedType, [TVar])+  , preparedNodePackers :: Map (TVar, Int) [UnresolvedPacker]+  -- ^ The (un)packers available in this module scope.+} deriving (Show, Ord, Eq)+type PreparedDag = DAG MVar [(EVar, EVar)] ParserNode++-- | Node description after type checking. This will later be fed into+-- `treeify` to make the SAnno objects that will be passed to Generator.+data TypedNode = TypedNode {+    typedNodeModuleName :: MVar+  , typedNodePath :: Maybe Path+  , typedNodeBody :: [Expr]+  , typedNodeTypeMap :: Map EVar TypeSet+  , typedNodeSourceMap :: Map (EVar, Lang) Source+  , typedNodeExports :: Set EVar+  , typedNodeTypedefs :: Map TVar (Type, [TVar])+  , typedNodePackers :: Map (TVar, Int) [UnresolvedPacker]+  , typedNodeConstructors :: Map TVar Source+  -- ^ The (un)packers available in this module scope.+} deriving (Show, Ord, Eq)+type TypedDag = DAG MVar [(EVar, EVar)] TypedNode
+ library/Morloc/Frontend/Parser.hs view
@@ -0,0 +1,642 @@+{-|+Module      : Morloc.Frontend.Parser+Description : Full parser for Morloc+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.Frontend.Parser+  ( readProgram+  , readType+  ) where++import Data.Void (Void)+import Morloc.Frontend.Namespace+import Text.Megaparsec+import Text.Megaparsec.Char+import qualified Morloc.Frontend.Lang.DefaultTypes as MLD+import qualified Control.Monad.State as CMS+import qualified Data.Map as Map+import qualified Data.Scientific as DS+import qualified Data.Set as Set+import qualified Data.Char as DC+import qualified Morloc.Data.Text as MT+import qualified Morloc.Language as ML+import qualified Morloc.System as MS+import qualified Text.Megaparsec.Char.Lexer as L++type Parser a = CMS.StateT ParserState (Parsec Void MT.Text) a++data ParserState = ParserState {+    stateLang :: Maybe Lang+  , stateModulePath :: Maybe Path+  , stateIndex :: Int+  , stateGenerics :: [TVar] -- store the observed generic variables in the current type+                            -- you should reset the field before parsing a new type +}++emptyState :: ParserState+emptyState = ParserState {+    stateLang = Nothing+  , stateModulePath = Nothing+  , stateIndex = 1+  , stateGenerics = []+}++newvar :: Maybe Lang -> Parser TVar+newvar lang = do+  s <- CMS.get+  let i = stateIndex s +  CMS.put (s {stateIndex = i + 1}) +  return (TV lang ("p" <> MT.show' i))++setLang :: Maybe Lang -> Parser ()+setLang lang = do+  s <- CMS.get+  CMS.put (s { stateLang = lang })++resetGenerics :: Parser ()+resetGenerics = do+  s <- CMS.get+  CMS.put (s { stateGenerics = [] })++appendGenerics :: TVar -> Parser ()+appendGenerics v@(TV _ vstr) = do+  s <- CMS.get+  let isGeneric = maybe False (DC.isLower . fst) (MT.uncons vstr)+      gs = stateGenerics s+      gs' = if isGeneric then v : gs else gs+  CMS.put (s {stateGenerics = gs'})++readProgram+  :: Maybe Path+  -> MT.Text+  -> DAG MVar Import ParserNode+  -> DAG MVar Import ParserNode+readProgram f sourceCode p =+  case runParser+         (CMS.runStateT (sc >> pProgram <* eof) pstate)+         (maybe "<expr>" (MT.unpack . unPath) f)+         sourceCode of+    Left err -> error (show err)+    Right (es, _) -> foldl (\d (k,xs,n) -> Map.insert k (n,xs) d) p es +  where+    pstate = emptyState { stateModulePath = f }++readType :: MT.Text -> UnresolvedType+readType typeStr =+  case runParser (CMS.runStateT (pTypeGen <* eof) emptyState) "" typeStr of+    Left err -> error (show err)+    Right (es, _) -> es++many1 :: Parser a -> Parser [a]+many1 p = do+  x <- p+  xs <- many p+  return (x : xs)++-- sc stands for space consumer+sc :: Parser ()+sc = L.space space1 lineComment blockComment+  where+    lineComment = L.skipLineComment "--"+    blockComment = L.skipBlockComment "{-" "-}"++symbol = L.symbol sc++-- A lexer where space is consumed after every token (but not before)+lexeme :: Parser a -> Parser a+lexeme = L.lexeme sc++number :: Parser DS.Scientific+number = lexeme $ L.signed sc L.scientific -- `empty` because no space is allowed++parens :: Parser a -> Parser a+parens p = lexeme $ between (symbol "(") (symbol ")") p++brackets :: Parser a -> Parser a+brackets p = lexeme $ between (symbol "[") (symbol "]") p++braces :: Parser a -> Parser a+braces p = lexeme $ between (symbol "{") (symbol "}") p++angles :: Parser a -> Parser a+angles p = lexeme $ between (symbol "<") (symbol ">") p++reservedWords :: [MT.Text]+reservedWords =+  [ "module"+  , "source"+  , "from"+  , "where"+  , "import"+  , "export"+  , "as"+  , "True"+  , "False"+  , "type"+  ]++operatorChars :: String+operatorChars = ":!$%&*+./<=>?@\\^|-~#"++delimiter :: Parser ()+delimiter = many1 (symbol ";") >> return ()++op :: MT.Text -> Parser MT.Text+op o = (lexeme . try) (symbol o <* notFollowedBy (oneOf operatorChars))++reserved :: MT.Text -> Parser MT.Text+reserved w = try (symbol w)++stringLiteral :: Parser MT.Text+stringLiteral = do+  _ <- symbol "\""+  s <- many (noneOf ['"'])+  _ <- symbol "\""+  return $ MT.pack s++name :: Parser MT.Text+name = (lexeme . try) (p >>= check)+  where+    p = fmap MT.pack $ (:) <$> letterChar <*> many (alphaNumChar <|> char '_')+    check x =+      if elem x reservedWords+        then failure Nothing Set.empty -- TODO: error message+        else return x++data Toplevel+  = TModule (MVar, [(MVar, Import)], ParserNode)+  | TModuleBody ModuleBody++data ModuleBody+  = MBImport Import+  -- ^ module name, function name and optional alias+  | MBExport EVar+  | MBTypeDef TVar [TVar] UnresolvedType+  | MBBody Expr++pProgram :: Parser [(MVar, [(MVar, Import)], ParserNode)]+pProgram = do+  f <- CMS.gets stateModulePath+  -- allow ';' at the beginning (if you're into that sort of thing)+  optional delimiter+  es <- many pToplevel+  let mods = [m | (TModule m) <- es]+  case [e | (TModuleBody e) <- es] of+    [] -> return mods+    es' -> return $ makeModule f (MVar "Main") es' : mods++pToplevel :: Parser Toplevel+pToplevel =+  try (fmap TModule pModule <* optional delimiter) <|>+  fmap TModuleBody (pModuleBody <* optional delimiter)++pModule :: Parser (MVar, [(MVar, Import)], ParserNode)+pModule = do+  f <- CMS.gets stateModulePath+  _ <- reserved "module"+  moduleName' <- name+  mes <- braces (optional delimiter >> many1 pModuleBody)+  return $ makeModule f (MVar moduleName') mes++makeModule :: Maybe Path -> MVar -> [ModuleBody] -> (MVar, [(MVar, Import)], ParserNode)+makeModule f n mes = (n, edges, node) where+  imports' = [x | (MBImport x) <- mes]+  exports' = Set.fromList [x | (MBExport x) <- mes]+  body' = [x | (MBBody x) <- mes]+  srcMap = (Map.fromList . concat)+           [[((srcAlias s, srcLang s), s) | s <- ss ] | (SrcE ss) <- body']+  typedefmap = Map.fromList [(v, (t, vs)) | MBTypeDef v vs t <- mes]+  edges = [(importModuleName i, i) | i <- imports']+  node = ParserNode+    { parserNodePath = f+    , parserNodeBody = body'+    , parserNodeSourceMap = srcMap+    , parserNodeExports = exports'+    , parserNodeTypedefs = typedefmap+    }++pModuleBody :: Parser ModuleBody+pModuleBody =+        try pTypedef <* optional delimiter+    <|> try pImport <* optional delimiter +    <|> try pExport <* optional delimiter +    <|> try pStatement' <* optional delimiter+    <|> pExpr' <* optional delimiter+  where+    pStatement' = fmap MBBody pStatement+    pExpr' = fmap MBBody pExpr++pTypedef :: Parser ModuleBody+pTypedef = pTypedefType <|> pTypedefObject++pTypedefType :: Parser ModuleBody+pTypedefType = do+  _ <- reserved "type"+  lang <- optional (try pLang)+  setLang lang+  (v, vs) <- pTypedefTermUnpar <|> pTypedefTermPar+  _ <- symbol "="+  t <- pType+  setLang Nothing+  return (MBTypeDef v vs t)++pTypedefObject :: Parser ModuleBody+pTypedefObject = do+  r <- pNamType+  lang <- optional (try pLang)+  setLang lang+  (v, vs) <- pTypedefTermUnpar <|> pTypedefTermPar+  _ <- symbol "="+  constructor <- name <|> stringLiteral+  entries <- braces (sepBy1 pNamEntryU (symbol ",")) >>= mapM (desugarTableEntries lang r)+  let t = NamU r (TV lang constructor) (map VarU vs) entries+  setLang Nothing+  return $ MBTypeDef v vs t++desugarTableEntries+  :: Maybe Lang+  -> NamType+  -> (MT.Text, UnresolvedType)+  -> Parser (MT.Text, UnresolvedType)+desugarTableEntries _ NamRecord entry = return entry+desugarTableEntries _ NamObject entry = return entry+desugarTableEntries lang NamTable (k0, t0) = (,) k0 <$> f t0 where+  f :: UnresolvedType -> Parser UnresolvedType+  f (ForallU v t) = ForallU v <$> f t+  f t = return $ head (MLD.defaultList lang t)++pNamType :: Parser NamType+pNamType = choice [pNamObject, pNamTable, pNamRecord] ++pNamObject :: Parser NamType+pNamObject = do+  _ <- reserved "object" +  return NamObject++pNamTable :: Parser NamType+pNamTable = do+  _ <- reserved "table" +  return NamTable++pNamRecord :: Parser NamType+pNamRecord = do+  _ <- reserved "record" +  return NamRecord++pTypedefTermUnpar :: Parser (TVar, [TVar])+pTypedefTermUnpar = do+  v <- name+  lang <- CMS.gets stateLang+  return (TV lang v, [])++pTypedefTermPar :: Parser (TVar, [TVar])+pTypedefTermPar = do+  vs <- parens (many1 name)+  lang <- CMS.gets stateLang+  return (TV lang (head vs), map (TV lang) (tail vs))++pImport :: Parser ModuleBody+pImport = do+  _ <- reserved "import"+  n <- name+  imports <-+    optional $+    parens (sepBy pImportTerm (symbol ",")) <|> fmap (\x -> [(EVar x, EVar x)]) name+  return . MBImport $+    Import+      { importModuleName = MVar n+      , importInclude = imports+      , importExclude = []+      , importNamespace = Nothing+      }++pImportTerm :: Parser (EVar, EVar)+pImportTerm = do+  n <- name+  a <- option n (reserved "as" >> name)+  return (EVar n, EVar a)++pExport :: Parser ModuleBody+pExport = fmap (MBExport . EVar) $ reserved "export" >> name++pStatement :: Parser Expr+pStatement = try pDeclaration <|> pSignature++pDeclaration :: Parser Expr+pDeclaration = try pFunctionDeclaration <|> pDataDeclaration++pDataDeclaration :: Parser Expr+pDataDeclaration = do+  v <- name+  _ <- symbol "="+  e <- pExpr+  return (Declaration (EVar v) e)++pFunctionDeclaration :: Parser Expr+pFunctionDeclaration = do+  v <- name+  args <- many1 name+  _ <- op "="+  e <- pExpr+  return $ Declaration (EVar v) (curryLamE (map EVar args) e)+  where+    curryLamE [] e' = e'+    curryLamE (v:vs') e' = LamE v (curryLamE vs' e')++pSignature :: Parser Expr+pSignature = do+  v <- name+  lang <- optional (try pLang)+  setLang lang+  _ <- op "::"+  props <- option [] (try pPropertyList)+  t <- pTypeGen+  constraints <-+    option [] $ reserved "where" >> braces (sepBy pConstraint (symbol ";"))+  setLang Nothing+  return $+    Signature+      (EVar v)+      (EType+         { etype = t+         , eprop = Set.fromList props+         , econs = Set.fromList constraints+         })++pLang :: Parser Lang+pLang = do+  langStr <- name+  case ML.readLangName langStr of+    (Just lang) -> return lang+    Nothing -> fancyFailure . Set.singleton . ErrorFail+      $ "Langage '" <> MT.unpack langStr <> "' is not supported"+ +-- | match an optional tag that precedes some construction+tag :: Parser a -> Parser (Maybe MT.Text)+tag p = optional (try tag')+  where+    tag' = do+      l <- name+      _ <- op ":"+      _ <- lookAhead p+      return l++pPropertyList :: Parser [Property]+pPropertyList =+  (parens (sepBy1 pProperty (symbol ",")) <|> sepBy1 pProperty (symbol ",")) <*+  op "=>"++pProperty :: Parser Property+pProperty = do+  ps <- many1 name+  case ps of+    ["pack"] -> return Pack+    ["unpack"] -> return Unpack+    ["cast"] -> return Cast+    _ -> return (GeneralProperty ps)++pConstraint :: Parser Constraint+pConstraint = fmap (Con . MT.pack) (many (noneOf ['{', '}']))++pExpr :: Parser Expr+pExpr =+      try pAcc+  <|> try pNamE+  <|> try pTuple+  <|> try pUni+  <|> try pAnn+  <|> try pApp+  <|> try pStrE+  <|> try pLogE+  <|> try pNumE+  <|> try pSrcE+  <|> pListE+  <|> parens pExpr+  <|> pLam+  <|> pVar++pSrcE :: Parser Expr+pSrcE = do+  modulePath <- CMS.gets stateModulePath+  reserved "source"+  language <- pLang+  srcfile <- optional (reserved "from" >> stringLiteral |>> Path)+  rs <- parens (sepBy1 pImportSourceTerm (symbol ","))+  srcFile <- case (modulePath, srcfile) of+    -- build a path to the source file by searching+    -- > source "R" from "foo.R" ("Foo" as foo, "bar")+    (Just f, Just srcfile') -> return . Just $ MS.combine (MS.takeDirectory f) srcfile'+    -- we are sourcing from the language base+    -- > source "R" ("sqrt", "t.test" as t_test)+    (Just _, Nothing) -> return Nothing+    -- this case SHOULD only occur in testing where the source file does not exist+    -- file non-existence will be caught later+    (Nothing, s) -> return s +  return $ SrcE [Source { srcName = srcVar+                        , srcLang = language+                        , srcPath = srcFile+                        , srcAlias = aliasVar+                        } | (srcVar, aliasVar) <- rs]++pImportSourceTerm :: Parser (Name, EVar)+pImportSourceTerm = do+  n <- stringLiteral+  a <- option n (reserved "as" >> name)+  return (Name n, EVar a)++pNamE :: Parser Expr+pNamE = fmap RecE $ braces (sepBy1 pNamEntryE (symbol ","))++pNamEntryE :: Parser (EVar, Expr)+pNamEntryE = do+  n <- name+  _ <- symbol "="+  e <- pExpr+  return (EVar n, e)++pListE :: Parser Expr+pListE = fmap ListE $ brackets (sepBy pExpr (symbol ","))++pTuple :: Parser Expr+pTuple = do+  _ <- op "("+  e <- pExpr+  _ <- op ","+  es <- sepBy1 pExpr (op ",")+  _ <- op ")"+  return (TupleE (e : es))++pUni :: Parser Expr+pUni = symbol "Null" >> return UniE++pAcc :: Parser Expr+pAcc = do+  e <- parens pExpr <|> pNamE <|> pVar+  _ <- symbol "@"+  f <- name+  return $ AccE e (EVar f) ++pAnn :: Parser Expr+pAnn = do+  e <-+    parens pExpr <|> pVar <|> pListE <|> try pNumE <|> pLogE <|> pStrE+  _ <- op "::"+  t <- pTypeGen+  return $ AnnE e [t]++pApp :: Parser Expr+pApp = do+  f <- parens pExpr <|> pVar+  (e:es) <- many1 s+  return $ foldl AppE (AppE f e) es+  where+    s =   try pAnn+      <|> try (parens pExpr)+      <|> try pUni+      <|> try pStrE+      <|> try pLogE+      <|> try pNumE+      <|> pListE+      <|> pTuple+      <|> pNamE+      <|> pVar++pLogE :: Parser Expr+pLogE = pTrue <|> pFalse+  where+    pTrue = reserved "True" >> return (LogE True)+    pFalse = reserved "False" >> return (LogE False)++pStrE :: Parser Expr+pStrE = fmap StrE stringLiteral++pNumE :: Parser Expr+pNumE = fmap NumE number++pLam :: Parser Expr+pLam = do+  _ <- symbol "\\"+  vs <- many1 pEVar+  _ <- symbol "->"+  e <- pExpr+  return (curryLamE vs e)+  where+    curryLamE [] e' = e'+    curryLamE (v:vs') e' = LamE v (curryLamE vs' e')++pVar :: Parser Expr+pVar = fmap VarE pEVar++pEVar :: Parser EVar+pEVar = fmap EVar name++pTypeGen :: Parser UnresolvedType+pTypeGen = do+  resetGenerics+  t <- pType+  s <- CMS.get+  return $ forallWrap (unique (reverse (stateGenerics s))) t+  where+    forallWrap :: [TVar] -> UnresolvedType -> UnresolvedType+    forallWrap [] t = t+    forallWrap (v:vs) t = ForallU v (forallWrap vs t)++pType :: Parser UnresolvedType+pType =+      pExistential+  <|> try pFunU+  <|> try pUniU+  <|> try pNamU+  <|> try pArrU+  <|> try parensType+  <|> pListU+  <|> pTupleU+  <|> pVarU++pUniU :: Parser UnresolvedType+pUniU = do+  _ <- symbol "("+  _ <- symbol ")"+  lang <- CMS.gets stateLang+  v <- newvar lang+  return (ExistU v [] (MLD.defaultNull lang))++parensType :: Parser UnresolvedType+parensType = do+  _ <- tag (symbol "(")+  t <- parens pType+  return t++pTupleU :: Parser UnresolvedType+pTupleU = do+  lang <- CMS.gets stateLang+  _ <- tag (symbol "(")+  ts <- parens (sepBy1 pType (symbol ","))+  return $ head (MLD.defaultTuple lang ts)++pNamU :: Parser UnresolvedType+pNamU = do+  _ <- tag (symbol "{")+  entries <- braces (sepBy1 pNamEntryU (symbol ","))+  lang <- CMS.gets stateLang+  return $ head (MLD.defaultRecord lang entries)++pNamEntryU :: Parser (MT.Text, UnresolvedType)+pNamEntryU = do+  n <- name+  _ <- op "::"+  t <- pType+  return (n, t)++pExistential :: Parser UnresolvedType+pExistential = do+  v <- angles name+  return (ExistU (TV Nothing v) [] [])++pArrU :: Parser UnresolvedType+pArrU = do+  lang <- CMS.gets stateLang+  _ <- tag (name <|> stringLiteral)+  n <- name <|> stringLiteral+  args <- many1 pType'+  return $ ArrU (TV lang n) args+  where+    pType' = try pUniU <|> try parensType <|> pVarU <|> pListU <|> pTupleU <|> pNamU++pFunU :: Parser UnresolvedType+pFunU = do+  t <- pType'+  _ <- op "->"+  ts <- sepBy1 pType' (op "->")+  return $ foldr1 FunU (t : ts)+  where+    pType' = try pUniU <|> try parensType <|> try pArrU <|> pVarU <|> pListU <|> pTupleU <|> pNamU++pListU :: Parser UnresolvedType+pListU = do+  _ <- tag (symbol "[")+  t <- brackets pType+  lang <- CMS.gets stateLang+  return $ head (MLD.defaultList lang t)++pVarU :: Parser UnresolvedType+pVarU = try pVarConU <|> pVarGenU++pVarConU :: Parser UnresolvedType+pVarConU = do+  lang <- CMS.gets stateLang+  _ <- tag stringLiteral+  n <- stringLiteral+  return $ VarU (TV lang n)++pVarGenU :: Parser UnresolvedType+pVarGenU = do+  lang <- CMS.gets stateLang+  _ <- tag name+  n <- name+  let v = TV lang n+  appendGenerics v  -- add the term to the generic list IF generic+  return $ VarU v
+ library/Morloc/Frontend/PartialOrder.hs view
@@ -0,0 +1,168 @@+{-|+Module      : Morloc.Frontend.PartialOrder+Description : Partial order implementation for types+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Frontend.PartialOrder (+    substitute+  , free+  , isSubtypeOf+  , equivalent+  , mostGeneral+  , mostSpecific+  , mostSpecificSubtypes+  , (<=)+) where++import Morloc.Frontend.Namespace+import qualified Morloc.Data.Text as MT+import qualified Data.Set as Set+import qualified Data.PartialOrd as P++-- | substitute all appearances of a given variable with a given new type+substitute :: TVar -> UnresolvedType -> UnresolvedType -> UnresolvedType+substitute v (ForallU q r) t = +  if Set.member (VarU q) (free t)+  then+    let q' = getNewVariable r t -- get unused variable name from [a, ..., z, aa, ...]+        r' = substitute q (VarU q') r -- substitute the new variable into the unqualified type+    in ForallU q' (substitute v r' t)+  else+    ForallU q (substitute v r t)+substitute v r t = sub t+  where+    sub :: UnresolvedType -> UnresolvedType+    sub t'@(VarU v')+      | v == v' = r+      | otherwise = t'+    sub (FunU t1 t2) = FunU (sub t1) (sub t2)+    sub t'@(ForallU x t'')+      | v /= x = ForallU x (sub t'')+      | otherwise = t' -- allows shadowing of the variable+    sub (ArrU v' ts) = ArrU v' (map sub ts)+    sub (NamU namType v' ts rs) = NamU namType v' (map sub ts) [(x, sub t') | (x, t') <- rs]+    sub (ExistU v' ps ds) = ExistU v' (map sub ps) (map sub ds)++free :: UnresolvedType -> Set.Set UnresolvedType+free v@(VarU _) = Set.singleton v+free v@(ExistU _ [] _) = Set.singleton v+free (ExistU v ts _) = Set.unions $ Set.singleton (ArrU v ts) : map free ts+free (FunU t1 t2) = Set.union (free t1) (free t2)+free (ForallU v t) = Set.delete (VarU v) (free t)+free (ArrU _ xs) = Set.unions (map free xs)+free (NamU _ _ _ rs) = Set.unions [free t | (_, t) <- rs]++-- Types are partially ordered, 'forall a . a' is lower (more generic) than+-- Int. But 'forall a . a -> a' cannot be compared to 'forall a . a', since+-- they are different kinds.+-- The order of types is used to choose the most specific serialization functions.+-- As far as serialization is concerned, properties and constraints do not matter.+instance P.PartialOrd UnresolvedType where+  (<=) (VarU v1) (VarU v2) = v1 == v2+  (<=) (ExistU v1 [] _) (ExistU v2 [] _) = v1 == v2+  (<=) (ExistU v1 ts1 _) (ExistU v2 ts2 _)+    =  v1 == v2+    && length ts1 == length ts2+    && foldl (&&) True (zipWith (P.<=) ts1 ts2)+  (<=) (FunU t11 t12) (FunU t21 t22)+    =  (P.<=) t11 t21+    && (P.<=) t22 t12+  (<=) (ArrU v1 []) (ArrU v2 []) = v1 == v2+  (<=) (ArrU v1 ts1) (ArrU v2 ts2)+    =  v1 == v2+    && length ts1 == length ts2+    && foldl (&&) True (zipWith (P.<=) ts1 ts2)+  (<=) (NamU _ v1 _ es1) (NamU _ v2 _ es2)+    =  v1 == v2+    && length ts1 == length ts2+    && foldl (&&) True (zipWith (P.<=) ts1 ts2)+    where+      ts1 = map snd es1+      ts2 = catMaybes $ map (\(k,_) -> lookup k es2) es1+  (<=) (ForallU v t1) t2+    | (P.==) (ForallU v t1) t2 = True+    | otherwise = (P.<=) (substituteFirst v t1 t2) t2+  (<=) _ _ = False++  (==) (ForallU v1 t1) (ForallU v2 t2) =+    if Set.member (VarU v1) (free t2)+    then+      let v = getNewVariable t1 t2+      in (P.==) (substitute v1 (VarU v) t1) (substitute v2 (VarU v) t2)+    else (P.==) t1 (substitute v2 (VarU v1) t2)+  (==) a b = a == b++-- Substitute all v for the first term in t2 that corresponds to v in t1. If v+-- does not occur in t1, then t1 is returned unchanged (e.g., `forall a . Int`).+substituteFirst :: TVar -> UnresolvedType -> UnresolvedType -> UnresolvedType+substituteFirst v t1 t2 = case findFirst v t1 t2 of+  (Just t) -> substitute v t t1+  Nothing -> t1++-- | get a fresh variable name that is not used in t1 or t2+getNewVariable :: UnresolvedType -> UnresolvedType -> TVar+getNewVariable t1 t2 = findNew variables (Set.union (allVars t1) (allVars t2))+  where +    variables = [1 ..] >>= flip replicateM ['a' .. 'z']++    findNew :: [String] -> Set.Set UnresolvedType -> TVar+    findNew [] _ = error "Could not fresh variable in an infinite list ... odd"+    findNew (x:xs) ts+      | Set.member (VarU v) ts = findNew xs ts +      | otherwise = v+      where+        v = TV (langOf t1) (MT.pack x)++    allVars :: UnresolvedType -> Set.Set UnresolvedType+    allVars (ForallU v t) = Set.union (Set.singleton (VarU v)) (allVars t)+    allVars t = free t+++findFirst :: TVar -> UnresolvedType -> UnresolvedType -> Maybe UnresolvedType+findFirst v (VarU v') t2+  | v == v' = Just t2+  | otherwise = Nothing+findFirst v (ForallU v1 t1) (ForallU v2 t2)+  | v == v1 = Nothing+  | otherwise = findFirst v t1 (substitute v2 (VarU v1) t2)+findFirst v (ForallU v1 t1) t2+  | v == v1 = Nothing+  | otherwise = findFirst v (substitute v1 (VarU v1) t1) t2+findFirst v (FunU t11 t12) (FunU t21 t22)+  = case (findFirst v t11 t21, findFirst v t12 t22) of+    (Just t, _) -> Just t+    (_, Just t) -> Just t+    _ -> Nothing+findFirst v (ArrU _ ts1) (ArrU _ ts2)+  = listToMaybe . catMaybes $ zipWith (findFirst v) ts1 ts2+findFirst v (NamU _ _ _ es1) (NamU _ _ _ es2)+  = listToMaybe . catMaybes $ zipWith (findFirst v) ts1 ts2+    where+      ts1 = map snd es1+      ts2 = catMaybes $ map (\(k,_) -> lookup k es2) es1+findFirst _ _ _ = Nothing++-- | is t1 a generalization of t2?+isSubtypeOf :: UnresolvedType -> UnresolvedType -> Bool+isSubtypeOf t1 t2 = case P.compare t1 t2 of+  (Just x) -> x <= EQ+  _ -> False++equivalent :: UnresolvedType -> UnresolvedType -> Bool+equivalent t1 t2 = isSubtypeOf t1 t2 && isSubtypeOf t2 t1++-- | find all types that are not greater than any other type+mostGeneral :: [UnresolvedType] -> [UnresolvedType]+mostGeneral ts = P.minima ts++-- | find all types that are not less than any other type+mostSpecific :: [UnresolvedType] -> [UnresolvedType]+mostSpecific ts = P.maxima ts++-- | find the most specific subtypes+mostSpecificSubtypes :: UnresolvedType -> [UnresolvedType] -> [UnresolvedType]+mostSpecificSubtypes t ts = mostSpecific $ filter (\t2 -> isSubtypeOf t2 t) ts
+ library/Morloc/Frontend/Pretty.hs view
@@ -0,0 +1,145 @@+{-|+Module      : Morloc.Frontend.Pretty+Description : Pretty is as pretty does+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Frontend.Pretty+  ( module Morloc.Pretty+  , cute+  , ugly+  , prettyExpr+  , prettyGammaIndex+  ) where++import Morloc.Frontend.Namespace+import qualified Data.Map as Map+import qualified Data.Set as Set+import Morloc.Data.Doc hiding (putDoc)+import Morloc.Pretty+import Data.Text.Prettyprint.Doc.Render.Terminal (putDoc, AnsiStyle)++cute :: DAG MVar [(EVar, EVar)] TypedNode -> IO ()+cute d = mapM_ (putDoc . cute') (Map.toList d) where+  cute' :: (MVar, (TypedNode, [(MVar, [(EVar, EVar)])])) -> Doc AnsiStyle+  cute' (v, (n, xs)) = block 4 (pretty v) (cuteBody n xs)++cuteBody :: TypedNode -> [(MVar, [(EVar, EVar)])] -> Doc AnsiStyle+cuteBody t xs = prettyPackMap (typedNodePackers t) <> line+ <> vsep (cuteSources (typedNodeSourceMap t)) <> line + <> vsep (map (uncurry cuteImport) xs) <> line <> cuteTypedNode t++cuteImport :: MVar -> [(EVar, EVar)] -> Doc AnsiStyle+cuteImport m xs+  = "from" <+> pretty m <+> "import"+  <+> tupled (map (\(v1,v2) -> pretty v1 <+> "as" <+> pretty v2) xs)++cuteSources :: Map.Map (EVar, Lang) Source -> [Doc AnsiStyle]+cuteSources m = map (\((v,l),src) -> pretty v <> "@" <> pretty l <+> pretty src) (Map.toList m)++cuteTypedNode :: TypedNode -> Doc AnsiStyle+cuteTypedNode t = vsep (map prettyExpr (typedNodeBody t))++-- FIXME: why exactly do I even have this ugly function???+ugly :: DAG MVar [(EVar, EVar)] TypedNode -> IO ()+ugly = cute ++prettyTypeSet :: TypeSet -> Doc AnsiStyle+prettyTypeSet (TypeSet Nothing ts)+  = encloseSep "(" ")" ";" (map (prettyGreenUnresolvedType . etype) ts)+prettyTypeSet (TypeSet (Just t) ts)+  = encloseSep "(" ")" ";" (map (prettyGreenUnresolvedType . etype) (t:ts))+++prettyGammaIndex :: GammaIndex -> Doc AnsiStyle+prettyGammaIndex (VarG tv) = "VarG:" <+> pretty tv+prettyGammaIndex (AnnG e ts) = "AnnG:" <+> prettyExpr e <+> prettyTypeSet ts+prettyGammaIndex (ExistG tv ts ds)+  = "ExistG:"+  <+> pretty tv+  <+> list (map (parens . prettyGreenUnresolvedType) ts)+  <+> list (map (parens . prettyGreenUnresolvedType) ds)+prettyGammaIndex (SolvedG tv t) = "SolvedG:" <+> pretty tv <+> "=" <+> prettyGreenUnresolvedType t+prettyGammaIndex (MarkG tv) = "MarkG:" <+> pretty tv+prettyGammaIndex (MarkEG ev) = "MarkG:" <+> pretty ev+prettyGammaIndex (SrcG (Source ev1 lang _ _)) = "SrcG:" <+> pretty ev1 <+> viaShow lang+prettyGammaIndex (UnsolvedConstraint t1 t2) = "UnsolvedConstraint:" <+> prettyGreenUnresolvedType t1 <+> prettyGreenUnresolvedType t2++prettyExpr :: Expr -> Doc AnsiStyle+prettyExpr UniE = "()"+prettyExpr (VarE s) = pretty s+prettyExpr (AccE e k) = parens (prettyExpr e) <> "@" <> pretty k +prettyExpr (LamE n e) = "\\" <> pretty n <+> "->" <+> prettyExpr e+prettyExpr (AnnE e ts) = parens+  $   prettyExpr e+  <+> "::"+  <+> encloseSep "(" ")" "; " (map prettyGreenUnresolvedType ts)+prettyExpr (AppE e1@(LamE _ _) e2) = parens (prettyExpr e1) <+> prettyExpr e2+prettyExpr (AppE e1 e2) = parens (prettyExpr e1) <+> parens (prettyExpr e2)+prettyExpr (NumE x) = pretty (show x)+prettyExpr (StrE x) = dquotes (pretty x)+prettyExpr (LogE x) = pretty x+prettyExpr (Declaration v e) = pretty v <+> "=" <+> prettyExpr e+prettyExpr (ListE xs) = list (map prettyExpr xs)+prettyExpr (TupleE xs) = tupled (map prettyExpr xs)+prettyExpr (SrcE []) = ""+prettyExpr (SrcE srcs@(Source _ lang (Just f) _ : _)) =+  "source" <+>+  viaShow lang <+>+  "from" <+>+  pretty f <+>+  tupled+    (map+       (\(n, a) ->+          pretty n <>+          if unName n == unEVar a+            then ""+            else (" as" <> pretty a))+       rs)+  where+    rs = [(n, a) | (Source n _ _ a) <- srcs]+prettyExpr (SrcE srcs@(Source _ lang Nothing _ : _)) =+  "source" <+>+  viaShow lang <+>+  tupled+    (map+       (\(n, a) ->+          pretty n <>+          if unName n == unEVar a+            then ""+            else (" as" <> pretty a))+       rs)+  where+    rs = [(n,a) | (Source n _ _ a) <- srcs]+prettyExpr (RecE entries) =+  encloseSep+    "{"+    "}"+    ", "+    (map (\(v, e) -> pretty v <+> "=" <+> prettyExpr e) entries)+prettyExpr (Signature v e) =+  pretty v <+> elang' <> "::" <+> eprop' <> etype' <> econs'+  where+    elang' :: Doc AnsiStyle+    elang' = maybe "" (\lang -> viaShow lang <> " ") (langOf . etype $ e)+    eprop' :: Doc AnsiStyle+    eprop' =+      case Set.toList (eprop e) of+        [] -> ""+        xs -> tupled (map prettyProperty xs) <+> "=> "+    etype' :: Doc AnsiStyle+    etype' = prettyGreenUnresolvedType (etype e)+    econs' :: Doc AnsiStyle+    econs' =+      case Set.toList (econs e) of+        [] -> ""+        xs -> " where" <+> tupled (map (\(Con x) -> pretty x) xs)++prettyProperty :: Property -> Doc ann+prettyProperty Pack = "pack"+prettyProperty Unpack = "unpack"+prettyProperty Cast = "cast"+prettyProperty (GeneralProperty ts) = hsep (map pretty ts)
+ library/Morloc/Frontend/Treeify.hs view
@@ -0,0 +1,282 @@+{-|+Module      : Morloc.Frontend.Treeify+Description : Translate from the frontend DAG to the backend SAnno AST forest+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Frontend.Treeify (treeify) where++import Morloc.Frontend.Namespace+import Morloc.Data.Doc+import Morloc.Frontend.PartialOrder ()+import qualified Morloc.Data.Text as MT+import qualified Morloc.Monad as MM+import qualified Morloc.Data.DAG as MDD+import qualified Data.Map as Map+import qualified Data.Set as Set+import Morloc.Frontend.Pretty ()++data TermOrigin = Declared Expr | Sourced Source+  deriving(Show, Ord, Eq)++treeify+  :: DAG MVar [(EVar, EVar)] TypedNode+  -> MorlocMonad [SAnno GMeta Many [CType]]+treeify d+  | Map.size d == 0 = return []+  | otherwise = case MDD.roots d of+    [] -> MM.throwError CyclicDependency+    [k] -> case MDD.lookupNode k d of+      Nothing -> MM.throwError . DagMissingKey . render $ pretty k+      (Just n) -> do+        -- initialize state counter to 0, used to index manifolds+        MM.startCounter+        mapM (collect d n) (Set.toList (typedNodeExports n))+    _ -> MM.throwError . CallTheMonkeys $ "How did you end up with so many roots?"+++-- -- | Build the call tree for a single nexus command. The result is ambiguous,+-- -- with 1 or more possible tree topologies, each with one or more possible for+-- -- each function.+collect+  :: DAG MVar [(EVar, EVar)] TypedNode+  -> TypedNode+  -> EVar+  -> MorlocMonad (SAnno GMeta Many [CType])+collect d n v = do+  trees <- collectSExprs d n v++  -- Just look at one x, since any should emit the same GMeta (if not, then+  -- something is broken upstream of GMeta is not general enough)+  gmeta <- makeGMeta (Just v) n Nothing++  return $ SAnno (Many trees) gmeta++collectSExprs+  :: DAG MVar [(EVar, EVar)] TypedNode+  -> TypedNode+  -> EVar+  -> MorlocMonad [(SExpr GMeta Many [CType], [CType])]+collectSExprs d n v = do+  -- DAG MVar None (EVar, (TypedNode, [TermOrigin]))+  let termTree = MDD.lookupAliasedTerm v (typedNodeModuleName n) (makeTermOrigin v) d++  -- DAG MVar None [(SExpr GMeta Many [CType], [CType])]+  sexprTree <- MDD.mapNodeM (\(v',(n',ts)) -> collectTerms d v' n' ts) termTree++  -- [(SExpr GMeta Many [CType], [CType])]+  let trees = concat . MDD.nodes $ sexprTree++  return trees+++-- | Find info common across realizations of a given term in a given module+makeGMeta+  :: Maybe EVar+  -> TypedNode+  -> Maybe GType+  -> MorlocMonad GMeta+makeGMeta name n gtype = do+  i <- MM.getCounter+  case name >>= (flip Map.lookup) (typedNodeTypeMap n) of+    (Just (TypeSet (Just e) _)) -> do+      let g = (Just . GType) $ resolve (etype e)+      return $ GMeta+        { metaId = i+        , metaGType = maybe g Just gtype+        , metaName = name+        , metaProperties = eprop e+        , metaConstraints = econs e+        , metaPackers = typedNodePackers n+        , metaConstructors = typedNodeConstructors n+        , metaTypedefs = typedNodeTypedefs n+        }+    _ -> do+      return $ GMeta+        { metaId = i+        , metaGType = gtype+        , metaName = name+        , metaProperties = Set.empty+        , metaConstraints = Set.empty+        , metaPackers = typedNodePackers n+        , metaConstructors = typedNodeConstructors n+        , metaTypedefs = typedNodeTypedefs n+        }++makeTermOrigin+  :: EVar+  -> TypedNode+  -> (TypedNode, [TermOrigin])+makeTermOrigin v n = (n, declared ++ sourced) where+  declared = [Declared e | (Declaration v' e) <- typedNodeBody n, v' == v]+  sourced = map Sourced+          $ filter (\s -> srcAlias s == v) (Map.elems $ typedNodeSourceMap n)+++collectTerms+  :: DAG MVar [(EVar, EVar)] TypedNode+  -> EVar+  -> TypedNode+  -> [TermOrigin]+  -> MorlocMonad [(SExpr GMeta Many [CType], [CType])]+collectTerms d v n ts = mapM (collectTerm d v n) ts+++-- Notice that `args` is NOT an input to collectTerm. Morloc uses lexical+-- scoping, and the input to collectTerm is the origin of a term, so the+-- definition of the term is outside the scope of the parent expression.+collectTerm+  :: DAG MVar [(EVar, EVar)] TypedNode+  -> EVar+  -> TypedNode+  -> TermOrigin+  -> MorlocMonad (SExpr GMeta Many [CType], [CType])+collectTerm _ v n (Sourced src)+  = case Map.lookup v (typedNodeTypeMap n) of+    Nothing -> MM.throwError . CallTheMonkeys $ "No type found for this"+    (Just (TypeSet _ es)) -> do+      let ts = [etype e | e <- es, Just (srcLang src) == langOf e]+          ts' = map resolve ts+      return (CallS src, map CType ts')+collectTerm d _ n (Declared (AnnE e ts)) = do+  ts' <- getCTypes ts+  xs <- collectExpr d Set.empty n ts' e+  case xs of+    [x] -> return x+    _ -> MM.throwError . GeneratorError $+      "Expected exactly one topology for a declared term"+collectTerm _ _ _ (Declared _) = MM.throwError . GeneratorError $+  "Invalid expression in CollectTerm Declared, expected AnnE"+++collectAnno+  :: DAG MVar [(EVar, EVar)] TypedNode+  -> Set.Set EVar+  -> TypedNode+  -> Expr+  -> MorlocMonad (SAnno GMeta Many [CType])+collectAnno d args n (AnnE e ts) = do+  gtype <- getGType ts+  gmeta <- makeGMeta (getExprName e) n gtype+  ts' <- getCTypes ts+  trees <- collectExpr d args n ts' e+  return $ SAnno (Many trees) gmeta+collectAnno _ _ _ _ = error "impossible bug - unannotated expression"++getCTypes :: [UnresolvedType] -> MorlocMonad [CType]+getCTypes ts = do+  let ts' = map resolve [t | t <- ts, isJust (langOf t)]+  return $ map CType ts'++++collectExpr+  :: DAG MVar [(EVar, EVar)] TypedNode+  -> Set.Set EVar+  -> TypedNode+  -> [CType]+  -> Expr+  -> MorlocMonad [(SExpr GMeta Many [CType], [CType])]+collectExpr _ _ _ ts (UniE)   = return [(UniS, ts)]+collectExpr _ _ _ ts (NumE x) = return [(NumS x, ts)]+collectExpr _ _ _ ts (LogE x) = return [(LogS x, ts)]+collectExpr _ _ _ ts (StrE x) = return [(StrS x, ts)]+collectExpr d args n ts (VarE v)+  | Set.member v args = return [(VarS v, ts)]+  | otherwise = do+      xs <- collectSExprs d n v+      let chosen = map (chooseTypes ts) xs+      return chosen+  where+    -- FIXME: The typesystem should handle this. It should unroll every+    -- type as far as it can be unrolled, and infer specialized types all+    -- the way down. Multiple declarations of every term within a given+    -- language should be allowed. The function below will only work in+    -- special cases where there is A) a single instance of the term in+    -- each language and B) types beneath the term (if this is a+    -- composition) do not depend on the type on top.+    chooseTypes+      :: [CType]+      -> (SExpr GMeta Many [CType], [CType])+      -> (SExpr GMeta Many [CType], [CType])+    chooseTypes ts1 (x, ts2) =+      (x, [ t+          | t <- ts1+          , t' <- ts2+          , langOf t == langOf t'])+collectExpr d args n ts (AccE e k) = do+  e' <- collectAnno d args n e+  return [(AccS e' k, ts)]+collectExpr d args n ts (ListE es) = do+  es' <- mapM (collectAnno d args n) es+  return [(ListS es', ts)]+collectExpr d args n ts (TupleE es) = do+  es' <- mapM (collectAnno d args n) es+  return [(TupleS es', ts)]+collectExpr d args n ts (RecE entries) = do+  es' <- mapM (collectAnno d args n) (map snd entries)+  let entries' = zip (map fst entries) es'+  return [(RecS entries', ts)]+collectExpr d args n ts e@(LamE _ _) = do+  case unrollLambda e of+    (args', e') -> do+      e'' <- collectAnno d (Set.union args (Set.fromList args')) n e'+      return [(LamS args' e'', ts)]+collectExpr d args n _ (AppE e1 e2) = do+  -- The topology may vary. It could be a direct binary function. Or+  -- it could be a partially applied function. So it is necessary to map+  -- over the Many.+  (SAnno (Many fs) g1) <- collectAnno d args n e1+  e2' <- collectAnno d args n e2+  mapM (app g1 e2') fs+-- None of these should occur unless there is a bug in the code+collectExpr _ _ _ _ x@(AnnE _ _) = error $ show x+collectExpr _ _ _ _ x@(SrcE _) = error $ show x+collectExpr _ _ _ _ x@(Signature _ _) = error $ show x+collectExpr _ _ _ _ x@(Declaration _ _) = error $ show x++++app+  :: GMeta+  -> SAnno GMeta Many [CType]+  -> (SExpr GMeta Many [CType], [CType])+  -> MorlocMonad (SExpr GMeta Many [CType], [CType])+app _ e2 ((AppS f es), ts) = do+  ts' <- mapM partialApplyConcrete ts+  return (AppS f (es ++ [e2]), ts')+app g e2 (f, ts) = do+  ts' <- mapM partialApplyConcrete ts+  return (AppS (SAnno (Many [(f, ts)]) g) [e2], ts')++partialApplyConcrete :: CType -> MorlocMonad CType+partialApplyConcrete t =+  fmap CType $ partialApply (unCType t)++partialApply :: Type -> MorlocMonad Type+partialApply (FunT _ t) = return t+partialApply _ = MM.throwError . GeneratorError $+  "Cannot partially apply a non-function type"++getExprName :: Expr -> Maybe EVar+getExprName (VarE v) = Just v+getExprName _ = Nothing++getGType :: [UnresolvedType] -> MorlocMonad (Maybe GType)+getGType ts = do+  let ts' = map resolve [t | t <- ts, isNothing (langOf t)]+  case map GType ts' of+    [] -> return Nothing+    [x] -> return $ Just x+    xs -> MM.throwError . GeneratorError $+      "Expected 0 or 1 general types, found " <> MT.show' (length xs)++unrollLambda :: Expr -> ([EVar], Expr)+unrollLambda (LamE v e2) = case unrollLambda e2 of+  (vs, e) -> (v:vs, e)+unrollLambda (AnnE (LamE v e2) _) = case unrollLambda e2 of+  (vs, e) -> (v:vs, e)+unrollLambda e = ([], e)
+ library/Morloc/Internal.hs view
@@ -0,0 +1,139 @@+{-|+Module      : Morloc.Internal+Description : Internal utility functions+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++This module serves as a proto-prelude. Eventually I will probably want to+abandon the default prelude and create my own. But not just yet.+-}+module Morloc.Internal+  ( ifelse+  , conmap+  , unique+  , duplicates+  , module Data.Maybe+  , module Data.Either+  , module Data.List.Extra+  , module Control.Monad+  , module Control.Monad.IO.Class+  , module Data.Monoid+  -- Data.Char characters+  , isUpper+  , isLower+  -- ** selected functions from Data.Foldable+  , foldlM+  , foldrM+  -- ** selected functions from Data.Tuple.Extra+  , uncurry3+  , curry3+  , third+  -- ** operators+  , (|>>) -- piped fmap+  , (</>) -- Filesystem utility operators from System.FilePath+  , (<|>) -- alternative operator+  , (&&&) -- (a -> a') -> (b -> b') -> (a, b) -> (a', b')+  , (***) -- (a -> b) -> (a -> c) -> a -> (b, c) +  -- ** map and set helper functions+  , keyset+  , valset+  , mapFold+  , mapSum+  , mapSumWith+  -- ** safe versions of errant functions+  , module Safe+  , maximumOnMay+  , minimumOnMay+  , maximumOnDef+  , minimumOnDef+  ) where++-- Don't import anything from Morloc here. This module should be VERY lowest+-- in the hierarchy, to avoid circular dependencies, since the lexer needs to+-- access it.+import Control.Monad+import Control.Monad.IO.Class+import Control.Applicative ((<|>))+import Data.Either+import Data.Foldable (foldlM, foldrM)+import Data.List.Extra hiding (list) -- 'list' conflicts with Doc+import Data.Tuple.Extra ((***), (&&&))+import Data.Maybe+import Data.Monoid+import Data.Char (isUpper, isLower)+import Safe hiding (at)+import System.FilePath+import qualified Data.Map as Map+import qualified Data.Set as Set++maximumOnMay :: Ord b => (a -> b) -> [a] -> Maybe a+maximumOnMay _ [] = Nothing+maximumOnMay f xs = Just $ maximumOn f xs++minimumOnMay :: Ord b => (a -> b) -> [a] -> Maybe a+minimumOnMay _ [] = Nothing+minimumOnMay f xs = Just $ minimumOn f xs++maximumOnDef :: Ord b => a -> (a -> b) -> [a] -> a+maximumOnDef x _ [] = x+maximumOnDef _ f xs = maximumOn f xs++minimumOnDef :: Ord b => a -> (a -> b) -> [a] -> a+minimumOnDef x _ [] = x+minimumOnDef _ f xs = minimumOn f xs++uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d +uncurry3 f (x, y, z) = f x y z++curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d+curry3 f = \x y z -> f (x, y, z)++third :: (a, b, c) -> c+third (_, _, x) = x++keyset :: Ord k => Map.Map k b -> Set.Set k+keyset = Set.fromList . Map.keys++valset :: Ord b => Map.Map k b -> Set.Set b+valset = Set.fromList . Map.elems++mapFold :: Monoid b => (a -> b -> b) -> Map.Map k a -> b+mapFold f = Map.foldr f mempty++mapSum :: Monoid a => Map.Map k a -> a+mapSum = Map.foldr mappend mempty++mapSumWith :: Monoid b => (a -> b) -> Map.Map k a -> b+mapSumWith f = Map.foldr (\x y -> mappend y (f x)) mempty++ifelse :: Bool -> a -> a -> a+ifelse True x _ = x+ifelse False _ y = y++conmap :: (a -> [b]) -> [a] -> [b]+conmap f = concat . map f++-- | remove duplicated elements in a list while preserving order+unique :: Ord a => [a] -> [a]+unique xs0 = unique' Set.empty xs0 where +  unique' _   [] = []+  unique' set (x:xs)+    | Set.member x set = unique' set xs+    | otherwise = x : unique' (Set.insert x set) xs++-- | Build an ordered list of duplicated elements+duplicates :: Ord a => [a] -> [a] +duplicates xs = unique $ filter isDuplicated xs where+  -- countMap :: Ord a => Map.Map a Int+  countMap = Map.fromList . map (\ks -> (head ks, length ks)) . group . sort $ xs++  -- isDuplicated :: Ord a => a -> Bool+  isDuplicated k = fromJust (Map.lookup k countMap) > 1++-- | pipe the lhs functor into the rhs function+infixl 1 |>>++(|>>) :: Functor f => f a -> (a -> b) -> f b+(|>>) = flip fmap
+ library/Morloc/Language.hs view
@@ -0,0 +1,137 @@+{-|+Module      : Language+Description : Handling for specific languages+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++The purpose of this module currently is to unify language naming conventions.+We need to streamline the process of adding new languages to Morloc. This+module should serve as the starting place for adding a new language.+-}+module Morloc.Language+  ( Lang(..)+  , mapLang+  , parseExtension+  , makeExtension+  , showLangName+  , readLangName+  , makeExecutableName+  , makeSourceName+  , standardizeLangName+  , pairwiseCost+  ) where++import Morloc.Internal+import Data.Text (Text)++-- | Programming languages in the Morloc ecosystem. This is the type that+-- should be used to refer to a language (don't use raw strings). Some of these+-- are languages that can be sourced (Python, R and C). Perl is currently used+-- only in generating the nexus file.+data Lang+  = Python3Lang+  | RLang+  | CLang+  | CppLang+  | PerlLang+  deriving (Ord, Eq, Show)++-- | Map a function over each supported language+mapLang :: (Lang -> a) -> [a]+mapLang f =+  [ f Python3Lang+  , f RLang+  , f CLang+  , f CppLang+  , f PerlLang+  ]++-- | very rough function overhead costs that can be used when no benchmark info is available+-- `Nothing` indicates that the language pair are not interoperable+pairwiseCost :: Lang -> Lang -> Maybe Int+-- functional overhead in each language+pairwiseCost CLang       CLang       = Just 1+pairwiseCost CppLang     CppLang     = Just 1+pairwiseCost PerlLang    PerlLang    = Just 10+pairwiseCost Python3Lang Python3Lang = Just 10+pairwiseCost RLang       RLang       = Just 100+-- pairs of languages for which foreign calls are optimized+pairwiseCost CppLang CLang = Just 1+-- cost of naive foreign function calls+pairwiseCost _ CLang       = Just 100+pairwiseCost _ CppLang     = Just 100+pairwiseCost _ Python3Lang = Just 10000+pairwiseCost _ PerlLang    = Just 10000+pairwiseCost _ RLang       = Just 1000000++-- | Try to determine the source language for a file from its extension+parseExtension :: Text -> Maybe Lang+parseExtension "loc" = Nothing+parseExtension "py" = Just Python3Lang+parseExtension "R" = Just RLang+parseExtension "c" = Just CLang+parseExtension "h" = Just CLang+parseExtension "cpp" = Just CppLang+parseExtension "hpp" = Just CppLang+parseExtension "pl" = Just PerlLang+parseExtension _ = Nothing++-- | Create an extension for a given language+makeExtension :: Lang -> Text+makeExtension Python3Lang = "py"+makeExtension RLang = "R"+makeExtension CLang = "c"+makeExtension CppLang = "cpp"+makeExtension PerlLang = "pl"++-- | Create the name of a given language. This is the internal standard name+-- for the language and the string language name used in the RDF.+showLangName :: Lang -> Text+showLangName Python3Lang = "python3"+showLangName RLang = "R"+showLangName CLang = "C"+showLangName CppLang = "Cpp"+showLangName PerlLang = "Perl"++-- | Read the name of a given language and try to translate it+readLangName :: Text -> Maybe Lang+readLangName "python" = Just Python3Lang+readLangName "python3" = Just Python3Lang+readLangName "py" = Just Python3Lang+readLangName "R" = Just RLang+readLangName "r" = Just RLang+readLangName "C" = Just CLang+readLangName "c" = Just CLang+readLangName "cpp" = Just CppLang+readLangName "Cpp" = Just CppLang+readLangName "C++" = Just CppLang+readLangName "c++" = Just CppLang+readLangName "Perl" = Just PerlLang+readLangName "perl" = Just PerlLang+readLangName _ = Nothing++-- | Generate a name for a pool top-level source file given a language.+makeSourceName ::+     Lang+  -> Text -- ^ basename+  -> Text -- ^ source file basename+makeSourceName lang base = base <> "." <> makeExtension lang++-- | Generate a name for a pool executable file given a language. For+-- interpreted languages this will be the same as the output of the+-- @makeSourceName@ function.+makeExecutableName ::+     Lang+  -> Text -- ^ basename+  -> Text -- ^ executable file basename+makeExecutableName CLang base = base <> "-c.out"+makeExecutableName CppLang base = base <> "-cpp.out"+makeExecutableName lang base = makeSourceName lang base -- For interpreted languages++-- TODO: Use this function at the parsing stage to standardize names+-- | Convert a given language name to the standard form of the name (e.g., "py"+-- to "python3")+standardizeLangName :: Text -> Maybe Text+standardizeLangName = fmap showLangName . readLangName
+ library/Morloc/Module.hs view
@@ -0,0 +1,121 @@+{-|+Module      : Module+Description : Morloc module imports and paths +Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.Module+  ( ModuleSource(..)+  , installModule+  , findModule+  , loadModuleMetadata+  ) where++import Morloc.Namespace+import Morloc.Data.Doc+import qualified Morloc.Config as Config+import qualified Morloc.Data.Text as MT+import qualified Morloc.Monad as MM+import qualified Morloc.System as MS++import Data.Aeson (FromJSON(..), (.!=), (.:?), withObject)+import qualified Data.Yaml.Config as YC++instance FromJSON PackageMeta where+  parseJSON = withObject "object" $ \o ->+    PackageMeta <$> o .:? "name"        .!= ""+                <*> o .:? "version"     .!= ""+                <*> o .:? "homepage"    .!= ""+                <*> o .:? "synopsis"    .!= ""+                <*> o .:? "description" .!= ""+                <*> o .:? "category"    .!= ""+                <*> o .:? "license"     .!= ""+                <*> o .:? "author"      .!= ""+                <*> o .:? "maintainer"  .!= ""+                <*> o .:? "github"      .!= ""+                <*> o .:? "bug-reports" .!= ""+                <*> o .:? "gcc-flags"   .!= ""++-- | Specify where a module is located +data ModuleSource+  = LocalModule (Maybe MT.Text)+  -- ^ A module in the working directory+  | GithubRepo MT.Text+  -- ^ A module stored in an arbitrary Github repo: "<username>/<reponame>"+  | CoreGithubRepo MT.Text+  -- ^ The repo name of a core package, e.g., "math"++-- | Look for a local morloc module.+findModule :: MVar -> MorlocMonad Path+findModule moduleName = do+  config <- MM.ask+  let lib = Config.configLibrary config+  let allPaths = getModulePaths lib moduleName+  existingPaths <- liftIO . fmap catMaybes . mapM getFile $ allPaths+  case existingPaths of+    (x:_) -> return x+    [] ->+      MM.throwError . CannotLoadModule . render $+        "module not found among the paths:" <+> list (map pretty allPaths)++-- | Give a module path (e.g. "/your/path/foo.loc") find the package metadata.+-- It currently only looks for a file named "package.yaml" in the same folder+-- as the main "*.loc" file. +findModuleMetadata :: Path -> IO (Maybe Path)+findModuleMetadata mainFile =+  getFile $ MS.combine (MS.takeDirectory mainFile) (Path "package.yaml")++loadModuleMetadata :: Path -> MorlocMonad ()+loadModuleMetadata main = do+  maybef <- liftIO $ findModuleMetadata main+  meta <-+    case maybef of+      (Just f) -> liftIO $ YC.loadYamlSettings [MT.unpack . unPath $ f] [] YC.ignoreEnv+      Nothing -> return defaultPackageMeta+  state <- MM.get+  MM.put (appendMeta meta state)+  where+    appendMeta :: PackageMeta -> MorlocState -> MorlocState+    appendMeta m s = s {statePackageMeta = m : (statePackageMeta s)}++-- | Find an ordered list of possible locations to search for a module+getModulePaths :: Path -> MVar -> [Path]+getModulePaths (Path lib) (MVar base) = map Path+  [ base <> ".loc"                              -- "./${base}.loc"+  , base <> "/" <> "main.loc"                   -- "${base}/main.loc"+  , lib <> "/" <> base <> ".loc"                -- "${LIB}/${base}.loc"+  , lib <> "/" <> base <> "/" <> "main.loc"     -- "${LIB}/${base}/main.loc"+  , lib <> "/" <> base <> "/" <> base <> ".loc" -- "${LIB}/${base}/${base}.loc"+  ]++getFile :: Path -> IO (Maybe Path)+getFile x = do+  exists <- MS.fileExists x+  return $+    if exists+      then Just x+      else Nothing++-- | Attempt to clone a package from github+installGithubRepo ::+     MT.Text -- ^ the repo path ("<username>/<reponame>")+  -> MT.Text -- ^ the url for github (e.g., "https://github.com/")+  -> MorlocMonad ()+installGithubRepo repo url = do+  config <- MM.ask+  let (Path lib) = Config.configLibrary config+  let cmd = MT.unwords ["git clone", url, lib <> "/" <> repo]+  MM.runCommand "installGithubRepo" cmd++-- | Install a morloc module+installModule :: ModuleSource -> MorlocMonad ()+installModule (GithubRepo repo) =+  installGithubRepo repo ("https://github.com/" <> repo)+installModule (CoreGithubRepo name) =+  installGithubRepo name ("https://github.com/morloclib/" <> name)+installModule (LocalModule Nothing) =+  MM.throwError (NotImplemented "module installation from working directory")+installModule (LocalModule (Just _)) =+  MM.throwError (NotImplemented "module installation from local directory")
+ library/Morloc/Monad.hs view
@@ -0,0 +1,156 @@+{-|+Module      : Morloc.Monad+Description : A great big stack of monads+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental++MorlocMonad is a monad stack that is passed throughout the morloc codebase.+Most functions that raise errors, perform IO, or access global configuration+will return `MorlocMonad a` types. The stack consists of a State, Writer,+Except, and Reader monad.+-}+module Morloc.Monad+  ( MorlocReturn+  , runMorlocMonad+  , evalMorlocMonad+  , writeMorlocReturn+  , runCommand+  , runCommandWith+  , logFile+  , logFileWith+  , readLang+  , say+  -- * reusable counter+  , startCounter+  , getCounter+  -- * re-exports+  , module Control.Monad.Trans+  , module Control.Monad.Except+  , module Control.Monad.Reader+  , module Control.Monad.State+  , module Control.Monad.Writer+  ) where++import Control.Monad.Except+import Control.Monad.Reader+import Control.Monad.State+import Control.Monad.Trans+import Control.Monad.Writer+import Morloc.Error () -- for MorlocError Show instance+import Morloc.Namespace+import Morloc.Data.Doc+import System.IO (stderr)+import qualified Morloc.Data.Text as MT+import qualified Morloc.Language as ML+import qualified System.Directory as SD+import qualified System.Exit as SE+import qualified System.Process as SP++runMorlocMonad ::+     Int -> Config -> MorlocMonad a -> IO (MorlocReturn a)+runMorlocMonad v config ev =+  runStateT (runWriterT (runExceptT (runReaderT ev config))) (emptyState v)++-- | Evaluate a morloc monad+evalMorlocMonad ::+     Int+  -> Config -- ^ use default config object if Nothing+  -> MorlocMonad a+  -> IO a+evalMorlocMonad v config m = do+  ((x, _), _) <- runMorlocMonad v config m+  case x of+    (Left err) -> error (show err)+    (Right value) -> return value ++emptyState :: Int -> MorlocState+emptyState v = MorlocState {+    statePackageMeta = []+  , stateVerbosity = v+  , stateCounter = -1+}++startCounter :: MorlocMonad ()+startCounter = do+  s <- get+  put $ s {stateCounter = 0}++getCounter :: MorlocMonad Int+getCounter = do+  s <- get+  let i = stateCounter s+  put $ s {stateCounter = (stateCounter s) + 1}+  return i++writeMorlocReturn :: MorlocReturn a -> IO ()+writeMorlocReturn ((Left err, msgs), _)+  =  MT.hPutStrLn stderr (MT.unlines msgs) -- write messages+  >> MT.hPutStrLn stderr (MT.show' err) -- write terminal failing message+writeMorlocReturn ((_, msgs), _) = MT.hPutStrLn stderr (MT.unlines msgs)++-- | Execute a system call+runCommand ::+     MT.Text -- function making the call (used only in debugging messages on error)+  -> MT.Text -- system command+  -> MorlocMonad ()+runCommand loc cmd = do+  liftIO . MT.putStrLn $ "$ " <> cmd+  (exitCode, _, err) <-+    liftIO $ SP.readCreateProcessWithExitCode (SP.shell . MT.unpack $ cmd) []+  case exitCode of+    SE.ExitSuccess -> tell [MT.pack err]+    _ -> throwError (SystemCallError cmd loc (MT.pack err)) |>> (\_ -> ())++say :: MDoc -> MorlocMonad ()+say d = liftIO . putDoc $ " : " <> d <> "\n"++-- | Execute a system call and return a function of the STDOUT+runCommandWith ::+     MT.Text -- function making the call (used only in debugging messages on error)+  -> (MT.Text -> a) -- ^ A function of the output (run on success)+  -> MT.Text -- ^ System command+  -> MorlocMonad a+runCommandWith loc f cmd = do+  liftIO . MT.putStrLn $ "$ " <> cmd+  (exitCode, out, err) <-+    liftIO $ SP.readCreateProcessWithExitCode (SP.shell . MT.unpack $ cmd) []+  case exitCode of+    SE.ExitSuccess -> return $ f (MT.pack out)+    _ -> throwError (SystemCallError cmd loc (MT.pack err))++-- | Write a object to a file in the Morloc temporary directory+logFile ::+     Show a+  => String -- ^ A filename+  -> a+  -> MorlocMonad a+logFile s m = do+  tmpdir <- asks configTmpDir+  liftIO $ SD.createDirectoryIfMissing True (MT.unpack . unPath $ tmpdir)+  let path = (MT.unpack . unPath $ tmpdir) <> "/" <> s+  liftIO $ MT.writeFile path (MT.pretty m)+  return m++-- | Write a object to a file in the Morloc temporary directory+logFileWith ::+     (Show b)+  => String -- ^ A filename+  -> (a -> b) -- ^ A function to convert a to something presentable+  -> a+  -> MorlocMonad a+logFileWith s f m = do+  tmpdir <- asks configTmpDir+  liftIO $ SD.createDirectoryIfMissing True (MT.unpack . unPath $ tmpdir)+  let path = (MT.unpack . unPath $ tmpdir) <> "/" <> s+  liftIO $ MT.writeFile path (MT.pretty (f m))+  return m++-- | Attempt to read a language name. This is a wrapper around the+-- @Morloc.Language::readLangName@ that appropriately handles error.+readLang :: MT.Text -> MorlocMonad Lang+readLang langStr =+  case ML.readLangName langStr of+    (Just x) -> return x+    Nothing -> throwError $ UnknownLanguage langStr
+ library/Morloc/Namespace.hs view
@@ -0,0 +1,505 @@+{-|+Module      : Morloc.Namespace+Description : All types and datastructures+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}++module Morloc.Namespace+  (+  -- ** re-export supplements to Prelude+    module Morloc.Internal+  -- ** Synonyms+  , MDoc+  , DAG+  -- ** Other functors+  , None(..)+  , One(..)+  , Many(..)+  -- ** Newtypes+  , CType(..)+  , ctype+  , GType(..)+  , generalType+  , NamType(..)+  , EVar(..)+  , MVar(..)+  , TVar(..)+  , unTVar+  , Name(..)+  , Path(..)+  , Code(..)+  -- ** Language+  , Lang(..)+  -- ** Data+  , Script(..)+  -- ** Serialization+  , UnresolvedPacker(..)+  , PackMap+  --------------------+  -- ** Error handling+  , MorlocError(..)+  -- ** Configuration+  , Config(..)+  -- ** Morloc monad+  , MorlocMonad+  , MorlocState(..)+  , MorlocReturn+  -- ** Package metadata+  , PackageMeta(..)+  , defaultPackageMeta+  -- * Types+  , Type(..)+  , UnresolvedType(..)+  , unresolvedType2type+  , Source(..)+  -- ** Type extensions+  , Constraint(..)+  , Property(..)+  -- ** Types used in post-typechecking tree+  , SAnno(..)+  , SExpr(..)+  , GMeta(..)+  -- ** Typeclasses+  , HasOneLanguage(..)+  , Typelike(..)+  ) where++import Control.Monad.Except (ExceptT)+import Control.Monad.Reader (ReaderT)+import Control.Monad.State (StateT)+import Control.Monad.Writer (WriterT)+import Data.Map.Strict (Map)+import Data.Monoid+import Data.Scientific (Scientific)+import Data.Set (Set)+import Data.Text (Text)+import Data.Text.Prettyprint.Doc (Doc)+import Data.Void (Void)+import Morloc.Internal+import Text.Megaparsec.Error (ParseError)+import Morloc.Language (Lang(..))++-- | no annotations for now+type MDoc = Doc ()++-- | A general purpose Directed Acyclic Graph (DAG)+type DAG key edge node = Map key (node, [(key, edge)])++type MorlocMonadGen c e l s a+   = ReaderT c (ExceptT e (WriterT l (StateT s IO))) a++type MorlocReturn a = ((Either MorlocError a, [Text]), MorlocState)++data MorlocState = MorlocState {+    statePackageMeta :: [PackageMeta]+  , stateVerbosity :: Int+  , stateCounter :: Int+}++type MorlocMonad a = MorlocMonadGen Config MorlocError [Text] MorlocState a++newtype Name = Name {unName :: Text} deriving (Show, Eq, Ord)+newtype Path = Path {unPath :: Text} deriving (Show, Eq, Ord)+newtype Code = Code {unCode :: Text} deriving (Show, Eq, Ord)++-- | Stores everything needed to build one file+data Script =+  Script+    { scriptBase :: !String -- ^ script basename (no extension)+    , scriptLang :: !Lang -- ^ script language+    , scriptCode :: !Code -- ^ full script source code+    , scriptCompilerFlags :: [Text] -- ^ compiler/interpreter flags+    , scriptInclude :: [Path] -- ^ paths to morloc module directories+    }+  deriving (Show, Ord, Eq)++data UnresolvedPacker =+  UnresolvedPacker+    { unresolvedPackerTerm :: Maybe EVar+    -- ^ The general import term used for this type. For example, the 'Map'+    -- type may have language-specific realizations such as 'dict' or 'hash',+    -- but it is imported as 'import xxx (Map)'.+    , unresolvedPackerCType :: UnresolvedType+    -- ^ The decomposed (unpacked) type+    , unresolvedPackerForward :: [Source]+    -- ^ The unpack function, there may be more than one, the compiler will make+    -- a half-hearted effort to find the best one. It is called "Forward" since+    -- it is moves one step towards serialization.+    , unresolvedPackerReverse :: [Source]+    }+  deriving (Show, Ord, Eq)++type PackMap = Map (TVar, Int) [UnresolvedPacker]++data MorlocError+  -- | Raised when assumptions about the input RDF are broken. This should not+  -- occur for RDF that has been validated.+  = InvalidRDF Text+  -- | Raised for calls to unimplemented features+  | NotImplemented Text+  -- | Raised for unsupported features (such as specific languages)+  | NotSupported Text+  -- | Raised by parsec on parse errors+  | SyntaxError (ParseError Char Void)+  -- | Raised when someone didn't customize their error messages+  | UnknownError+  -- | Raised when an unsupported language is encountered+  | UnknownLanguage Text+  -- | Raised when parent and child types conflict+  | TypeConflict Text Text+  -- | Raised for general type errors+  | TypeError Text+  -- | Raised when a module cannot be loaded +  | CannotLoadModule Text+  -- | System call failed+  | SystemCallError Text Text Text+  -- | Raised when there is an error in the code generators+  | GeneratorError Text+  -- | Missing a serialization or deserialization function+  | SerializationError Text+  -- | Error in building a pool (i.e., in a compiled language)+  | PoolBuildError Text+  -- | Raise error if inappropriate function is called on unrealized manifold+  | NoBenefits+  -- | Raise when a type alias substitution fails+  | SelfRecursiveTypeAlias TVar+  | MutuallyRecursiveTypeAlias [TVar]+  | BadTypeAliasParameters TVar Int Int +  | ConflictingTypeAliases Type Type+  -- | Problems with the directed acyclic graph datastructures+  | DagMissingKey Text+  -- | Raised when a branch is reached that should not be possible+  | CallTheMonkeys Text+  --------------- T Y P E   E R R O R S --------------------------------------+  | MissingGeneralType+  | AmbiguousGeneralType+  | SubtypeError Type Type+  | ExistentialError+  | UnsolvedExistentialTerm+  | BadExistentialCast+  | AccessError Text+  | NonFunctionDerive+  | UnboundVariable EVar+  | OccursCheckFail+  | EmptyCut+  | TypeMismatch+  | ToplevelRedefinition+  | BadRecordAccess+  | NoAnnotationFound -- I don't know what this is for+  | OtherError Text -- TODO: remove this option+  -- container errors+  | EmptyTuple+  | TupleSingleton+  | EmptyRecord+  -- module errors+  | MultipleModuleDeclarations [MVar]+  | BadImport MVar EVar+  | CannotFindModule MVar+  | CyclicDependency+  | SelfImport MVar+  | BadRealization+  | TooManyRealizations+  | MissingSource+  -- serialization errors+  | MissingPacker Text CType+  | MissingUnpacker Text CType+  -- type extension errors+  | AmbiguousPacker TVar+  | AmbiguousUnpacker TVar+  | AmbiguousCast TVar TVar+  | IncompatibleRealization MVar+  | MissingAbstractType+  | ExpectedAbstractType+  | CannotInferConcretePrimitiveType+  | ToplevelStatementsHaveNoLanguage+  | InconsistentWithinTypeLanguage+  | CannotInferLanguageOfEmptyRecord+  | ConflictingSignatures+  | CompositionsMustBeGeneral+  | IllegalConcreteAnnotation+  deriving (Eq)++data PackageMeta =+  PackageMeta+    { packageName :: !Text+    , packageVersion :: !Text+    , packageHomepage :: !Text+    , packageSynopsis :: !Text+    , packageDescription :: !Text+    , packageCategory :: !Text+    , packageLicense :: !Text+    , packageAuthor :: !Text+    , packageMaintainer :: !Text+    , packageGithub :: !Text+    , packageBugReports :: !Text+    , packageGccFlags :: !Text+    }+  deriving (Show, Ord, Eq)++defaultPackageMeta :: PackageMeta+defaultPackageMeta =+  PackageMeta+    { packageName = ""+    , packageVersion = ""+    , packageHomepage = ""+    , packageSynopsis = ""+    , packageDescription = ""+    , packageCategory = ""+    , packageLicense = ""+    , packageAuthor = ""+    , packageMaintainer = ""+    , packageGithub = ""+    , packageBugReports = ""+    , packageGccFlags = ""+    }++-- | Configuration object that is passed with MorlocMonad+data Config =+  Config+    { configHome :: !Path+    , configLibrary :: !Path+    , configTmpDir :: !Path+    , configLangPython3 :: !Path+    -- ^ path to python interpreter+    , configLangR :: !Path+    -- ^ path to R interpreter+    , configLangPerl :: !Path+    -- ^ path to perl interpreter+    }+  deriving (Show, Ord, Eq)+++-- ================ T Y P E C H E C K I N G  =================================++newtype EVar = EVar { unEVar :: Text } deriving (Show, Eq, Ord)+newtype MVar = MVar { unMVar :: Text } deriving (Show, Eq, Ord)++data TVar = TV (Maybe Lang) Text deriving (Show, Eq, Ord)++-- | Let the TVar type behave like the EVar and MVar newtypes+unTVar :: TVar -> Text+unTVar (TV _ t) = t++data Source =+  Source+    { srcName :: Name+      -- ^ the name of the function in the source language+    , srcLang :: Lang+    , srcPath :: Maybe Path+    , srcAlias :: EVar+      -- ^ the morloc alias for the function (if no alias is explicitly given,+      -- this will be equal to the name+    }+  deriving (Ord, Eq, Show)++-- g: an annotation for the group of child trees (what they have in common)+-- f: a collection - before realization this will probably be Set+--                 - after realization it will be One+-- c: an annotation for the specific child tree+data SAnno g f c = SAnno (f (SExpr g f c, c)) g++data None = None+data One a = One a+data Many a = Many [a]++instance Functor One where+  fmap f (One x) = One (f x)++data SExpr g f c+  = UniS+  | VarS EVar+  | AccS (SAnno g f c) EVar+  | ListS [SAnno g f c]+  | TupleS [SAnno g f c]+  | LamS [EVar] (SAnno g f c)+  | AppS (SAnno g f c) [SAnno g f c]+  | NumS Scientific+  | LogS Bool+  | StrS Text+  | RecS [(EVar, SAnno g f c)]+  | CallS Source++-- | Description of the general manifold+data GMeta = GMeta {+    metaId :: Int+  , metaGType :: Maybe GType+  , metaName :: Maybe EVar -- the name, if relevant+  , metaProperties :: Set Property+  , metaConstraints :: Set Constraint+  , metaPackers :: Map (TVar, Int) [UnresolvedPacker]+  -- ^ The (un)packers available in this node's module scope. FIXME: kludge+  , metaConstructors :: Map TVar Source+  -- ^ The constructors in this node's module scope. FIXME: kludge+  , metaTypedefs :: Map TVar (Type, [TVar])+  -- ^ Everything needed to make the prototypes and serialization generic+  -- functions in C+++} deriving (Show, Ord, Eq)++newtype CType = CType { unCType :: Type }+  deriving (Show, Ord, Eq)++newtype GType = GType { unGType :: Type }+  deriving (Show, Ord, Eq)++-- a safe alternative to the CType constructor+ctype :: Type -> CType+ctype t+  | isJust (langOf t) = CType t+  | otherwise = error "COMPILER BUG - incorrect assignment to concrete type"++-- a safe alternative to the GType constructor+generalType :: Type -> GType+generalType t+  | isNothing (langOf t) = GType t+  | otherwise = error "COMPILER BUG - incorrect assignment to general type"++data NamType+  = NamRecord+  | NamObject+  | NamTable+  deriving(Show, Ord, Eq)++-- | Types, see Dunfield Figure 6+data Type+  = UnkT TVar+  -- ^ Unknown type: these may be serialized forms that do not need to be+  -- unserialized in the current environment but will later be passed to an+  -- environment where they can be deserialized. Alternatively, terms that are+  -- used within dynamic languages may need to type annotation.+  | VarT TVar+  -- ^ (a)+  | FunT Type Type+  -- ^ (A->B)  -- positional parameterized types+  | ArrT TVar [Type]+  -- ^ f [Type]  -- keyword parameterized types+  | NamT NamType TVar [Type] [(Text, Type)] +  -- ^ Foo { bar :: A, baz :: B }+  deriving (Show, Ord, Eq)++-- | Types, see Dunfield Figure 6+data UnresolvedType+  = VarU TVar+  -- ^ (a)+  | ExistU TVar [UnresolvedType] [UnresolvedType]+  -- ^ (a^) will be solved into one of the other types+  | ForallU TVar UnresolvedType+  -- ^ (Forall a . A)+  | FunU UnresolvedType UnresolvedType+  -- ^ (A->B)+  | ArrU TVar [UnresolvedType] -- positional parameterized types+  -- ^ f [UnresolvedType]+  | NamU NamType TVar [UnresolvedType] [(Text, UnresolvedType)] -- keyword parameterized types+  -- ^ Foo { bar :: A, baz :: B }+  deriving (Show, Ord, Eq)++unresolvedType2type :: UnresolvedType -> Type +unresolvedType2type (VarU v) = VarT v+unresolvedType2type (FunU t1 t2) = FunT (unresolvedType2type t1) (unresolvedType2type t2) +unresolvedType2type (ArrU v ts) = ArrT v (map unresolvedType2type ts)+unresolvedType2type (NamU r v ts rs) = NamT r v (map unresolvedType2type ts) (zip (map fst rs) (map (unresolvedType2type . snd) rs))+unresolvedType2type (ExistU _ _ _) = error "Cannot cast existential type to Type"+unresolvedType2type (ForallU _ _) = error "Cannot cast universal type as Type"+++data Property+  = Pack -- data structure to JSON+  | Unpack -- JSON to data structure+  | Cast -- casts from type A to B+  | GeneralProperty [Text]+  deriving (Show, Eq, Ord)++-- | Eventually, Constraint should be a richer type, but for they are left as+-- unparsed lines of text+newtype Constraint =+  Con Text+  deriving (Show, Eq, Ord)++class Typelike a where+  typeOf :: a -> Type++  -- | Break a type into its input arguments, and final output+  -- For example: decompose ((a -> b) -> [a] -> [b]) would +  -- yield ([(a->b), [a]], [b])+  decompose :: a -> ([a], a)++  -- | like @decompose@ but concatentates the output type+  decomposeFull :: a -> [a]+  decomposeFull t = case decompose t of+    (xs, x) -> (xs ++ [x])++  nargs :: a -> Int+  nargs t = case typeOf t of+    (FunT _ t') -> 1 + nargs t'+    _ -> 0++instance Typelike Type where+  typeOf = id++  decompose (FunT t1 t2) = case decompose t2 of+    (ts, finalType) -> (t1:ts, finalType) +  decompose t = ([], t)+++instance Typelike CType where+  typeOf (CType t) = t ++  decompose t0 = case (decompose (unCType t0)) of+    (ts, t) -> (map CType ts, CType t)++instance Typelike GType where+  typeOf (GType t) = t ++  decompose t0 = case (decompose (unGType t0)) of+    (ts, t) -> (map GType ts, GType t)++class HasOneLanguage a where+  langOf :: a -> Maybe Lang+  langOf' :: a -> Lang++  langOf x = Just (langOf' x) +  langOf' x = fromJust (langOf x)++instance HasOneLanguage CType where+  langOf (CType t) = langOf t++-- | Determine the language from a type, fail if the language is inconsistent.+-- Inconsistency in language should be impossible at the syntactic level, thus+-- an error in this function indicates a logical bug in the typechecker.+instance HasOneLanguage Type where+  langOf (UnkT (TV lang _)) = lang+  langOf (VarT (TV lang _)) = lang+  langOf x@(FunT t1 t2)+    | langOf t1 == langOf t2 = langOf t1+    | otherwise = error $ "inconsistent languages in" <> show x+  langOf x@(ArrT (TV lang _) ts)+    | all ((==) lang) (map langOf ts) = lang+    | otherwise = error $ "inconsistent languages in " <> show x +  langOf (NamT _ _ _ []) = error "empty records are not allowed"+  langOf x@(NamT _ (TV lang _) _ ts)+    | all ((==) lang) (map (langOf . snd) ts) = lang+    | otherwise = error $ "inconsistent languages in " <> show x++instance HasOneLanguage TVar where+  langOf (TV lang _) = lang++instance HasOneLanguage UnresolvedType where+  langOf (VarU (TV lang _)) = lang+  langOf x@(ExistU (TV lang _) ts _)+    | all ((==) lang) (map langOf ts) = lang+    | otherwise = error $ "inconsistent languages in " <> show x+  langOf x@(ForallU (TV lang _) t)+    | lang == langOf t = lang+    | otherwise = error $ "inconsistent languages in " <> show x+  langOf x@(FunU t1 t2)+    | langOf t1 == langOf t2 = langOf t1+    | otherwise = error $ "inconsistent languages in" <> show x+  langOf x@(ArrU (TV lang _) ts)+    | all ((==) lang) (map langOf ts) = lang+    | otherwise = error $ "inconsistent languages in " <> show x +  langOf (NamU _ _ _ []) = error "empty records are not allowed"+  langOf x@(NamU _ (TV lang _) _ rs)+    | all ((==) lang) (map (langOf . snd) rs) = lang+    | otherwise = error $ "inconsistent languages in " <> show x
+ library/Morloc/Pretty.hs view
@@ -0,0 +1,247 @@+{-|+Module      : Morloc.Pretty+Description : Pretty print instances+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.Pretty+  ( prettyType+  , prettyGreenType+  , prettyGreenUnresolvedType+  , prettyScream+  , prettyLinePrefixes+  , prettyUnresolvedPacker+  , prettyPackMap+  , prettySAnnoMany+  , prettySAnnoOne+  ) where++import Data.Text.Prettyprint.Doc.Render.Terminal+import Morloc.Data.Doc+import Morloc.Namespace+import qualified Morloc.Data.Text as MT+import qualified Data.Map as Map+import qualified Data.Text.Prettyprint.Doc.Render.Terminal.Internal as Style++instance Pretty MVar where+  pretty = pretty . unMVar++instance Pretty EVar where+  pretty = pretty . unEVar++instance Pretty Path where+  pretty = pretty . unPath++instance Pretty Code where+  pretty = pretty . unCode++instance Pretty Name where+  pretty = pretty . unName++instance Pretty TVar where+  pretty (TV Nothing t) = pretty t+  pretty (TV (Just lang) t) = pretty t <> "@" <> pretty (show lang)++instance Pretty Lang where+  pretty = viaShow++instance Pretty Source where+  pretty (Source name lang pathmay alias)+    = "source" <+> pretty lang+    <> maybe "" (\path->" from" <+> dquotes (pretty path)) pathmay+    <+> dquotes (pretty name) <+> "as" <+> pretty alias++typeStyle =+  Style.SetAnsiStyle+    { Style.ansiForeground = Just (Vivid, Green)+    , Style.ansiBackground = Nothing+    , Style.ansiBold = Nothing+    , Style.ansiItalics = Nothing+    , Style.ansiUnderlining = Just Underlined+    }++screamStyle =+  Style.SetAnsiStyle+    { Style.ansiForeground = Just (Vivid, Red)+    , Style.ansiBackground = Nothing+    , Style.ansiBold = Nothing+    , Style.ansiItalics = Nothing+    , Style.ansiUnderlining = Just Underlined+    }+++prettyGreenType :: Type -> Doc AnsiStyle+prettyGreenType t = annotate typeStyle (prettyType t)++forallVars :: UnresolvedType -> [Doc AnsiStyle]+forallVars (ForallU (TV _ v) t) = pretty v : forallVars t+forallVars _ = []++forallBlock :: UnresolvedType -> Doc AnsiStyle+forallBlock (ForallU _ t) = forallBlock t+forallBlock t = prettyGreenUnresolvedType t+++prettyScream :: MT.Text -> Doc AnsiStyle+prettyScream x = annotate screamStyle (pretty x)++prettyLinePrefixes :: MT.Text -> Doc ann -> Doc ann +prettyLinePrefixes prefix d =+  pretty . MT.unlines . map (\l -> prefix <> l) $ MT.lines (render d)+++class PrettyType a where+  prettyType :: a -> Doc ann++instance PrettyType Type where+  prettyType (UnkT (TV _ v)) = "*" <> pretty v+  prettyType (VarT (TV _ "Unit")) = "()"+  prettyType (VarT v) = pretty v+  prettyType (FunT t1@(FunT _ _) t2) =+    parens (prettyType t1) <+> "->" <+> prettyType t2+  prettyType (FunT t1 t2) = prettyType t1 <+> "->" <+> prettyType t2+  prettyType (ArrT (TV Nothing "List") [t]) = brackets (prettyType t)+  prettyType (ArrT v ts) = pretty v <+> hsep (map prettyType ts)+  prettyType (NamT _ (TV Nothing _) _ entries) =+    encloseSep "{" "}" ","+      (map (\(v, e) -> pretty v <> ":" <> prettyType e) entries)+  prettyType (NamT _ (TV (Just lang) t) _ entries) =+    pretty t <> "@" <> viaShow lang <+>+    encloseSep "{" "}" ","+      (map (\(v, e) -> pretty v <> ":" <> prettyType e) entries)+++instance PrettyType GType where+  prettyType = prettyType . unGType++instance PrettyType CType where+  prettyType = prettyType . unCType+++prettyGreenUnresolvedType :: UnresolvedType -> Doc AnsiStyle+prettyGreenUnresolvedType t = annotate typeStyle (prettyUnresolvedType t)++prettyUnresolvedType :: UnresolvedType -> Doc AnsiStyle+prettyUnresolvedType (ExistU v ts ds)+  = angles $ (pretty v)+  <> list (map prettyUnresolvedType ts)+  <> list (map prettyUnresolvedType ds)+prettyUnresolvedType t@(ForallU _ _) =+  "forall" <+> hsep (forallVars t) <+> "." <+> forallBlock t+prettyUnresolvedType (VarU (TV _ "Unit")) = "()"+prettyUnresolvedType (VarU v) = pretty v+prettyUnresolvedType (FunU t1@(FunU _ _) t2) =+  parens (prettyUnresolvedType t1) <+> "->" <+> prettyUnresolvedType t2+prettyUnresolvedType (FunU t1 t2) = prettyUnresolvedType t1 <+> "->" <+> prettyUnresolvedType t2+prettyUnresolvedType (ArrU v ts) = pretty v <+> hsep (map prettyUnresolvedType ts)+prettyUnresolvedType (NamU r (TV Nothing _) _ entries) =+  viaShow r <> encloseSep "{" "}" ", "+    (map (\(v, e) -> pretty v <+> "=" <+> prettyUnresolvedType e) entries)+prettyUnresolvedType (NamU r (TV (Just lang) t) _ entries) =+  pretty t <> "@" <> viaShow lang <+>+  viaShow r <> encloseSep "{" "}" ", "+    (map (\(v, e) -> pretty v <+> "=" <+> prettyUnresolvedType e) entries)++prettyUnresolvedPacker :: UnresolvedPacker -> Doc AnsiStyle+prettyUnresolvedPacker (UnresolvedPacker v t fs rs) = vsep+  [ pretty v+  , prettyGreenUnresolvedType t +  , "forward:" <+> hsep (map (\s -> pretty (srcAlias s) <> "@" <> pretty (srcLang s)) fs)+  , "reverse:" <+> hsep (map (\s -> pretty (srcAlias s) <> "@" <> pretty (srcLang s)) rs)+  ]++prettyPackMap :: PackMap -> Doc AnsiStyle+prettyPackMap m =  "----- pacmaps ----\n"+                <> vsep (map f (Map.toList m))+                <> "\n------------------" where+  f :: ((TVar, Int), [UnresolvedPacker]) -> Doc AnsiStyle+  f ((v, i), ps) =+    block 4+      ("packmap" <+> pretty v <> parens (pretty i))+      (vsep $ map prettyUnresolvedPacker ps)+++prettySAnnoMany :: SAnno GMeta Many [CType] -> MDoc+prettySAnnoMany (SAnno (Many xs0) g) =+     pretty (metaId g)+  <> maybe "" (\n -> " " <> pretty n) (metaName g)+  <+> "::" <+> maybe "_" prettyType (metaGType g)+  <> line <> indent 5 (vsep (map writeSome xs0))+  where+    writeSome :: (SExpr GMeta Many [CType], [CType]) -> MDoc+    writeSome (s, ts)+      =  "_ ::"+      <+> encloseSep "{" "}" ";" (map prettyType ts)+      <> line <> writeExpr s++    writeExpr :: SExpr GMeta Many [CType] -> MDoc+    writeExpr (AccS x k) = pretty k <+> "from " <> nest 2 (prettySAnnoMany x) +    writeExpr (ListS xs) = list (map prettySAnnoMany xs)+    writeExpr (TupleS xs) = list (map prettySAnnoMany xs)+    writeExpr (RecS entries) = encloseSep "{" "}" "," $+      map (\(k,v) -> pretty k <+> "=" <+> prettySAnnoMany v) entries+    writeExpr (LamS vs x)+      = "LamS"+      <+> list (map pretty vs)+      <> line <> indent 2 (prettySAnnoMany x)+    writeExpr (AppS f xs) = "AppS" <+> indent 2 (vsep (prettySAnnoMany f : map prettySAnnoMany xs))+    writeExpr x = descSExpr x++-- For example @prettySAnnoOne id Nothing@ for the most simple printer+prettySAnnoOne+  :: (a -> CType) -> Maybe (a -> MDoc) -> SAnno GMeta One a -> MDoc+prettySAnnoOne getType extra s = hang 2 . vsep $ ["AST:", describe s]+  where+    addExtra x = case extra of+      (Just f) -> " " <> f x+      Nothing -> ""++    describe (SAnno (One (x@(AccS _ _), _)) _) = descSExpr x+    describe (SAnno (One (x@(ListS _), _)) _) = descSExpr x+    describe (SAnno (One (x@(TupleS _), _)) _) = descSExpr x+    describe (SAnno (One (x@(RecS _), _)) _) = descSExpr x+    describe (SAnno (One (x@(AppS f xs), c)) g) =+      hang 2 . vsep $+        [ pretty (metaId g) <+> descSExpr x <+> parens (prettyType (getType c)) <> addExtra c+        , describe f+        ] ++ map describe xs+    describe (SAnno (One (f@(LamS _ x), c)) g) = do+      hang 2 . vsep $+        [ pretty (metaId g)+            <+> name (getType c) g+            <+> descSExpr f+            <+> parens (prettyType (getType c))+            <> addExtra c+        , describe x+        ]+    describe (SAnno (One (x, c)) g) =+          pretty (metaId g)+      <+> descSExpr x+      <+> parens (prettyType (getType c))+      <>  addExtra c++    name :: CType -> GMeta -> MDoc+    name t g =+      let lang = fromJust (langOf t)+      in maybe+          ("_" <+> viaShow lang <+> "::")+          (\x -> pretty x <+> viaShow lang <+> "::")+          (metaName g)++descSExpr :: SExpr g f c -> MDoc+descSExpr (UniS) = "UniS"+descSExpr (VarS v) = "VarS" <+> pretty v+descSExpr (CallS src)+  =   "CallS"+  <+> pretty (srcAlias src) <+> "<" <> viaShow (srcLang src) <> ">"+descSExpr (AccS _ k) = "@" <> pretty k+descSExpr (ListS _) = "ListS"+descSExpr (TupleS _) = "TupleS"+descSExpr (LamS vs _) = "LamS" <+> hsep (map pretty vs)+descSExpr (AppS _ _) = "AppS"+descSExpr (NumS _) = "NumS"+descSExpr (LogS _) = "LogS"+descSExpr (StrS _) = "StrS"+descSExpr (RecS _) = "RecS"
+ library/Morloc/ProgramBuilder/Build.hs view
@@ -0,0 +1,50 @@+{-|+Module      : Morloc.ProgramBuilder.Build+Description : Manage system requirements and project building for pools+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.ProgramBuilder.Build+  ( buildProgram+  ) where++import Morloc.Namespace+import qualified Morloc.Data.Text as MT+import qualified Morloc.Language as ML+import qualified Morloc.Monad as MM++import qualified System.Directory as SD++buildProgram :: (Script, [Script]) -> MorlocMonad ()+buildProgram (nexus, pools) = mapM_ build (nexus : pools)++build :: Script -> MorlocMonad ()+build s =+  case scriptLang s of+    Python3Lang -> liftIO $ writeInterpreted s+    RLang -> liftIO $ writeInterpreted s+    PerlLang -> liftIO $ writeInterpreted s+    CLang -> gccBuild s "gcc"+    CppLang -> gccBuild s "g++ --std=c++11" -- TODO: I need more rigorous build handling++-- | Compile a C program+gccBuild :: Script -> MT.Text -> MorlocMonad ()+gccBuild s cmd = do+  let src = ML.makeSourceName (scriptLang s) (MT.pack (scriptBase s))+  let exe = ML.makeExecutableName (scriptLang s) (MT.pack (scriptBase s))+  let inc = ["-I" <> unPath i | i <- scriptInclude s]+  liftIO $ MT.writeFile (MT.unpack src) (unCode (scriptCode s))+  MM.runCommand "GccBuild" $+    MT.unwords ([cmd, "-o", exe, src] ++ scriptCompilerFlags s ++ inc)++-- | Build an interpreted script.+writeInterpreted :: Script -> IO ()+writeInterpreted s = do+  let f =+        MT.unpack $+        ML.makeExecutableName (scriptLang s) (MT.pack (scriptBase s))+  MT.writeFile f (unCode (scriptCode s))+  p <- SD.getPermissions f+  SD.setPermissions f (p {SD.executable = True})
+ library/Morloc/Quasi.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++module Morloc.Quasi+  ( idoc+  ) where++import Language.Haskell.TH+import Language.Haskell.TH.Quote+import qualified Morloc.Data.Doc as G++import qualified Language.Haskell.Meta.Parse as MP++import Text.Parsec++type Parser = Parsec String ()++data I+  = S String+  | V String++pIs :: Parser [I]+pIs = many1 (try pV <|> try pS <|> try pE) <* eof++pV :: Parser I+pV = fmap V $ between (string "#{") (char '}') (many1 (noneOf "}"))++pS :: Parser I+pS = fmap S $ many1 (noneOf "#")++-- | match a literal '#' sign+pE :: Parser I+pE = fmap (S . return) $ char '#' <* notFollowedBy (char '}')++-- | __i__nterpolated __doc__ument+idoc :: QuasiQuoter+idoc =+  QuasiQuoter+    { quoteExp = compile+    , quotePat = error "Can't handle patterns"+    , quoteType = error "Can't handle types"+    , quoteDec = error "Can't handle declarations"+    }+  where+    compile :: String -> Q Exp+    compile txt =+      case parse pIs "" txt of+        Left err -> error $ show err+        Right xs -> return $ AppE (VarE 'G.hcat) (ListE (map qI xs))+          where qI :: I -> Exp+                qI (S x) = (LitE (StringL x))+                qI (V x) =+                  case MP.parseExp x of+                    (Right hask) -> hask -- a Haskell expression+                    (Left err) -> error err
+ library/Morloc/System.hs view
@@ -0,0 +1,54 @@+{-|+Module      : Morloc.System+Description : Handle dependencies and environment setup+Copyright   : (c) Zebulun Arendsee, 2020+License     : GPL-3+Maintainer  : zbwrnz@gmail.com+Stability   : experimental+-}+module Morloc.System+  ( loadYamlConfig+  , getHomeDirectory+  , appendPath+  , takeDirectory+  , takeFileName+  , combine+  , fileExists+  ) where++import Morloc.Namespace +import qualified Morloc.Data.Text as MT++import Data.Aeson (FromJSON(..))+import qualified Data.Yaml.Config as YC+import qualified System.Directory as Sys+import qualified System.FilePath.Posix as Path+import qualified System.Directory as SD++combine :: Path -> Path -> Path+combine (Path x) (Path y) = Path . MT.pack $ Path.combine (MT.unpack x) (MT.unpack y)++fileExists :: Path -> IO Bool+fileExists = SD.doesFileExist . MT.unpack . unPath++takeDirectory :: Path -> Path+takeDirectory (Path x) = Path . MT.pack . Path.takeDirectory $ MT.unpack x++takeFileName :: Path -> Path+takeFileName (Path x) = Path . MT.pack . Path.takeFileName $ MT.unpack x++-- | Append POSIX paths encoded as Text+appendPath :: Path -> Path -> Path+appendPath base path = combine path base++getHomeDirectory :: IO Path+getHomeDirectory = fmap (Path . MT.pack) Sys.getHomeDirectory++loadYamlConfig ::+     FromJSON a+  => Maybe [Path] -- ^ possible locations of the config file +  -> YC.EnvUsage -- ^ default values taken from the environment (or a hashmap)+  -> IO a -- ^ default configuration+  -> IO a+loadYamlConfig (Just fs) e _ = YC.loadYamlSettings (map (MT.unpack . unPath) fs) [] e+loadYamlConfig Nothing _ d = d
+ morloc.cabal view
@@ -0,0 +1,179 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.0.+--+-- see: https://github.com/sol/hpack+--+-- hash: d4df6b564bd5942c82443aacd8df43de1c0c279cbd59f183a720cb8e52fc1b16++name:           morloc+version:        0.33.0+synopsis:       A multi-lingual, typed, workflow language+description:    See GitHub README <https://github.com/morloc-project/morloc#readme>+category:       Language, Compiler, Code Generation+homepage:       https://github.com/morloc-project/morloc+bug-reports:    https://github.com/morloc-project/morloc/issues+author:         Zebulun Arendsee+maintainer:     zbwrbz@gmail.com+copyright:      2020 Zebulun Arendsee+license:        GPL-3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://github.com/morloc-project/morloc++library+  exposed-modules:+      Morloc+      Morloc.CodeGenerator.Generate+      Morloc.CodeGenerator.Grammars.Common+      Morloc.CodeGenerator.Grammars.Macro+      Morloc.CodeGenerator.Grammars.Translator.Cpp+      Morloc.CodeGenerator.Grammars.Translator.Python3+      Morloc.CodeGenerator.Grammars.Translator.R+      Morloc.CodeGenerator.Grammars.Translator.Source.CppInternals+      Morloc.CodeGenerator.Internal+      Morloc.CodeGenerator.Namespace+      Morloc.CodeGenerator.Nexus+      Morloc.CodeGenerator.Serial+      Morloc.Config+      Morloc.Data.DAG+      Morloc.Data.Doc+      Morloc.Data.Text+      Morloc.Error+      Morloc.Frontend.API+      Morloc.Frontend.Desugar+      Morloc.Frontend.Infer+      Morloc.Frontend.Internal+      Morloc.Frontend.Lang.DefaultTypes+      Morloc.Frontend.Namespace+      Morloc.Frontend.Parser+      Morloc.Frontend.PartialOrder+      Morloc.Frontend.Pretty+      Morloc.Frontend.Treeify+      Morloc.Internal+      Morloc.Language+      Morloc.Module+      Morloc.Monad+      Morloc.Namespace+      Morloc.Pretty+      Morloc.ProgramBuilder.Build+      Morloc.Quasi+      Morloc.System+  other-modules:+      Paths_morloc+  hs-source-dirs:+      library+  default-extensions: FlexibleContexts OverloadedStrings BangPatterns GeneralizedNewtypeDeriving ViewPatterns+  ghc-options: -Wall -Wcompat -fwarn-unused-binds -fwarn-unused-imports -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -fwarn-redundant-constraints -fno-warn-missing-signatures -fno-warn-unused-do-bind -fno-warn-orphans+  build-depends:+      aeson >=1.2.4.0 && <1.3+    , base >=4.7 && <5+    , bytestring >=0.10.8.2 && <0.11+    , containers >=0.5.10.2 && <0.6+    , directory >=1.3.0.2 && <1.4+    , extra >=1.6.5 && <1.7+    , filepath >=1.4.1.2 && <1.5+    , haskell-src-meta >=0.8.0.2 && <0.9+    , megaparsec >=6.4.1 && <6.5+    , mtl >=2.2.2 && <2.3+    , parsec >=3.1.13.0 && <3.2+    , partial-order >=0.1.2.1 && <0.2+    , pretty-simple >=2.1.0.0 && <2.2+    , prettyprinter >=1.2.0.1 && <1.3+    , prettyprinter-ansi-terminal >=1.1.1.2 && <1.2+    , process >=1.6.1.0 && <1.7+    , raw-strings-qq ==1.1.*+    , safe >=0.3.17 && <0.4+    , scientific >=0.3.5.3 && <0.4+    , template-haskell >=2.12.0.0 && <2.13+    , text >=1.2.3.0 && <1.3+    , unordered-containers >=0.2.9.0 && <0.3+    , yaml >=0.8.29 && <0.9+  default-language: Haskell2010++executable morloc+  main-is: Main.hs+  other-modules:+      Subcommands+      Paths_morloc+  hs-source-dirs:+      executable+  default-extensions: FlexibleContexts OverloadedStrings BangPatterns GeneralizedNewtypeDeriving ViewPatterns+  ghc-options: -Wall -Wcompat -fwarn-unused-binds -fwarn-unused-imports -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -fwarn-redundant-constraints -fno-warn-missing-signatures -fno-warn-unused-do-bind -fno-warn-orphans -Wall -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      aeson >=1.2.4.0 && <1.3+    , base >=4.7 && <5+    , bytestring >=0.10.8.2 && <0.11+    , containers >=0.5.10.2 && <0.6+    , directory >=1.3.0.2 && <1.4+    , docopt >=0.7.0.5 && <0.8+    , extra >=1.6.5 && <1.7+    , filepath >=1.4.1.2 && <1.5+    , haskell-src-meta >=0.8.0.2 && <0.9+    , megaparsec >=6.4.1 && <6.5+    , morloc+    , mtl >=2.2.2 && <2.3+    , parsec >=3.1.13.0 && <3.2+    , partial-order >=0.1.2.1 && <0.2+    , pretty-simple >=2.1.0.0 && <2.2+    , prettyprinter >=1.2.0.1 && <1.3+    , prettyprinter-ansi-terminal >=1.1.1.2 && <1.2+    , process >=1.6.1.0 && <1.7+    , raw-strings-qq ==1.1.*+    , safe >=0.3.17 && <0.4+    , scientific >=0.3.5.3 && <0.4+    , template-haskell >=2.12.0.0 && <2.13+    , text >=1.2.3.0 && <1.3+    , unordered-containers >=0.2.9.0 && <0.3+    , yaml >=0.8.29 && <0.9+  default-language: Haskell2010++test-suite morloc-test+  type: exitcode-stdio-1.0+  main-is: Main.hs+  other-modules:+      GoldenMakefileTests+      PropertyTests+      UnitTypeTests+      Paths_morloc+  hs-source-dirs:+      test-suite+  default-extensions: FlexibleContexts OverloadedStrings BangPatterns GeneralizedNewtypeDeriving ViewPatterns+  ghc-options: -Wall -Wcompat -fwarn-unused-binds -fwarn-unused-imports -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates -fwarn-redundant-constraints -fno-warn-missing-signatures -fno-warn-unused-do-bind -fno-warn-orphans -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      QuickCheck >=2.10.1 && <2.11+    , aeson >=1.2.4.0 && <1.3+    , base >=4.10.1.0 && <4.11+    , bytestring >=0.10.8.2 && <0.11+    , containers >=0.5.10.2 && <0.6+    , directory >=1.3.0.2 && <1.4+    , extra >=1.6.5 && <1.7+    , filepath >=1.4.1.2 && <1.5+    , haskell-src-meta >=0.8.0.2 && <0.9+    , megaparsec >=6.4.1 && <6.5+    , morloc+    , mtl >=2.2.2 && <2.3+    , parsec >=3.1.13.0 && <3.2+    , partial-order >=0.1.2.1 && <0.2+    , pretty-simple >=2.1.0.0 && <2.2+    , prettyprinter >=1.2.0.1 && <1.3+    , prettyprinter-ansi-terminal >=1.1.1.2 && <1.2+    , process >=1.6.1.0 && <1.7+    , raw-strings-qq ==1.1.*+    , safe >=0.3.17 && <0.4+    , scientific >=0.3.5.3 && <0.4+    , tasty >=1.0.1.1 && <1.1+    , tasty-golden >=2.3.1.3 && <2.4+    , tasty-hunit >=0.10.0.1 && <0.11+    , tasty-quickcheck >=0.9.2 && <0.10+    , template-haskell >=2.12.0.0 && <2.13+    , text >=1.2.3.0 && <1.3+    , unordered-containers >=0.2.9.0 && <0.3+    , yaml >=0.8.29 && <0.9+  default-language: Haskell2010
+ test-suite/GoldenMakefileTests.hs view
@@ -0,0 +1,37 @@+module GoldenMakefileTests+  ( goldenMakefileTest+  ) where++import Test.Tasty+import Test.Tasty.Golden+import qualified System.Process as SP+import qualified System.Directory as SD+import qualified System.IO as SI++goldenMakefileTest :: String -> String -> TestTree+goldenMakefileTest msg testdir =+  let dir = testdir+      expFile = testdir ++ "/exp.txt"+      obsFile = testdir ++ "/obs.txt"+  in+      goldenVsFile+        msg+        expFile+        obsFile+        (makeManifoldFile dir)++makeManifoldFile :: String -> IO ()+makeManifoldFile path = do+  abspath <- SD.makeAbsolute path+  devnull <- SI.openFile "/dev/null" SI.WriteMode+  SP.runProcess+    "make" -- command+    ["-C", abspath, "--quiet"] -- arguments+    Nothing -- optional path to working diretory+    Nothing -- optional environment+    Nothing -- stdin handle+    (Just devnull) -- stdout handle+    (Just devnull) -- stderr handle+    >>= SP.waitForProcess++  SP.callProcess "make" ["-C", abspath, "--quiet", "clean"]
+ test-suite/Main.hs view
@@ -0,0 +1,181 @@+import Test.Tasty+import qualified System.Directory as SD++import PropertyTests (propertyTests)+import UnitTypeTests+import GoldenMakefileTests (goldenMakefileTest)++main = do+  wd <- SD.getCurrentDirectory >>= SD.makeAbsolute+  let golden = \msg f -> goldenMakefileTest msg (wd ++ "/test-suite/golden-tests/" ++ f)+  defaultMain $+    testGroup+      "Morloc tests"+      [ packerTests+      , unitTypeTests+      , typeOrderTests+      , typeAliasTests+      , propertyTests+      , jsontype2jsonTests+      , recordAccessTests++      , golden "import-1" "import-1"++      , golden "argument-form-1-c" "argument-form-1-c"+      , golden "argument-form-1-py" "argument-form-1-py"+      , golden "argument-form-1-r" "argument-form-1-r"++      , golden "argument-form-2-c" "argument-form-2-c"+      , golden "argument-form-2-py" "argument-form-2-py"+      , golden "argument-form-2-r" "argument-form-2-r"++      -- see github issue #7+      , golden "argument-form-3-c" "argument-form-3-c"+      , golden "argument-form-3-py" "argument-form-3-py"+      , golden "argument-form-3-r" "argument-form-3-r"++      , golden "argument-form-4-c" "argument-form-4-c"+      , golden "argument-form-4-py" "argument-form-4-py"+      , golden "argument-form-4-r" "argument-form-4-r"++      , golden "argument-form-5-c" "argument-form-5-c"+      , golden "argument-form-5-py" "argument-form-5-py"+      , golden "argument-form-5-r" "argument-form-5-r"++      , golden "argument-form-6-c" "argument-form-6-c"+      , golden "argument-form-6-py" "argument-form-6-py"+      , golden "argument-form-6-r" "argument-form-6-r"++      , golden "argument-form-7-c" "argument-form-7-c"+      , golden "argument-form-7-py" "argument-form-7-py"+      , golden "argument-form-7-r" "argument-form-7-r"++      , golden "argument-form-8-c" "argument-form-8-c"+      , golden "argument-form-8-py" "argument-form-8-py"+      , golden "argument-form-8-r" "argument-form-8-r"++      , golden "defaults-1-py" "defaults-1-py"++      , golden "interop-1-py" "interop-1-py"+      , golden "interop-1-r" "interop-1-r"+      , golden "interop-2" "interop-2"++      , golden "manifold-form-0" "manifold-form-0"+      , golden "manifold-form-0x" "manifold-form-0x"+      , golden "manifold-form-1" "manifold-form-1"+      , golden "manifold-form-2" "manifold-form-2"+      , golden "manifold-form-2x" "manifold-form-2x"+      , golden "manifold-form-3" "manifold-form-3"+      , golden "manifold-form-3x" "manifold-form-3x"+      , golden "manifold-form-4_c" "manifold-form-4_c"+      , golden "manifold-form-4_py" "manifold-form-4_py"+      , golden "manifold-form-4_r" "manifold-form-4_r"+      , golden "manifold-form-5_c" "manifold-form-5_c"+      , golden "manifold-form-5_py" "manifold-form-5_py"+      , golden "manifold-form-5_r" "manifold-form-5_r"+      , golden "manifold-form-6_c" "manifold-form-6_c"+      , golden "manifold-form-6_py" "manifold-form-6_py"+      , golden "manifold-form-6_r" "manifold-form-6_r"++      -- see github issue #9+      , golden "manifold-form-7_c" "manifold-form-7_c"+      , golden "manifold-form-7_py" "manifold-form-7_py"+      , golden "manifold-form-7_r" "manifold-form-7_r"++      , golden "records-1-py" "records-1-py"+      , golden "records-1-r" "records-1-r"+      -- see github issue #8+      , golden "records-1-c" "records-1-c"++      , golden "selection-1" "selection-1"+      , golden "selection-2" "selection-2"+      , golden "selection-3" "selection-3"+      , golden "selection-4" "selection-4"++      -- import two instances in one languages for a function+      -- this is also a test of a function that is defind in a local file+      , golden "multiple-instances-1-c" "multiple-instances-1-c"+      , golden "multiple-instances-1-py" "multiple-instances-1-py"+      , golden "multiple-instances-1-r" "multiple-instances-1-r"+      -- multiple sources and a declaration+      , golden "multiple-instances-2-c" "multiple-instances-2-c"+      , golden "multiple-instances-2-py" "multiple-instances-2-py"+      , golden "multiple-instances-2-r" "multiple-instances-2-r"+      -- tests of module forms+      -- where *-sid+      --   s - number of sourced instances+      --   i - number of imported instances+      --   d - number of declared instances+      , golden "module-form-00n" "module-form-00n"+      , golden "module-form-011" "module-form-011"+      , golden "module-form-01n" "module-form-01n"+      , golden "module-form-0n0" "module-form-0n0"+      , golden "module-form-0n1" "module-form-0n1"+      , golden "module-form-101" "module-form-101"+      , golden "module-form-10n" "module-form-10n"+      , golden "module-form-110" "module-form-110"+      , golden "module-form-111" "module-form-111"+      , golden "module-form-1n0" "module-form-1n0"+      , golden "module-form-n00" "module-form-n00"+      , golden "module-form-n01" "module-form-n01"+      , golden "module-form-n10" "module-form-n10"++      -- tests of serialization+      -- , golden "c  S" "serial-form-1-c"+      -- , golden "py S" "serial-form-1-py"+      -- , golden "r  S" "serial-form-1-r"+      , golden "C serial-form-2-c"  "serial-form-2-c"+      , golden "C serial-form-2-py" "serial-form-2-py"+      , golden "C serial-form-2-r"  "serial-form-2-r"+      -- , golden "c  R" "serial-form-3-c"+      -- , golden "py R" "serial-form-3-py"+      -- , golden "r  R" "serial-form-3-r"+      -- outer simple type+      , golden "S(S) serial-form-4-c"  "serial-form-4-c"+      , golden "S(S) serial-form-4-py" "serial-form-4-py"+      , golden "S(S) serial-form-4-r"  "serial-form-4-r"+      , golden "S(C) serial-form-5-c"  "serial-form-5-c"+      , golden "S(C) serial-form-5-py" "serial-form-5-py"+      , golden "S(C) serial-form-5-r"  "serial-form-5-r"+      , golden "S(R) serial-form-6-c"  "serial-form-6-c"+      , golden "S(R) serial-form-6-py" "serial-form-6-py"+      , golden "S(R) serial-form-6-r"  "serial-form-6-r"+      -- outer constructed type+      , golden "C(S) serial-form-7-c"  "serial-form-7-c"+      , golden "C(S) serial-form-7-py" "serial-form-7-py"+      , golden "C(S) serial-form-7-r"  "serial-form-7-r"+      , golden "C(C) serial-form-8-c"  "serial-form-8-c"+      , golden "C(C) serial-form-8-py" "serial-form-8-py"+      , golden "C(C) serial-form-8-r"  "serial-form-8-r"+      , golden "C(R) serial-form-9-c"  "serial-form-9-c"+      , golden "C(R) serial-form-9-py" "serial-form-9-py"+      , golden "C(R) serial-form-9-r"  "serial-form-9-r"+      -- outer record type+      , golden "R(S) serial-form-10-c"  "serial-form-10-c"+      , golden "R(S) serial-form-10-py" "serial-form-10-py"+      , golden "R(S) serial-form-10-r"  "serial-form-10-r"+      , golden "R(C) serial-form-11-c"  "serial-form-11-c"+      , golden "R(C) serial-form-11-py" "serial-form-11-py"+      , golden "R(C) serial-form-11-r"  "serial-form-11-r"+      , golden "R(R) serial-form-12-c"  "serial-form-12-c"+      , golden "R(R) serial-form-12-py" "serial-form-12-py"+      , golden "R(R) serial-form-12-r"  "serial-form-12-r"+      -- table handling+      , golden "C++ table default"    "table-1-c"+      , golden "py3 table default"    "table-1-py"+      , golden "R table default"      "table-1-r"+      , golden "C++ table object"     "table-2-c"+      , golden "py3 table object"     "table-2-py"+      , golden "R table object"       "table-2-r"+      -- object handling+      , golden "C++ object handling"  "object-1-c"+      , golden "py3 object handling"  "object-1-py"+      , golden "R object handling"    "object-1-r"+      -- record access+      , golden "record-access-gen"    "record-access-gen"+      , golden "record-access-c"      "record-access-c"+      , golden "record-access-py"     "record-access-py"+      , golden "record-access-r"      "record-access-r"+      -- type identities+      , golden "type-identities-c"    "type-identities-c"+      ]
+ test-suite/PropertyTests.hs view
@@ -0,0 +1,63 @@+module PropertyTests+  ( propertyTests+  ) where++import Morloc.Namespace++import qualified Control.Monad as CM+import qualified Data.Set as Set+import qualified Data.Text as T+import qualified Test.QuickCheck as QC+import Safe (headMay)+import Test.Tasty+import Test.Tasty.QuickCheck as TQC++propertyTests =+  testGroup+    "internal list function properties"+    [ TQC.testProperty "unique makes unique lists" prop_unique_unique+    , TQC.testProperty "unique preserves original order" prop_unique_preserves_order+    , TQC.testProperty "duplicates makes unique lists" prop_duplicates_unique+    , TQC.testProperty "duplicates preserves original order" prop_duplicates_preserves_order+    ]++-- for the uniq family of functions (unique, duplicates, isSorted), I will test+-- on the numbers 1 to 5. If the desired property holds over this set, they+-- will hold over any ordered set. +one2five :: [Int] -> [Int]+one2five = map (\x -> mod (abs x) 5)++prop_unique_unique :: [Int] -> Bool+prop_unique_unique [] = True+prop_unique_unique xs =+  let xs' = one2five xs+  in length (unique xs') == Set.size (Set.fromList xs') ++-- This test asserts that the first element in the original and unique list is+-- the same. This guarantee alone does not entirely guantee that the original+-- order is preserved, but it is close.+prop_unique_preserves_order :: [Int] -> Bool+prop_unique_preserves_order xs = headMay xs == headMay (unique xs)++-- Each element in the duplicates return list is unique+prop_duplicates_unique :: [Int] -> Bool+prop_duplicates_unique [] = True+prop_duplicates_unique xs =+  let xs' = duplicates (one2five xs)+  in length xs' == Set.size (Set.fromList xs')++prop_duplicates_preserves_order :: [Int] -> Bool+prop_duplicates_preserves_order xs = f Set.empty xs (duplicates xs) where+  f _ _ [] = True+  f _ [] _ = False+  f skipped (y:rs) (y':rs')+    -- if the original and duplicated elements match:+    | y == y' =+      -- if the current element was previously skipped+      if Set.member y' skipped+      -- then the duplicates function failed to respect the initial order+      then False+      -- else continue checking on the next elements+      else f skipped rs rs'+    -- otherwise store record the skipped value and continue+    | otherwise = f (Set.insert y skipped) rs (y':rs')
+ test-suite/UnitTypeTests.hs view
@@ -0,0 +1,1348 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}++module UnitTypeTests+  ( unitTypeTests+  , typeOrderTests+  , typeAliasTests+  , jsontype2jsonTests+  , packerTests+  , recordAccessTests+  ) where++import Morloc.Frontend.Namespace+import Morloc.Frontend.Parser+import Morloc.CodeGenerator.Namespace+import Text.RawString.QQ+import Morloc.CodeGenerator.Grammars.Common (jsontype2json)+import qualified Morloc.Data.Doc as Doc+import qualified Morloc.Data.DAG as MDD+import Morloc.Frontend.Infer hiding(typecheck)+import Morloc.Frontend.Desugar (desugar)+import Morloc (typecheck)+import qualified Morloc.Monad as MM+import qualified Morloc.Frontend.PartialOrder as MP++import qualified Data.Text as T+import qualified Data.PartialOrd as DP+import qualified Data.Map as Map+import Test.Tasty+import Test.Tasty.HUnit++main :: Ord k => (n -> a) -> DAG k e n -> a+main f d = case MDD.roots d of+  [] -> error "Missing or circular module"+  [k] -> case Map.lookup k d of+    (Just (m,_)) -> f m+  _ -> error "Cannot handle multiple roots"++mainDecMap :: TypedDag -> [(EVar, Expr)]+mainDecMap d = [(v, e) | (Declaration v e) <- main typedNodeBody d]++-- get the toplevel type of a fully annotated expression+typeof :: [Expr] -> [UnresolvedType]+typeof es = f' . head . reverse $ es+  where+    f' (Signature _ e) = [etype e]+    f' e@(AnnE _ ts) = ts+    f' t = error ("No annotation found for: " <> show t)++run :: T.Text -> IO (Either MorlocError TypedDag)+run code = do+  ((x, _), _) <- MM.runMorlocMonad 0 emptyConfig (typecheck Nothing (Code code))+  return x+  where+    emptyConfig =  Config+        { configHome = Path ""+        , configLibrary = Path ""+        , configTmpDir = Path ""+        , configLangPython3 = Path ""+        , configLangR = Path ""+        , configLangPerl = Path ""+        }++assertTerminalType :: String -> T.Text -> [UnresolvedType] -> TestTree+assertTerminalType msg code t = testCase msg $ do+  result <- run code+  case result of+    -- the order of the list is not important, so sort before comparing+    (Right es') -> assertEqual "" (sort t) (sort (typeof (main typedNodeBody es')))+    (Left err) -> error $+      "The following error was raised: " <> show err <> "\nin:\n" <> show code++-- remove all type annotations and type signatures+unannotate :: [Expr] -> [Expr]+unannotate = mapMaybe unannotate' where+  unannotate' :: Expr -> Maybe Expr+  unannotate' (AnnE e t) = unannotate' e+  unannotate' (ListE xs) = Just $ ListE (unannotate xs)+  unannotate' (TupleE xs) = Just $ TupleE (unannotate xs)+  unannotate' (LamE v e) = LamE <$> pure v <*> unannotate' e+  unannotate' (AppE e1 e2) = AppE <$> unannotate' e1 <*> unannotate' e2+  unannotate' (Declaration v e) = Declaration <$> pure v <*> unannotate' e+  unannotate' (Signature v t) = Nothing+  unannotate' e = Just e++-- assert the full expression with all annotations removed+assertTerminalExpr :: String -> T.Text -> Expr -> TestTree+assertTerminalExpr = assertTerminalExpr' unannotate++-- assert the full expression with complete sub-expression annotations+assertTerminalExprWithAnnot :: String -> T.Text -> Expr -> TestTree+assertTerminalExprWithAnnot = assertTerminalExpr' id ++-- assert the last expression in the main module, process the expression with f+assertTerminalExpr' :: ([Expr] -> [Expr]) -> String -> T.Text -> Expr -> TestTree+assertTerminalExpr' f msg code expr = testCase msg $ do+  result <- run code+  case result of+    -- the order of the list is not important, so sort before comparing+    (Right es') ->+      assertEqual "" expr (head . reverse . sort . f . main typedNodeBody $ es')+    (Left err) -> error $+      "The following error was raised: " <> show err <> "\nin:\n" <> show code++exprEqual :: String -> T.Text -> T.Text -> TestTree+exprEqual msg code1 code2 =+  testCase msg $ do+  result1 <- run code1+  result2 <- run code2+  case (result1, result2) of+    (Right e1, Right e2) -> assertEqual "" e1 e2+    _ -> error $ "Expected equal"++exprTestFull :: String -> T.Text -> T.Text -> TestTree+exprTestFull msg code expCode =+  testCase msg $ do+  result <- run code+  case result of+    (Right e)+      -> assertEqual ""+            (main typedNodeBody e)+            (main parserNodeBody $ readProgram Nothing expCode Map.empty)+    (Left err) -> error (show err)++assertPacker :: String -> T.Text -> Map.Map (TVar, Int) [UnresolvedPacker] -> TestTree+assertPacker msg code expPacker =+  testCase msg $ do+  result <- run code+  case result of+    (Right e)+      -> assertEqual ""+            (main typedNodePackers e)+            expPacker+    (Left err) -> error (show err)++-- assert the exact expressions+exprTestFullDec :: String -> T.Text -> [(EVar, Expr)] -> TestTree+exprTestFullDec msg code expCode =+  testCase msg $ do+  result <- run code+  case result of+    (Right e) -> assertEqual "" (mainDecMap e) expCode+    (Left err) -> error (show err)++exprTestBad :: String -> T.Text -> TestTree+exprTestBad msg code =+  testCase msg $ do+  result <- run code+  case result of+    (Right _) -> assertFailure . T.unpack $ "Expected '" <> code <> "' to fail"+    (Left _) -> return ()++expectError :: String -> MorlocError -> T.Text -> TestTree+expectError msg err code =+  testCase msg $ do+  result <- run code+  case result of+    (Right _) -> assertFailure . T.unpack $ "Expected failure"+    (Left err) -> return ()++testPasses :: String -> T.Text -> TestTree+testPasses msg code =+  testCase msg $ do+  result <- run code+  case result of+    (Right _) -> return ()+    (Left e) ->+      assertFailure $+      "Expected this test to pass, but it failed with the message: " <> show e++testEqual :: (Eq a, Show a) => String -> a -> a -> TestTree+testEqual msg x y =+  testCase msg $ assertEqual "" x y++testNotEqual :: Eq a => String -> a -> a -> TestTree+testNotEqual msg x y =+  testCase msg $ assertEqual "" (x == y) False++testTrue :: String -> Bool -> TestTree+testTrue msg x =+  testCase msg $ assertEqual "" x True++testFalse :: String -> Bool -> TestTree+testFalse msg x =+  testCase msg $ assertEqual "" x False++bool = VarU (TV Nothing "Bool")++num = VarU (TV Nothing "Num")++str = VarU (TV Nothing "Str")++fun [] = error "Cannot infer type of empty list"+fun [t] = t+fun (t:ts) = FunU t (fun ts)++forall [] t = t+forall (s:ss) t = ForallU (TV Nothing s) (forall ss t)++forallc _ [] t = t+forallc lang (s:ss) t = ForallU (TV (Just lang) s) (forallc lang ss t)++var s = VarU (TV Nothing s)+varc l s = VarU (TV (Just l) s)++arrc l s ts = ArrU (TV (Just l) s) ts++arr s ts = ArrU (TV Nothing s) ts++lst t = arr "List" [t]++tuple ts = ArrU v ts+  where+    v = (TV Nothing . T.pack) ("Tuple" ++ show (length ts))++record rs = NamU NamRecord (TV Nothing "Record") [] rs++recordAccessTests =+  testGroup+    "Test record access"+    [ assertTerminalType +      "Access into anonymous record"+      "{a = 5, b = \"asdf\"}@b;"+      [str]+    , assertTerminalType +      "Access record variable"+      [r| record Person = Person {a :: Num, b :: Str};+          bar :: Person;+          bar@b;+      |]+      [str]+    , assertTerminalType +      "Access record-returning expression"+      [r| record Person = Person {a :: Num, b :: Str};+          bar :: Num -> Person;+          (bar 5)@b;+      |]+      [str]+    , assertTerminalType +      "Access into tupled"+      [r| record Person = Person {a :: Num, b :: Str};+          bar :: Num -> Person;+          ((bar 5)@a, (bar 6)@b);+      |]+      [tuple [num, str]]+    , assertTerminalType +      "Access multiple languages"+      [r| record Person = Person {a :: Num, b :: Str};+          record R Person = Person {a :: "numeric", b :: "character"};+          bar :: Person;+          bar R :: Person;+          bar@b;+      |]+      [str, varc RLang "character"]+    ]++packerTests =+  testGroup+    "Test building of packer maps"+    [ assertPacker "no import packer"+        [r| source Cpp from "map.h" ( "mlc_packMap" as packMap+                                    , "mlc_unpackMap" as unpackMap);+            packMap :: pack => ([a],[b]) -> Map a b;+            unpackMap :: unpack => Map a b -> ([a],[b]);+            packMap Cpp :: pack => ([a],[b]) -> "std::map<$1,$2>" a b;+            unpackMap Cpp :: unpack => "std::map<$1,$2>" a b -> ([a],[b]);+            export Map;+        |]+        ( Map.singleton+            (TV (Just CppLang) "std::map<$1,$2>", 2)+            [ UnresolvedPacker {+                unresolvedPackerTerm = (Just (EVar "Map"))+              , unresolvedPackerCType+                = forallc CppLang ["a","b"]+                  ( arrc CppLang "std::tuple<$1,$2>" [ arrc CppLang "std::vector<$1>" [varc CppLang "a"]+                                                     , arrc CppLang "std::vector<$1>" [varc CppLang "b"]])+              , unresolvedPackerForward+                = [Source (Name "mlc_packMap") CppLang (Just (Path "map.h")) (EVar ("packMap"))]+              , unresolvedPackerReverse+                = [Source (Name "mlc_unpackMap") CppLang (Just (Path "map.h")) (EVar ("unpackMap"))]+              }+            ]+        )++    , assertPacker "with importing and aliases"+        [r| module A {+              source Cpp from "map.h" ( "mlc_packMap" as packMap+                                      , "mlc_unpackMap" as unpackMap);+              packMap :: pack => ([a],[b]) -> Map a b;+              unpackMap :: unpack => Map a b -> ([a],[b]);+              packMap Cpp :: pack => ([a],[b]) -> "std::map<$1,$2>" a b;+              unpackMap Cpp :: unpack => "std::map<$1,$2>" a b -> ([a],[b]);+              export Map;+            };+            module Main {+              import A (Map as Hash);+            }+        |]+        ( Map.singleton+            (TV (Just CppLang) "std::map<$1,$2>", 2)+            [ UnresolvedPacker {+                unresolvedPackerTerm = (Just (EVar "Hash"))+              , unresolvedPackerCType+                = forallc CppLang ["a","b"]+                  ( arrc CppLang "std::tuple<$1,$2>" [ arrc CppLang "std::vector<$1>" [varc CppLang "a"]+                                                     , arrc CppLang "std::vector<$1>" [varc CppLang "b"]])+              , unresolvedPackerForward+                = [Source (Name "mlc_packMap") CppLang (Just (Path "map.h")) (EVar ("packMap"))]+              , unresolvedPackerReverse+                = [Source (Name "mlc_unpackMap") CppLang (Just (Path "map.h")) (EVar ("unpackMap"))]+              }+            ]+        )+    ]++jsontype2jsonTests =+  testGroup+    "Test conversion of JsonType's to JSON text"+    [ jsontest "value"+        (VarJ "int")+        [r|"int"|]+    , jsontest "array(value)"+        (ArrJ "list" [VarJ "int"])+        [r|{"list":["int"]}|]+    , jsontest "object(value)"+        (NamJ "Person" [("name", VarJ "Str"), ("age", VarJ "Int")])+        [r|{"Person":{"name":"Str","age":"Int"}}|]+    , jsontest "array(array)"+        (ArrJ "list" [ArrJ "matrix" [VarJ "int"]])+        [r|{"list":[{"matrix":["int"]}]}|]+    , jsontest "array(object)"+        (ArrJ "list" [(NamJ "Person" [("name", VarJ "Str"), ("age", VarJ "Int")])])+        [r|{"list":[{"Person":{"name":"Str","age":"Int"}}]}|]+    , jsontest "object(array)"+        (NamJ "Person" [("name", VarJ "Str"), ("friends", ArrJ "list" [VarJ "Str"])])+        [r|{"Person":{"name":"Str","friends":{"list":["Str"]}}}|]+    , jsontest "object(object)"+        (NamJ "Person"+          [ ("name", VarJ "Str")+          , ("pet", NamJ "Animal" [("name", VarJ "Str"), ("species", VarJ "Str")])+          ])+        [r|{"Person":{"name":"Str","pet":{"Animal":{"name":"Str","species":"Str"}}}}|]+    ]+  where+    jsontest msg t j = testEqual msg (Doc.render $ jsontype2json t) j++typeAliasTests =+  testGroup+    "Test type alias substitutions"+    [ assertTerminalType+        "non-parametric, general type alias"+        (T.unlines+          [ "type Foo = A;"+          , "f :: Foo -> B;"+          , "f"+          ]+        )+        [fun [var "A", var "B"]]+    , assertTerminalType+        "deep type substitution: `[Foo] -> B`"+        (T.unlines+          [ "type Foo = A;"+          , "f :: [Foo] -> B;"+          , "f"+          ]+        )+        [fun [lst (var "A"), var "B"]]+    , assertTerminalType+        "deep type substitution: `[Foo] -> Foo`"+        (T.unlines+          [ "type Foo = A;"+          , "f :: [Foo] -> Foo;"+          , "f"+          ]+        )+        [fun [lst (var "A"), var "A"]]+    , assertTerminalType+        "deep type substitution: `[Foo] -> { a = Foo }`"+        (T.unlines+          [ "type Foo = A;"+          , "f :: [Foo] -> { a :: Foo } ;"+          , "f"+          ]+        )+        [fun [lst (var "A"), record [("a", var "A")]]]+    , assertTerminalType+        "parametric alias, general type alias"+        (T.unlines+          [ "type (Foo a b) = (a,b);"+          , "f :: Foo X Y -> Z;"+          , "f"+          ]+        )+        [fun [tuple [var "X", var "Y"], var "Z"]]+    , assertTerminalType+        "non-parametric alias, concrete type alias"+        (T.unlines+          [ "type C Num = double;"+          , "f C :: Num -> \"int\";"+          , "f"+          ]+        )+        [fun [varc CLang "double", varc CLang "int"]]+    , assertTerminalType+        "language-specific types are be nested"+        (T.unlines+          [ "type R Num = \"numeric\";"+          , "f R :: [Num] -> \"integer\";"+          , "f"+          ]+        )+        [fun [arrc RLang "list" [varc RLang "numeric"], varc RLang "integer"]]+    , assertTerminalType+        "no substitution is across languages"+        (T.unlines+          [ "type Num = \"numeric\";"+          , "f R :: [Num] -> \"integer\";"+          , "f"+          ]+        )+        [fun [arrc RLang "list" [varc RLang "Num"], varc RLang "integer"]]+    , assertTerminalType+        "parametric alias, concrete type alias"+        (T.unlines+          [ "type Cpp (Map a b) = \"std::map<$1,$2>\" a b;"+          , "f Cpp :: Map \"int\" \"double\" -> \"int\";"+          , "f"+          ]+        )+        [ fun [arrc CppLang "std::map<$1,$2>" [varc CppLang "int", varc CppLang "double"]+              , varc CppLang "int"]]+    , assertTerminalType+        "nested in signature"+        (T.unlines+          [ "type Cpp (Map a b) = \"std::map<$1,$2>\" a b;"+          , "f Cpp :: Map \"string\" (Map \"double\" \"int\") -> \"int\";"+          , "f"+          ]+        )+        [ fun [arrc CppLang "std::map<$1,$2>" [varc CppLang "string"+              , arrc CppLang "std::map<$1,$2>" [varc CppLang "double", varc CppLang "int"]]+              , varc CppLang "int"]]+    , assertTerminalType+        "nested types"+        (T.unlines+          [ "type A = B;"+          , "type B = C;"+          , "foo :: A -> B -> C;"+          , "foo"+          ]+        )+        [fun [var "C", fun [var "C", var "C"]]]++    , assertTerminalType+        "existentials are resolved"+        (T.unlines+          [ "type Cpp (A a b) = \"map<$1,$2>\" a b;"+          , "foo Cpp :: A D [B] -> X;"+          , "foo"+          ]+        )+        [fun [ arrc CppLang "map<$1,$2>" [varc CppLang "D", arrc CppLang "std::vector<$1>" [varc CppLang "B"]]+             , varc CppLang "X"]]+    , expectError+        "fail neatly for self-recursive type aliases"+        (SelfRecursiveTypeAlias (TV Nothing "A"))+        (T.unlines+          [ "type A = (A,A);"+          , "foo :: A -> B -> C;"+          , "foo"+          ]+        )+    -- -- TODO: find a way to catch mutually recursive type aliases+    -- , expectError+    --     "fail neatly for mutually-recursive type aliases"+    --     (MutuallyRecursiveTypeAlias [TV Nothing "A", TV Nothing "B"])+    --     (T.unlines+    --       [ "type A = B;"+    --       , "type B = A;"+    --       , "foo :: A -> B -> C;"+    --       , "foo"+    --       ]+    --     )+    , expectError+        "fail on too many type aliases parameters"+        (BadTypeAliasParameters (TV Nothing "A") 0 1)+        (T.unlines+          [ "type A = B;"+          , "foo :: A Int -> C;"+          , "foo"+          ]+        )+    , expectError+        "fail on too few type aliases parameters"+        (BadTypeAliasParameters (TV Nothing "A") 1 0)+        (T.unlines+          [ "type (A a) = (a,a);"+          , "foo :: A -> C;"+          , "foo"+          ]+        )+    -- import tests ---------------------------------------+    , assertTerminalType+        "non-parametric, general type alias, imported"+        (T.unlines+          [ "module M1 { type Foo = A; export Foo;}"+          , "module Main { import M1 (Foo); f :: Foo -> B;  f;}"+          ]+        )+        [fun [var "A", var "B"]]+    , assertTerminalType+        "non-parametric, general type alias, reimported"+        (T.unlines+          [ "module M3 { type Foo = A; export Foo;}"+          , "module M2 { import M3 (Foo); export Foo;}"+          , "module M1 { import M2 (Foo); export Foo;}"+          , "module Main { import M1 (Foo); f :: Foo -> B;  f;}"+          ]+        )+        [fun [var "A", var "B"]]+    , assertTerminalType+        "non-parametric, general type alias, imported aliased"+        (T.unlines+          [ "module M1 { type Foo = A; export Foo;}"+          , "module Main { import M1 (Foo as Bar); f :: Bar -> B;  f;}"+          ]+        )+        [fun [var "A", var "B"]]+    , assertTerminalType+        "non-parametric, general type alias, reimported aliased"+        (T.unlines+          [ "module M3 { type Foo1 = A; export Foo1;}"+          , "module M2 { import M3 (Foo1 as Foo2); export Foo2;}"+          , "module M1 { import M2 (Foo2 as Foo3); export Foo3;}"+          , "module Main { import M1 (Foo3 as Foo4); f :: Foo4 -> B;  f;}"+          ]+        )+        [fun [var "A", var "B"]]+    , assertTerminalType+        "non-parametric, concrete type alias, reimported aliased"+        (T.unlines+          [ "module M3 { type Cpp Foo1 = \"int\"; type R Foo1 = \"integer\"; export Foo1;}"+          , "module M2 { import M3 (Foo1 as Foo2); export Foo2;}"+          , "module M1 { import M2 (Foo2 as Foo3); export Foo3;}"+          , "module Main { import M1 (Foo3 as Foo4); f Cpp :: Foo4 -> \"double\";  f;}"+          ]+        )+        [ fun [varc CppLang "int", varc CppLang "double"] ]+    , assertTerminalType+        "non-parametric, general type alias, duplicate import"+        (T.unlines+          [ "module M2 { type Foo = A; export Foo;}"+          , "module M1 { type Foo = A; export Foo;}"+          , "module Main { import M1 (Foo); import M2 (Foo); f :: Foo -> B;  f;}"+          ]+        )+        [fun [var "A", var "B"]]+    , assertTerminalType+        "parametric alias, general type alias, duplicate import"+        (T.unlines+          [ "module M2 { type (Foo a b) = (a,b); export Foo; }"+          , "module M1 { type (Foo c d) = (c,d); export Foo; }"+          , "module Main { import M1 (Foo); import M2 (Foo); f :: Foo X Y -> Z; f; }"+          ]+        )+        [fun [tuple [var "X", var "Y"], var "Z"]]+    ]++typeOrderTests =+  testGroup+    "Tests of type partial ordering (subtype)"+    [ testTrue+        "a <: Num"+        (MP.isSubtypeOf (forall ["a"] (var "a")) num)+    , testFalse+        "Num !< forall a . a"+        (MP.isSubtypeOf num (forall ["a"] (var "a")))+    , testTrue+        "forall a . (Num, a) <: (Num, Str)"+        (MP.isSubtypeOf (forall ["a"] (tuple [num, var "a"])) (tuple [num, str]))+    , testTrue+        "forall a b . (a, b) <: (Num, Str)"+        (MP.isSubtypeOf (forall ["a", "b"] (tuple [var "a", var "b"])) (tuple [num, str]))+    , testTrue+        "forall a . (Num, a) <: forall b . (Num, b)"+        (MP.isSubtypeOf+          (forall ["a"] (tuple [num, var "a"]))+          (forall ["b"] (tuple [num, var "b"])))+    , testTrue+        "forall a . a <: (Num, Str)"+        (MP.isSubtypeOf (forall ["a"] (var "a")) (tuple [num, str]))+    , testTrue+        "forall a . a <: forall a b . (a, b)"+        (MP.isSubtypeOf (forall ["a"] (var "a")) (forall ["a", "b"] (tuple [var "a", var "b"])))+    -- cannot compare+    , testFalse+        "[Num] !< Num"+        (MP.isSubtypeOf (lst num) num)+    , testFalse+        "Num !< [Num]"+        (MP.isSubtypeOf num (lst num))+    -- partial order of types+    , testTrue+        "forall a . [a] <= [Int]"+        ((forall ["a"] (lst (var "a"))) MP.<= (lst (var "a")))+    , testFalse+        "[Int] !< forall a . [a]"+        ((lst (var "a")) MP.<= (forall ["a"] (lst (var "a"))))+    , testTrue+        "forall a . (Num, a) <= (Num, Bool)"+        ((forall ["a"] (tuple [num, var "a"])) MP.<= (tuple [num, bool]))+    , testFalse+        "(Num, Bool) !<= forall a . (Num, a)"+        ((tuple [num, bool]) MP.<= (forall ["a"] (tuple [num, var "a"])))+    , testTrue+        "forall a b . (a, b) <= forall c . (Num, c)"+        ((forall ["a", "b"] (tuple [var "a", var "b"])) MP.<= (forall ["c"] (tuple [num, var "c"])))+    , testFalse+        "forall c . (Num, c) !<= forall a b . (a, b)"+        ((forall ["c"] (tuple [num, var "c"])) MP.<= (forall ["a", "b"] (tuple [var "a", var "b"])))+    , testTrue+        "forall a . a <= forall a b . (a, b)"+        ((forall ["a"] (var "a")) MP.<= (forall ["a", "b"] (tuple [var "a", var "b"])))+    -- test "mostSpecific"+    , testEqual+        "mostSpecific [Num, Str, forall a . a] = [Num, Str]"+        (MP.mostSpecific [num, str, forall ["a"] (var "a")])+        [num, str]+    -- test "mostGeneral"+    , testEqual+        "mostGeneral [Num, Str, forall a . a] = forall a . a"+        (MP.mostGeneral [num, str, forall ["a"] (var "a")])+        [forall ["a"] (var "a")]+    -- test mostSpecificSubtypes+    , testEqual+        "mostSpecificSubtypes: Num against [forall a . a]"+        (MP.mostSpecificSubtypes num [forall ["a"] (var "a")])+        [forall ["a"] (var "a")]++    -- test mostSpecificSubtypes different languages+    , testEqual+        "mostSpecificSubtypes: different languages"+        (MP.mostSpecificSubtypes (varc RLang "num") [forallc CLang ["a"] (var "a")])+        []++    -- test mostSpecificSubtypes for tuples+    , testEqual+        "mostSpecificSubtypes: tuples"+        (MP.mostSpecificSubtypes+          (tuple [num, num])+          [ forall ["a"] (var "a")+          , forall ["a", "b"] (tuple [var "a", var "b"])+          , forall ["a", "b", "c"] (tuple [var "a", var "b", var "c"])+          ]+        )+        [forall ["a", "b"] (tuple [var "a", var "b"])]++    -- test mostSpecificSubtypes for tuples+    , testEqual+        "mostSpecificSubtypes: with partially generic tuples"+        (MP.mostSpecificSubtypes+          (forall ["a"] (tuple [num, var "a"]))+          [ forall ["a"] (var "a")+          , forall ["a", "b"] (tuple [var "a", var "b"])+          , forall ["a"] (tuple [num, var "a"])+          , forall ["a"] (tuple [num, bool])+          , forall ["a", "b", "c"] (tuple [var "a", var "b", var "c"])+          ]+        )+        [forall ["a"] (tuple [num, var "a"])]+    ]++unitTypeTests =+  testGroup+    "Typechecker unit tests"+    -- comments+    [ assertTerminalType "block comments (1)" "{- -} 42" [num]+    , assertTerminalType "block comments (2)" " {--} 42{-   foo -} " [num]+    , assertTerminalType "line comments (3)" "-- foo\n 42" [num]+    -- semicolons+    , assertTerminalType "semicolons are allowed at the end" "42;" [num]+    -- primitives+    , assertTerminalType "primitive integer" "42" [num]+    , assertTerminalType "primitive big integer" "123456789123456789123456789" [num]+    , assertTerminalType "primitive decimal" "4.2" [num]+    , assertTerminalType "primitive negative number" "-4.2" [num]+    , assertTerminalType "primitive positive number (with sign)" "+4.2" [num]+    , assertTerminalType "primitive scientific large exponent" "4.2e3000" [num]+    , assertTerminalType+        "primitive scientific irregular"+        "123456789123456789123456789e-3000"+       [num]+    , assertTerminalType+        "primitive big real"+        "123456789123456789123456789.123456789123456789123456789"+       [num]+    , assertTerminalType "primitive boolean" "True" [bool]+    , assertTerminalType "primitive string" "\"this is a string literal\"" [str]+    , assertTerminalType "primitive integer annotation" "42 :: Num" [num]+    , assertTerminalType "primitive boolean annotation" "True :: Bool" [bool]+    , assertTerminalType "primitive double annotation" "4.2 :: Num" [num]+    , assertTerminalType+        "primitive string annotation"+        "\"this is a string literal\" :: Str"+        [str]+    , assertTerminalType "primitive declaration" "x = True; 4.2" [num]+    -- declarations+    , assertTerminalType+        "identity function declaration and application"+        "f x = x; f 42"+       [num]+    , assertTerminalType+        "snd function declaration and application"+        "snd x y = y; snd True 42"+        [num]++    , assertTerminalType+        "explicit annotation within an application"+        "f :: Num -> Num; f (42 :: Num)"+        [num]++    -- lambdas+    , assertTerminalExpr+        "functions return lambda expressions"+        "\\x -> 42"+        (LamE (EVar "x") (NumE 42.0))+    , assertTerminalType+        "functions can be passed"+        "g f = f 42; g"+        [forall ["a"] (fun [(fun [num, var "a"]), var "a"])]+    , assertTerminalType+        "function with parameterized types"+        "f :: A B -> C; f"+        [fun [arr "A" [var "B"], var "C"]]+    , assertTerminalType "fully applied lambda (1)" "(\\x y -> x) 1 True" [num]+    , assertTerminalType "fully applied lambda (2)" "(\\x -> True) 42" [bool]+    , assertTerminalType "fully applied lambda (3)" "(\\x -> (\\y -> True) x) 42" [bool]+    , assertTerminalType "fully applied lambda (4)" "(\\x -> (\\y -> x) True) 42" [num]+    , assertTerminalType+        "unapplied lambda, polymorphic (1)"+        "(\\x -> True)"+        [forall ["a"] (fun [var "a", bool])]+    , assertTerminalType+        "unapplied lambda, polymorphic (2)"+        "(\\x y -> x) :: a -> b -> a"+        [forall ["a", "b"] (fun [var "a", var "b", var "a"])]+    , assertTerminalType+        "annotated, fully applied lambda"+        "((\\x -> x) :: a -> a) True"+        [bool]+    , assertTerminalType+        "annotated, partially applied lambda"+        "((\\x y -> x) :: a -> b -> a) True"+        [forall ["a"] (fun [var "a", bool])]+    , assertTerminalType+        "recursive functions are A-OK"+        "\\f -> f 5"+        [forall ["a"] (fun [fun [num, var "a"], var "a"])]++    -- applications+    , assertTerminalType+        "primitive variable in application"+        "x = True; (\\y -> y) x"+        [bool]+    , assertTerminalType+        "function variable in application"+        "f = (\\x y -> x); f 42"+        [forall ["a"] (fun [var "a", num])]+    , assertTerminalType+        "partially applied function variable in application"+        "f = (\\x y -> x); x = f 42; x"+        [forall ["a"] (fun [var "a", num])]+    , exprTestBad+        "applications with too many arguments fail"+        "f :: a; f Bool 12"+    , exprTestBad+        "applications with mismatched types fail (1)"+        "abs :: Num -> Num; abs True"+    , exprTestBad+        "applications with mismatched types fail (2)"+        "f = 14; g = \\x h -> h x; (g True) f"+    , expectError+        "applications of non-functions should fail (1)"+        NonFunctionDerive+        "f = 5; g = \\x -> f x; g 12"+    , expectError+        "applications of non-functions should fail (2)"+        NonFunctionDerive+        "f = 5; g = \\h -> h 5; g f"++    -- evaluation within containers+    , expectError+        "arguments to a function are monotypes"+        (SubtypeError (unresolvedType2type num) (unresolvedType2type bool))+        "f :: a -> a; g = \\h -> (h 42, h True); g f"+    , assertTerminalType+        "polymorphism under lambdas (203f8c) (1)"+        "f :: a -> a; g = \\h -> (h 42, h 1234); g f"+        [tuple [num, num]]+    , assertTerminalType+        "polymorphism under lambdas (203f8c) (2)"+        "f :: a -> a; g = \\h -> [h 42, h 1234]; g f"+        [lst num]++    -- binding+    , assertTerminalType+        "annotated variables without definition are legal"+        "x :: Num"+        [num]+    , assertTerminalType+        "unannotated variables with definition are legal"+        "x = 42; x"+        [num]+    , exprTestBad+        "unannotated variables without definitions are illegal ('\\x -> y')"+        "\\x -> y"++    -- parameterized types+    , assertTerminalType+        "parameterized type (n=1)"+        "xs :: Foo A"+        [arr "Foo" [var "A"]]+    , assertTerminalType+        "parameterized type (n=2)"+        "xs :: Foo A B"+        [arr "Foo" [var "A", var "B"]]+    , assertTerminalType+        "nested parameterized type"+        "xs :: Foo (Bar A) [B]"+        [arr "Foo" [arr "Bar" [var "A"], arr "List" [var "B"]]]+    , assertTerminalType+        "language inference in lists #1"+        (T.unlines+          [ "bar Cpp :: \"float\" -> \"std::vector<$1>\" \"float\";"+          , "bar x = [x];"+          , "bar 5;"+          ])+        [arrc CppLang "std::vector<$1>" [varc CppLang "float"], lst (var "Num")]+    , assertTerminalType+        "language inference in lists #2"+        (T.unlines+          [ "mul :: Num -> Num -> Num;"+          , "mul Cpp :: \"int\" -> \"int\" -> \"int\";"+          , "foo = mul 2;"+          , "bar Cpp :: \"int\" -> \"std::vector<$1>\" \"int\";"+          , "bar x = [foo x, 42];"+          , "bar 5"+          ])+        [lst (var "Num"), arrc CppLang "std::vector<$1>" [varc CppLang "int"]]++    -- type signatures and higher-order functions+    , assertTerminalType+        "type signature: identity function"+        "f :: a -> a; f 42"+        [num]+    , assertTerminalType+        "type signature: apply function with primitives"+        "apply :: (Num -> Bool) -> Num -> Bool; f :: Num -> Bool; apply f 42"+        [bool]+    , assertTerminalType+        "type signature: generic apply function"+        "apply :: (a->b) -> a -> b; f :: Num -> Bool; apply f 42"+        [bool]+    , assertTerminalType+        "type signature: map"+        "map :: (a->b) -> [a] -> [b]; f :: Num -> Bool; map f [5,2]"+        [lst bool]+    , assertTerminalType+        "type signature: sqrt with realizations"+        "sqrt :: Num -> Num; sqrt R :: \"numeric\" -> \"numeric\"; sqrt"+        [ fun [num, num]+        , fun [varc RLang "numeric", varc RLang "numeric"]]++    -- shadowing+    , assertTerminalType+        "name shadowing in lambda expressions"+        "f x = (14,x); g x f = f x; g True f"+        [tuple [num, bool]]+    , assertTerminalType+        "function passing without shadowing"+        "f x = (14,x); g foo = foo True; g f"+        [tuple [num, bool]]+    , assertTerminalType+        "shadowed qualified type variables (7ffd52a)"+        "f :: a -> a; g :: a -> Num; g f"+        [num]+    , assertTerminalType+        "non-shadowed qualified type variables (7ffd52a)"+        "f :: a -> a; g :: b -> Num; g f"+        [num]++    -- lists+    , assertTerminalType "list of primitives" "[1,2,3]" [lst num]+    , assertTerminalType+        "list containing an applied variable"+        "f :: a -> a; [53, f 34]"+        [lst num]+    , assertTerminalType "empty list" "[]" [forall ["a"] (lst (var "a"))]+    , assertTerminalType+        "list in function signature and application"+        "f :: [Num] -> Bool; f [1]"+        [bool]+    , assertTerminalType+        "list in generic function signature and application"+        "f :: [a] -> Bool; f [1]"+        [bool]+    , exprTestBad "failure on heterogenous list" "[1,2,True]"++    -- tuples+    , assertTerminalType+        "tuple of primitives"+        "(4.2, True)"+        [tuple [num, bool]]+    , assertTerminalType+        "tuple containing an applied variable"+        "f :: a -> a; (f 53, True)"+        [tuple [num, bool]]+    , assertTerminalType+        "check 2-tuples type signature"+        "f :: (Num, Str)"+        [tuple [num, str]]+    , assertTerminalType "1-tuples are just for grouping" "f :: (Num)" [num]++    --- FIXME - distinguish between Unit an Null+    -- unit type+    , assertTerminalType+        "unit as input"+        "f :: () -> Bool"+        [fun [VarU (TV Nothing "Unit"), bool]]++    , assertTerminalType+        "unit as output"+        "f :: Bool -> ()"+        [fun [bool, VarU (TV Nothing "Unit")]]++    -- -- TODO: reconsider what an empty tuple is+    -- -- I am inclined to cast it as the unit type+    -- , assertTerminalType "empty tuples are of unit type" "f :: ()" UniT++    -- records+    , assertTerminalType+        "primitive record statement"+        "{x=42, y=\"yolo\"}"+        [record [("x", num), ("y", str)]]+    , assertTerminalType+        "primitive record signature"+        "Foo :: {x :: Num, y :: Str}"+        [record [("x", num), ("y", str)]]+    , assertTerminalType+        "primitive record declaration"+        "foo = {x = 42, y = \"yolo\"}; foo"+        [record [("x", num), ("y", str)]]+    , assertTerminalType+        "nested records"+        "Foo :: {x :: Num, y :: {bob :: Num, tod :: Str}}"+        [record [("x", num), ("y", record [("bob", num), ("tod", str)])]]+    , assertTerminalType+        "records with variables"+        "a=42; b={x=a, y=\"yolo\"}; f=\\b->b; f b"+        [record [("x", num), ("y", str)]]+    , assertTerminalType+        "records with bound variables"+        "foo a = {x=a, y=\"yolo\"}; foo 42;"+        [record [("x", num), ("y", str)]]++    -- extra space+    , assertTerminalType "leading space" " 42" [num]+    , assertTerminalType "trailing space" "42 " [num]++    -- adding signatures to declarations+    , assertTerminalType+        "declaration with a signature (1)"+        "f :: a -> a; f x = x; f 42"+        [num]+    , assertTerminalType+        "declaration with a signature (2)"+        "f :: Num -> Bool; f x = True; f 42"+        [bool]+    , assertTerminalType+        "declaration with a signature (3)"+        "f :: Num -> Bool; f x = True; f"+        [fun [num, bool]]+    , expectError+        "primitive type mismatch should raise error"+        (SubtypeError (unresolvedType2type num) (unresolvedType2type bool))+        "f :: Num -> Bool; f x = 9999"++    -- tags+    , exprEqual+        "variable tags"+        "F :: Int"+        "F :: foo:Int"+    , exprEqual+        "list tags"+        "F :: [Int]"+        "F :: foo:[Int]"+    , exprEqual+        "tags on parenthesized types"+        "F :: Int"+        "F :: f:(Int)"+    , exprEqual+        "record tags"+        "F :: {x::Int, y::Str}"+        "F :: foo:{x::Int, y::Str}"+    , exprEqual+        "nested tags (tuple)"+        "F :: (Int, Str)"+        "F :: foo:(i:Int, s:Str)"+    , exprEqual "nested tags (list)" "F :: [Int]" "F :: xs:[x:Int]"+    , exprEqual+        "nested tags (record)"+        "F :: {x::Int, y::Str}"+        "F :: foo:{x::(i:Int), y::Str}"++    -- properties+    , assertTerminalType "property syntax (1)" "f :: Foo => Num; f" [num]+    , assertTerminalType "property syntax (2)" "f :: Foo bar => Num; f" [num]+    , assertTerminalType "property syntax (3)" "f :: Foo a, Bar b => Num; f" [num]+    , assertTerminalType "property syntax (4)" "f :: (Foo a) => Num; f" [num]+    , assertTerminalType "property syntax (5)" "f :: (Foo a, Bar b) => Num; f" [num]+    -- constraints+    , assertTerminalType "constraint syntax (1)" "f :: Num where {ladida}; f" [num]+    , assertTerminalType+        "constraint syntax (1)"+        "f :: Num where { ladida ; foo }; f"+        [num]++    -- tests modules+    , assertTerminalType "basic Main module" "module Main {[1,2,3]}" [lst num]+    , (flip $ assertTerminalType "import/export") [lst num] $+      T.unlines+        [ "module Foo {export x; x = 42};"+        , "module Bar {export f; f :: a -> [a]};"+        , "module Main {import Foo (x); import Bar (f); f x}"+        ]+    , (flip $ assertTerminalType "import/export") [varc RLang "numeric"] $+      T.unlines+        [ "module Foo {export x; x = [1,2,3]};"+        , "module Bar {export f; f R :: [\"numeric\"] -> \"numeric\"};"+        , "module Main {import Foo (x); import Bar (f); f x}"+        ]++    , (flip $ assertTerminalType "multiple imports") [varc Python3Lang "float", varc RLang "numeric"] $+      T.unlines+        [ "module Foo {export f; f py :: [\"float\"] -> \"float\"};"+        , "module Bar {export f; f R :: [\"numeric\"] -> \"numeric\"};"+        , "module Main {import Foo (f); import Bar (f); f [1,2,3]}"+        ]++    , assertTerminalType+        "Allow gross overuse of semicolons"+        ";;;;;module foo{;42;  ;};"+        [num]+    , expectError+        "fail on import of non-existing variable"+        (BadImport (MVar "Foo") (EVar "x")) $+        T.unlines+          ["module Foo {export y; y = 42};", "module Main {import Foo (x); x}"]+    , expectError+        "fail on cyclic dependency"+        CyclicDependency $+        T.unlines+          [ "module Foo {import Bar (y); export x; x = 42};"+          , "module Bar {import Foo (x); export y; y = 88}"+          ]+    , expectError "fail on self import"+        (SelfImport (MVar "Foo")) $+        T.unlines ["module Foo {import Foo (x); x = 42}"]+    , expectError+        "fail on import of non-exported variable"+        (BadImport (MVar "Foo") (EVar "x")) $+        T.unlines ["module Foo {x = 42};", "module Main {import Foo (x); x}"]++    -- test realization integration+    , assertTerminalType+        "a realization can be defined following general type signature"+        (T.unlines ["f :: Num -> Num;", "f r :: \"integer\" -> \"integer\";", "f 44"])+        [num, varc RLang "integer"]+    , assertTerminalType+        "realizations can map one general type to multiple specific ones"+        (T.unlines ["f :: Num -> Num;", "f r :: \"integer\" -> \"numeric\";", "f 44"])+        [num, varc RLang "numeric"]+    , assertTerminalType+        "realizations can map multiple general type to one specific one"+        (T.unlines ["f :: Num -> Nat;", "f r :: \"integer\" -> \"integer\";", "f 44"])+        [var "Nat", varc RLang "integer"]+    , assertTerminalType+        "multiple realizations for different languages can be defined"+        (T.unlines+          [ "f :: Num -> Num;"+          , "f r :: \"integer\" -> \"integer\";"+          , "f c :: \"int\" -> \"int\";"+          , "f 44"+          ])+        [num, varc CLang "int", varc RLang "integer"]+    , assertTerminalType+        "realizations with parameterized variables"+        (T.unlines+          [ "f :: [Num] -> Num;"+          , "f r :: \"$1\" \"integer\" -> \"integer\";"+          , "f cpp :: \"std::vector<$1>\" \"int\" -> \"int\";"+          , "f [44]"+          ])+        [num, varc CppLang "int", varc RLang "integer"]+    , assertTerminalType+        "realizations can use quoted variables"+        (T.unlines+          [ "sum :: [Num] -> Num;"+          , "sum c :: \"$1*\" \"double\" -> \"double\";"+          , "sum cpp :: \"std::vector<$1>\" \"double\" -> \"double\";"+          , "sum [1,2]"+          ])+        [num, varc CLang "double", varc CppLang "double"]+    , assertTerminalType+        "the order of general signatures and realizations does not matter (1)"+        (T.unlines+          [ "f r :: \"integer\" -> \"integer\";"+          , "f :: Num -> Num;"+          , "f c :: \"int\" -> \"int\";"+          , "f 44"+          ])+        [num, varc CLang "int", varc RLang "integer"]+    , assertTerminalType+        "the order of general signatures and realizations does not matter (2)"+        (T.unlines+          [ "f r :: \"integer\" -> \"integer\";"+          , "f c :: \"int\" -> \"int\";"+          , "f :: Num -> Num;"+          , "f 44"+          ])+        [num, varc CLang "int", varc RLang "integer"]+    , assertTerminalType+        "multiple realizations for a single language cannot be defined"+        (T.unlines+          [ "f r :: A -> B;"+          , "f r :: C -> D;"+          , "f 1"+          ])+        [varc RLang "B", varc RLang "D"]+    , assertTerminalType+        "general signatures are optional"+        (T.unlines ["f r :: \"integer\" -> \"integer\";", "f 44"])+        [varc RLang "integer"]+    , assertTerminalType +        "compositions can have concrete realizations"+        "f r :: \"integer\" -> \"integer\"; f x = 42; f 44"+        [varc RLang "integer", num]+    , expectError+       "arguments number in realizations must equal the general case (1)"+        BadRealization $+        T.unlines+          ["f :: Num -> String -> Num;", "f r :: \"integer\" -> \"integer\";", "f 44"]+    , expectError+         "arguments number in realizations must equal the general case (2)"+         BadRealization $+         T.unlines+           ["f   :: Num -> Num;", "f r :: \"integer\" -> \"integer\" -> string;", "f 44"]+    , assertTerminalType+        "multiple realizations for one type"+        (T.unlines+          [ "foo :: Num -> Num;"+          , "foo r :: A -> B;"+          , "foo c :: C -> D;"+          , "bar c :: C -> C;"+          , "foo (bar 1);"+          ])+        [num, varc CLang "D", varc RLang "B"]+    , assertTerminalType+      "concrete snd: simple test with containers"+      (T.unlines+        [ "snd :: (a, b) -> b;"+        , "snd r :: list a b -> b;"+        , "snd (1, True);"+        ])+        [bool, varc RLang "logical"]+    , assertTerminalType+      "concrete map: single map, single f"+      (T.unlines+        [ "map cpp :: (a -> b) -> \"std::vector<$1>\" a -> \"std::vector<$1>\" b;"+        , "f cpp :: \"double\" -> \"double\";"+        , "map f [1,2]"+        ])+      [arrc CppLang "std::vector<$1>" [varc CppLang "double"]]+    , assertTerminalType+      "concrete map: multiple maps, single f"+      (T.unlines+        [ "map :: (a -> b) -> [a] -> [b];"+        , "map c :: (a -> b) -> \"std::vector<$1>\" a -> \"std::vector<$1>\" b;"+        , "map r :: (a -> b) -> vector a -> vector b;"+        , "f c :: \"double\" -> \"double\";"+        , "map f [1,2]"+        ])+      [ forall ["a"] (arr "List" [var "a"])+      , forallc RLang ["a"] (arrc RLang "vector" [varc RLang "a"])+      , arrc CLang "std::vector<$1>" [varc CLang "double"]+      ]+    , assertTerminalType+      "infer type signature from concrete functions"+      (T.unlines+        [ "sqrt :: Num -> Num;" +        , "sqrt R :: \"numeric\" -> \"numeric\";"+        , "foo x = sqrt x;"+        , "sqrt 42"+        ])+      [num, varc RLang "numeric"]+    , assertTerminalType+      "calls cross-language"+      (T.unlines+        [ "f R :: A -> B;"+        , "g Cpp :: B -> C;"+        , "g (f 4);"+        ])+      [varc CppLang "C"]+    , assertTerminalType+      "language branching"+      (T.unlines+        [ "id R :: a -> a;"+        , "sqrt C :: \"double\" -> \"double\";"+        , "sqrt R :: \"numeric\" -> \"numeric\";"+        , "id (sqrt 4);"+        ])+      [varc RLang "numeric"]+    , assertTerminalType+      "obligate foreign call"+      (T.unlines+        [ "foo r :: (a -> a) -> a -> a;"+        , "f c :: \"int\" -> \"int\";"+        , "foo f 42"+        ])+      [varc RLang "numeric"]+    , assertTerminalType+      "obligate foreign call - tupled"+      (T.unlines+        [ "foo r :: (a -> a) -> a -> (a,a);"+        , "f c :: \"int\" -> \"int\";"+        , "foo f 42"+        ])+      [arrc RLang "tuple" [varc RLang "numeric", varc RLang "numeric"]]+    , assertTerminalType+      "declarations represent all realizations"+      (T.unlines+        [ "sqrt :: Num -> Num;"+        , "sqrt r :: \"integer\" -> \"numeric\";"+        , "foo x = sqrt x;"+        , "foo"+        ])+      [fun [num, num], fun [varc RLang "integer", varc RLang "numeric"]]++    , assertTerminalType+      "all internal concrete and general types are right"+      (T.unlines+        [ "snd :: a -> b -> b;"+        , "snd Cpp :: a -> b -> b;"+        , "sqrt :: Num -> Num;"+        , "sqrt Cpp :: \"double\" -> \"double\";"+        , "foo x = snd x (sqrt x);"+        , "foo"+        ])+      [fun [num, num], fun [varc CppLang "double", varc CppLang "double"]]++    , assertTerminalType+      "declaration general type signatures are respected"+      (T.unlines+        [ "sqrt cpp :: \"double\" -> \"double\";"+        , "sqrt :: a -> a;"+        , "foo :: Num -> Num;"+        , "foo x = sqrt x;"+        , "foo"+        ])+      [fun [num, num], fun [varc CppLang "double", varc CppLang "double"]]++    , assertTerminalExprWithAnnot+      "all internal concrete and general types are right"+      (T.unlines+        [ "snd :: a -> b -> b;"+        , "snd Cpp :: a -> b -> b;"+        , "sqrt :: Num -> Num;"+        , "sqrt Cpp :: \"double\" -> \"double\";"+        , "foo x = snd x (sqrt x);"+        ])+      (Declaration (EVar "foo")+        (AnnE (LamE (EVar "x")+          (AnnE (AppE+            (AnnE (AppE+              (AnnE (VarE (EVar "snd"))+                [ fun [num, num, num]+                , fun [varc CppLang "double", varc CppLang "double", varc CppLang "double"]])+              (AnnE (VarE (EVar "x"))+                [num,varc CppLang "double"]))+              [ FunU num num+              , FunU (varc CppLang "double") (varc CppLang "double")])+            (AnnE (AppE+              (AnnE (VarE (EVar "sqrt"))+                [ FunU num num+                , FunU (varc CppLang "double") (varc CppLang "double")])+              (AnnE (VarE (EVar "x"))+                [ num+                , varc CppLang "double"]))+              [num,varc CppLang "double"]))+            [num,varc CppLang "double"]))+          [ FunU num num+          , FunU (varc CppLang "double") (varc CppLang "double")]))++    -- internal+    , exprTestFull+        "every sub-expression should be annotated in output"+        "f :: a -> Bool; f 42"+        "f :: a -> Bool; (((f :: Num -> Bool) (42 :: Num)) :: Bool)"++    -- -- TODO: resurrect to test github issue #7+    -- , exprTestFullDec+    --     "concrete types should be inferred for declared variables"+    --     (T.unlines+    --       [ "id :: Num -> Num;"+    --       , "id C :: \"int\" -> \"int\";"+    --       , "id x = x;"+    --       , "y = 40;"+    --       , "foo = id y;"+    --       ]+    --     )+    --     [ (EVar "foo",+    --       AnnE (AppE+    --           (AnnE (VarE (EVar "id")) [fun [num, num], fun [varc CLang "int", varc CLang "int"]])+    --           (AnnE (VarE (EVar "y")) [num, varc CLang "int"])+    --                                      -- ^ The purpose of this test is to assert that the above+    --                                      -- type is defined. As of commit 'c31660a0', `y` was assigned+    --                                      -- only the general type Num.+    --         )+    --       [num, varc CLang "int"]+    --       )+    --     , (EVar "id",+    --       AnnE (LamE (EVar "x")+    --           (AnnE (VarE (EVar "x"))+    --             [num, varc CLang "int"]))+    --         [fun [num, num], fun [varc CLang "int", varc CLang "int"]])+    --     , (EVar "y", AnnE (NumE 40.0) [num])+    --     ]++    -- default list evaluation of arguments+    , assertTerminalType+        "can infer multiple argument types"+        (T.unlines+          [ "ith :: [Num] -> Num -> Num;"+          , "ith R :: [\"numeric\"] -> \"numeric\" -> \"numeric\";"+          , "snd x = ith x 2;"+          , "snd [1,2,3];"+          ])+        [num, varc RLang "numeric"]+    ]