[
  {
    "path": ".gitignore",
    "content": "/target\n**/*.rs.bk\nCargo.lock\nbin/\npkg/\nwasm-pack.log\n\nlocal.html\nsrc/grammar.rs\n\ntest*.ml\nperf.*\n*.svg\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"cubiml-demo\"\nversion = \"0.0.0\"\nauthors = [\"Robert Grosse <n210241048576@gmail.com>\"]\nlicense = \"Apache-2.0/MIT\"\nedition = \"2018\"\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[features]\ndefault = [\"console_error_panic_hook\"]\n\n[dependencies]\nlalrpop-util = { version = \"0.20.2\", features = [\"lexer\"] }\nwasm-bindgen = \"0.2.63\"\n\n# The `console_error_panic_hook` crate provides better debugging of panics by\n# logging them with `console.error`. This is great for development, but requires\n# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for\n# code size when deploying.\nconsole_error_panic_hook = { version = \"0.1.6\", optional = true }\n\n[dev-dependencies]\nwasm-bindgen-test = \"0.3.13\"\n\n[profile.release]\n# Tell `rustc` to optimize for small code size.\nopt-level = \"s\"\n"
  },
  {
    "path": "LICENSE_APACHE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "LICENSE_MIT",
    "content": "Copyright (c) 2020 Robert Grosse <n210241048576@gmail.com>\n\nPermission is hereby granted, free of charge, to any\nperson obtaining a copy of this software and associated\ndocumentation files (the \"Software\"), to deal in the\nSoftware without restriction, including without\nlimitation the rights to use, copy, modify, merge,\npublish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software\nis furnished to do so, subject to the following\nconditions:\n\nThe above copyright notice and this permission notice\nshall be included in all copies or substantial portions\nof the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF\nANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED\nTO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A\nPARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT\nSHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR\nIN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "\nCubiml is a simple ML-like programming language with subtyping and full type inference. You can try it out online in your browser [here](https://storyyeller.github.io/cubiml-demo/demo.html). Cubiml uses _cubic biunification_, a faster and simpler type inference algorithm based on [Algebraic Subtyping](https://www.cs.tufts.edu/~nr/cs257/archive/stephen-dolan/thesis.pdf). Cubiml is not intended to be used in its own right, but rather to serve as a tutorial for implementing cubic biunification, and therefore has a deliberately minimal feature set. \n\n## Usage\n\nYou can try out cubiml online in your browser at https://storyyeller.github.io/cubiml-demo/demo.html. \n\n## A quick tour of cubiml\n\nCubiml syntax is mostly a subset of Ocaml syntax.\n\n#### Conditionals\n\nIn cubiml `if` is an expression, not a statement. The general form is `if <expr> then <expr> else <expr>` where the `<expr>`s are sub-expressions. The first expression is evaluated, and depending on whether it is true or false, one of the other two subexpressions is evaluated, and the result of the `if` expression is that expression's value. For example, evaluating `if false then \"Hello\" else \"World\"` would result in `\"World\"`. You can think of this as similar to the ternary operator (`a ? b : c`) present in some programming languages.\n\n#### Records and fields\n\nRecords are a grouping of zero or more named values similar to \"objects\" or \"structs\" in other programming languages and are defined via `{name1=val1; name2=val2; ...}`. You can access the value of a field using the usual `.name` syntax. For example `{a=true; b=\"Hello\"; c={}}.b` would evaluate to `\"Hello\"`.\n\nThere is a special shorthand syntax for fields with the same name as their value - `{a; b; c=4}` is equivalent to `{a=a; b=b; c=4}`.\n\nUnlike in Ocaml, records are structurally typed. In fact, Cubiml has *only* structural types, with no named types.\n\n#### Functions\n\nIn cubiml, all functions are required to take exactly one argument for simplicity. They are defined by `fun <arg> -> <expr>`. For example, a function which returns its argument unchanged could be written as `fun x -> x`. Functions are called by simply suffixing an argument, i.e. writing `a b` where `a` is the function to be called and `b` is the argument. For example \n\n    (fun b -> if b then \"Hello\" else \"World\") true\n\nwould evaluate to `\"Hello\"`, while \n\n    (fun x -> x.foo) {bar=false; foo=\"Bob\"}\n\nwould evaluate to `\"Bob\"`. \n\n\nYou can work around the one-argument limitation and simulate multiple function arguments by passing in a record. For example, instead of \n\n```js\nfunction sum(a, b) {\n    return a + b;\n}\n\nsum(7, 8)\n```\n\nin cubiml, you can do\n\n```ocaml\nlet sum = fun args -> args.a + args.b;\n\nsum {a=7; b=8}\n```\n\nIn fact, you can simplify this further using destructuring patterns:\n\n```ocaml\nlet sum = fun {a; b} -> a + b;\n\nsum {a=7; b=8}\n```\n\nUnlike in Ocaml, `a b c` is parsed as `a (b c)` rather than `(a b) c`. Since all functions and variants have exactly one argument in Cubiml, this behavior is much more convenient than the Ocaml behavior.\n\n#### Let bindings\n\nNo programming language would be complete without the ability to bind values to a variable name for later reference. In cubiml, this is done slightly differently than you may be used to. The general format is `let <name> = <expr1> in <expr2>`, where the variable `<name>` is visible in the body of `<expr2>`. The entire thing is an expression which evaluates to whatever `<expr2>` evaluates to.\n\nFor example,\n```ocaml\nlet x = 7 * 8 in {foo=x; bar=x}     \n```\nwould evaluate to `{foo=56; bar=56}`.\n\nLet bindings can of course be nested like any other expression. For example, \n```ocaml\nlet x = 3 + 4 in\n    let y = x * 2 in\n        {x=x; y=y}\n```\nwould evaluate to `{x=7; y=14}`.\n\n\nThis provides an equivalent to traditional imperative style code like the following that you might see in other languages\n\n```js\nlet x = 3 + 4;\nlet y = x * 2;\nreturn {x=x, y=y};\n```\n\nUnlike in OCaml, you can *also* use semicolons rather than \"in\". `let x = 4; x + x` and `let x = 4 in x + x` are exactly equivalent except that \"in\" has higher precendence than \";\". In most cases, you will need to wrap your code in parenthesis or `begin`/`end` when using semicolons.\n\nTherefore, the previous example could also be written using semicolons like this:\n\n```ocaml\nbegin\n    let x = 3 + 4;\n    let y = x * 2;\n    {x=x; y=y}\nend\n```\n\n\nNote that the above format produces an expression which can be used in any context where an expression is expected. Cubiml follows the ML philosophy that (nearly) everything is an expression, even conditionals, function definitions, variable bindings, and other things that are statements in some languages.\n\nHowever, this style is inconvenient when  interactively entering code into a REPL ([Read Evaluate Print Loop](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)), because it requires you to input the entire program at once. To handle this, cubiml allows an alternate non-expression format at the top level of your code. At the top level, you can omit the `in <expr>` part, in which case the let statement produces a global binding which is visible to all subsequent code. For example, here is a possible session of code entered into the cubiml REPL. \n\n```\n>> let x = {}\n\n{}\n\n>> let y = {x=x; other=false}\n\n{x={}; other=false}\n\n>> let z = {other=y.other; foo={y=y; x=x}}\n\n{other=false; foo={y={x={}; other=false}; x=...}}\n```\n\nYou can also separate multiple top level definitions with semicolons if you are entering multiple items at once.\n\n```\n>> let a = z.foo.y; let b = true\n\ntrue\n\n>> if b then y else x\n\n{x={}; other=false}\n```\n\nYou can also use destructuring patterns in let assignments. For example, `let {a; b=c} = {a=3; b=4};` results in two variables, `a` set to 3 and `c` set to 4.\n\n\n#### Recursive let bindings\n\nSometimes, one wishes to have functions that call themselves recursively. Unfortunately, this is impossible with the above constructs since plain let-expressions can only refer to variables that were already defined. \n\nIn order to support recursion, cubiml offers _recursive let expressions_ which are defined via `let rec` and allow the definition of the variable to refer to itself. For example, you could define a recursive fibonacci function as follows:\n\n```ocaml\nlet rec fib = fun x ->\n    if x <= 1 then \n        1\n    else\n        fib(x - 1) + fib(x - 2)\n```\n\nIn order to avoid code referring to variables that don't exist yet, the right hand side of `let rec` variable definitions is restricted to be a function definition.\n\n\n#### Mutual recursion\n\nThe above syntax works for a single function that refers to itself, but in some cases, you may want to have multiple functions that each refer to each other. Unlike in the case with `let`, simply nesting `let rec`s won't work. Therefore, `let rec` allows _multiple_ variable bindings, separated by `and`. For example, you can define mutually recursive `even` and `odd` functions as follows:\n\n```ocaml\nlet rec even = fun x -> if x == 0 then true else odd(x - 1)\n    and odd = fun x -> if x == 0 then false else even(x - 1)\n```\n\n#### Case types and matching\n\nSometimes you need to make different decisions based on runtime data in a type safe manner. Cubiml supports this via _case types_, also known as _sum types_ or _enums_. Basically, the way they work is that you can wrap a value with a tag, and then later match against it. The match expression has branches that execute different code depending on the runtime value of the tag. Crucially, each match branch has access to the static type of the original wrapped value for that specific tag. You can think of it like a simpler, statically checked version of Java's visitor pattern or a giant switch statement on an union in C.\n\nTo wrap a value, prefix it with a grave (\\`) character and an uppercase Tag. E.g. `` `Foo {hello=\"Hello\"}``\n\nYou can later match on it like follows\n\n```ocaml\nlet calculate_area = fun shape ->\n    match shape with\n        | `Circle v -> v.rad *. v.rad *. 3.1415926\n        | `Rectangle v -> v.length *. v.height;\n\ncalculate_area `Circle {rad=6.7}\ncalculate_area `Rectangle {height=1.1; length=2.2}\n```\n\nNotice that within the Circle branch, the code can access the rad field, and within the Rectangle branch, it can access the length and height field. Case types and matches let you essentially \"unmix\" distinct data types after they are mixed together in the program flow. Without case types, this would be impossible to do in a type safe manner.\n\n#### Wildcard matches\n\nMatch expressions can optionally end with a wildcard match, which is the same as a regular case match except that it doesn't include a tag. The wildcard branch will be taken if the runtime tag of the matched value does not match any of the explicitly listed tags in the match expression.\n\n```ocaml\nlet calculate_area = fun shape ->\n    match shape with\n        | `Circle v -> v.rad *. v.rad *. 3.1415926\n        | `Rectangle v -> v.length *. v.height\n        |  v -> \"got something unexpected!\"\n```\n\nWithin a wildcard match, the bound variable has the same type as the input expression, except with the explicitly matched cases statically excluded. For example, in the `calculate_area` example above, `v` would have the type \"same as `shape` except not a `Circle` or `Rectangle`\".\n\nThis makes it possible to further match on the wildcard value elsewhere. For example, in the below code, the new `calculate_area2` function explicitly handles the `Square` case and otherwise defers to the previously defined function to handle the `Circle` and `Rectangle` cases. This works because the compiler knows that the `v` in the wildcard branch is not a `Square`, so it will not complain that the original `calculate_area` function fails to handle squares.\n\n```ocaml\nlet calculate_area = fun shape ->\n    match shape with\n        | `Circle v -> v.rad *. v.rad *. 3.1415926\n        | `Rectangle v -> v.length *. v.height;\n\nlet calculate_area2 = fun shape ->\n    match shape with\n        | `Square v -> v.len *. v.len\n        |  v -> calculate_area v;\n\ncalculate_area2 `Circle {rad=6.7}\ncalculate_area2 `Square {len=9.17}\n```\n\n#### Literals \n\nCubiml has boolean, int, float, string, and null literals. Integers are arbitrary precision and can't have leading zeros. Floating point literals must contain a decimal point, but the fraction part is optional. Strings are double quoted and use backslash escapes.\n\n```ocaml\ntrue;\nfalse;\nnull;\n0;\n-213132;\n999999999999999999999999999999999999999999999999;\n8.213;\n-1.;\n-0.01e33;\n7.e-77;\n\"\";\n\"Hello world!\";\n\"Quote -> \\\" backslash -> \\\\ Single quote -> \\'\"\n```\n\n#### Operators\n\nUse `+, -, *, /, %` for integer math. For floating point math, add a `.` to the end of the operator, e.g. `7.5 /. 12.0`. String concatenation is `^`. \n\n`<, <=, >, >=` can compare both ints and floats. `==` and `!=` compare values of any type (values of different types compare unequal).\n\n```ocaml\n5 * 2 + 1;\n-1 <= 0;\n7.5 /. 12.0;\n0 > 0.1;\n9 == \"what?\";\n\"Hello,\" ^ \" World!\";\n\"\" != {}\n```\n\n#### References\n\nCubiml supports mutability via ML-style references. You can simulate traditional mutable fields by storing a reference in a record field, and a mutable variable by storing a reference in a variable and so on.\n\nML references are not quite the same as what you may be used to. They are pointers to a mutable, garbage collected storage location on the heap and support three operations:\n\n* `ref x` creates a new reference that initially holds the value `x`. Note that this _copies_ the value of `x` to a new location and returns a pointer to that location. `ref foo.bar` returns a pointer to a location that is initialized to the value of `foo.bar`, rather than a pointer to the field of `foo` itself.\n* `!r` _dereferences_ the reference `r` and returns whatever value is currently stored inside. Note that this differs from the `!` operator in C style syntax.\n* `r := x` _stores_ `x` inside the reference `r`, overwriting whatever value was previously stored there. Traditionally, this operation returns a unit value (i.e. empty record), but cubiml instead follows the approach of C-style assignments as Javascript does, where assignment returns the new value.\n\nNote that references may be aliased. For example, we can create a reference `a` and copy it to the variable `b`. Then any changes made via `b` are visible via `a` and vice versa, as shown in the following REPL session.\n\n```\n>> let a = ref 42\n\nref 42\n\n>> let b = a\n\nref 42\n\n>> b := 77\n\n77\n\n>> !a\n\n77\n```\n\n#### Record extension\n\nWhen creating a record, you can optionally copy over all the fields from another record by writing `foo with` at the start of the record, where `foo` is the record you want to copy from.\n\n```ocaml\nlet foo = {a=1; b=\"\"; c=false};\nlet bar = {foo with a=true; d=-23}\n```\n\nThe value you are copying fields from does not have to be a statically known value. It can be any arbitrary expression (as long as you surround it in parenthesis).\n\n```\n>> let baz = {(if foo.c then foo else bar) with c=true}\n\n{a=true; b=\"\"; c=true; d=-23}\n```\n\n#### Comments\n\nComments use `(* *)` and cannot be nested. \n\n```ocaml\n(* define x = 4 *)\nlet x = 4;\n\nlet y = x + (\n    (* 2 is an int *) \n    2\n);\n\nlet z = (* let's define a new record! *) {\n    (* a is a record field *)\n    a = x;\n\n    b = \n        (* comments can also go before expressions *) \n        \"Hello world!\";\n\n    c = match `Some y with\n        | `Some y -> y\n        (* this match arm isn't actually reachable *)\n        | `None _ -> _\n}\n```\n\n### Type annotations\n\nExpressions can manually be annotated with a type via `(expr : type)`, e.g. `(24 : int)` or `(fun x -> x : str -> str)`. Type annotations can be one of the following:\n\n#### Base types\n\nThe primitive types are `bool`, `float`, `int`, `str`, `null`, `number`, `top`, `bot`. \n\n`number` represents a value that can be an `int` _or_ a `float`.\n\n`top` is the supertype of all types. `bot` is the subtype of all types. It's impossible to do anything useful with a value of type `top`, while it is impossible to _create_ a value of type `bot`. \n\n#### Nullable types\n\n`type?`\n\n#### Functions types\n\n`type -> type`\n\nFunction type arrows have the lowest precedence. For example `int -> int?` is parsed as `int -> (int?)`, i.e. a function that takes an int and returns an int or null. To represent a _function_ or null, you need to instead write `(int -> int)?`.\n\n#### Reference types\n\n`type ref`, `type readonly ref`, or `type writeonly ref`\n\n#### Record types\n\nExplicit list of fields:\n\n`{field1: type1; field2: type2}`\n\nExplicit list of fields plus any number of fields not mentioned:\n\n`{_ with field1: type1; field2: type2}`\n\nNote: The list of fields cannot be empty. `{}` is not a valid type annotation. Use `top` instead.\n\n#### Case types \n\nExplicit list of cases:\n\n`[tag1 of type1 | tag2 of type2]`\n\nExplicit list of cases plus any number of cases not mentioned:\n\n`[_ | tag1 of type1 | tag2 of type2]`\n\nNote: The list of cases cannot be empty. `[]` is not a valid type annotation. Use `bot` instead.\n\n#### Holes\n\n`_` creates a fresh type variable. Effectively, this leaves a hole which gets filled in with the corresponding part of the inferred type. It is useful if you only want to constrain part of the type with a type annotation. \n\nFor example if you have a record `foo`, with fields `a` and `b` you could write `(foo : {a: int; b: _})` to ensure `foo.a` is an int while placing no constraints on `foo.b`. \n\n`_` can also be used to extend record and case type annotations with any number of fields or tags not specified. For example the above example could also be written `(foo : {_ with a: int})`. This says that `foo` has a field named `a` that is an `int` and can have any number of other fields with any types. Likewise you can write types like ``[_ | `A of int | `B of str]`` to represent a case type where the `A` tag has type `int`, the `B` tag has type `str`, and there can be any number of other tags not specified with any types.\n\n#### Recursive types\n\nYou can give an explicit name to a type via `type as 'name` and then reference it later via `'name`. This lets you express recursive types. For example, the following code demonstrates a type annotation with a simple recursive list type:\n\n```ocaml\nlet rec build_list = fun n ->\n    if n < 0 then\n        null\n    else\n        {val=n; next=build_list (n - 1)};\n\nlet list = (build_list 4 : {val: int; next: 'list}? as 'list)\n```\n\nType aliases can appear anywhere within a type annotation, not just at the top level. This means you can define multiple type variables within a type annotation, allowing you to express mutually recursive types.\n\n\n## Building cubiml from source\n\nYou will need to have lalrpop and wasm-pack installed. First, generate the parser code\n\n    lalrpop src/grammar.lalr\n\nThen build the wasm module and js wrapper with wasm-pack\n\n    wasm-pack build --target web\n"
  },
  {
    "path": "demo.html",
    "content": "<html>\n    <meta charset=\"utf-8\">\n    <head>\n        <title>CubiML demo</title>\n    </head>\n    <body>\n        <noscript><em><strong>Error: This demo requires Javascript to run.</strong></em></noscript>\n        <cubiml-demo></cubiml-demo>\n    </body>\n</html>\n\n<script src='demo.js'></script>\n\n"
  },
  {
    "path": "demo.js",
    "content": "'use strict';\n\n\nlet mod = null;\nconst mod_promise = import('./pkg/cubiml_demo.js').then(\n    m => (mod = m, mod.default()));\n\n\n\nconst HTML = `\n<style>\n    #container {\n        height: 32em;\n        position: relative;\n        font: medium monospace;\n    }\n    #container.loading {\n        opacity: 85%;\n    }\n\n    #container form {\n        margin: 0;\n    }\n    #container, #prompt, #editor {\n        background: darkslategrey;\n        color: white;\n    }\n\n    #loading {\n        position: absolute;\n        top: 15%;\n        left: 12%;\n    }\n\n\n    #pane1, #pane2 {\n        float: left;\n        width: 50%;\n        height: 100%;\n\n        display: flex;\n        flex-direction: column;\n    }\n    #editor {\n        height: 100%;\n        resize: none;\n        margin: 0;\n    }\n\n\n    #container .error {\n        background: darkred;\n    }\n\n    #container pre {\n        white-space: pre-wrap;\n        overflow-wrap: anywhere;\n        margin: 0;\n    }\n\n    #output {\n        overflow-y: scroll;\n    }\n    #input-line {\n        display: flex;\n    }\n    #prompt {\n        flex-grow: 1;\n        border: 0;\n    }\n    #space-below-prompt {\n        flex-grow: 1;\n    }\n</style>\n\n\n<div id=container class=loading>\n    <div id=loading>Loading, please wait...</div>\n\n    <div id=pane1>\n        <textarea id=editor>\n(* calculate fibonacci numbers recursively *)\nlet fib =\n    let rec fib_sub = fun {n; a; b} ->\n        if n <= 1 then\n            a\n        else\n            fib_sub {n=n - 1; a=a + b; b=a}\n    in\n    fun n -> fib_sub {n; a=1; b=1};\n\n(* matching on case types *)\nlet area = fun x ->\n    match x with\n        | \\`Square x -> x.len *. x.len\n        | \\`Rect x -> x.height *. x.width;\n\nprint \"area \\`Square {len=4.}; =\", area \\`Square {len=4.};\nprint \"area \\`Rect {height=4.; width=2.5}; =\", area \\`Rect {height=4.; width=2.5};\n\n(* wildcard match delegates to first area function\n    for the non-Circle cases in a type safe manner *)\nlet area = fun x ->\n    match x with\n        | \\`Circle x -> x.radius *. x.radius *. 3.1415926\n        | x -> area x;\n\nprint \"area \\`Square {len=4.}; =\", area \\`Square {len=4.};\nprint \"area \\`Rect {height=4.; width=2.5}; =\", area \\`Rect {height=4.; width=2.5};\nprint \"area \\`Circle {radius=1.2}; =\", area \\`Circle {radius=1.2};\n\n\n(* ints are arbitrary precision *)\n(* 999th fibonacci number = 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875 *)\nprint \"fib 999 =\", fib 999;\n\n\n        </textarea>\n        <button id=compile-and-run type=button>Compile and run</button>\n    </div>\n\n    <div id=pane2>\n        <div id=output>\n        </div>\n\n        <form id=rhs-form>\n        <pre id=input-line>&gt;&gt; <input id=prompt type=\"text\" autocomplete=\"off\" placeholder=\"Enter code here or to the left\" disabled></pre>\n        </form>\n\n        <div id=space-below-prompt></div>\n    </div>\n</div>\n`;\n\nclass CubimlDemo extends HTMLElement {\n  constructor() {\n    // Always call super first in constructor\n    super();\n\n    // Create a shadow root\n    const shadow = this.attachShadow({mode: 'open'});\n    shadow.innerHTML = HTML;\n\n    mod_promise.then(\n        wasm => initializeRepl(shadow, mod.State.new(), Printer)).catch(\n        e => {shadow.getElementById('loading').textContent = 'Failed to load demo: ' + e});\n\n  }\n}\ncustomElements.define('cubiml-demo', CubimlDemo);\n\n\nfunction initializeRepl(root, compiler, Printer) {\n    console.log('Initializing REPL');\n    const container = root.getElementById('container');\n    const output = root.getElementById('output');\n    const prompt = root.getElementById('prompt');\n    const editor = root.getElementById('editor');\n\n    function addOutput(line, cls) {\n        const l = document.createElement('pre');\n        l.textContent = line;\n        if (cls) {\n            l.classList.add(cls);\n        }\n        output.appendChild(l);\n        return l;\n    }\n\n    const $ = Object.create(null);\n    const history = [];\n    let history_offset = -1;\n\n    function execCode(script) {\n        let compiled;\n        try {\n            if (!compiler.process(script)) {return [false, compiler.get_err()];}\n            compiled = '(' + compiler.get_output() + ')';\n        } catch (e) {\n            return [false, 'Internal compiler error: ' + e.toString()];\n        }\n\n        try {\n            const p = new Printer;\n            const val = eval(compiled);\n            p.visit(val);\n            return [true, p.parts.join('')];\n        } catch (e) {\n            return [false, 'An error occurred during evaluation in the repl: ' + e.toString()];\n        }\n    }\n\n    function processCode(script) {\n        const [success, res] = execCode(script);\n        addOutput(res, success ? 'success' : 'error');\n        // scroll output window to the bottom\n        output.scrollTop = output.scrollHeight;\n        return success;\n    }\n\n\n    function processReplInput(line) {\n        line = line.trim();\n        if (!line) {return;}\n\n        history_offset = -1;\n        if (history[history.length-1] !== line) {history.push(line);}\n        // \\u00a0 = non breaking space\n        addOutput('>>\\u00a0' + line, 'input');\n        processCode(line);\n    }\n\n    root.getElementById('compile-and-run').addEventListener('click', e => {\n        const s = editor.value.trim();\n        if (!s) {return;}\n\n        // Clear repl output\n        output.textContent = '';\n        compiler.reset();\n        if (processCode(s)) {prompt.focus({preventScroll: true})}\n    });\n\n    // Implement repl command history\n    prompt.addEventListener('keydown', e => {\n        switch (e.key) {\n            case 'ArrowDown': history_offset -= 1; break;\n            case 'ArrowUp': history_offset += 1; break;\n            default: return;\n        }\n        e.preventDefault();\n\n        if (history_offset >= history.length) {history_offset = history.length - 1;}\n        if (history_offset < 0) {history_offset = 0;}\n        prompt.value = history[history.length - history_offset - 1];\n    });\n\n    // If they click in the space below the prompt, focus on the prompt to make it easier to select\n    root.getElementById('space-below-prompt').addEventListener('click', e => {\n        e.preventDefault();\n        prompt.focus({preventScroll: true});\n    });\n\n    root.getElementById('rhs-form').addEventListener('submit', e => {\n        e.preventDefault();\n        const s = prompt.value.trim();\n        prompt.value = '';\n\n        if (!s) {return;}\n        processReplInput(s);\n    });\n\n    container.classList.remove('loading');\n    prompt.disabled = false;\n    container.removeChild(root.getElementById('loading'));\n    console.log('Initialized REPL');\n\n    // Run the example code\n    processCode(editor.value.trim())\n}\n\nclass Printer {\n    constructor() {\n        this.parts = [];\n        this.seen = new WeakSet;\n    }\n\n    visit(e) {\n        const type = typeof e;\n        if (type === 'boolean' || type === 'bigint') {this.parts.push(e.toString()); return;}\n        if (type === 'string') {this.parts.push(JSON.stringify(e)); return;}\n        if (type === 'number') {\n            let s = e.toString();\n            if (/^-?\\d+$/.test(s)) {s += '.0'}\n            this.parts.push(s);\n            return;\n        }\n        if (type === 'function') {this.parts.push('<fun>'); return;}\n        if (type === 'symbol') {this.parts.push('<sym>'); return;}\n        if (e === null) {this.parts.push('null'); return;}\n        if (e === undefined) {this.parts.push('<undefined>'); return;}\n\n        if (this.seen.has(e)) {this.parts.push('...'); return;}\n        this.seen.add(e);\n\n        if (e.$tag) {\n            this.parts.push(e.$tag);\n            if (!e.$val || typeof e.$val !== 'object') {\n                this.parts.push(' ');\n            }\n            this.visit(e.$val);\n        } else if ('$p' in e) {\n            this.parts.push('ref ');\n            this.visit(e.$p);\n        } else {\n            this.parts.push('{');\n            let first = true;\n            for (const [k, v] of Object.entries(e)) {\n                if (!first) {this.parts.push('; ')}\n                first = false;\n\n                this.parts.push(k + '=');\n                this.visit(v);\n            }\n            this.parts.push('}');\n        }\n    }\n\n    println(...args) {\n        for (let arg of args) {\n            if (typeof arg === 'string') {\n                this.parts.push(arg);\n            } else {\n                this.visit(arg);\n            }\n\n            this.parts.push(' ');\n        }\n        this.parts.pop();\n        this.parts.push('\\n');\n    }\n\n    // print(e) {this.visit(e); return this.parts.join('');}\n}\n"
  },
  {
    "path": "rustfmt.toml",
    "content": "max_width = 125\n"
  },
  {
    "path": "src/ast.rs",
    "content": "use crate::spans::{Span, Spanned};\n\n#[derive(Debug, Clone)]\npub enum Literal {\n    Bool,\n    Float,\n    Int,\n    Null,\n    Str,\n}\n\n#[derive(Debug, Clone)]\npub enum Op {\n    Add,\n    Sub,\n    Mult,\n    Div,\n    Rem,\n\n    Lt,\n    Lte,\n    Gt,\n    Gte,\n\n    Eq,\n    Neq,\n}\n\n#[derive(Debug, Clone)]\npub enum OpType {\n    IntOp,\n    FloatOp,\n    StrOp,\n\n    IntOrFloatCmp,\n    AnyCmp,\n}\n\ntype LetDefinition = (LetPattern, Box<Expr>);\ntype VarDefinition = (String, Box<Expr>);\n\n#[derive(Debug, Clone)]\npub enum LetPattern {\n    Var(String),\n    Record(Vec<(Spanned<String>, Box<LetPattern>)>),\n}\n\n#[derive(Debug, Clone)]\npub enum MatchPattern {\n    Case(String, String),\n    Wildcard(String),\n}\n\n#[derive(Debug, Clone)]\npub enum Expr {\n    BinOp(Spanned<Box<Expr>>, Spanned<Box<Expr>>, OpType, Op, Span),\n    Block(Vec<Statement>, Box<Expr>),\n    Call(Box<Expr>, Box<Expr>, Span),\n    Case(Spanned<String>, Box<Expr>),\n    FieldAccess(Box<Expr>, String, Span),\n    FuncDef(Spanned<(LetPattern, Box<Expr>)>),\n    If(Spanned<Box<Expr>>, Box<Expr>, Box<Expr>),\n    Literal(Literal, Spanned<String>),\n    Match(Box<Expr>, Vec<(Spanned<MatchPattern>, Box<Expr>)>, Span),\n    NewRef(Box<Expr>, Span),\n    Record(Option<Box<Expr>>, Vec<(Spanned<String>, Box<Expr>)>, Span),\n    RefGet(Spanned<Box<Expr>>),\n    RefSet(Spanned<Box<Expr>>, Box<Expr>),\n    Typed(Box<Expr>, TypeExpr),\n    Variable(Spanned<String>),\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\npub enum Readability {\n    ReadWrite,\n    ReadOnly,\n    WriteOnly,\n}\n\n#[derive(Debug, Clone)]\npub enum TypeExpr {\n    Alias(Box<TypeExpr>, Spanned<String>),\n    Case(Option<Box<TypeExpr>>, Vec<(Spanned<String>, Box<TypeExpr>)>, Span),\n    Func(Spanned<(Box<TypeExpr>, Box<TypeExpr>)>),\n    Ident(Spanned<String>),\n    Nullable(Box<TypeExpr>, Span),\n    Record(Option<Box<TypeExpr>>, Vec<(Spanned<String>, Box<TypeExpr>)>, Span),\n    Ref(Box<TypeExpr>, Spanned<Readability>),\n    TypeVar(Spanned<String>),\n}\n\n#[derive(Debug, Clone)]\npub enum Statement {\n    Empty,\n    Expr(Expr),\n    LetDef(LetDefinition),\n    LetRecDef(Vec<VarDefinition>),\n    Println(Vec<Expr>),\n}\n"
  },
  {
    "path": "src/codegen.rs",
    "content": "use std::collections::HashMap;\nuse std::mem::swap;\n\nuse crate::ast;\nuse crate::js;\n\npub struct ModuleBuilder {\n    scope_expr: js::Expr,\n    scope_counter: u64,\n    param_counter: u64,\n    var_counter: u64,                    // For choosing new var names\n    bindings: HashMap<String, js::Expr>, // ML name -> JS exor for current scope\n    changes: Vec<(String, Option<js::Expr>)>,\n}\nimpl ModuleBuilder {\n    pub fn new() -> Self {\n        Self {\n            scope_expr: js::var(\"$\".to_string()),\n            scope_counter: 0,\n            param_counter: 0,\n            var_counter: 0,\n            bindings: HashMap::new(),\n            changes: Vec::new(),\n        }\n    }\n\n    pub fn compile_script(&mut self, def: &[ast::Statement]) -> js::Expr {\n        compile_script(self, def)\n    }\n\n    fn ml_scope<T>(&mut self, cb: impl FnOnce(&mut Self) -> T) -> T {\n        let n = self.changes.len();\n        let res = cb(self);\n        while self.changes.len() > n {\n            let (k, old) = self.changes.pop().unwrap();\n            match old {\n                Some(v) => self.bindings.insert(k, v),\n                None => self.bindings.remove(&k),\n            };\n        }\n        res\n    }\n\n    fn fn_scope<T>(&mut self, cb: impl FnOnce(&mut Self) -> T) -> T {\n        let old_var_counter = self.var_counter;\n        let old_param_counter = self.param_counter;\n        let old_scope_counter = self.scope_counter;\n        self.var_counter = 0;\n\n        let res = self.ml_scope(cb);\n\n        self.var_counter = old_var_counter;\n        self.param_counter = old_param_counter;\n        self.scope_counter = old_scope_counter;\n        res\n    }\n\n    fn set_binding(&mut self, k: String, v: js::Expr) {\n        let old = self.bindings.insert(k.clone(), v);\n        self.changes.push((k, old));\n    }\n\n    fn new_var_name(&mut self) -> String {\n        let js_name = format!(\"v{}\", self.var_counter);\n        self.var_counter += 1;\n        js_name\n    }\n\n    fn new_temp_var(&mut self) -> js::Expr {\n        let js_name = format!(\"t{}\", self.var_counter);\n        self.var_counter += 1;\n\n        js::field(self.scope_expr.clone(), js_name)\n    }\n\n    fn new_var(&mut self, ml_name: &str) -> js::Expr {\n        let js_name = self.new_var_name();\n        let expr = js::field(self.scope_expr.clone(), js_name);\n        self.set_binding(ml_name.to_string(), expr.clone());\n        expr\n    }\n\n    fn new_scope_name(&mut self) -> String {\n        let js_name = format!(\"s{}\", self.scope_counter);\n        self.scope_counter += 1;\n        js_name\n    }\n\n    fn new_param_name(&mut self) -> String {\n        let js_name = format!(\"p{}\", self.param_counter);\n        self.param_counter += 1;\n        js_name\n    }\n}\n\nfn compile(ctx: &mut ModuleBuilder, expr: &ast::Expr) -> js::Expr {\n    match expr {\n        ast::Expr::BinOp((lhs_expr, lhs_span), (rhs_expr, rhs_span), op_type, op, full_span) => {\n            let lhs = compile(ctx, lhs_expr);\n            let rhs = compile(ctx, rhs_expr);\n            let jsop = match op {\n                ast::Op::Add => js::Op::Add,\n                ast::Op::Sub => js::Op::Sub,\n                ast::Op::Mult => js::Op::Mult,\n                ast::Op::Div => js::Op::Div,\n                ast::Op::Rem => js::Op::Rem,\n\n                ast::Op::Lt => js::Op::Lt,\n                ast::Op::Lte => js::Op::Lte,\n                ast::Op::Gt => js::Op::Gt,\n                ast::Op::Gte => js::Op::Gte,\n\n                ast::Op::Eq => js::Op::Eq,\n                ast::Op::Neq => js::Op::Neq,\n            };\n            js::binop(lhs, rhs, jsop)\n        }\n        ast::Expr::Block(statements, rest_expr) => {\n            ctx.ml_scope(|ctx| {\n                let mut exprs = Vec::new(); // a list of assignments, followed by rest\n\n                for stmt in statements {\n                    compile_statement(ctx, &mut exprs, stmt);\n                }\n\n                exprs.push(compile(ctx, rest_expr));\n                js::comma_list(exprs)\n            })\n        }\n        ast::Expr::Call(func, arg, _) => {\n            let lhs = compile(ctx, func);\n            let rhs = compile(ctx, arg);\n            js::call(lhs, rhs)\n        }\n        ast::Expr::Case((tag, _), expr) => {\n            let tag = js::lit(format!(\"\\\"{}\\\"\", tag));\n            let expr = compile(ctx, expr);\n            js::obj(None, vec![(\"$tag\".to_string(), tag), (\"$val\".to_string(), expr)])\n        }\n        ast::Expr::FieldAccess(lhs_expr, name, _) => {\n            let lhs = compile(ctx, lhs_expr);\n            js::field(lhs, name.clone())\n        }\n        ast::Expr::FuncDef(((arg_pattern, body_expr), _)) => {\n            ctx.fn_scope(|ctx| {\n                let new_scope_name = ctx.new_scope_name();\n                let mut scope_expr = js::var(new_scope_name.clone());\n                swap(&mut scope_expr, &mut ctx.scope_expr);\n\n                //////////////////////////////////////////////////////\n                let js_pattern = compile_let_pattern(ctx, arg_pattern);\n                let body = compile(ctx, body_expr);\n                //////////////////////////////////////////////////////\n\n                swap(&mut scope_expr, &mut ctx.scope_expr);\n                js::func(js_pattern, new_scope_name, body)\n            })\n        }\n        ast::Expr::If((cond_expr, _), then_expr, else_expr) => {\n            let cond_expr = compile(ctx, cond_expr);\n            let then_expr = compile(ctx, then_expr);\n            let else_expr = compile(ctx, else_expr);\n            js::ternary(cond_expr, then_expr, else_expr)\n        }\n        ast::Expr::Literal(type_, (code, _)) => {\n            let mut code = code.clone();\n            if let ast::Literal::Int = type_ {\n                code.push_str(\"n\");\n            }\n            if code.starts_with(\"-\") {\n                js::unary_minus(js::lit(code[1..].to_string()))\n            } else {\n                js::lit(code)\n            }\n        }\n        ast::Expr::Match(match_expr, cases, _) => {\n            let temp_var = js::field(ctx.scope_expr.clone(), ctx.new_var_name());\n            let part1 = js::assign(temp_var.clone(), compile(ctx, match_expr));\n\n            let tag_expr = js::field(temp_var.clone(), \"$tag\".to_string());\n            let val_expr = js::field(temp_var.clone(), \"$val\".to_string());\n\n            let mut branches = Vec::new();\n            for ((pattern, _), rhs_expr) in cases {\n                use ast::MatchPattern::*;\n                match pattern {\n                    Case(tag, name) => {\n                        ctx.ml_scope(|ctx| {\n                            ctx.set_binding(name.to_string(), val_expr.clone());\n                            branches.push((tag.as_str(), compile(ctx, rhs_expr)));\n                        });\n                    }\n                    Wildcard(name) => {\n                        ctx.ml_scope(|ctx| {\n                            ctx.set_binding(name.to_string(), temp_var.clone());\n                            branches.push((\"\", compile(ctx, rhs_expr)));\n                        });\n                    }\n                }\n            }\n\n            let mut res = branches.pop().unwrap().1;\n            while let Some((tag, rhs_expr)) = branches.pop() {\n                assert!(tag.len() > 0);\n                let cond = js::eqop(tag_expr.clone(), js::lit(format!(\"\\\"{}\\\"\", tag)));\n                res = js::ternary(cond, rhs_expr, res);\n            }\n            js::comma_pair(part1, res)\n        }\n        ast::Expr::NewRef(expr, span) => {\n            let expr = compile(ctx, expr);\n            js::obj(None, vec![(\"$p\".to_string(), expr)])\n        }\n        ast::Expr::Record(proto, fields, span) => js::obj(\n            proto.as_ref().map(|expr| compile(ctx, expr)),\n            fields\n                .iter()\n                .map(|((name, _), expr)| (name.clone(), compile(ctx, expr)))\n                .collect(),\n        ),\n        ast::Expr::RefGet((expr, span)) => {\n            let expr = compile(ctx, expr);\n            js::field(expr, \"$p\".to_string())\n        }\n        ast::Expr::RefSet((lhs_expr, lhs_span), rhs_expr) => {\n            let lhs = compile(ctx, lhs_expr);\n            let rhs = compile(ctx, rhs_expr);\n            js::assign(js::field(lhs, \"$p\".to_string()), rhs)\n        }\n        ast::Expr::Typed(expr, _) => compile(ctx, expr),\n        ast::Expr::Variable((name, _)) => ctx.bindings.get(name).unwrap().clone(),\n    }\n}\n\nfn compile_let_pattern_flat(ctx: &mut ModuleBuilder, out: &mut Vec<js::Expr>, pat: &ast::LetPattern, rhs: js::Expr) {\n    use ast::LetPattern::*;\n    match pat {\n        Var(ml_name) => {\n            let lhs = ctx.new_var(ml_name);\n            out.push(js::assign(lhs, rhs));\n        }\n        Record(pairs) => {\n            // Assign the rhs to a temporary value, and then do a = temp.foo for each field\n            let lhs = ctx.new_temp_var();\n            out.push(js::assign(lhs.clone(), rhs));\n\n            for ((name, _), pat) in pairs.iter() {\n                compile_let_pattern_flat(ctx, out, pat, js::field(lhs.clone(), name.clone()));\n            }\n        }\n    }\n}\n\nfn compile_let_pattern(ctx: &mut ModuleBuilder, pat: &ast::LetPattern) -> js::Expr {\n    use ast::LetPattern::*;\n    match pat {\n        Var(ml_name) => {\n            let js_arg = js::var(ctx.new_param_name());\n            ctx.set_binding(ml_name.to_string(), js_arg.clone());\n            js_arg\n        }\n        Record(pairs) => js::obj(\n            None,\n            pairs\n                .iter()\n                .map(|((name, _), pat)| (name.clone(), compile_let_pattern(ctx, &*pat)))\n                .collect(),\n        ),\n    }\n}\n\nfn compile_statement(ctx: &mut ModuleBuilder, exprs: &mut Vec<js::Expr>, stmt: &ast::Statement) {\n    use ast::Statement::*;\n    match stmt {\n        Empty => {}\n        Expr(expr) => exprs.push(compile(ctx, expr)),\n        LetDef((pat, var_expr)) => {\n            let rhs = compile(ctx, var_expr);\n            compile_let_pattern_flat(ctx, exprs, pat, rhs);\n        }\n        LetRecDef(defs) => {\n            let mut vars = Vec::new();\n            let mut rhs_exprs = Vec::new();\n            for (name, _) in defs {\n                vars.push(ctx.new_var(name))\n            }\n            for (_, expr) in defs {\n                rhs_exprs.push(compile(ctx, expr))\n            }\n\n            for (lhs, rhs) in vars.into_iter().zip(rhs_exprs) {\n                exprs.push(js::assign(lhs, rhs));\n            }\n        }\n        Println(args) => {\n            let args = args.iter().map(|expr| compile(ctx, expr)).collect();\n            exprs.push(js::println(args));\n        }\n    }\n}\n\nfn compile_script(ctx: &mut ModuleBuilder, parsed: &[ast::Statement]) -> js::Expr {\n    let mut exprs = Vec::new();\n\n    for item in parsed {\n        compile_statement(ctx, &mut exprs, item);\n    }\n\n    if exprs.is_empty() {\n        js::lit(\"0\".to_string())\n    } else {\n        js::comma_list(exprs)\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test() {\n        use ast::*;\n\n        fn intlit(s: &str) -> Box<ast::Expr> {\n            Box::new(Expr::Literal(Literal::Int, s.to_string()))\n        }\n\n        let mut mb = ModuleBuilder::new();\n        assert_eq!(compile(&mut mb, &intlit(\"-1\")).to_source(), \"-1n\");\n        assert_eq!(\n            compile(&mut mb, &Expr::FieldAccess(intlit(\"-1\"), \"toString\".to_string())).to_source(),\n            \"(-1n).toString\"\n        );\n\n        assert_eq!(\n            compile(\n                &mut mb,\n                &Expr::BinOp(intlit(\"42\"), intlit(\"-1\"), ast::OpType::IntOp, ast::Op::Sub)\n            )\n            .to_source(),\n            \"42n- -1n\"\n        );\n    }\n}\n"
  },
  {
    "path": "src/core.rs",
    "content": "use std::collections::HashMap;\nuse std::error;\nuse std::fmt;\n\nuse crate::reachability;\nuse crate::spans::{Span, SpannedError as TypeError};\n\ntype ID = usize;\n\n#[derive(Copy, Clone, Debug)]\npub struct Value(ID);\n#[derive(Copy, Clone, Debug)]\npub struct Use(ID);\n\npub type LazyFlow = (Value, Use);\n\n#[derive(Debug, Clone)]\nenum VTypeHead {\n    VBool,\n    VFloat,\n    VInt,\n    VNull,\n    VStr,\n    VFunc {\n        arg: Use,\n        ret: Value,\n    },\n    VObj {\n        fields: HashMap<String, Value>,\n        proto: Option<Value>,\n    },\n    VCase {\n        case: (String, Value),\n    },\n    VRef {\n        write: Option<Use>,\n        read: Option<Value>,\n    },\n}\n#[derive(Debug, Clone)]\nenum UTypeHead {\n    UBool,\n    UFloat,\n    UInt,\n    UNull,\n    UStr,\n    UIntOrFloat,\n    UFunc {\n        arg: Value,\n        ret: Use,\n    },\n    UObj {\n        field: (String, Use),\n    },\n    UCase {\n        cases: HashMap<String, (Use, LazyFlow)>,\n        wildcard: Option<(Use, LazyFlow)>,\n    },\n    URef {\n        write: Option<Value>,\n        read: Option<Use>,\n    },\n    UNullCase {\n        nonnull: Use,\n    },\n}\n\nfn check_heads(\n    lhs_ind: ID,\n    lhs: &(VTypeHead, Span),\n    rhs_ind: ID,\n    rhs: &(UTypeHead, Span),\n    out: &mut Vec<(Value, Use)>,\n) -> Result<(), TypeError> {\n    use UTypeHead::*;\n    use VTypeHead::*;\n\n    match (&lhs.0, &rhs.0) {\n        (&VBool, &UBool) => Ok(()),\n        (&VFloat, &UFloat) => Ok(()),\n        (&VInt, &UInt) => Ok(()),\n        (&VNull, &UNull) => Ok(()),\n        (&VStr, &UStr) => Ok(()),\n        (&VInt, &UIntOrFloat) => Ok(()),\n        (&VFloat, &UIntOrFloat) => Ok(()),\n\n        (&VFunc { arg: arg1, ret: ret1 }, &UFunc { arg: arg2, ret: ret2 }) => {\n            out.push((ret1, ret2));\n            // flip the order since arguments are contravariant\n            out.push((arg2, arg1));\n            Ok(())\n        }\n        (\n            &VObj {\n                fields: ref fields1,\n                proto,\n            },\n            &UObj { field: (ref name, rhs2) },\n        ) => {\n            // Check if the accessed field is defined\n            if let Some(&lhs2) = fields1.get(name) {\n                out.push((lhs2, rhs2));\n                Ok(())\n            } else if let Some(lhs2) = proto {\n                out.push((lhs2, Use(rhs_ind)));\n                Ok(())\n            } else {\n                Err(TypeError::new2(\n                    format!(\"TypeError: Missing field {}\\nNote: Field is accessed here\", name),\n                    rhs.1,\n                    \"But the record is defined without that field here.\",\n                    lhs.1,\n                ))\n            }\n        }\n        (\n            &VCase { case: (ref name, lhs2) },\n            &UCase {\n                cases: ref cases2,\n                wildcard,\n            },\n        ) => {\n            // Check if the right case is handled\n            if let Some((rhs2, lazy_flow)) = cases2.get(name).copied() {\n                out.push((lhs2, rhs2));\n                out.push(lazy_flow);\n                Ok(())\n            } else if let Some((rhs2, lazy_flow)) = wildcard {\n                out.push((Value(lhs_ind), rhs2));\n                out.push(lazy_flow);\n                Ok(())\n            } else {\n                Err(TypeError::new2(\n                    format!(\"TypeError: Unhandled case {}\\nNote: Case originates here\", name),\n                    lhs.1,\n                    \"But it is not handled here.\",\n                    rhs.1,\n                ))\n            }\n        }\n\n        (&VRef { read: r1, write: w1 }, &URef { read: r2, write: w2 }) => {\n            if let Some(r2) = r2 {\n                if let Some(r1) = r1 {\n                    out.push((r1, r2));\n                } else {\n                    return Err(TypeError::new2(\n                        \"TypeError: Reference is not readable.\\nNote: Ref is made write-only here\",\n                        lhs.1,\n                        \"But is read here.\",\n                        rhs.1,\n                    ));\n                }\n            }\n            if let Some(w2) = w2 {\n                if let Some(w1) = w1 {\n                    // flip the order since writes are contravariant\n                    out.push((w2, w1));\n                } else {\n                    return Err(TypeError::new2(\n                        \"TypeError: Reference is not writable.\\nNote: Ref is made read-only here\",\n                        lhs.1,\n                        \"But is written here.\",\n                        rhs.1,\n                    ));\n                }\n            }\n            Ok(())\n        }\n\n        (&VNull, &UNullCase { .. }) => Ok(()),\n        (_, &UNullCase { nonnull }) => {\n            out.push((Value(lhs_ind), nonnull));\n            Ok(())\n        }\n\n        _ => {\n            let found = match lhs.0 {\n                VBool => \"boolean\",\n                VFloat => \"float\",\n                VInt => \"integer\",\n                VNull => \"null\",\n                VStr => \"string\",\n                VFunc { .. } => \"function\",\n                VObj { .. } => \"record\",\n                VCase { .. } => \"case\",\n                VRef { .. } => \"ref\",\n            };\n            let expected = match rhs.0 {\n                UBool => \"boolean\",\n                UFloat => \"float\",\n                UInt => \"integer\",\n                UNull => \"null\",\n                UStr => \"string\",\n                UIntOrFloat => \"float or integer\",\n                UFunc { .. } => \"function\",\n                UObj { .. } => \"record\",\n                UCase { .. } => \"case\",\n                URef { .. } => \"ref\",\n                UNullCase { .. } => unreachable!(),\n            };\n\n            Err(TypeError::new2(\n                format!(\"TypeError: Value is required to be a {} here,\", expected),\n                rhs.1,\n                format!(\"But that value may be a {} originating here.\", found),\n                lhs.1,\n            ))\n        }\n    }\n}\n\n#[derive(Debug, Clone)]\nenum TypeNode {\n    Var,\n    Value((VTypeHead, Span)),\n    Use((UTypeHead, Span)),\n}\n#[derive(Debug, Clone)]\npub struct TypeCheckerCore {\n    r: reachability::Reachability,\n    types: Vec<TypeNode>,\n}\nimpl TypeCheckerCore {\n    pub fn new() -> Self {\n        Self {\n            r: Default::default(),\n            types: Vec::new(),\n        }\n    }\n\n    pub fn flow(&mut self, lhs: Value, rhs: Use) -> Result<(), TypeError> {\n        let mut pending_edges = vec![(lhs, rhs)];\n        let mut type_pairs_to_check = Vec::new();\n        while let Some((lhs, rhs)) = pending_edges.pop() {\n            self.r.add_edge(lhs.0, rhs.0, &mut type_pairs_to_check);\n\n            // Check if adding that edge resulted in any new type pairs needing to be checked\n            while let Some((lhs, rhs)) = type_pairs_to_check.pop() {\n                if let TypeNode::Value(lhs_head) = &self.types[lhs] {\n                    if let TypeNode::Use(rhs_head) = &self.types[rhs] {\n                        check_heads(lhs, lhs_head, rhs, rhs_head, &mut pending_edges)?;\n                    }\n                }\n            }\n        }\n        assert!(pending_edges.is_empty() && type_pairs_to_check.is_empty());\n        Ok(())\n    }\n\n    fn new_val(&mut self, val_type: VTypeHead, span: Span) -> Value {\n        let i = self.r.add_node();\n        assert!(i == self.types.len());\n        self.types.push(TypeNode::Value((val_type, span)));\n        Value(i)\n    }\n\n    fn new_use(&mut self, constraint: UTypeHead, span: Span) -> Use {\n        let i = self.r.add_node();\n        assert!(i == self.types.len());\n        self.types.push(TypeNode::Use((constraint, span)));\n        Use(i)\n    }\n\n    pub fn var(&mut self) -> (Value, Use) {\n        let i = self.r.add_node();\n        assert!(i == self.types.len());\n        self.types.push(TypeNode::Var);\n        (Value(i), Use(i))\n    }\n\n    pub fn bool(&mut self, span: Span) -> Value {\n        self.new_val(VTypeHead::VBool, span)\n    }\n    pub fn float(&mut self, span: Span) -> Value {\n        self.new_val(VTypeHead::VFloat, span)\n    }\n    pub fn int(&mut self, span: Span) -> Value {\n        self.new_val(VTypeHead::VInt, span)\n    }\n    pub fn null(&mut self, span: Span) -> Value {\n        self.new_val(VTypeHead::VNull, span)\n    }\n    pub fn str(&mut self, span: Span) -> Value {\n        self.new_val(VTypeHead::VStr, span)\n    }\n\n    pub fn bool_use(&mut self, span: Span) -> Use {\n        self.new_use(UTypeHead::UBool, span)\n    }\n    pub fn float_use(&mut self, span: Span) -> Use {\n        self.new_use(UTypeHead::UFloat, span)\n    }\n    pub fn int_use(&mut self, span: Span) -> Use {\n        self.new_use(UTypeHead::UInt, span)\n    }\n    pub fn null_use(&mut self, span: Span) -> Use {\n        self.new_use(UTypeHead::UNull, span)\n    }\n    pub fn str_use(&mut self, span: Span) -> Use {\n        self.new_use(UTypeHead::UStr, span)\n    }\n    pub fn int_or_float_use(&mut self, span: Span) -> Use {\n        self.new_use(UTypeHead::UIntOrFloat, span)\n    }\n\n    pub fn func(&mut self, arg: Use, ret: Value, span: Span) -> Value {\n        self.new_val(VTypeHead::VFunc { arg, ret }, span)\n    }\n    pub fn func_use(&mut self, arg: Value, ret: Use, span: Span) -> Use {\n        self.new_use(UTypeHead::UFunc { arg, ret }, span)\n    }\n\n    pub fn obj(&mut self, fields: Vec<(String, Value)>, proto: Option<Value>, span: Span) -> Value {\n        let fields = fields.into_iter().collect();\n        self.new_val(VTypeHead::VObj { fields, proto }, span)\n    }\n    pub fn obj_use(&mut self, field: (String, Use), span: Span) -> Use {\n        self.new_use(UTypeHead::UObj { field }, span)\n    }\n\n    pub fn case(&mut self, case: (String, Value), span: Span) -> Value {\n        self.new_val(VTypeHead::VCase { case }, span)\n    }\n    pub fn case_use(&mut self, cases: Vec<(String, (Use, LazyFlow))>, wildcard: Option<(Use, LazyFlow)>, span: Span) -> Use {\n        let cases = cases.into_iter().collect();\n        self.new_use(UTypeHead::UCase { cases, wildcard }, span)\n    }\n\n    pub fn reference(&mut self, write: Option<Use>, read: Option<Value>, span: Span) -> Value {\n        self.new_val(VTypeHead::VRef { write, read }, span)\n    }\n    pub fn reference_use(&mut self, write: Option<Value>, read: Option<Use>, span: Span) -> Use {\n        self.new_use(UTypeHead::URef { write, read }, span)\n    }\n\n    pub fn null_check_use(&mut self, nonnull: Use, span: Span) -> Use {\n        self.new_use(UTypeHead::UNullCase { nonnull }, span)\n    }\n\n    pub fn save(&self) -> SavePoint {\n        (self.types.len(), self.r.clone())\n    }\n    pub fn restore(&mut self, mut save: SavePoint) {\n        self.types.truncate(save.0);\n        std::mem::swap(&mut self.r, &mut save.1);\n    }\n}\n\ntype SavePoint = (usize, reachability::Reachability);\n"
  },
  {
    "path": "src/grammar.lalr",
    "content": "use lalrpop_util::ParseError;\n\nuse super::ast; // super instead of self because lalrpop wraps this in an internal module\nuse super::spans;\n\n#[LALR]\ngrammar(ctx: &mut spans::SpanMaker<'input>);\n\nextern {\n    type Error = (&'static str, spans::Span);\n}\n\n// Tokens ////////////////////////////////////////////////////////////\nmatch {\n    r\"\\s*\" => { }, // The default whitespace skipping is disabled if an `ignore pattern` is specified\n    r\"//[^\\n\\r]*[\\n\\r]*\" => { }, // Skip `// comments`\n    r#\"\\(\\*[^*]*\\*+(?:[^\\)*][^*]*\\*+)*\\)\"# => { },  // Skip `(* comments *)`\n} else {\n    _\n}\n\n\nIdent: String = <r\"[a-z_]\\w*\"> => String::from(<>);\nTag: String = <r\"`[A-Z0-9]\\w*\"> => String::from(<>);\n\nIntLiteral: String = {\n    <l: @L> <s: r\"-?(?:[0-9]+)\"> <r: @R> =>? {\n        let s2 = s.trim_start_matches('-');\n        if s2 != \"0\" && s2.starts_with(\"0\") {\n            Err(ParseError::User {\n                error: (\"SyntaxError: Numbers can't contain leading 0s\", ctx.span(l, r))\n            })\n        } else {\n            Ok(String::from(s))\n        }\n    },\n};\nFloatLiteral: String =\n    <r\"-?(?:0|[1-9][0-9]*)\\.[0-9]*(?:[eE]-?[0-9]+)?\"> => String::from(<>);\nStringLiteral: String =\n    <r#\"\"[^\\\\\"\\n\\r]*(?:\\\\[tn'\"\\\\][^\\\\\"\\n\\r]*)*\"\"#> => String::from(<>);\n\n\n// make sure __proto__ is not considered a valid identifier\nIllegal = \"__proto__\";\n\n\n// Macros ////////////////////////////////////////////////////////////\nBox<T>: Box<T> = {\n    <T> => Box::new(<>),\n}\nSepList<T, Sep>: Vec<T> = {\n    <v:(<T> Sep)*> <e:T> => {\n        let mut v = v;\n        v.push(e);\n        v\n    }\n};\nSepListOpt<T, Sep>: Vec<T> = {\n    SepList<T, Sep>,\n    => Vec::new(),\n};\n\n\n\n\n\n\nSpanned<T>: spans::Spanned<T> = {\n    <l: @L> <val: T> <r: @R> => (val, ctx.span(l, r))\n};\n\n// Types /////////////////////////////////////////////////////////////\nRecordTypeExtension = <Box<Type>> \"with\";\nKeyPairType = {\n    <Spanned<Ident>> \":\" <Box<Type>>,\n}\nRecordTypeSub = \"{\" <RecordTypeExtension?> <SepList<KeyPairType, \";\">> \"}\";\nRecordType: ast::TypeExpr = {\n    Spanned<RecordTypeSub> => {\n        let ((ext, fields), span) = <>;\n        ast::TypeExpr::Record(ext, fields, span)\n    }\n}\n\nCaseTypeExtension = <Box<Type>> \"|\";\nVariantType = <Spanned<Tag>> \"of\" <Box<NoFunType>>;\nCaseTypeSub = \"[\" <CaseTypeExtension?> <SepList<VariantType, \"|\">> \"]\";\nCaseType: ast::TypeExpr = {\n    Spanned<CaseTypeSub> => {\n        let ((ext, cases), span) = <>;\n        ast::TypeExpr::Case(ext, cases, span)\n    }\n}\n\nRefReadability: ast::Readability = {\n    \"readonly\" \"ref\" => ast::Readability::ReadOnly,\n    \"writeonly\" \"ref\" => ast::Readability::WriteOnly,\n    \"ref\" => ast::Readability::ReadWrite,\n}\nRefType: ast::TypeExpr = {\n    Box<NoFunType> Spanned<RefReadability> => ast::TypeExpr::Ref(<>),\n}\n\nQMark: spans::Span = Spanned<\"?\"> => <>.1;\nNullableType: ast::TypeExpr = {\n    Box<NoFunType> QMark => ast::TypeExpr::Nullable(<>),\n}\n\nTypeVar = \"'\" <Ident>;\n\nNoFunType: ast::TypeExpr = {\n    Spanned<Ident> => ast::TypeExpr::Ident(<>),\n    <Box<NoFunType>> \"as\" <Spanned<TypeVar>> => ast::TypeExpr::Alias(<>),\n    Spanned<TypeVar> => ast::TypeExpr::TypeVar(<>),\n\n    RecordType,\n    CaseType,\n    RefType,\n    NullableType,\n    \"(\" <Type> \")\",\n}\nFuncTypeSub = <Box<NoFunType>> \"->\" <Box<Type>>;\nFuncType: ast::TypeExpr = {\n    Spanned<FuncTypeSub> => ast::TypeExpr::Func(<>),\n}\n\nType = {\n    NoFunType,\n    FuncType,\n}\n\n\n\n\n//////////////////////////////////////////////////////////////////////\n// Expressions ///////////////////////////////////////////////////////\n//////////////////////////////////////////////////////////////////////\n\n\n\n// SimpleExpr ////////////////////////////////////////////////////////\nFieldAccess: ast::Expr = {\n    <lhs: Box<SimpleExpr>> <rhs: Spanned<(\".\" Ident)>> => ast::Expr::FieldAccess(lhs, (rhs.0).1, rhs.1),\n}\n\nRecordExtension = {\n    <Box<CallExpr>> \"with\"\n}\nKeyPairExpr: (spans::Spanned<String>, Box<ast::Expr>) = {\n    <Spanned<Ident>> \"=\" <Box<NoSemiExpr>>,\n    <name: Spanned<Ident>> => (name.clone(), Box::new(ast::Expr::Variable(name))),\n}\nRecordSub = \"{\" <RecordExtension?> <SepListOpt<KeyPairExpr, \";\">> \"}\";\nRecord: ast::Expr = {\n    Spanned<RecordSub> => {\n        let ((proto, fields), span) = <>;\n        ast::Expr::Record(proto, fields, span)\n    }\n}\n\nVarOrLiteral: ast::Expr = {\n    Spanned<Ident> =>\n        match <>.0.as_str() {\n            \"false\" | \"true\" => ast::Expr::Literal(ast::Literal::Bool, <>),\n            \"null\" => ast::Expr::Literal(ast::Literal::Null, <>),\n            _ => ast::Expr::Variable(<>)\n        }\n    ,\n\n    Spanned<FloatLiteral> => ast::Expr::Literal(ast::Literal::Float, <>),\n    Spanned<IntLiteral> => ast::Expr::Literal(ast::Literal::Int, <>),\n    Spanned<StringLiteral> => ast::Expr::Literal(ast::Literal::Str, <>),\n}\n\nSimpleExpr = {\n    FieldAccess,\n    Record,\n    VarOrLiteral,\n    \"(\" <Expr> \")\",\n    \"(\" <Box<Expr>> \":\" <Type> \")\" => ast::Expr::Typed(<>),\n    \"begin\" <Expr> \"end\",\n}\n//////////////////////////////////////////////////////////////////////\n\n\n// RefExpr ///////////////////////////////////////////////////////////\nRefGet: ast::Expr = {\n    \"!\" <Spanned<Box<RefExpr>>> => ast::Expr::RefGet(<>)\n}\nRefExpr = {\n    SimpleExpr,\n    RefGet,\n}\n//////////////////////////////////////////////////////////////////////\n\n// CallExpr //////////////////////////////////////////////////////////\nCall: ast::Expr = {\n    Spanned<Box<RefExpr>> Box<CallExpr> => {\n        let ((lhs, span), rhs) = (<>);\n        ast::Expr::Call(lhs, rhs, span)\n    }\n}\nCase: ast::Expr = {\n    <Spanned<Tag>> <Box<CallExpr>> => ast::Expr::Case(<>),\n}\nNewRef: ast::Expr = {\n    Spanned<(\"ref\" Box<CallExpr>)> => {\n        let ((_, expr), span) = <>;\n        ast::Expr::NewRef(expr, span)\n    }\n}\n\nCallExpr = {\n    RefExpr,\n    Call,\n    Case,\n    NewRef,\n}\n//////////////////////////////////////////////////////////////////////\n\n// Binary expressions/////////////////////////////////////////////////\nMultOpSub: (ast::OpType, ast::Op) = {\n    \"*\" => (ast::OpType::IntOp, ast::Op::Mult),\n    \"/\" => (ast::OpType::IntOp, ast::Op::Div),\n    \"%\" => (ast::OpType::IntOp, ast::Op::Rem),\n    \"*.\" => (ast::OpType::FloatOp, ast::Op::Mult),\n    \"/.\" => (ast::OpType::FloatOp, ast::Op::Div),\n    \"%.\" => (ast::OpType::FloatOp, ast::Op::Rem),\n}\nMultOp: ast::Expr = {\n    Spanned<(Spanned<Box<MultExpr>> MultOpSub Spanned<Box<CallExpr>>)> => {\n        let ((lhs, op, rhs), span) = <>;\n        ast::Expr::BinOp(lhs, rhs, op.0, op.1, span)\n    },\n}\nAddOpSub: (ast::OpType, ast::Op) = {\n    \"+\" => (ast::OpType::IntOp, ast::Op::Add),\n    \"-\" => (ast::OpType::IntOp, ast::Op::Sub),\n    \"+.\" => (ast::OpType::FloatOp, ast::Op::Add),\n    \"-.\" => (ast::OpType::FloatOp, ast::Op::Sub),\n    \"^\" => (ast::OpType::StrOp, ast::Op::Add),\n}\nAddOp: ast::Expr = {\n    Spanned<(Spanned<Box<AddExpr>> AddOpSub Spanned<Box<MultExpr>>)> => {\n        let ((lhs, op, rhs), span) = <>;\n        ast::Expr::BinOp(lhs, rhs, op.0, op.1, span)\n    },\n}\nCmpOpSub: (ast::OpType, ast::Op) = {\n    \"<\" => (ast::OpType::IntOrFloatCmp, ast::Op::Lt),\n    \"<=\" => (ast::OpType::IntOrFloatCmp, ast::Op::Lte),\n    \">\" => (ast::OpType::IntOrFloatCmp, ast::Op::Gt),\n    \">=\" => (ast::OpType::IntOrFloatCmp, ast::Op::Gte),\n\n    \"==\" => (ast::OpType::AnyCmp, ast::Op::Eq),\n    \"!=\" => (ast::OpType::AnyCmp, ast::Op::Neq),\n}\nCmpOp: ast::Expr = {\n    Spanned<(Spanned<Box<AddExpr>> CmpOpSub Spanned<Box<AddExpr>>)> => {\n        let ((lhs, op, rhs), span) = <>;\n        ast::Expr::BinOp(lhs, rhs, op.0, op.1, span)\n    },\n}\n\n\nMultExpr = {\n    CallExpr,\n    MultOp,\n}\nAddExpr = {\n    MultExpr,\n    AddOp,\n}\nCompareExpr = {\n    AddExpr,\n    CmpOp,\n}\n//////////////////////////////////////////////////////////////////////\n\n// Top level expressions /////////////////////////////////////////////\nKeyPairPattern: (spans::Spanned<String>, Box<ast::LetPattern>) = {\n    <name: Spanned<Ident>> \"=\" <pat: LetPattern> => (name, Box::new(pat)),\n    <name: Spanned<Ident>> => (name.clone(), Box::new(ast::LetPattern::Var(name.0))),\n}\nLetPattern: ast::LetPattern = {\n    <Ident> => ast::LetPattern::Var(<>),\n    \"{\" <SepList<KeyPairPattern, \";\">> \"}\" => ast::LetPattern::Record(<>),\n}\nFuncSub = \"fun\" <LetPattern> \"->\" <Box<NoSemiExpr>>;\nFuncDef: ast::Expr = {\n    Spanned<FuncSub> => ast::Expr::FuncDef(<>),\n}\n\n\nIf: ast::Expr = {\n    \"if\" <Spanned<Box<Expr>>> \"then\" <Box<Expr>> \"else\" <Box<NoSemiExpr>> => ast::Expr::If(<>),\n}\n\n\nLetLHS = {\n    \"let\" <LetPattern> \"=\" <Box<NoSemiExpr>>,\n}\nLetRHS = {\n    \"in\" <Box<NoSemiExpr>>,\n}\nLet: ast::Expr = {\n    <lhs: LetLHS> <rhs: LetRHS> => ast::Expr::Block(vec![ast::Statement::LetDef(lhs)], rhs),\n}\n\n\nLetRecDef = {\n    <Ident> \"=\" <Box<FuncDef>>,\n}\nLetRecLHS = {\n    \"let\" \"rec\" <SepList<LetRecDef, \"and\">>,\n}\nLetRec: ast::Expr = {\n     <lhs: LetRecLHS> <rhs: LetRHS> => ast::Expr::Block(vec![ast::Statement::LetRecDef(lhs)], rhs),\n}\n\n\nMatchPattern: ast::MatchPattern = {\n    Tag Ident => ast::MatchPattern::Case(<>),\n    Ident => ast::MatchPattern::Wildcard(<>),\n}\nMatchArm = {\n    \"|\" <Spanned<MatchPattern>> \"->\" <Box<CompareExpr>>,\n}\nMatchSub = \"match\" <Spanned<Box<Expr>>> \"with\" <MatchArm+>;\nMatch: ast::Expr = {\n    MatchSub => {\n        let ((param, span), arms) = <>;\n        ast::Expr::Match(param, arms, span)\n    }\n}\n\n\nRefSet: ast::Expr = {\n    <Spanned<Box<CallExpr>>> \":=\" <Box<NoSemiExpr>> => ast::Expr::RefSet(<>)\n}\n\n\n\nNoSemiExpr = {\n    CompareExpr,\n    FuncDef,\n    If,\n    Let,\n    LetRec,\n    Match,\n    RefSet,\n}\nExpr: ast::Expr = {\n    <stmts: (<Statement> \";\")*> <rest: NoSemiExpr> => {\n        if stmts.is_empty() {\n            rest\n        } else {\n            ast::Expr::Block(stmts, Box::new(rest))\n        }\n    }\n}\n//////////////////////////////////////////////////////////////////////\n\n\n\nStatement: ast::Statement = {\n    <LetLHS> => ast::Statement::LetDef(<>),\n    <LetRecLHS> => ast::Statement::LetRecDef(<>),\n    <NoSemiExpr> => ast::Statement::Expr(<>),\n    \"print\" <SepListOpt<NoSemiExpr, \",\">> => ast::Statement::Println(<>),\n    => ast::Statement::Empty,\n}\n\npub Script = {\n   <SepList<Statement, \";\">>\n}\n"
  },
  {
    "path": "src/js.rs",
    "content": "#![allow(dead_code)]\n\n#[derive(Debug, Clone, Copy)]\npub enum Op {\n    Add,\n    Sub,\n    Mult,\n    Div,\n    Rem,\n\n    Lt,\n    Lte,\n    Gt,\n    Gte,\n\n    Eq,\n    Neq,\n}\n\n/////////////////////////////////////////////////////////////////////////////////////////////\npub fn assign(lhs: Expr, rhs: Expr) -> Expr {\n    Expr(Expr2::Assignment(lhs.0.into(), rhs.0.into()))\n}\npub fn binop(lhs: Expr, rhs: Expr, op: Op) -> Expr {\n    Expr(Expr2::BinOp(lhs.0.into(), rhs.0.into(), op))\n}\npub fn call(lhs: Expr, rhs: Expr) -> Expr {\n    Expr(Expr2::Call(lhs.0.into(), rhs.0.into()))\n}\npub fn comma_pair(lhs: Expr, rhs: Expr) -> Expr {\n    Expr(Expr2::Comma(lhs.0.into(), rhs.0.into()))\n}\npub fn unary_minus(rhs: Expr) -> Expr {\n    Expr(Expr2::Minus(rhs.0.into()))\n}\npub fn eqop(lhs: Expr, rhs: Expr) -> Expr {\n    Expr(Expr2::BinOp(lhs.0.into(), rhs.0.into(), Op::Eq))\n}\npub fn field(lhs: Expr, rhs: String) -> Expr {\n    Expr(Expr2::Field(lhs.0.into(), rhs))\n}\npub fn lit(code: String) -> Expr {\n    Expr(Expr2::Literal(code))\n}\npub fn ternary(cond: Expr, e1: Expr, e2: Expr) -> Expr {\n    Expr(Expr2::Ternary(cond.0.into(), e1.0.into(), e2.0.into()))\n}\npub fn var(s: String) -> Expr {\n    Expr(Expr2::Var(s))\n}\n\npub fn comma_list(mut exprs: Vec<Expr>) -> Expr {\n    // Reverse the list so we can easily create a left-recursive structure instead of right recursive\n    exprs.reverse();\n    let mut res = exprs.pop().unwrap().0;\n    while let Some(expr) = exprs.pop() {\n        res = Expr2::Comma(Box::new(res), expr.0.into());\n    }\n    Expr(res)\n}\n\npub fn println(exprs: Vec<Expr>) -> Expr {\n    Expr(Expr2::Print(exprs.into_iter().map(|e| e.0).collect()))\n}\n\npub fn func(arg: Expr, scope: String, body: Expr) -> Expr {\n    Expr(Expr2::ArrowFunc(Box::new(arg.0), scope, Box::new(body.0)))\n}\n\npub fn obj(spread: Option<Expr>, fields: Vec<(String, Expr)>) -> Expr {\n    let mut prop_defs = Vec::new();\n    if let Some(v) = spread {\n        prop_defs.push(PropertyDefinition::Spread(v.0.into()));\n    }\n    for (name, v) in fields {\n        prop_defs.push(PropertyDefinition::Named(name, v.0.into()));\n    }\n\n    Expr(Expr2::Obj(prop_defs))\n}\n\n#[derive(Clone, Debug)]\npub struct Expr(Expr2);\nimpl Expr {\n    // pub fn add_parens(&mut self) {\n    //     self.0.add_parens();\n    // }\n\n    pub fn to_source(mut self) -> String {\n        self.0.add_parens();\n\n        let mut s = \"\".to_string();\n        self.0.write(&mut s);\n        s\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq)]\nenum Token {\n    OTHER,\n    BRACE,\n    PAREN,\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]\nenum Precedence {\n    PRIMARY = 0,\n    MEMBER,\n    CALL,\n    LHS,\n    UNARY,\n    EXPONENT,\n    MULTIPLICATIVE,\n    ADDITIVE,\n    SHIFT,\n    RELATIONAL,\n    EQUALITY,\n    LOR,\n    CONDITIONAL,\n    ASSIGN,\n    EXPR,\n}\n\n#[derive(Clone, Debug)]\nenum PropertyDefinition {\n    Named(String, Box<Expr2>),\n    Spread(Box<Expr2>),\n}\n\n#[derive(Clone, Debug)]\nenum Expr2 {\n    Paren(Box<Expr2>),\n    Literal(String),\n    Obj(Vec<PropertyDefinition>),\n    Var(String),\n\n    Field(Box<Expr2>, String),\n\n    Call(Box<Expr2>, Box<Expr2>),\n\n    Minus(Box<Expr2>),\n\n    BinOp(Box<Expr2>, Box<Expr2>, Op),\n\n    Ternary(Box<Expr2>, Box<Expr2>, Box<Expr2>),\n\n    Assignment(Box<Expr2>, Box<Expr2>),\n    ArrowFunc(Box<Expr2>, String, Box<Expr2>),\n\n    Comma(Box<Expr2>, Box<Expr2>),\n\n    // Temp hack\n    Print(Vec<Expr2>),\n}\nimpl Expr2 {\n    fn precedence(&self) -> Precedence {\n        use Expr2::*;\n        use Op::*;\n        use Precedence::*;\n        match self {\n            Paren(..) => PRIMARY,\n            Literal(..) => PRIMARY,\n            Obj(..) => PRIMARY,\n            Var(..) => PRIMARY,\n            Field(..) => MEMBER,\n            Call(..) => CALL,\n            Minus(..) => UNARY,\n            BinOp(_, _, op) => match op {\n                Mult | Div | Rem => MULTIPLICATIVE,\n                Add | Sub => ADDITIVE,\n                Lt | Lte | Gt | Gte => RELATIONAL,\n                Eq | Neq => EQUALITY,\n            },\n            Ternary(..) => CONDITIONAL,\n            Assignment(..) => ASSIGN,\n            ArrowFunc(..) => ASSIGN,\n            Comma(..) => EXPR,\n            Print(..) => CALL,\n        }\n    }\n\n    fn first(&self) -> Token {\n        use Expr2::*;\n        use Token::*;\n        match self {\n            Paren(..) => PAREN,\n            Literal(..) => OTHER,\n            Obj(..) => BRACE,\n            Var(..) => OTHER,\n            Field(lhs, ..) => lhs.first(),\n            Call(lhs, ..) => lhs.first(),\n            Minus(..) => OTHER,\n            BinOp(lhs, ..) => lhs.first(),\n            Ternary(lhs, ..) => lhs.first(),\n            Assignment(lhs, ..) => lhs.first(),\n            ArrowFunc(..) => PAREN,\n            Comma(lhs, ..) => lhs.first(),\n            Print(..) => OTHER,\n        }\n    }\n\n    fn write(&self, out: &mut String) {\n        match self {\n            Self::Paren(e) => {\n                *out += \"(\";\n                e.write(out);\n                *out += \")\";\n            }\n            Self::Literal(code) => {\n                *out += code;\n            }\n            Self::Obj(fields) => {\n                *out += \"{\";\n                for prop_def in fields {\n                    use PropertyDefinition::*;\n                    match prop_def {\n                        Named(name, val) => {\n                            *out += \"'\";\n                            *out += name;\n                            *out += \"': \";\n                            val.write(out);\n                        }\n                        Spread(val) => {\n                            *out += \"...\";\n                            val.write(out);\n                        }\n                    }\n\n                    *out += \", \";\n                }\n                *out += \"}\";\n            }\n            Self::Var(name) => {\n                *out += name;\n            }\n            Self::Field(lhs, rhs) => {\n                lhs.write(out);\n                *out += \".\";\n                *out += rhs;\n            }\n            Self::Call(lhs, rhs) => {\n                lhs.write(out);\n                *out += \"(\";\n                rhs.write(out);\n                *out += \")\";\n            }\n            Self::Minus(e) => {\n                *out += \"-\";\n                e.write(out);\n            }\n            Self::BinOp(lhs, rhs, op) => {\n                use Op::*;\n                let opstr = match op {\n                    Add => \"+\",\n                    Sub => \"- \",\n                    Mult => \"*\",\n                    Div => \"/\",\n                    Rem => \"%\",\n\n                    Lt => \"<\",\n                    Lte => \"<=\",\n                    Gt => \">\",\n                    Gte => \">=\",\n\n                    Eq => \"===\",\n                    Neq => \"!==\",\n                };\n\n                lhs.write(out);\n                *out += opstr;\n                rhs.write(out);\n            }\n            Self::Ternary(cond, e1, e2) => {\n                cond.write(out);\n                *out += \" ? \";\n                e1.write(out);\n                *out += \" : \";\n                e2.write(out);\n            }\n            Self::Assignment(lhs, rhs) => {\n                lhs.write(out);\n                *out += \" = \";\n                rhs.write(out);\n            }\n            Self::ArrowFunc(arg, scope_arg, body) => {\n                *out += \"(\";\n                arg.write(out);\n                *out += \", \";\n                *out += scope_arg;\n                *out += \"={}) => \";\n                body.write(out);\n            }\n            Self::Comma(lhs, rhs) => {\n                lhs.write(out);\n                *out += \", \";\n                rhs.write(out);\n            }\n            Self::Print(exprs) => {\n                *out += \"p.println(\";\n                for ex in exprs {\n                    ex.write(out);\n                    *out += \", \";\n                }\n                *out += \")\";\n            }\n        }\n    }\n\n    fn wrap_in_parens(&mut self) {\n        use Expr2::*;\n        let dummy = Literal(\"\".to_string());\n        let temp = std::mem::replace(self, dummy);\n        *self = Paren(Box::new(temp));\n    }\n\n    fn ensure(&mut self, required: Precedence) {\n        if self.precedence() > required {\n            self.wrap_in_parens();\n        }\n    }\n\n    fn add_parens(&mut self) {\n        use Precedence::*;\n        match self {\n            Self::Paren(e) => {\n                e.add_parens();\n            }\n            Self::Literal(code) => {}\n            Self::Obj(fields) => {\n                for prop_def in fields {\n                    use PropertyDefinition::*;\n                    match prop_def {\n                        Named(name, val) => {\n                            val.add_parens();\n                            val.ensure(ASSIGN);\n                        }\n                        Spread(val) => {\n                            val.add_parens();\n                            val.ensure(ASSIGN);\n                        }\n                    }\n                }\n            }\n            Self::Var(name) => {}\n            Self::Field(lhs, rhs) => {\n                lhs.add_parens();\n                lhs.ensure(MEMBER);\n            }\n            Self::Call(lhs, rhs) => {\n                lhs.add_parens();\n                lhs.ensure(MEMBER);\n                rhs.add_parens();\n                rhs.ensure(ASSIGN);\n            }\n            Self::Minus(e) => {\n                e.add_parens();\n                e.ensure(UNARY);\n            }\n            Self::BinOp(lhs, rhs, op) => {\n                use Op::*;\n                let req = match op {\n                    Mult | Div | Rem => (MULTIPLICATIVE, EXPONENT),\n                    Add | Sub => (ADDITIVE, MULTIPLICATIVE),\n                    Lt | Lte | Gt | Gte => (RELATIONAL, SHIFT),\n                    Eq | Neq => (EQUALITY, RELATIONAL),\n                };\n\n                lhs.add_parens();\n                lhs.ensure(req.0);\n                rhs.add_parens();\n                rhs.ensure(req.1);\n            }\n            Self::Ternary(cond, e1, e2) => {\n                cond.add_parens();\n                e1.add_parens();\n                e1.ensure(ASSIGN);\n                e2.add_parens();\n                e2.ensure(ASSIGN);\n            }\n            Self::Assignment(lhs, rhs) => {\n                lhs.add_parens();\n                lhs.ensure(LHS);\n                rhs.add_parens();\n                rhs.ensure(ASSIGN);\n            }\n            Self::ArrowFunc(arg, scope_arg, body) => {\n                arg.add_parens();\n                body.add_parens();\n                body.ensure(ASSIGN);\n                // body can't be an expression starting with \"{\"\n                if body.first() == Token::BRACE {\n                    body.wrap_in_parens();\n                }\n            }\n            Self::Comma(lhs, rhs) => {\n                lhs.add_parens();\n                rhs.add_parens();\n                rhs.ensure(ASSIGN);\n            }\n            Self::Print(exprs) => {\n                for ex in exprs {\n                    ex.add_parens();\n                    ex.ensure(PRIMARY);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#![allow(dead_code)]\n#![allow(unused_imports)]\n#![allow(unused_variables)]\n\nmod ast;\nmod codegen;\nmod core;\nmod grammar;\nmod js;\nmod reachability;\nmod spans;\nmod typeck;\nmod utils;\n\nuse wasm_bindgen::prelude::*;\n\nuse std::fmt::Display;\nuse std::mem;\n\nuse lalrpop_util::ParseError;\n\nuse self::codegen::ModuleBuilder;\nuse self::grammar::ScriptParser;\nuse self::spans::{SpanMaker, SpanManager, SpannedError};\nuse self::typeck::TypeckState;\n\nfn convert_parse_error<T: Display>(mut sm: SpanMaker, e: ParseError<usize, T, (&'static str, spans::Span)>) -> SpannedError {\n    match e {\n        ParseError::InvalidToken { location } => {\n            SpannedError::new1(\"SyntaxError: Invalid token\", sm.span(location, location))\n        }\n        ParseError::UnrecognizedEof { location, expected } => SpannedError::new1(\n            format!(\n                \"SyntaxError: Unexpected end of input.\\nNote: expected tokens: [{}]\\nParse error occurred here:\",\n                expected.join(\", \")\n            ),\n            sm.span(location, location),\n        ),\n        ParseError::UnrecognizedToken { token, expected } => SpannedError::new1(\n            format!(\n                \"SyntaxError: Unexpected token {}\\nNote: expected tokens: [{}]\\nParse error occurred here:\",\n                token.1,\n                expected.join(\", \")\n            ),\n            sm.span(token.0, token.2),\n        ),\n        ParseError::ExtraToken { token } => {\n            SpannedError::new1(\"SyntaxError: Unexpected extra token\", sm.span(token.0, token.2))\n        }\n        ParseError::User { error: (msg, span) } => SpannedError::new1(msg, span),\n    }\n}\n\n#[wasm_bindgen]\npub struct State {\n    parser: ScriptParser,\n    spans: SpanManager,\n\n    checker: TypeckState,\n    compiler: ModuleBuilder,\n\n    out: Option<String>,\n    err: Option<String>,\n}\n#[wasm_bindgen]\nimpl State {\n    pub fn new() -> Self {\n        State {\n            parser: ScriptParser::new(),\n            spans: SpanManager::default(),\n\n            checker: TypeckState::new(),\n            compiler: ModuleBuilder::new(),\n\n            out: None,\n            err: None,\n        }\n    }\n\n    fn process_sub(&mut self, source: &str) -> Result<String, SpannedError> {\n        let mut span_maker = self.spans.add_source(source.to_owned());\n\n        let ast = self\n            .parser\n            .parse(&mut span_maker, source)\n            .map_err(|e| convert_parse_error(span_maker, e))?;\n        let _t = self.checker.check_script(&ast)?;\n        let js_ast = self.compiler.compile_script(&ast);\n        Ok(js_ast.to_source())\n    }\n\n    pub fn process(&mut self, source: &str) -> bool {\n        let res = self.process_sub(source);\n        match res {\n            Ok(s) => {\n                self.out = Some(s);\n                true\n            }\n            Err(e) => {\n                self.err = Some(e.print(&self.spans));\n                false\n            }\n        }\n    }\n\n    pub fn get_output(&mut self) -> Option<String> {\n        self.out.take()\n    }\n    pub fn get_err(&mut self) -> Option<String> {\n        self.err.take()\n    }\n\n    pub fn reset(&mut self) {\n        mem::swap(&mut self.checker, &mut TypeckState::new());\n        mem::swap(&mut self.compiler, &mut ModuleBuilder::new());\n    }\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![allow(dead_code)]\n#![allow(unused_imports)]\n#![allow(unused_variables)]\n\nuse std::env;\nuse std::fs;\nuse std::time::Instant;\n\nuse cubiml_demo::State;\n\nfn main() {\n    let mut state = State::new();\n    for fname in env::args().skip(1) {\n        println!(\"Processing {}\", fname);\n        let data = fs::read_to_string(fname).unwrap();\n        // println!(\">> {}\", data);\n\n        let t0 = Instant::now();\n        let res = if state.process(&data) {\n            state.get_output().unwrap()\n        } else {\n            state.get_err().unwrap()\n        };\n        dbg!(t0.elapsed());\n\n        println!(\"{}\", res);\n    }\n}\n"
  },
  {
    "path": "src/reachability.rs",
    "content": "use std::collections::HashSet;\n\n#[derive(Debug, Default, Clone)]\nstruct OrderedSet<T> {\n    v: Vec<T>,\n    s: HashSet<T>,\n}\nimpl<T: Eq + std::hash::Hash + Clone> OrderedSet<T> {\n    fn insert(&mut self, value: T) -> bool {\n        if self.s.insert(value.clone()) {\n            self.v.push(value);\n            true\n        } else {\n            false\n        }\n    }\n\n    fn iter(&self) -> std::slice::Iter<T> {\n        self.v.iter()\n    }\n}\n\ntype ID = usize;\n\n#[derive(Debug, Default, Clone)]\npub struct Reachability {\n    upsets: Vec<OrderedSet<ID>>,\n    downsets: Vec<OrderedSet<ID>>,\n}\nimpl Reachability {\n    pub fn add_node(&mut self) -> ID {\n        let i = self.upsets.len();\n        self.upsets.push(Default::default());\n        self.downsets.push(Default::default());\n        i\n    }\n\n    pub fn add_edge(&mut self, lhs: ID, rhs: ID, out: &mut Vec<(ID, ID)>) {\n        let mut work = vec![(lhs, rhs)];\n\n        while let Some((lhs, rhs)) = work.pop() {\n            // Insert returns false if the edge is already present\n            if !self.downsets[lhs].insert(rhs) {\n                continue;\n            }\n            self.upsets[rhs].insert(lhs);\n            // Inform the caller that a new edge was added\n            out.push((lhs, rhs));\n\n            for &lhs2 in self.upsets[lhs].iter() {\n                work.push((lhs2, rhs));\n            }\n            for &rhs2 in self.downsets[rhs].iter() {\n                work.push((lhs, rhs2));\n            }\n        }\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn test() {\n        let mut r = Reachability::default();\n        for i in 0..10 {\n            r.add_node();\n        }\n\n        let mut out = Vec::new();\n        r.add_edge(0, 8, &mut out);\n        assert_eq!(out, vec![(0, 8)]);\n        out.clear();\n        r.add_edge(0, 8, &mut out);\n        assert_eq!(out, vec![]);\n\n        r.add_edge(0, 3, &mut out);\n        r.add_edge(1, 3, &mut out);\n        r.add_edge(2, 3, &mut out);\n        r.add_edge(4, 5, &mut out);\n        r.add_edge(4, 6, &mut out);\n        r.add_edge(4, 7, &mut out);\n        r.add_edge(6, 7, &mut out);\n        r.add_edge(9, 1, &mut out);\n        r.add_edge(9, 8, &mut out);\n\n        out.clear();\n        r.add_edge(3, 4, &mut out);\n\n        let mut expected = Vec::new();\n        for &lhs in &[0, 1, 2, 3, 9] {\n            for &rhs in &[4, 5, 6, 7] {\n                expected.push((lhs, rhs));\n            }\n        }\n\n        out.sort_unstable();\n        expected.sort_unstable();\n        assert_eq!(out, expected);\n    }\n}\n"
  },
  {
    "path": "src/spans.rs",
    "content": "use std::collections::{HashMap, HashSet};\nuse std::error;\nuse std::fmt;\n\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\npub struct Span(usize);\n\npub type Spanned<T> = (T, Span);\n\n#[derive(Debug, Default)]\npub struct SpanManager {\n    sources: Vec<String>,\n    spans: Vec<(usize, usize, usize)>,\n}\nimpl SpanManager {\n    pub fn add_source(&mut self, source: String) -> SpanMaker {\n        let i = self.sources.len();\n        self.sources.push(source);\n        SpanMaker {\n            parent: self,\n            source_ind: i,\n            pool: Default::default(),\n        }\n    }\n\n    pub fn print(&self, span: Span) -> String {\n        let (source_ind, l, r) = self.spans[span.0];\n        let source = &self.sources[source_ind];\n\n        let mut out = String::new();\n\n        assert!(l <= r);\n        let (before, source) = source.split_at(l);\n\n        // The matched span may contain newlines, so cut it off at the first newline if applicable\n        let tok = source.split_at(r - l).0.split('\\n').next().unwrap();\n        let after = source.split_at(tok.len()).1;\n        // let (source, after) = source.split_at(r);\n\n        let mut b_iter = before.rsplit('\\n');\n        let line_before = b_iter.next().unwrap();\n        let mut a_iter = after.split('\\n');\n        let line_after = a_iter.next().unwrap();\n\n        // Lines before the span\n        if let Some(line) = b_iter.next() {\n            if let Some(line) = b_iter.next() {\n                out += line;\n                out += \"\\n\";\n            }\n            out += line;\n            out += \"\\n\";\n        }\n\n        // Line of the span\n        out += line_before;\n        out += tok;\n        out += line_after;\n        out += \"\\n\";\n\n        // highlight line\n        out += &\" \".repeat(line_before.len());\n        out += \"^\";\n        out += &\"~\".repeat(std::cmp::max(1, tok.len()) - 1);\n        out += &\" \".repeat(line_after.len());\n        out += \"\\n\";\n\n        // Lines after the span\n        for _ in 0..2 {\n            if let Some(line) = a_iter.next() {\n                out += line;\n                out += \"\\n\";\n            }\n        }\n\n        out\n    }\n\n    fn new_span(&mut self, source_ind: usize, l: usize, r: usize) -> Span {\n        let i = self.spans.len();\n        self.spans.push((source_ind, l, r));\n        Span(i)\n    }\n}\n\n#[derive(Debug)]\npub struct SpanMaker<'a> {\n    parent: &'a mut SpanManager,\n    source_ind: usize,\n    pool: HashMap<(usize, usize), Span>,\n}\nimpl<'a> SpanMaker<'a> {\n    pub fn span(&mut self, l: usize, r: usize) -> Span {\n        // Make the borrow checker happy\n        let source_ind = self.source_ind;\n        let parent = &mut self.parent;\n\n        *self.pool.entry((l, r)).or_insert_with(|| parent.new_span(source_ind, l, r))\n    }\n}\n\n#[derive(Debug)]\npub struct SpannedError {\n    pairs: Vec<(String, Span)>,\n}\n\nimpl SpannedError {\n    pub fn new1(s1: impl Into<String>, s2: Span) -> Self {\n        let p1 = (s1.into(), s2);\n        SpannedError { pairs: vec![p1] }\n    }\n\n    pub fn new2(s1: impl Into<String>, s2: Span, s3: impl Into<String>, s4: Span) -> Self {\n        let p1 = (s1.into(), s2);\n        let p2 = (s3.into(), s4);\n        SpannedError { pairs: vec![p1, p2] }\n    }\n\n    pub fn print(&self, sm: &SpanManager) -> String {\n        let mut out = String::new();\n        for (msg, span) in self.pairs.iter() {\n            out += &msg;\n            out += \"\\n\";\n            out += &sm.print(*span);\n        }\n        out\n    }\n}\nimpl fmt::Display for SpannedError {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        Ok(())\n    }\n}\nimpl error::Error for SpannedError {}\n"
  },
  {
    "path": "src/typeck.rs",
    "content": "use std::cell::{Cell, RefCell};\nuse std::collections::{HashMap, HashSet};\nuse std::error;\nuse std::fmt;\nuse std::rc::Rc;\n\nuse crate::ast;\nuse crate::core::*;\nuse crate::spans::{Span, SpannedError as SyntaxError};\n\ntype Result<T> = std::result::Result<T, SyntaxError>;\n\n#[derive(Clone)]\nenum Scheme {\n    Mono(Value),\n    PolyLet(Rc<RefCell<PolyLet>>),\n    PolyLetRec(Rc<RefCell<PolyLetRec>>, usize),\n}\n\nstruct PolyLet {\n    saved_bindings: Bindings,\n    saved_expr: ast::Expr,\n    cached: Option<Value>,\n}\nimpl PolyLet {\n    fn new(saved_bindings: Bindings, saved_expr: ast::Expr, engine: &mut TypeCheckerCore) -> Result<Self> {\n        let mut s = Self {\n            saved_bindings,\n            saved_expr,\n            cached: None,\n        };\n        s.cached = Some(s.check(engine)?);\n        Ok(s)\n    }\n\n    fn check(&mut self, engine: &mut TypeCheckerCore) -> Result<Value> {\n        if let Some(v) = self.cached.take() {\n            return Ok(v);\n        }\n        check_expr(engine, &mut self.saved_bindings, &self.saved_expr)\n    }\n}\n\nstruct PolyLetRec {\n    saved_bindings: Bindings,\n    saved_defs: Vec<(String, Box<ast::Expr>)>,\n    cached: Option<Vec<(Value, Use)>>,\n}\nimpl PolyLetRec {\n    fn new(\n        saved_bindings: Bindings,\n        saved_defs: Vec<(String, Box<ast::Expr>)>,\n        engine: &mut TypeCheckerCore,\n    ) -> Result<Self> {\n        let mut s = Self {\n            saved_bindings,\n            saved_defs,\n            cached: None,\n        };\n        s.cached = Some(s.check(engine)?);\n        Ok(s)\n    }\n\n    fn check(&mut self, engine: &mut TypeCheckerCore) -> Result<Vec<(Value, Use)>> {\n        if let Some(v) = self.cached.take() {\n            return Ok(v);\n        }\n\n        let saved_defs = &self.saved_defs;\n        self.saved_bindings.in_child_scope(|bindings| {\n            let mut temp_vars = Vec::with_capacity(saved_defs.len());\n            for (name, _) in saved_defs.iter() {\n                let (temp_type, temp_bound) = engine.var();\n                bindings.insert(name.clone(), temp_type);\n                temp_vars.push((temp_type, temp_bound));\n            }\n\n            for ((_, expr), (_, bound)) in saved_defs.iter().zip(&temp_vars) {\n                let var_type = check_expr(engine, bindings, expr)?;\n                engine.flow(var_type, *bound)?;\n            }\n\n            Ok(temp_vars)\n        })\n    }\n}\n\nstruct UnwindPoint(usize);\nstruct Bindings {\n    m: HashMap<String, Scheme>,\n    changes: Vec<(String, Option<Scheme>)>,\n}\nimpl Bindings {\n    fn new() -> Self {\n        Self {\n            m: HashMap::new(),\n            changes: Vec::new(),\n        }\n    }\n\n    fn get(&self, k: &str) -> Option<&Scheme> {\n        self.m.get(k)\n    }\n\n    fn insert_scheme(&mut self, k: String, v: Scheme) {\n        let old = self.m.insert(k.clone(), v);\n        self.changes.push((k, old));\n    }\n\n    fn insert(&mut self, k: String, v: Value) {\n        self.insert_scheme(k, Scheme::Mono(v))\n    }\n\n    fn unwind_point(&mut self) -> UnwindPoint {\n        UnwindPoint(self.changes.len())\n    }\n\n    fn unwind(&mut self, n: UnwindPoint) {\n        let n = n.0;\n        while self.changes.len() > n {\n            let (k, old) = self.changes.pop().unwrap();\n            match old {\n                Some(v) => self.m.insert(k, v),\n                None => self.m.remove(&k),\n            };\n        }\n    }\n\n    fn in_child_scope<T>(&mut self, cb: impl FnOnce(&mut Self) -> T) -> T {\n        let n = self.unwind_point();\n        let res = cb(self);\n        self.unwind(n);\n        res\n    }\n}\n\nstruct TypeVarBindings {\n    level: u32,\n    // Only valid if u32 <= self.level\n    m: HashMap<String, ((Value, Use), u32, Span)>,\n}\nimpl TypeVarBindings {\n    fn new() -> Self {\n        Self {\n            level: 0,\n            m: HashMap::new(),\n        }\n    }\n\n    fn insert(&mut self, name: String, v: (Value, Use), span: Span) -> Option<Span> {\n        self.m.insert(name, (v, self.level + 1, span)).map(|t| t.2)\n    }\n}\n\nfn parse_type(engine: &mut TypeCheckerCore, bindings: &mut TypeVarBindings, tyexpr: &ast::TypeExpr) -> Result<(Value, Use)> {\n    use ast::TypeExpr::*;\n    match tyexpr {\n        Alias(lhs, (name, span)) => {\n            let (utype_value, utype) = engine.var();\n            let (vtype, vtype_bound) = engine.var();\n\n            let old = bindings.insert(name.to_string(), (vtype, utype), *span);\n            if let Some(old_span) = old {\n                return Err(SyntaxError::new2(\n                    format!(\"SyntaxError: Redefinition of type variable '{}\", name),\n                    *span,\n                    \"Note: Type variable was already defined here\",\n                    old_span,\n                ));\n            }\n\n            let lhs_type = parse_type(engine, bindings, lhs)?;\n            engine.flow(lhs_type.0, vtype_bound)?;\n            engine.flow(utype_value, lhs_type.1)?;\n            // Make alias permanent by setting level to 0 now that definition is complete\n            bindings.m.get_mut(name).unwrap().1 = 0;\n            Ok((vtype, utype))\n        }\n        Case(ext, cases, span) => {\n            // Create a dummy variable to use as the lazy flow values\n            let dummy = engine.var();\n            let (vtype, vtype_bound) = engine.var();\n\n            let utype_wildcard = if let Some(ext) = ext {\n                let ext_type = parse_type(engine, bindings, ext)?;\n                engine.flow(ext_type.0, vtype_bound)?;\n                Some((ext_type.1, dummy))\n            } else {\n                None\n            };\n\n            // Must do this *after* parsing wildcard as wildcards are unguarded\n            bindings.level += 1;\n            let mut utype_case_arms = Vec::new();\n            for ((tag, tag_span), wrapped_expr) in cases {\n                let wrapped_type = parse_type(engine, bindings, wrapped_expr)?;\n\n                let case_value = engine.case((tag.clone(), wrapped_type.0), *tag_span);\n                engine.flow(case_value, vtype_bound)?;\n                utype_case_arms.push((tag.clone(), (wrapped_type.1, dummy)));\n            }\n            bindings.level -= 1;\n\n            let utype = engine.case_use(utype_case_arms, utype_wildcard, *span);\n            Ok((vtype, utype))\n        }\n        Func(((lhs, rhs), span)) => {\n            bindings.level += 1;\n            let lhs_type = parse_type(engine, bindings, lhs)?;\n            let rhs_type = parse_type(engine, bindings, rhs)?;\n            bindings.level -= 1;\n\n            let utype = engine.func_use(lhs_type.0, rhs_type.1, *span);\n            let vtype = engine.func(lhs_type.1, rhs_type.0, *span);\n            Ok((vtype, utype))\n        }\n        Ident((s, span)) => match s.as_str() {\n            \"bool\" => Ok((engine.bool(*span), engine.bool_use(*span))),\n            \"float\" => Ok((engine.float(*span), engine.float_use(*span))),\n            \"int\" => Ok((engine.int(*span), engine.int_use(*span))),\n            \"null\" => Ok((engine.null(*span), engine.null_use(*span))),\n            \"str\" => Ok((engine.str(*span), engine.str_use(*span))),\n            \"number\" => {\n                let (vtype, vtype_bound) = engine.var();\n                let float_lit = engine.float(*span);\n                let int_lit = engine.int(*span);\n                engine.flow(float_lit, vtype_bound)?;\n                engine.flow(int_lit, vtype_bound)?;\n                Ok((vtype, engine.int_or_float_use(*span)))\n            }\n            \"top\" => {\n                let (_, utype) = engine.var();\n                let (vtype, vtype_bound) = engine.var();\n                let float_lit = engine.float(*span);\n                let bool_lit = engine.bool(*span);\n                engine.flow(float_lit, vtype_bound)?;\n                engine.flow(bool_lit, vtype_bound)?;\n                Ok((vtype, utype))\n            }\n            \"bot\" => {\n                let (vtype, _) = engine.var();\n                let (utype_value, utype) = engine.var();\n                let float_lit = engine.float_use(*span);\n                let bool_lit = engine.bool_use(*span);\n                engine.flow(utype_value, float_lit)?;\n                engine.flow(utype_value, bool_lit)?;\n                Ok((vtype, utype))\n            }\n            \"_\" => Ok(engine.var()),\n            _ => Err(SyntaxError::new1(\n                \"SyntaxError: Unrecognized simple type (choices are bool, float, int, str, number, null, top, bot, or _)\",\n                *span,\n            )),\n        },\n        Nullable(lhs, span) => {\n            let lhs_type = parse_type(engine, bindings, lhs)?;\n            let utype = engine.null_check_use(lhs_type.1, *span);\n\n            let (vtype, vtype_bound) = engine.var();\n            let null_lit = engine.null(*span);\n            engine.flow(lhs_type.0, vtype_bound)?;\n            engine.flow(null_lit, vtype_bound)?;\n            Ok((vtype, utype))\n        }\n        Record(ext, fields, span) => {\n            let (utype_value, utype) = engine.var();\n\n            let vtype_wildcard = if let Some(ext) = ext {\n                let ext_type = parse_type(engine, bindings, ext)?;\n                engine.flow(utype_value, ext_type.1)?;\n                Some(ext_type.0)\n            } else {\n                None\n            };\n\n            // Must do this *after* parsing wildcard as wildcards are unguarded\n            bindings.level += 1;\n            let mut vtype_fields = Vec::new();\n            for ((name, name_span), wrapped_expr) in fields {\n                let wrapped_type = parse_type(engine, bindings, wrapped_expr)?;\n\n                let obj_use = engine.obj_use((name.clone(), wrapped_type.1), *name_span);\n                engine.flow(utype_value, obj_use)?;\n                vtype_fields.push((name.clone(), wrapped_type.0));\n            }\n            bindings.level -= 1;\n\n            let vtype = engine.obj(vtype_fields, vtype_wildcard, *span);\n            Ok((vtype, utype))\n        }\n        Ref(lhs, (rw, span)) => {\n            use ast::Readability::*;\n            bindings.level += 1;\n            let lhs_type = parse_type(engine, bindings, lhs)?;\n            bindings.level -= 1;\n\n            let write = if *rw == ReadOnly {\n                (None, None)\n            } else {\n                (Some(lhs_type.1), Some(lhs_type.0))\n            };\n            let read = if *rw == WriteOnly {\n                (None, None)\n            } else {\n                (Some(lhs_type.0), Some(lhs_type.1))\n            };\n\n            let vtype = engine.reference(write.0, read.0, *span);\n            let utype = engine.reference_use(write.1, read.1, *span);\n            Ok((vtype, utype))\n        }\n        TypeVar((name, span)) => {\n            if let Some((res, lvl, _)) = bindings.m.get(name.as_str()).copied() {\n                if lvl <= bindings.level {\n                    Ok(res)\n                } else {\n                    Err(SyntaxError::new1(\n                    format!(\"SyntaxError: Unguarded type variable {}. Recursive type variables must be nested within a case, record field, function, or ref\", name),\n                    *span))\n                }\n            } else {\n                Err(SyntaxError::new1(\n                    format!(\"SyntaxError: Undefined type variable {}\", name),\n                    *span,\n                ))\n            }\n        }\n    }\n}\n\nfn parse_type_signature(engine: &mut TypeCheckerCore, tyexpr: &ast::TypeExpr) -> Result<(Value, Use)> {\n    let mut bindings = TypeVarBindings::new();\n    parse_type(engine, &mut bindings, tyexpr)\n}\n\nfn process_let_pattern(engine: &mut TypeCheckerCore, bindings: &mut Bindings, pat: &ast::LetPattern) -> Result<Use> {\n    use ast::LetPattern::*;\n\n    let (arg_type, arg_bound) = engine.var();\n    match pat {\n        Var(name) => {\n            bindings.insert(name.clone(), arg_type);\n        }\n        Record(pairs) => {\n            let mut field_names = HashMap::with_capacity(pairs.len());\n\n            for ((name, name_span), sub_pattern) in pairs {\n                if let Some(old_span) = field_names.insert(&*name, *name_span) {\n                    return Err(SyntaxError::new2(\n                        \"SyntaxError: Repeated field pattern name\",\n                        *name_span,\n                        \"Note: Field was already bound here\",\n                        old_span,\n                    ));\n                }\n\n                let field_bound = process_let_pattern(engine, bindings, &*sub_pattern)?;\n                let bound = engine.obj_use((name.clone(), field_bound), *name_span);\n                engine.flow(arg_type, bound)?;\n            }\n        }\n    };\n    Ok(arg_bound)\n}\n\nfn check_expr(engine: &mut TypeCheckerCore, bindings: &mut Bindings, expr: &ast::Expr) -> Result<Value> {\n    use ast::Expr::*;\n\n    match expr {\n        BinOp((lhs_expr, lhs_span), (rhs_expr, rhs_span), op_type, op, full_span) => {\n            use ast::OpType::*;\n            let lhs_type = check_expr(engine, bindings, lhs_expr)?;\n            let rhs_type = check_expr(engine, bindings, rhs_expr)?;\n\n            Ok(match op_type {\n                IntOp => {\n                    let lhs_bound = engine.int_use(*lhs_span);\n                    let rhs_bound = engine.int_use(*rhs_span);\n                    engine.flow(lhs_type, lhs_bound)?;\n                    engine.flow(rhs_type, rhs_bound)?;\n                    engine.int(*full_span)\n                }\n                FloatOp => {\n                    let lhs_bound = engine.float_use(*lhs_span);\n                    let rhs_bound = engine.float_use(*rhs_span);\n                    engine.flow(lhs_type, lhs_bound)?;\n                    engine.flow(rhs_type, rhs_bound)?;\n                    engine.float(*full_span)\n                }\n                StrOp => {\n                    let lhs_bound = engine.str_use(*lhs_span);\n                    let rhs_bound = engine.str_use(*rhs_span);\n                    engine.flow(lhs_type, lhs_bound)?;\n                    engine.flow(rhs_type, rhs_bound)?;\n                    engine.str(*full_span)\n                }\n                IntOrFloatCmp => {\n                    let lhs_bound = engine.int_or_float_use(*lhs_span);\n                    let rhs_bound = engine.int_or_float_use(*rhs_span);\n                    engine.flow(lhs_type, lhs_bound)?;\n                    engine.flow(rhs_type, rhs_bound)?;\n                    engine.bool(*full_span)\n                }\n                AnyCmp => engine.bool(*full_span),\n            })\n        }\n        Block(statements, rest_expr) => {\n            assert!(statements.len() >= 1);\n            let mark = bindings.unwind_point();\n\n            for stmt in statements.iter() {\n                check_statement(engine, bindings, stmt)?;\n            }\n\n            let result_type = check_expr(engine, bindings, rest_expr)?;\n            bindings.unwind(mark);\n            Ok(result_type)\n        }\n        Call(func_expr, arg_expr, span) => {\n            let func_type = check_expr(engine, bindings, func_expr)?;\n            let arg_type = check_expr(engine, bindings, arg_expr)?;\n\n            let (ret_type, ret_bound) = engine.var();\n            let bound = engine.func_use(arg_type, ret_bound, *span);\n            engine.flow(func_type, bound)?;\n            Ok(ret_type)\n        }\n        Case((tag, span), val_expr) => {\n            let val_type = check_expr(engine, bindings, val_expr)?;\n            Ok(engine.case((tag.clone(), val_type), *span))\n        }\n        FieldAccess(lhs_expr, name, span) => {\n            let lhs_type = check_expr(engine, bindings, lhs_expr)?;\n\n            let (field_type, field_bound) = engine.var();\n            let bound = engine.obj_use((name.clone(), field_bound), *span);\n            engine.flow(lhs_type, bound)?;\n            Ok(field_type)\n        }\n        FuncDef(((arg_pattern, body_expr), span)) => {\n            let (arg_bound, body_type) = bindings.in_child_scope(|bindings| {\n                let arg_bound = process_let_pattern(engine, bindings, arg_pattern)?;\n                let body_type = check_expr(engine, bindings, body_expr)?;\n                Ok((arg_bound, body_type))\n            })?;\n            Ok(engine.func(arg_bound, body_type, *span))\n        }\n        If((cond_expr, span), then_expr, else_expr) => {\n            // Handle conditions of the form foo == null and foo != null specially\n            if let BinOp((lhs, _), (rhs, _), ast::OpType::AnyCmp, op, ..) = &**cond_expr {\n                if let Variable((name, _)) = &**lhs {\n                    if let Literal(ast::Literal::Null, ..) = **rhs {\n                        if let Some(scheme) = bindings.get(name.as_str()) {\n                            if let Scheme::Mono(lhs_type) = scheme {\n                                // Flip order of branches if they wrote if foo == null instead of !=\n                                let (ok_expr, else_expr) = match op {\n                                    ast::Op::Neq => (then_expr, else_expr),\n                                    ast::Op::Eq => (else_expr, then_expr),\n                                    _ => unreachable!(),\n                                };\n\n                                let (nnvar_type, nnvar_bound) = engine.var();\n                                let bound = engine.null_check_use(nnvar_bound, *span);\n                                engine.flow(*lhs_type, bound)?;\n\n                                let ok_type = bindings.in_child_scope(|bindings| {\n                                    bindings.insert(name.clone(), nnvar_type);\n                                    check_expr(engine, bindings, ok_expr)\n                                })?;\n                                let else_type = check_expr(engine, bindings, else_expr)?;\n\n                                let (merged, merged_bound) = engine.var();\n                                engine.flow(ok_type, merged_bound)?;\n                                engine.flow(else_type, merged_bound)?;\n                                return Ok(merged);\n                            }\n                        }\n                    }\n                }\n            }\n\n            let cond_type = check_expr(engine, bindings, cond_expr)?;\n            let bound = engine.bool_use(*span);\n            engine.flow(cond_type, bound)?;\n\n            let then_type = check_expr(engine, bindings, then_expr)?;\n            let else_type = check_expr(engine, bindings, else_expr)?;\n\n            let (merged, merged_bound) = engine.var();\n            engine.flow(then_type, merged_bound)?;\n            engine.flow(else_type, merged_bound)?;\n            Ok(merged)\n        }\n        Literal(type_, (code, span)) => {\n            use ast::Literal::*;\n            let span = *span;\n            Ok(match type_ {\n                Bool => engine.bool(span),\n                Float => engine.float(span),\n                Int => engine.int(span),\n                Null => engine.null(span),\n                Str => engine.str(span),\n            })\n        }\n        Match(match_expr, cases, span) => {\n            let match_type = check_expr(engine, bindings, match_expr)?;\n            let (result_type, result_bound) = engine.var();\n\n            // Result types from the match arms\n            let mut case_type_pairs = Vec::with_capacity(cases.len());\n            let mut wildcard_type = None;\n\n            // Pattern reachability checking\n            let mut case_names = HashMap::with_capacity(cases.len());\n            let mut wildcard = None;\n\n            for ((pattern, pattern_span), rhs_expr) in cases {\n                if let Some(old_span) = wildcard {\n                    return Err(SyntaxError::new2(\n                        \"SyntaxError: Unreachable match pattern\",\n                        *pattern_span,\n                        \"Note: Unreachable due to previous wildcard pattern here\",\n                        old_span,\n                    ));\n                }\n\n                use ast::MatchPattern::*;\n                match pattern {\n                    Case(tag, name) => {\n                        if let Some(old_span) = case_names.insert(&*tag, *pattern_span) {\n                            return Err(SyntaxError::new2(\n                                \"SyntaxError: Unreachable match pattern\",\n                                *pattern_span,\n                                \"Note: Unreachable due to previous case pattern here\",\n                                old_span,\n                            ));\n                        }\n\n                        let (wrapped_type, wrapped_bound) = engine.var();\n                        let rhs_type = bindings.in_child_scope(|bindings| {\n                            bindings.insert(name.clone(), wrapped_type);\n                            check_expr(engine, bindings, rhs_expr)\n                        })?;\n\n                        case_type_pairs.push((tag.clone(), (wrapped_bound, (rhs_type, result_bound))));\n                    }\n                    Wildcard(name) => {\n                        wildcard = Some(*pattern_span);\n\n                        let (wrapped_type, wrapped_bound) = engine.var();\n                        let rhs_type = bindings.in_child_scope(|bindings| {\n                            bindings.insert(name.clone(), wrapped_type);\n                            check_expr(engine, bindings, rhs_expr)\n                        })?;\n\n                        wildcard_type = Some((wrapped_bound, (rhs_type, result_bound)));\n                    }\n                }\n            }\n\n            let bound = engine.case_use(case_type_pairs, wildcard_type, *span);\n            engine.flow(match_type, bound)?;\n\n            Ok(result_type)\n        }\n        NewRef(expr, span) => {\n            let expr_type = check_expr(engine, bindings, expr)?;\n            let (read, write) = engine.var();\n            engine.flow(expr_type, write)?;\n            Ok(engine.reference(Some(write), Some(read), *span))\n        }\n        Record(proto, fields, span) => {\n            let proto_type = match proto {\n                Some(expr) => Some(check_expr(engine, bindings, expr)?),\n                None => None,\n            };\n\n            let mut field_names = HashMap::with_capacity(fields.len());\n            let mut field_type_pairs = Vec::with_capacity(fields.len());\n            for ((name, name_span), expr) in fields {\n                if let Some(old_span) = field_names.insert(&*name, *name_span) {\n                    return Err(SyntaxError::new2(\n                        \"SyntaxError: Repeated field name\",\n                        *name_span,\n                        \"Note: Field was already defined here\",\n                        old_span,\n                    ));\n                }\n\n                let t = check_expr(engine, bindings, expr)?;\n                field_type_pairs.push((name.clone(), t));\n            }\n            Ok(engine.obj(field_type_pairs, proto_type, *span))\n        }\n        RefGet((expr, span)) => {\n            let expr_type = check_expr(engine, bindings, expr)?;\n\n            let (cell_type, cell_bound) = engine.var();\n            let bound = engine.reference_use(None, Some(cell_bound), *span);\n            engine.flow(expr_type, bound)?;\n            Ok(cell_type)\n        }\n        RefSet((lhs_expr, lhs_span), rhs_expr) => {\n            let lhs_type = check_expr(engine, bindings, lhs_expr)?;\n            let rhs_type = check_expr(engine, bindings, rhs_expr)?;\n\n            let bound = engine.reference_use(Some(rhs_type), None, *lhs_span);\n            engine.flow(lhs_type, bound)?;\n            Ok(rhs_type)\n        }\n        Typed(expr, sig) => {\n            let expr_type = check_expr(engine, bindings, expr)?;\n            let sig_type = parse_type_signature(engine, sig)?;\n            engine.flow(expr_type, sig_type.1)?;\n            Ok(sig_type.0)\n        }\n        Variable((name, span)) => {\n            if let Some(scheme) = bindings.get(name.as_str()) {\n                match scheme {\n                    Scheme::Mono(v) => Ok(*v),\n                    Scheme::PolyLet(cb) => cb.borrow_mut().check(engine),\n                    Scheme::PolyLetRec(cb, i) => Ok(cb.borrow_mut().check(engine)?[*i].0),\n                }\n            } else {\n                Err(SyntaxError::new1(format!(\"SyntaxError: Undefined variable {}\", name), *span))\n            }\n        }\n    }\n}\n\nfn check_let_def(\n    engine: &mut TypeCheckerCore,\n    bindings: &mut Bindings,\n    lhs: &ast::LetPattern,\n    expr: &ast::Expr,\n) -> Result<()> {\n    use ast::LetPattern::*;\n    if let ast::Expr::FuncDef((_, span)) = expr {\n        let name = match lhs {\n            Var(name) => name.to_owned(),\n            _ => return Err(SyntaxError::new1(format!(\"TypeError: Cannot destructure function\"), *span)),\n        };\n\n        let saved_bindings = Bindings {\n            m: bindings.m.clone(),\n            changes: Vec::new(),\n        };\n        let saved_expr = expr.clone();\n\n        let f = PolyLet::new(saved_bindings, saved_expr, engine)?;\n        bindings.insert_scheme(name, Scheme::PolyLet(Rc::new(RefCell::new(f))));\n    } else {\n        let var_type = check_expr(engine, bindings, expr)?;\n        let bound = process_let_pattern(engine, bindings, lhs)?;\n        engine.flow(var_type, bound)?;\n    }\n    Ok(())\n}\n\nfn check_let_rec_defs(\n    engine: &mut TypeCheckerCore,\n    bindings: &mut Bindings,\n    defs: &Vec<(String, Box<ast::Expr>)>,\n) -> Result<()> {\n    let saved_bindings = Bindings {\n        m: bindings.m.clone(),\n        changes: Vec::new(),\n    };\n    let saved_defs = defs.clone();\n\n    let f = PolyLetRec::new(saved_bindings, saved_defs, engine)?;\n    let f = Rc::new(RefCell::new(f));\n\n    for (i, (name, _)) in defs.iter().enumerate() {\n        bindings.insert_scheme(name.clone(), Scheme::PolyLetRec(f.clone(), i));\n    }\n    Ok(())\n}\n\nfn check_statement(engine: &mut TypeCheckerCore, bindings: &mut Bindings, def: &ast::Statement) -> Result<()> {\n    use ast::Statement::*;\n    match def {\n        Empty => {}\n        Expr(expr) => {\n            check_expr(engine, bindings, expr)?;\n        }\n        LetDef((pattern, var_expr)) => {\n            check_let_def(engine, bindings, pattern, var_expr)?;\n        }\n        LetRecDef(defs) => {\n            check_let_rec_defs(engine, bindings, defs)?;\n        }\n        Println(exprs) => {\n            for expr in exprs {\n                check_expr(engine, bindings, expr)?;\n            }\n        }\n    };\n    Ok(())\n}\n\npub struct TypeckState {\n    core: TypeCheckerCore,\n    bindings: Bindings,\n}\nimpl TypeckState {\n    pub fn new() -> Self {\n        Self {\n            core: TypeCheckerCore::new(),\n            bindings: Bindings::new(),\n        }\n    }\n\n    pub fn check_script(&mut self, parsed: &[ast::Statement]) -> Result<()> {\n        // Create temporary copy of the entire type state so we can roll\n        // back all the changes if the script contains an error.\n        let temp = self.core.save();\n        assert!(self.bindings.changes.is_empty());\n        let mark = self.bindings.unwind_point();\n\n        for item in parsed {\n            if let Err(e) = check_statement(&mut self.core, &mut self.bindings, item) {\n                // Roll back changes to the type state and bindings\n                self.core.restore(temp);\n                self.bindings.unwind(mark);\n                return Err(e);\n            }\n        }\n\n        // Now that script type-checked successfully, make the global definitions permanent\n        // by removing them from the changes rollback list\n        self.bindings.changes.clear();\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/utils.rs",
    "content": "#![allow(dead_code)]\npub fn set_panic_hook() {\n    // When the `console_error_panic_hook` feature is enabled, we can call the\n    // `set_panic_hook` function at least once during initialization, and then\n    // we will get better error messages if our code ever panics.\n    //\n    // For more details see\n    // https://github.com/rustwasm/console_error_panic_hook#readme\n    #[cfg(feature = \"console_error_panic_hook\")]\n    console_error_panic_hook::set_once();\n}\n"
  },
  {
    "path": "tests/web.rs",
    "content": "//! Test suite for the Web and headless browsers.\n\n#![cfg(target_arch = \"wasm32\")]\n\nextern crate wasm_bindgen_test;\nuse wasm_bindgen_test::*;\n\nwasm_bindgen_test_configure!(run_in_browser);\n\n#[wasm_bindgen_test]\nfn pass() {\n    assert_eq!(1 + 1, 2);\n}\n"
  }
]