valhallac

Compiler for set-theoretic programming language.
git clone git://git.knutsen.co/valhallac
Log | Files | Refs | README | LICENSE

commit 4439a2022ed785a16bc1a08a973b59bad2b12760
parent 003b52dfd433c0d036ee435213c608f82f974320
Author: Demonstrandum <moi@knutsen.co>
Date:   Thu, 11 Jun 2020 19:20:14 +0100

Resolve devel conflicts.

Diffstat:
M.gitignore | 4++++
D<main> | 0
MCargo.toml | 19++++++++++++++++---
MREADME.md | 32++++++++++++++++++++++++++++----
Massets/logo.svg | 52+++++++++++++++++++++++++++-------------------------
Aassets/valhalla_compiler_layout.svg | 4++++
Aassets/valhalla_compiler_layout.svg.png | 0
Abuild.rs | 42++++++++++++++++++++++++++++++++++++++++++
Mcurrent_compiler_test.md | 122++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Aempty.vh | 0
Asamples/ability.vh | 35+++++++++++++++++++++++++++++++++++
Msamples/call_tree.vh | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msamples/currying_infix.vh | 5+++--
Asamples/equ_solve.vh | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asamples/juxtaposition.vh | 24++++++++++++++++++++++++
Asamples/list.vh | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asamples/match.vh | 32++++++++++++++++++++++++++++++++
Asamples/peano.vh | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asamples/product_type.vh | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msamples/raw_hello_world.vh | 2+-
Msamples/set_comp.vh | 4++--
Asamples/singleton_return.vh | 13+++++++++++++
Msamples/subsets.vh | 8++++++--
Asamples/vect.vh | 25+++++++++++++++++++++++++
Ascripts/inspect.sh | 6++++++
Msrc/bin.rs | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/compiler/block.rs | 125+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/compiler/element.rs | 8++++----
Msrc/compiler/instructions.rs | 72++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/compiler/internal_functions.rs | 3+--
Msrc/compiler/marshal.rs | 186++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/compiler/types.rs | 3+--
Msrc/err.rs | 126++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/lib.rs | 20++++++++++++++------
Dsrc/syntax/analyser.rs | 359-------------------------------------------------------------------------------
Asrc/syntax/analysis/beta_reduction.rs | 0
Asrc/syntax/analysis/constant_fold.rs | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/syntax/analysis/mod.rs | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/syntax/analysis/type_balancer.rs | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/syntax/analysis/type_checker.rs | 171+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/syntax/analysis/type_inference.rs | 0
Asrc/syntax/analysis/type_resolver.rs | 424+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/syntax/ast.rs | 143+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/syntax/lexer.rs | 39++++++++++++++++++++++++++++++---------
Msrc/syntax/location.rs | 2+-
Msrc/syntax/mod.rs | 31++++++++++++++++++++++++++-----
Msrc/syntax/operators.rs | 65++++++++++++++++++++++++++++++++++-------------------------------
Msrc/syntax/parser.rs | 88++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/syntax/token.rs | 48+++++++++++++++++++++++++++++-------------------
Dtest.vh | 5-----
Atest_source.vh | 16++++++++++++++++
51 files changed, 2377 insertions(+), 760 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,6 @@ +# Personal +.ideas/ + # Generated by Cargo # will have compiled files and executables target/ @@ -31,6 +34,7 @@ Thumbs.db *.exe *.o *.so +*.out # Packages *.7z diff --git a/<main> b/<main> Binary files differ. diff --git a/Cargo.toml b/Cargo.toml @@ -1,18 +1,27 @@ [package] name = "valhallac" description = "Valhalla Language frontend, parser and AST compiler." +authors = ["Demonstrandum <moi@knutsen.co>"] + homepage = "https://knutsen.co" repository = "https://github.com/Demonstrandum/valhalla" documentation = "https://github.com/Demonstrandum/valhalla" + keywords = ["set-theory", "programming", "language", "parser", "compiler"] categories = ["parser-implementations", "parsing", "encoding", "command-line-interface"] -#license = "GPL-3.0" -license-file = "LICENSE.md" + +license = "GPL-3.0" readme = "README.md" + version = "0.1.0" -authors = ["Demonstrandum <moi@knutsen.co>"] edition = "2018" +build = "build.rs" + + +[features] +debug = [] # No deps. + [lib] name = "valhallac" path = "src/lib.rs" @@ -24,9 +33,13 @@ path = "src/bin.rs" [dependencies] lazy_static = "1.3.0" regex = "1" +toml = "0.5.6" unindent = "0.1.3" snailquote = "0.2.0" unicode-width = "0.1.5" enum-primitive-derive = "^0.1" num-traits = "^0.1" colored = "1.8" + +[build-dependencies] +toml = "0.5.6" diff --git a/README.md b/README.md @@ -3,9 +3,13 @@ </p> # Valhalla Programming Language +This is the parser and compiler for Valhalla, which excludes the virtual +machine that the compiled bytecode runs on, which is, +[Brokkr VM](https://github.com/Demonstrandum/brokkr). ## IN (HEAVY) DEVELOPMENT + What's been done so far on the front-end: - [ ] Parser @@ -35,13 +39,33 @@ What's been done so far on the front-end: come from nested closures (nested closures implement currying). - [ ] Optimise functions for tail calls. - [ ] Track variable and function types. - - [ ] Marshaling, i.e. serialising the bytecode and storing it in a file + - [x] Marshaling, i.e. serialising the bytecode and storing it in a file for future interpretation and execution by the virtual machine. - [ ] ... The VM, i.e. the backend for the language, is being developed independently and will have its own progress and check-list updates. +### Compile & Run + +In your shell, in the root of this repository, you may write: +```console +cargo run [source-file-to-compile.vh] [-o out-file] [-v] +``` + +or, have the compiler print out debug information like token streams, syntax trees, symbol tables, bytecode instructions, &ct., use `--features=debug`: + +```console +cargo run --features=debug [source-file.vh] +``` + +For example, you can run. +```console +cargo run test_source.vh -v # (verbose) +``` +to demonstrate compilation with the included test-file (`test_source.vh`). +The argument of a source-file to compile is, of course, necessary. + ### Example of what the compiler currently does: [current_compiler_test.md](https://github.com/valhalla-lang/valhallac/blob/master/current_compiler_test.md) @@ -65,9 +89,10 @@ verify proofs and such in and around set theory. The language is a general purpose, but instead of being totally object-oriented, or functional, etc., it's just set theory based. From what I've -gathered, it's not a very popular paradigm. +gathered, it's not a very popular paradigm... Likely for good reason, but hey, +it might be interesting. ### Dependencies Yikes... -![deps](https://github.com/Demonstrandum/valhalla/raw/master/graph.png)- \ No newline at end of file +![deps](https://github.com/Demonstrandum/valhalla/raw/master/graph.png) diff --git a/assets/logo.svg b/assets/logo.svg @@ -28,17 +28,17 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="0.49497475" - inkscape:cx="646.98434" - inkscape:cy="306.64558" + inkscape:zoom="1.979899" + inkscape:cx="37.694361" + inkscape:cy="307.05277" inkscape:document-units="mm" - inkscape:current-layer="layer1" + inkscape:current-layer="flowRoot6368" showgrid="false" - inkscape:window-width="1316" - inkscape:window-height="718" - inkscape:window-x="25" - inkscape:window-y="25" - inkscape:window-maximized="0" + inkscape:window-width="2920" + inkscape:window-height="1820" + inkscape:window-x="40" + inkscape:window-y="140" + inkscape:window-maximized="1" showguides="false" units="px" width="49px" /> @@ -50,7 +50,7 @@ <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> + <dc:title /> </cc:Work> </rdf:RDF> </metadata> @@ -538,21 +538,23 @@ x="39.399548" y="238.43359" ry="0.27640286" /> - <flowRoot - xml:space="preserve" - id="flowRoot6368" - style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'PR Viking';-inkscape-font-specification:'PR Viking';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none" - transform="matrix(0.33438337,0,0,0.39829988,-49.888756,-9.8397459)"><flowRegion - id="flowRegion6370" - style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'PR Viking';-inkscape-font-specification:'PR Viking';text-align:center;text-anchor:middle"><rect - id="rect6372" - width="87.63073" - height="77.276672" - x="263.39728" - y="615.67566" - style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'PR Viking';-inkscape-font-specification:'PR Viking';text-align:center;text-anchor:middle" /></flowRegion><flowPara - id="flowPara6374" - style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:61.33333588px;font-family:'PR Viking';-inkscape-font-specification:'PR Viking';fill:#ffffff">VH</flowPara></flowRoot> <path + <g + aria-label="VH" + transform="matrix(0.33438337,0,0,0.39829988,-49.888757,-10.006789)" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;line-height:1.25;font-family:'PR Viking';-inkscape-font-specification:'PR Viking';text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#f2ffff;fill-opacity:1;stroke:none" + id="flowRoot6368"> + <path + d="m 278.52214,631.77108 q 0.53906,0.23958 1.70703,0.53906 0.50912,0 1.46745,-0.17969 0.98828,-0.17968 1.46745,-0.17968 0.89844,0.11979 2.66536,0.23958 0.17969,0 0.50912,-0.20964 0.35937,-0.23958 0.56901,-0.20963 0.20963,0.0898 0.29948,0.41927 0.11979,0.35937 -0.0899,0.50911 -0.35937,0.50912 -1.19791,0.98829 -1.01823,0.59895 -1.31771,0.89843 -0.77865,0.71875 -0.77865,1.61719 0,0.53906 0.29948,1.4375 0.38933,1.22786 0.47917,1.61719 0.17969,0.92838 0.53906,2.8151 0.0899,0.7487 0.56901,2.2461 0.41927,0.26953 1.01823,0.92838 0.20964,0.47917 0.32943,1.31771 0.14974,1.01823 0.26953,1.31771 0.11979,0.29948 0.38932,0.80859 0.20964,0.6888 0.62891,2.00651 0.17969,0.29948 0.38932,0.92839 -0.20963,0.26953 -0.26953,0.77864 0.0898,0.17969 0.53906,0.26954 0.47917,0.0599 0.56901,0.26953 0.1198,0.44922 0,1.3177 -0.11979,0.86849 0,1.19792 0.20964,0.89844 1.16797,2.54557 0.62891,1.04818 0.8086,1.04818 0.32942,0 0.41927,-0.95833 0.0299,-0.59896 0.0898,-1.13802 0.0299,-0.11979 0.95833,-2.69532 0.65886,-1.82682 2.12631,-5.57031 0.29948,-0.6289 0.77864,-1.97656 0.03,-0.80859 0.38933,-2.39583 0.53906,-0.68881 1.34765,-2.12631 0.41927,-0.92838 0.6888,-2.42578 -0.0599,-0.89844 0.1198,-2.75521 0.20963,-0.95833 0.20963,-1.3776 0,-0.59896 -0.80859,-1.04818 -1.13802,-0.65885 -2.06641,-1.88672 -0.17969,-0.20963 0,-0.50911 0.11979,-0.32943 0.35938,-0.41927 0.29948,-0.0299 0.83854,0.20963 0.53906,0.20964 0.77864,0.20964 -0.14974,0 3.59375,-0.23958 2.03646,-0.17969 2.12631,-0.17969 0.20963,0.0299 0.47916,0.23958 0.26953,0.17969 0.44922,0.17969 0.20964,0 0.65886,-0.26953 0.44921,-0.26953 0.6888,-0.26953 0.23958,0.0299 0.53906,0.35937 0.32943,0.29948 0.53906,0.29948 0.17969,0 0.47917,-0.26953 0.29948,-0.29948 0.44922,-0.26953 0.20963,0.0898 0.35937,0.41927 0.0599,0.35937 -0.0898,0.50911 -0.50912,0.68881 -1.67708,1.4974 -1.34766,1.01823 -1.70704,1.3776 -0.35937,0.35938 -0.35937,0.86849 0,0.17969 0.14974,0.44922 0.17969,0.23959 0.14974,0.35938 -0.0898,0.20963 -0.56901,0.41927 -0.44922,0.17969 -0.53906,0.35937 -0.0899,0.23959 0.14974,0.7487 0.23958,0.50912 0.17968,0.7487 -0.11979,0.32943 -0.6289,0.83854 -0.50912,0.47917 -0.59896,0.77865 -0.11979,0.74869 -0.38932,2.36588 -0.20964,0.92839 -0.41928,1.4375 -0.32942,0.7487 -0.92838,2.00651 -0.38932,1.07813 -0.59896,1.85677 -0.17969,0.77865 -0.59896,2.30599 -0.41927,0.77865 -1.07812,2.39584 0.0599,0.6289 -0.11979,1.94661 -0.1198,0.41927 -0.77865,1.37761 -0.65885,0.92838 -0.6888,1.58724 -0.03,0.6289 -0.14974,1.88671 -0.20964,1.07813 -0.8086,2.2461 -0.32942,0.38932 -0.89843,1.34765 -0.11979,0.53907 -0.41927,1.5573 -0.32943,0.89843 -0.92839,2.69531 -0.20964,0.80859 -0.62891,2.36588 0,0 -0.47916,2.63542 -0.26953,1.58724 -1.01823,2.21615 -0.11979,0.0898 -0.20964,0.0898 -0.35937,0 -0.65885,-1.10807 -0.14974,-0.8086 -0.32943,-1.70703 -0.0898,-0.68881 -0.38932,-2.00651 -0.44922,-0.41927 -1.22787,-1.37761 -0.0898,-0.26953 0.0599,-0.65885 0.14974,-0.41927 0.0599,-0.59896 -0.0899,-0.14974 -0.44922,-0.35938 -0.35938,-0.23958 -0.41927,-0.44921 -0.0599,-0.1198 0.17968,-0.35938 0.23959,-0.26953 0.17969,-0.44922 -0.14974,-0.35937 -1.22786,-1.97656 -0.8086,-1.31771 -0.8086,-1.97656 0,-0.20964 0.11979,-1.01823 0.17969,-0.62891 0.0599,-0.98828 0,-0.1198 -0.50911,-0.1198 -0.26954,-0.0299 -0.29948,-0.44921 0,-0.44922 -0.0899,-1.28777 -0.41927,-0.98828 -1.10807,-2.93489 -0.26953,-1.19792 -0.89844,-3.50391 -0.44922,-1.13802 -1.04818,-2.48567 -0.38932,-0.50912 -0.89843,-1.82683 0.0299,-0.65885 -0.0899,-1.76692 -0.17968,0.0599 -0.38932,0 -0.11979,-0.92839 -0.53906,-2.66537 -0.14974,-0.50911 -0.8086,-1.4974 -0.65885,-0.98828 -0.77864,-1.55729 0,-1.19791 -0.38932,-3.47396 -0.14974,-0.35937 -0.89844,-0.83854 -0.71875,-0.47916 -0.83854,-0.98828 -0.1198,-0.50911 -0.83855,-1.22786 -0.6888,-0.7487 -0.6888,-1.19792 0,-0.59896 0.59896,-0.59896 0.17969,0 0.7487,0.11979 0.56901,0.1198 0.80859,0.0599 0.14974,0 0.41927,-0.20964 0.26953,-0.20963 0.44922,-0.20963 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:61.33333588px;font-family:'PR Viking';-inkscape-font-specification:'PR Viking';fill:#f2ffff;fill-opacity:1" + id="path1017" + inkscape:connector-curvature="0" /> + <path + d="m 313.65105,632.37004 q 2.45573,-0.0898 2.84505,0.0299 0.68881,-0.29947 1.52735,-0.41927 0.17968,0.17969 0.17968,0.38933 0,0.29948 -0.26953,0.83854 -0.23958,0.50911 -0.29948,0.59896 -0.14974,1.16797 -0.44921,3.65364 -0.0899,0.65886 -0.26954,2.00651 -0.0299,0.77865 -0.14974,2.21615 -0.14974,0.0898 -0.26953,0.32943 -0.17968,2.1263 0,3.32422 0,0.0898 0.26953,1.10807 0.26954,1.01823 0.26954,1.64713 0,0.0898 -0.1198,0.29948 -0.0898,0.17969 0,0.26953 0.20964,0.23959 0.65886,0.1198 0.83854,-0.41928 2.63542,-1.13803 0.32942,-0.0599 0.95833,-0.17968 0.50911,-0.23959 1.58724,-0.6888 0.71875,-0.20964 2.24609,-0.62891 0.86849,-0.17969 1.67709,-0.32943 1.01822,-0.26953 1.3776,-0.77864 0.20964,-0.23959 0.20964,-0.62891 0,-0.29948 -0.17969,-1.04818 -0.14974,-0.7487 -0.0899,-1.07812 0.20964,-0.59896 0.44922,-1.73698 0.26953,-3.29427 0,-4.70182 -0.11979,-0.0599 -0.44922,-1.46745 -0.0599,-0.20964 -0.6289,-0.92839 -0.53907,-0.7487 -0.53907,-1.04818 0,-0.38932 0.86849,-0.32942 1.22787,0.0898 1.16797,0.0898 0.98828,-0.17969 1.70703,0.0899 0.38933,0.14973 0.86849,0.0299 0.50912,-0.17968 0.56901,-0.17968 0.0899,0 1.61719,-0.1198 0.89844,-0.0898 1.28776,0.20964 0.11979,0.11979 0.11979,0.35937 0,0.23959 -0.41927,0.86849 -0.50911,0.7487 -0.53906,0.86849 -0.35937,0.59896 -0.80859,1.82683 0.0299,1.01823 0,3.05468 0,0.50912 -0.0899,1.10808 -0.0599,0.56901 0,1.19791 0,0.29948 0.1198,1.01823 0.11979,0.71875 0.11979,1.10808 0,0.11979 -0.26953,1.04817 -0.23959,0.89844 -0.26954,1.55729 -0.0299,0.7487 0.14974,1.07813 0.17969,0.29948 0.0899,1.19792 0.0599,0.29948 0,1.22786 0,0.44922 0.29948,3.32422 0.0599,0.56901 -0.14974,1.19792 -0.20964,0.59896 -0.20964,1.22786 0,0.71875 0.20964,1.88672 0.11979,0.62891 0.11979,1.25781 0,0.59896 -0.17969,1.85677 -0.14974,1.25782 -0.14974,2.15625 -0.14974,0.62891 -0.17969,1.28776 -0.0898,0.71875 0.1198,1.31771 0.20963,0.56901 0.26953,1.73698 0,1.31771 0.14974,1.91667 0.20963,1.01823 0.89843,2.03646 0.59896,0.92838 0.59896,1.28776 0,0.56901 -0.53906,0.56901 -0.14974,0 -0.41927,-0.0299 -0.23958,-0.0299 -0.35938,-0.0299 -0.17968,0 -0.32942,0.0299 -0.29948,0.17969 -0.8086,0 -0.65885,-0.11979 -0.77864,-0.11979 -0.03,0 -0.0899,0.0599 -0.0299,0.0599 -0.0299,0.0599 -1.19792,-0.41927 -1.28776,-0.41927 -0.29948,0.0299 -0.89844,0.29948 -0.56901,0.29948 -0.92839,0.29948 -1.52734,0 -1.61719,-0.50912 -0.11979,-0.47916 0.29948,-1.28776 0.62891,-1.10807 0.68881,-1.28776 -0.0899,-0.11979 -0.1198,-0.41927 0.23959,-0.95833 0.65886,-2.93489 0,-0.29948 0,-0.92839 0,-0.6888 -0.11979,-1.79688 -0.0899,-1.13802 -0.0899,-1.61718 0,-1.31771 0.50912,-2.36589 0.14974,-0.26953 0.0599,-1.01823 -0.14974,-0.95833 -0.0898,-1.07812 0.11979,-1.01823 0.11979,-1.79688 0,-0.65885 -0.20963,-1.34765 -0.17969,-0.71875 -0.17969,-1.19792 -0.0299,-0.38932 0.0898,-1.13802 0.11979,-0.77865 0.11979,-1.10807 0,-1.13803 -0.44921,-1.13803 -0.20964,0 -0.68881,0.29948 -0.59896,0.35938 -0.6888,0.35938 -0.20963,0.20963 -0.65885,0.59896 -0.68881,0 -1.97657,0.20963 -0.6289,0.47917 -1.85677,1.19792 -0.77864,0.11979 -2.30599,0.41927 -2.63541,0.65885 -2.63541,0.80859 0,1.37761 -0.41927,2.90495 -0.38933,1.52735 -0.38933,2.00651 0,0.50912 0.32943,1.22787 0.35938,0.71875 0.35938,1.25781 0,0.38932 -0.14974,1.19792 -0.1198,0.77864 -0.1198,1.10807 0,0.29948 -0.17968,0.86849 -0.14974,0.53906 -0.1198,0.86849 0.20964,1.16797 0.41928,3.44401 0,0.17969 -0.0599,0.50911 -0.0599,0.29948 -0.0599,0.44922 0,0.41927 0.38933,1.28776 0.38932,0.83854 0.38932,1.22787 0,0.20963 -0.0599,0.35937 -0.0599,0.17969 -0.92838,0.17969 -1.01823,-0.0299 -1.13802,0 -0.17969,0.0299 -0.50912,-0.11979 -0.29948,-0.11979 -0.44922,-0.11979 -0.41927,-0.0599 -1.4375,0.23958 -1.01822,0.29948 -1.55729,0.17969 -0.44922,-0.14974 -0.86849,-0.23959 -0.41927,-0.0599 -0.41927,-0.35937 0,-0.38932 0.53906,-1.28776 0.53907,-0.89844 0.59896,-1.19792 0.0599,-0.20963 -0.0299,-0.6289 -0.0899,-0.41928 -0.0899,-0.59896 0,-0.59896 0.0599,-1.64714 0.0299,-1.19791 0.0599,-1.67708 0,-2.57552 0.14974,-7.69662 0,-0.29948 0.11979,-1.04817 0.1198,-0.68881 0.1198,-1.10808 -0.29948,-1.58724 -0.71875,-4.82161 0,-0.32943 0.20963,-0.92839 0.20964,-0.6289 0.26953,-0.89843 0.11979,-1.73698 0,-3.02474 -0.0898,-0.59896 0.0899,-1.46745 0.20963,-0.86849 0.20963,-1.31771 0,-0.44922 -0.17969,-1.25781 -0.17968,-0.83854 -0.17968,-1.34766 0,-0.56901 0.11979,-1.64713 0.14974,-1.07813 0.14974,-1.67709 0,-1.13802 -0.35938,-2.1263 -0.0599,-0.23958 -0.65885,-1.13802 -0.44922,-0.6888 -0.44922,-1.16797 0,-0.71875 0.59896,-0.71875 0.20963,0 0.7487,0.20964 0.53906,0.17968 0.83854,0.17968 1.22786,0.0898 1.10807,0.0898 z" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:61.33333588px;font-family:'PR Viking';-inkscape-font-specification:'PR Viking';fill:#f2ffff;fill-opacity:1" + id="path1019" + inkscape:connector-curvature="0" /> + </g> + <path style="fill:#2b0000;fill-opacity:1;stroke:none;stroke-width:1.32919383;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" id="path6535" sodipodi:type="arc" diff --git a/assets/valhalla_compiler_layout.svg b/assets/valhalla_compiler_layout.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1171px" height="1120px" viewBox="-0.5 -0.5 1171 1120" content="&lt;mxfile host=&quot;app.diagrams.net&quot; modified=&quot;2020-04-11T12:56:41.241Z&quot; agent=&quot;5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4112.0 Safari/537.36&quot; etag=&quot;w-j6gdHpDYej5NxEsM1_&quot; version=&quot;12.9.11&quot; type=&quot;google&quot;&gt;&lt;diagram id=&quot;lDcHPcpqgxoEG1--uGtI&quot; name=&quot;Page-1&quot;&gt;7R3JduO48Wv85iQ/Ltp8tNX2LEmn+407mSSXeRAJSRiThAKCtjVfH6zcAErUSsrT1sFiCSDA2lEoFG/8Wfz+IwHr1WccwujGc8L3G//Tjcf+hhP2j0M2CuJMRhKyJCiUMLcAPKM/oQI6CpqhEKaVhhTjiKJ1FRjgJIEBrcAAIfit2myBo+qoa7CEBuA5AJEJ/Q2FdCWhU29SwH+CaLnSI7vjO/lLDHRj9STpCoT4rQTyH2/8GcGYym/x+wxGHHsaL7LfU8Ov+cQITGibDtPfp5/9b8u/ffvv6Kff4W9ffnz9aTwYqdu8gihTT6xmSzcaBW8rROHzGgT8+o3R+cZ/WNE4Ylcu+woitEzY9wgu2DweFiiKZjjChIESnLA+DyFIVzBUzfnFV0ApJImAeA6HqllAQuF74/O5OdYYv0EcQ0o2rInqMPB8hWnFawP3TgHeCsrdaXKsylSbKCBQ3LLM714glH1RON0Dv+70w+B3eFdFrzsysTscWbDr3Z0Au5vnL79Mv/785M7xL19+cX0KYDiY7EYuwVkSCuQ4DA07UH0Mbg1EWtDdiNtRFbVDE7M5L5cx6w/PhVnPwOzf4TtiapEB7xMQbVKUGriGIVOd6hITusJLzJo+FtCHKjWKNn/HeK1w/AekdKPsAMgorlKoEdcpzkgAtzyRNh6ALCHd0s6X7fizbKUcgRGg6LVqJmx0UF2/YsSmnFPcHVZJnhs8fQs5UdWrRs18GocT2NT7sxUgIGC8zcDPlEAQHydMJxCLaRVHE4tY2NT5+FxSMTKQ9hWQFCXL6xIFv6UoDE8tCkch3zeQ/w2/wKS33OpOu2bXOxNjmzVkkF9hiqOMIpxcF+MOWzLutCXjKsI5t56S7EOVum6CF4sUnkVfDw1a3s9TyhU2F4BNQsE7+/KNQNg3OfAsXvhl5cA1jZ0UhHHEPeo5s3jjJf/2lWC2oAPXJxjTloKhnYyeqHRzcSQ00ytDYj95eeh3zsvmmsfOyz8nC8gePDBx2GtO1hy6m5XHvWJlPe8SXR7f1wSmqVAmDidSb7jad/y+sXWjrzJbweDl6lxszZ272bitr3IhNh4bdHjKomjTdw7u3snwTCfjy5qiGKXKn3h6RvE6QgsUXKGD4bb1MLx+eRiW+OsMEwID2n+evugC0hq9NqOAn2CAScf8W2Otwxjas/gZ9gh+l+zrmV6F1iL95dtLhumsJDNt2GOM+Dr5YUMZ/4YWb3mBCecsEKzYvzkBCf8yE3QOMpIyQkeb6+H4ZkZuwfCjCzG8fZJmiPXnkD0qWvA5PFM2hcCy1onXDLcdbkhclEDusEsK+bYdY0mQBRbBsEDtoDHw/zK+u82Q5DvirwxSpCuoC9+ZYguEoIKEazeh6QSgTvAAJykFYjSYr7JSs52GsCeVk9PgyzMKH3+WY8affOKfbhjId+wMlIdDh+OxCjP0IiC67WFN34Qbxqf7JMFUfj+ZkTxq93yku/TFSA5N5+4YOdZ90zVIKpjWDflNB6mQjnvWwPXW7+ZdZjheowhqDVAX58d3Zo0p/znN5pSRdbvIy7n8FTWB39a7HV7Kvd06zXLUR1KV53NRgBIR+HGkz+bgRCzaslSI9Xwjm1kNQdfS7lSlfeDaUjrGF5V307M6Rt75xncupw7kdwXU4lhvt9Q9kMaUEvwCS/KonrhZThkPkM2/+Yi3I335HzUBcfHpvXK1UVcnlO9h22yWyZHibU9e0cYr5+6WySv3hIBNqdmaN0ibx3E109YHKgRB3vKknsXQdC3+Ad9KXB4WJMp1E4E0I4n4aUFwzGmzgoLpucmSewB4kYN7qrdcg7Kd6y2vRZLfBdRED4TebZv+cB6hd/1xhTfOJPNTz7EN89RSRVxCQbhmMsanTQJinh8pPBQRTJLBCgWJUPLCVQNzbruX8sm4iuLO1yJmkP4rhOsVjiwORXlT5dojPiNToK3tjhXoo4hj+o1FGNV5iHDw0j1H11fX+aGLzljaknN0H3LvgN0ehICC3HcGaQrjuY3VgRhmrlDsML2HxSpILIDtOY/Xxf+TlvzvNgSsLiMApgOidJDw9noiAXWd3gMJMBN2fyNIRG8EHz+JQM9V868tWcne0OuSgS25SSrQJhi40OanS6Q+KS/7lpNGF94IN9OTrp11vZas6007ZV3PQLwMAgNhMe1KpGvvowf8anrUKnbOJV5ETv+FCM34eS5LAB3EHEnJPOX/PoNghZIjsVw5RmeE3PLQ2vbQ+EmJNOz8NJFlGakT+WuoZk9Nt0Ut1VlFy/FFfXg0YFiCDP7AcchXpffqhxiFoVBONgJWSdyobE55znFioYB/Ngo0x8SzqL5zFKE6ZK4B5rEascdVinLPyztcEaRQOvSBuuKKmN0jvS11McfbPYPyAQY+BXP8unTzjnwulSikMwfSuRVhSxmxpKqhDmIiHuNIs/mg1PPY6Rc56434u2cjLcUc9YSEM/kKCOImwZj8Cr/J+W5+IHwIvnl07DTzlOTGWT4maUbk7CTyxBa0g+TOVpF/SGX+4UzIAOW5Rwm+kZtfBIQooBqrFZqxFo+EiCBTkGdHM30BltDW9j5KsW4qpwRoMQ+OKCbAEQah5AOU0oMRVEoPbMl+JcSExf69xgaglKB5JsRlJvhNJGiBArMxeEdxxuPtIGbaimoOZW5NFkMRaXQCpp8A4tywyJJABY6Y5/MCFepXSNxW40FPaA4lZuWGI28JabAdNzc8XbpxO+sCh9GthqFWQmCFCfqT76+eMAmwptlzF76k2n1r6YXzJWSfujrA3thnHszI45/ttllR5fREGFmIMLES4WwVGnwzCNDdwumgPOE7c51kf1LPujrrLHPYtmLdkaLD3cyB4kpu2QRjmhl7SaTzMkpJOg3pNtvTgRYgRswOajMqLQOTGn/Iu85F9BPNI5EBWP1RdJg1d1syu/KyRy/ArEkC9ugA53wUQJjVEUg4sJ/DF7itOy9wFgnLtO/jlQaM0Jw7Qq27BoCEeJ+heD6nMMjrKEsP6XjAU7XusgZc+ISTxbgYU3l0sm3vEP4BXjPOzyDZ59GKfpCgxT4E59lQh/TZb37ceeZSkyVIRQLjfbpHWYBCkKOluM3+d1gSplP2Yk82kCczlBq68EkNFBLlMtZpp+h489njzUOuOGs6bkBgmAXWszB7+RVbMpBKTgODL8TfjWVVfwLfoZb5MZqOzPCIXq+XfYfR6Fyug21xfk2ug/YIdrsOPSu0YsZctQSE6PUIz6HqFFRhKgdxAYLqnategJDnsoHnAG27xQKtal4tIGFxObxuTGfVZspOcqgygaJBzbpVYDNjbKu54T/ULEkVJHWVmGOh//PLvINda/NfGhVy5Ueta8Uj5mq0rjILIh6e1J3nuj3hKBSuY0P6567UbcF/p1hc90PnGgu2zpXusEW9wn4r3bZJcroyaV+UrrkZcBql+wmKgNZMaojHCMUoUQd4P6pYDf2+iZXfp/3jg8SqbT0Lv1/1LHzzLHBFrCTTCwTy7QexjSAslZKXz1iJSi2o/lFFZ+xURWds2yW9rEWynTe9KtFpWzpDo7UvomNzBQzRmeE4Fhttz9kclgse/SUtTQ/E5doD7hpbu8WlXwF3Pe9WluYJaTGROcBPWfqh5aS+0OmBnOwIclTI9c+E4CiqLlw/GIUmvSPQtbvMQ0t2+1YftS+KzLZvfoqV6DeA+OAzeayhWhzto4pV3Z/ufik6Mu1UvTCdMkpVCtVoYaYknjLVsIlEp6BI9bzgyJarq4lUeYvFuXJ1TYftV5hmEZVb7KXsfBGsBjKZK83mf8jkLZH+xsO5SRbPRfF+kf+Eq8fkZCmrBSREpFfJTvx+PxQn7SoET3+4bUHzfdNQVarL0UmoRxZnqNUrs6TJeM7EZIFTvG3DfuLcFElV6ar5ZRt9ygk+ihzeuEoOHQktC6Rj0ZCnyAq2J883HZdpqwmvlBDT2itJbC/4uSwlLDuRbr6JmK21o5DSkgNRgveUVJU8wnOeeJi6t1Vjp33q8hubLPQ8n6kzFZ1noWc1q+w7PbXnokOheakDt2NymhkavoWc5Ds5G8SzSk5LEfaLkrNV6DAJ7/lbFTmuI5CmzEdpVdusKJ9yUy6ewl+8sr1+irGMuxefOp1ubCto60ph91ndhgOPJbLY3nKnYUdWWRnWFoyDemq2jDsYZVbMGx34grF967UYE1Zs3HZegx0FXozncCrtz1QCyoyByBJQ6iCPOp6yJnBQLgrVp1JOA69OmO5rOVmzS46oHZukDEMc7yUayNjFmpeny+1Ocbg1Le9SlYpqSLoeXD52h1Y8osacW9WXu9TlCfXgsKHqTF4l1h16pj95RtVoFierq7S2utGr6ZRpO9V4yfK2lvD396VP+zp2/qS29BlYAq8X9a4scdfva5/2BDWMme3twxelpxmbuCdzRAkQ81YFVgYUxSLvrigD2Us6nsThaPP+7dwNvUgAaWTLqjy78W5rhDtbY7hVQh1qRv3J9vuc7hXGduI2b1Qe4k3+KhNlZUWZD1Ge+Fyu4+66hpbChs1voW7WKM6tNx0d+SYC7fJVWfVu2DuPb3TaAt3PukDxPgxdsKu7e5G6/w57E8s27LwfZ47cDt8ebidwm7MVR8fX9itP3DK8Bt8RzVUK+17SKOyquDe/OE6ftH5Z15aaD13Z1fpO5p1/O6mlcbQ2raPaGzqdcf6S6hMH8OqpX1ooGhfP9ZOiO9r7d1vb7xvAY5cEc01XNGeaafUZh5C3+D8=&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(255, 255, 255);"><defs/><g><rect x="0" y="0" width="920" height="170" fill="none" stroke="#000000" stroke-dasharray="1 1" pointer-events="all"/><rect x="720" y="340" width="450" height="290" fill="none" stroke="#000000" stroke-dasharray="1 1" pointer-events="all"/><rect x="280" y="230" width="190" height="340" fill="none" stroke="#000000" stroke-dasharray="1 1" pointer-events="all"/><path d="M 370 320 L 370 363.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 370 368.88 L 366.5 361.88 L 370 363.63 L 373.5 361.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 346px; margin-left: 370px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Lexical Analysis</div></div></div></foreignObject><text x="370" y="349" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Lexical Analysis</text></switch></g><rect x="310" y="260" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 311px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Character Stream</div></div></div></foreignObject><text x="370" y="294" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Character Stream</text></switch></g><path d="M 370 430 L 370 473.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 370 478.88 L 366.5 471.88 L 370 473.63 L 373.5 471.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 456px; margin-left: 370px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Parsing</div></div></div></foreignObject><text x="370" y="459" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Parsing</text></switch></g><rect x="310" y="370" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 400px; margin-left: 311px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Token Stream</div></div></div></foreignObject><text x="370" y="404" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Token Stream</text></switch></g><path d="M 370 540 L 370 613.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 370 618.88 L 366.5 611.88 L 370 613.63 L 373.5 611.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 590px; margin-left: 370px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Type Resolution</div></div></div></foreignObject><text x="370" y="593" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Type Resolution</text></switch></g><rect x="310" y="480" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 510px; margin-left: 311px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Abstract Syntax Tree</div></div></div></foreignObject><text x="370" y="514" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Abstract Syntax Tree</text></switch></g><path d="M 430 650 L 526.63 650" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 531.88 650 L 524.88 653.5 L 526.63 650 L 524.88 646.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 650px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Type<br />Propagation</div></div></div></foreignObject><text x="481" y="653" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Type...</text></switch></g><rect x="310" y="620" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 650px; margin-left: 311px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Resolved Tree</div></div></div></foreignObject><text x="370" y="654" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Resolved Tree</text></switch></g><path d="M 593 620 L 593 546.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 593 541.12 L 596.5 548.12 L 593 546.37 L 589.5 548.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 580px; margin-left: 593px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Type<br />Inference</div></div></div></foreignObject><text x="593" y="583" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Type...</text></switch></g><rect x="533" y="620" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 650px; margin-left: 534px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Expression Typed Tree</div></div></div></foreignObject><text x="593" y="654" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Expression Typed Tree</text></switch></g><path d="M 593 480 L 593 436.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 593 431.12 L 596.5 438.12 L 593 436.37 L 589.5 438.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 454px; margin-left: 593px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Type Checking</div></div></div></foreignObject><text x="593" y="458" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Type Checking</text></switch></g><rect x="533" y="480" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 510px; margin-left: 534px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Fully Typed Tree</div></div></div></foreignObject><text x="593" y="514" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Fully Typed Tree</text></switch></g><path d="M 593 370 L 593 326.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 593 321.12 L 596.5 328.12 L 593 326.37 L 589.5 328.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 344px; margin-left: 593px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Optimisation/Simplification</div></div></div></foreignObject><text x="593" y="348" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Optimisation/Simplification</text></switch></g><rect x="533" y="370" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 400px; margin-left: 534px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Correctly Typed Tree</div></div></div></foreignObject><text x="593" y="404" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Correctly Typed Tree</text></switch></g><path d="M 653 290 L 744.63 290" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 749.88 290 L 742.88 293.5 L 744.63 290 L 742.88 286.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 290px; margin-left: 702px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Decoration</div></div></div></foreignObject><text x="702" y="293" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Decoration</text></switch></g><rect x="533" y="260" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 534px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Simplified Tree</div></div></div></foreignObject><text x="593" y="294" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Simplified Tree</text></switch></g><path d="M 811 320 L 811 403.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 811 408.88 L 807.5 401.88 L 811 403.63 L 814.5 401.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 366px; margin-left: 811px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Emit Bytecode<br />for each branch, recursively</div></div></div></foreignObject><text x="811" y="369" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Emit Bytecode...</text></switch></g><path d="M 871 290 L 983.63 290" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 988.88 290 L 981.88 293.5 L 983.63 290 L 981.88 286.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 290px; margin-left: 931px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Identify Static<br />Imports</div></div></div></foreignObject><text x="931" y="293" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Identify Static...</text></switch></g><path d="M 811 260 L 811 116.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 811 111.12 L 814.5 118.12 L 811 116.37 L 807.5 118.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 150px; margin-left: 811px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "><font color="#000000">Identify explicit and implicit<br />constant expressions<br /></font></div></div></div></foreignObject><text x="811" y="153" fill="#7D7D7D" font-family="Helvetica" font-size="11px" text-anchor="middle">Identify explicit and implicit...</text></switch></g><rect x="751" y="260" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 752px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Decorated/Annotated Tree</div></div></div></foreignObject><text x="811" y="294" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Decorated/Annotated...</text></switch></g><path d="M 731 80 L 566.37 80" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 561.12 80 L 568.12 76.5 L 566.37 80 L 568.12 83.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 80px; margin-left: 645px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "><font color="#000000"><span style="font-size: 12px">Compile and<br />Execute subtrees<br /></span></font></div></div></div></foreignObject><text x="645" y="83" fill="#7D7D7D" font-family="Helvetica" font-size="11px" text-anchor="middle">Compile and...</text></switch></g><rect x="731" y="50" width="160" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 80px; margin-left: 732px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Trees containing code only used by constant expressions</div></div></div></foreignObject><text x="811" y="84" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Trees containing code only...</text></switch></g><path d="M 400 80 L 375 80 L 375 223.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 375 228.88 L 371.5 221.88 L 375 223.63 L 378.5 221.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 142px; margin-left: 375px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "><font color="#000000">Parse and evaluate<br />constant expressions</font></div></div></div></foreignObject><text x="375" y="145" fill="#7D7D7D" font-family="Helvetica" font-size="11px" text-anchor="middle">Parse and evaluate...</text></switch></g><rect x="400" y="50" width="160" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 80px; margin-left: 401px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">New evaluated source code returned from the execution of the constant expressions</div></div></div></foreignObject><text x="480" y="84" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">New evaluated source code...</text></switch></g><path d="M 1050 260 L 1050 200 L 375 200 L 375 223.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 375 228.88 L 371.5 221.88 L 375 223.63 L 378.5 221.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="990" y="260" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 991px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Dynamically or Statically link files</div></div></div></foreignObject><text x="1050" y="294" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Dynamically or Stati...</text></switch></g><path d="M 871 440 L 983.63 440" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 988.88 440 L 981.88 443.5 L 983.63 440 L 981.88 436.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 440px; margin-left: 931px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Peephole<br />Optimisations</div></div></div></foreignObject><text x="931" y="443" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Peephole...</text></switch></g><rect x="751" y="410" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 440px; margin-left: 752px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Bytecode Blocks</div></div></div></foreignObject><text x="811" y="444" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Bytecode Blocks</text></switch></g><path d="M 1050 470 L 1050 533.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 1050 538.88 L 1046.5 531.88 L 1050 533.63 L 1053.5 531.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 506px; margin-left: 1050px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Add metadata and assemble<br />all blocks into one stream</div></div></div></foreignObject><text x="1050" y="509" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Add metadata and assemble...</text></switch></g><rect x="990" y="410" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 440px; margin-left: 991px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Optimised Blocks</div></div></div></foreignObject><text x="1050" y="444" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Optimised Blocks</text></switch></g><path d="M 990 570 L 877.37 570" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 872.12 570 L 879.12 566.5 L 877.37 570 L 879.12 573.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 570px; margin-left: 930px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; ">Write to File</div></div></div></foreignObject><text x="930" y="573" fill="#000000" font-family="Helvetica" font-size="11px" text-anchor="middle">Write to File</text></switch></g><rect x="990" y="540" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 570px; margin-left: 991px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Compiled Bytecode Stream</div></div></div></foreignObject><text x="1050" y="574" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Compiled Bytecode St...</text></switch></g><path d="M 811 600 L 811 653.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 811 658.88 L 807.5 651.88 L 811 653.63 L 814.5 651.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="751" y="540" width="120" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 570px; margin-left: 752px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Executable File</div></div></div></foreignObject><text x="811" y="574" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Executable File</text></switch></g><rect x="751" y="660" width="120" height="60" fill="#ffffff" stroke="#7d7d7d" stroke-dasharray="3 3" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 690px; margin-left: 752px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Executed by Virtual<br /> Machine</div></div></div></foreignObject><text x="811" y="694" fill="#7D7D7D" font-family="Helvetica" font-size="12px" text-anchor="middle">Executed by Virtual...</text></switch></g><rect x="280" y="230" width="70" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 245px; margin-left: 281px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Syntax</div></div></div></foreignObject><text x="315" y="249" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Syntax</text></switch></g><rect x="280" y="950" width="320" height="160" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 318px; height: 1px; padding-top: 1030px; margin-left: 282px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><ul><li><b>Type Resolution: </b>Completes incomplete types.</li><li><b>Type Propagation:</b> Types expressions based on the types of their sub-expressions.</li><li><b>Type Inference: </b>Assigns types to variables based on how they're used.</li><li><b>Type Checking: </b>Ensures the tree is correctly typed, with no contradictions.  Error checking stage.  Also checks that correct overloads exist.</li><li><b>Decoration:</b> Tree is decorated with attributes, such as the maximum amount of arguments certain functions take, which overload is being used, etc.</li></ul></div></div></div></foreignObject><text x="282" y="1034" fill="#000000" font-family="Helvetica" font-size="12px">Type Resolution: Completes incomplete types.Type Prop...</text></switch></g><rect x="280" y="750" width="720" height="190" fill="none" stroke="#525252" stroke-dasharray="1 1" pointer-events="all"/><path d="M 440 802.5 L 473.63 802.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 478.88 802.5 L 471.88 806 L 473.63 802.5 L 471.88 799 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="300" y="775" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 803px; margin-left: 301px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><span style="text-align: left">Inlining and</span><span style="font-family: &quot;sbl biblit&quot; , &quot;sbl greek&quot; , &quot;athena&quot; , &quot;eb garamond&quot; , &quot;eb garamond 12&quot; , &quot;foulis greek&quot; , &quot;garamond libre&quot; , &quot;cardo&quot; , &quot;gentium plus&quot; , &quot;gentium&quot; , &quot;garamond&quot; , &quot;palatino linotype&quot; , &quot;dejavu sans&quot; , &quot;dejavu serif&quot; , &quot;freeserif&quot; , &quot;freesans&quot; , &quot;arial unicode ms&quot; , &quot;lucida sans unicode&quot; , &quot;lucida grande&quot; , &quot;code2000&quot; , sans-serif ; text-align: left"> β</span>-reduction</div></div></div></foreignObject><text x="370" y="806" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Inlining and β-reduction</text></switch></g><path d="M 620 802.5 L 653.63 802.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 658.88 802.5 L 651.88 806 L 653.63 802.5 L 651.88 799 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="480" y="775" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 803px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div style="text-align: left"><span><font face="sbl biblit, sbl greek, athena, eb garamond, eb garamond 12, foulis greek, garamond libre, cardo, gentium plus, gentium, garamond, palatino linotype, dejavu sans, dejavu serif, freeserif, freesans, arial unicode ms, lucida sans unicode, lucida grande, code2000, sans-serif" style="font-size: 12px">Constant Folding</font></span></div></div></div></div></foreignObject><text x="550" y="806" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Constant Folding</text></switch></g><path d="M 800 802.43 L 820 802.43 L 810 802.43 L 823.63 802.43" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 828.88 802.43 L 821.88 805.93 L 823.63 802.43 L 821.88 798.93 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="660" y="775" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 803px; margin-left: 661px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div style="text-align: left">Dead Code Elimination</div></div></div></div></foreignObject><text x="730" y="806" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Dead Code Elimination</text></switch></g><path d="M 830 887.43 L 810 887.43 L 820 887.43 L 806.37 887.43" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 801.12 887.43 L 808.12 883.93 L 806.37 887.43 L 808.12 890.93 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="830" y="860" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 888px; margin-left: 831px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Loop Invariant Code Motion </div></div></div></div></foreignObject><text x="900" y="891" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Loop Invariant Code Mot...</text></switch></g><path d="M 660 887.5 L 626.37 887.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 621.12 887.5 L 628.12 884 L 626.37 887.5 L 628.12 891 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="660" y="860" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 888px; margin-left: 661px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Common Subexpression Elimination</div></div></div></div></foreignObject><text x="730" y="891" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Common Subexpression El...</text></switch></g><path d="M 480 887.5 L 446.37 887.5" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 441.12 887.5 L 448.12 884 L 446.37 887.5 L 448.12 891 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="480" y="860" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 888px; margin-left: 481px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Loop Fission and Fusion</div></div></div></div></foreignObject><text x="550" y="891" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Loop Fission and Fusion</text></switch></g><rect x="300" y="860" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 888px; margin-left: 301px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div>Loop Unrolling</div></div></div></div></foreignObject><text x="370" y="891" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Loop Unrolling</text></switch></g><path d="M 900 830 L 900 850 L 900 840 L 900 853.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 900 858.88 L 896.5 851.88 L 900 853.63 L 903.5 851.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><rect x="830" y="775" width="140" height="55" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 803px; margin-left: 831px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><div style="text-align: left">Tail Call Optimisation</div></div></div></div></foreignObject><text x="900" y="806" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Tail Call Optimisation</text></switch></g><rect x="295" y="740" width="185" height="20" fill="#ffffff" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 183px; height: 1px; padding-top: 750px; margin-left: 296px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Simplification and Optimisation</div></div></div></foreignObject><text x="388" y="754" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Simplification and Optimisation</text></switch></g><rect x="733" y="950" width="207" height="40" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 205px; height: 1px; padding-top: 970px; margin-left: 735px;"><div style="box-sizing: border-box; font-size: 0; text-align: left; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Resulting Bytecode is also subject to a number of optimisations, referred to as 'Peephole Optimisations'.</div></div></div></foreignObject><text x="735" y="974" fill="#000000" font-family="Helvetica" font-size="12px">Resulting Bytecode is also subject...</text></switch></g><rect x="493" y="224" width="100" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 239px; margin-left: 494px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Static Analysis</div></div></div></foreignObject><text x="543" y="243" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Static Analysis</text></switch></g><rect x="1070" y="340" width="100" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 355px; margin-left: 1071px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Compilation</div></div></div></foreignObject><text x="1120" y="359" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Compilation</text></switch></g><rect x="811.5" y="239" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 249px; margin-left: 813px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">1<sup>st</sup></div></div></div></foreignObject><text x="832" y="253" fill="#7D7D7D" font-family="Helvetica" font-size="12px" text-anchor="middle">1st</text></switch></g><rect x="871" y="291" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 301px; margin-left: 872px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">2<sup>nd</sup></div></div></div></foreignObject><text x="891" y="305" fill="#7D7D7D" font-family="Helvetica" font-size="12px" text-anchor="middle">2nd</text></switch></g><rect x="811" y="320" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 330px; margin-left: 812px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">3<sup>rd</sup></div></div></div></foreignObject><text x="831" y="334" fill="#7D7D7D" font-family="Helvetica" font-size="12px" text-anchor="middle">3rd</text></switch></g><path d="M 630 100 L 630 150 Q 630 160 638.94 164.47 L 661.06 175.53 Q 670 180 670 190 L 670 380 Q 670 390 679.12 394.1 L 714.19 409.89" fill="none" stroke="#a3a3a3" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 718.98 412.04 L 711.16 412.36 L 714.19 409.89 L 714.03 405.98 Z" fill="#a3a3a3" stroke="#a3a3a3" stroke-miterlimit="10" pointer-events="all"/><rect x="30" y="50" width="160" height="60" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 80px; margin-left: 31px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">New tree with pre-evaluated expressions</div></div></div></foreignObject><text x="110" y="84" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">New tree with pre-evaluate...</text></switch></g><path d="M 400 80 L 196.37 80" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 191.12 80 L 198.12 76.5 L 196.37 80 L 198.12 83.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 80px; margin-left: 280px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "><font color="#000000">Insert evaluated and parsed<br />subexpressions into tree<br /></font></div></div></div></foreignObject><text x="280" y="83" fill="#7D7D7D" font-family="Helvetica" font-size="11px" text-anchor="middle">Insert eva...</text></switch></g><rect x="367.5" y="105" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 115px; margin-left: 369px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">1<sup>st</sup></div></div></div></foreignObject><text x="388" y="119" fill="#7D7D7D" font-family="Helvetica" font-size="12px" text-anchor="middle">1st</text></switch></g><rect x="330" y="40" width="40" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 50px; margin-left: 331px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">2<sup>nd</sup></div></div></div></foreignObject><text x="350" y="54" fill="#7D7D7D" font-family="Helvetica" font-size="12px" text-anchor="middle">2nd</text></switch></g><rect x="0" y="0" width="210" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 208px; height: 1px; padding-top: 15px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">Arbitrary Compile-time Execution</div></div></div></foreignObject><text x="105" y="19" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">Arbitrary Compile-time Execution</text></switch></g><path d="M 640 200 L 606.37 200" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 601.12 200 L 608.12 196.5 L 606.37 200 L 608.12 203.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><path d="M 170 290 L 303.63 290" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 308.88 290 L 301.88 293.5 L 303.63 290 L 301.88 286.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 290px; margin-left: 220px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 11px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; background-color: #ffffff; white-space: nowrap; "><font color="#000000">Read File</font></div></div></div></foreignObject><text x="220" y="293" fill="#7D7D7D" font-family="Helvetica" font-size="11px" text-anchor="middle">Read File</text></switch></g><rect x="50" y="260" width="120" height="60" rx="9" ry="9" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 290px; margin-left: 51px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #7D7D7D; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "><font color="#000000">Source File</font></div></div></div></foreignObject><text x="110" y="294" fill="#7D7D7D" font-family="Helvetica" font-size="12px" text-anchor="middle">Source File</text></switch></g><path d="M 533 290 L 508.55 304.82 Q 500 310 500 320 L 500 700 Q 500 710 510 710 L 610 710 Q 620 710 624.47 718.94 L 637.15 744.3" fill="none" stroke="#a3a3a3" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/><path d="M 639.5 749 L 633.24 744.3 L 637.15 744.3 L 639.5 741.17 Z" fill="#a3a3a3" stroke="#a3a3a3" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://desk.draw.io/support/solutions/articles/16000042487" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>+ \ No newline at end of file diff --git a/assets/valhalla_compiler_layout.svg.png b/assets/valhalla_compiler_layout.svg.png Binary files differ. diff --git a/build.rs b/build.rs @@ -0,0 +1,42 @@ +use std::env; +use std::fs::File; +use std::io::{Read, Write}; +use std::path::Path; +use toml; + +fn main() -> Result<(), Box<dyn std::error::Error>> { + let out_dir = env::var("OUT_DIR")?; + let dest_path = Path::new(&out_dir).join("version.rs"); + let mut f = File::create(&dest_path).unwrap(); + + let mut config_f = File::open("Cargo.toml")?; + let mut config_str = String::new(); + config_f.read_to_string(&mut config_str)?; + + let config: toml::Value = toml::from_str(&config_str) + .unwrap(); + + println!("{:#?}", config); + + match &config["package"]["version"] { + toml::Value::String(version) => { + if let &[major, minor, tiny] = version + .split(".") + .map(|s| s.parse::<u8>().unwrap()) + .collect::<Vec<_>>().as_slice() { + + f.write_all(format!(" + const fn read_version() -> (u8, u8, u8) {{ + return ({}, {}, {}); + }} + ", major, minor, tiny).as_bytes())?; + } else { + panic!("Version string should be three numbers \ + separated by two dots."); + } + } + _ => panic!("Version in `Config.toml' should be a string!") + } + + Ok(()) +} diff --git a/current_compiler_test.md b/current_compiler_test.md @@ -11,13 +11,13 @@ a n m = n - 2*m a 1 2 --- a = n |-> (m |-> n + 2*m) +-- a = n |-> (m |-> n - 2*m) -- |_____________| -- | --- func: `a__1` +-- func: `__a_final` -- |_____________________| -- | --- func: `a__0` +-- func: `__a__0` -- |__________________________| -- | -- func: a @@ -54,10 +54,10 @@ The code is lexically analysed and generates the following tokens-stream: [ Terminator: "\n" (6, 29):1 ], [ Terminator: "\n" (7, 29):1 ], [ Terminator: "\n" (8, 22):1 ], - [ Terminator: "\n" (9, 30):1 ], + [ Terminator: "\n" (9, 35):1 ], [ Terminator: "\n" (10, 31):1 ], [ Terminator: "\n" (11, 20):1 ], - [ Terminator: "\n" (12, 28):1 ], + [ Terminator: "\n" (12, 30):1 ], [ Terminator: "\n" (13, 32):1 ], [ Terminator: "\n" (14, 14):1 ], [ End-Of-File: "\u{0}" (15, 17):1 ] ] @@ -68,8 +68,7 @@ The code is lexically analysed and generates the following tokens-stream: From the token-stream, an abstract syntax tree (AST) is generated: ```hs [| - %newfile{ :filename test.vh } - %newline{ :line 1 } + %file{ :filename samples/functions.vh } %call{ :yield anything :callee ( @@ -119,7 +118,6 @@ From the token-stream, an abstract syntax tree (AST) is generated: } |] } - %newline{ :line 2 } %call{ :yield anything :callee ( @@ -185,7 +183,6 @@ From the token-stream, an abstract syntax tree (AST) is generated: } |] } - %newline{ :line 4 } %call{ :yield integer :callee ( @@ -203,7 +200,6 @@ From the token-stream, an abstract syntax tree (AST) is generated: %num{ :value 2; :yield natural } |] } - %newline{ :line 15 } |] ``` @@ -212,53 +208,72 @@ From the token-stream, an abstract syntax tree (AST) is generated: And the AST is compiled to bytecode. The following is a disassembly of the generated bytecode: -```lisp -a__1: - | ===Constants=============== - | 0 | 2 => (Nat) | - | ===Locals================== +```clojure +__a_final: + |[meta]: + | stack-depth: 2 + | file-name: samples/functions.vh + |====Constants=============== + | 0 | 2 (Nat) + |====Locals================== | 0 | m - | ===Globals================= + |====Globals================= | 0 | n - | ===Bytecodes=============== - | (00000010):PUSH_LOCAL 0 - | (00000001):PUSH_CONST 0 - | (00110001):N_MUL - | (00000011):PUSH_SUPER 0 - | (00110000):U_SUB + |====Bytecodes=============== + | (0xfe):SET_LINE 2 (0x0002) + | (0x02):PUSH_LOCAL 0 (0x0000) + | (0x01):PUSH_CONST 0 (0x0000) + | (0x31):N_MUL + | (0x03):PUSH_SUPER 0 (0x0000) + | (0x30):U_SUB + | (0x0d):YIELD -a__0: - | ===Constants=============== - | 0 | a__1 => (Block) | - | 1 | :a__1 => (Sym) | - | ===Locals================== +__a_0: + |[meta]: + | stack-depth: 2 + | file-name: samples/functions.vh + |====Constants=============== + | 0 | __a_final (Code) + | 1 | :__a_final (Sym) + |====Locals================== | 0 | n - | ===Globals================= - | ===Bytecodes=============== - | (00000001):PUSH_CONST 0 - | (00000001):PUSH_CONST 1 - | (00001100):MAKE_FUNC + |====Globals================= + |====Bytecodes=============== + | (0x01):PUSH_CONST 0 (0x0000) + | (0x01):PUSH_CONST 1 (0x0001) + | (0x0c):MAKE_FUNC + | (0x0d):YIELD <main>: - | ===Constants=============== - | 0 | a__0 => (Block) | - | 1 | :a => (Sym) | - | 2 | 2 => (Nat) | - | 3 | 1 => (Nat) | - | ===Locals================== + |[meta]: + | stack-depth: 3 + | file-name: samples/functions.vh + |====Constants=============== + | 0 | __a_0 (Code) + | 1 | :a (Sym) + | 2 | 2 (Nat) + | 3 | 1 (Nat) + |====Locals================== | 0 | a - | ===Globals================= - | ===Bytecodes=============== - | (11111110):SET_LINE 2 - | (00000001):PUSH_CONST 0 - | (00000001):PUSH_CONST 1 - | (00001100):MAKE_FUNC - | (00000101):STORE_LOCAL 0 - | (11111110):SET_LINE 4 - | (00000001):PUSH_CONST 2 - | (00000001):PUSH_CONST 3 - | (00000010):PUSH_LOCAL 0 - | (00001001):CALL_1 - | (00001001):CALL_1 - | (11111110):SET_LINE 15 -```- \ No newline at end of file + |====Globals================= + |====Bytecodes=============== + | (0xfe):SET_LINE 2 (0x0002) + | (0x01):PUSH_CONST 0 (0x0000) + | (0x01):PUSH_CONST 1 (0x0001) + | (0x0c):MAKE_FUNC + | (0x05):STORE_LOCAL 0 (0x0000) + | (0xfe):SET_LINE 4 (0x0004) + | (0x01):PUSH_CONST 2 (0x0002) + | (0x01):PUSH_CONST 3 (0x0003) + | (0x02):PUSH_LOCAL 0 (0x0000) + | (0x09):CALL_1 + | (0x09):CALL_1 + | (0x0d):YIELD +``` + +--- + +This bytecode you see visually represented gets marshalled into a string +of bytes according to a format (you may view it in `/src/compiler/marshal.rs`). + +The marshalled code gets stored in a file, and the VM may execute it. diff --git a/empty.vh b/empty.vh diff --git a/samples/ability.vh b/samples/ability.vh @@ -0,0 +1,35 @@ +-- Most of the things defined here are already defined +-- in the Prelude. + +id : 'A -> 'A +id x = x -- Identity function + +show : String -> String +show = id -- naturally. + +-- Showable represents the +-- union of all types that implement +-- the show function on type 'A. +ability Showable on 'A { + show : 'A -> String +} + +-- Example +num_to_string : Showable & Number -> String +num_to_string n = show n +-- Converts only Numbers that are showable. +-- This gives us some early checking essentially. + +stdout = IO::STDOUT () +-- Same as `puts'/`put' +println : Showable -> [] -- saying [] will throw away the result and give (). +println s = [| s; "\n" |] |> map stdout.IO::write <> show +println s = map (s |-> stdout.IO::write (show s)) [| s; "\n" |] +println s = IO::write stdout <| show s + "\n" -- all equiv. + +-- So if we try to print something that's +-- not showable, it won't accept it. +-- The error should tell you to consider implementing +-- `show' on that type. + + diff --git a/samples/call_tree.vh b/samples/call_tree.vh @@ -8,4 +8,56 @@ A <> B -- CALL B -- / \ -- / \ --- <> A- \ No newline at end of file +-- <> A + + +-- say we have the function definition: + +f : A -> B -> C +f a b = c where c = a + b + +-- ...is really saying... + +(((:) f) (((->) A) (((->) B) C))) +(((=) ((f a) b)) (((where) c) (((=) c) (((+) a) b)))) + +-- which is... + +-- CALL +-- / \ +-- CALL \ +-- / \ \ +-- : f \ +-- CALL +-- / \ +-- CALL \ +-- / \ \ +-- -> A \ +-- CALL +-- / \ +-- CALL C +-- / \ +-- -> B +-- +-- CALL +-- / \ +-- CALL \ +-- / \ \ +-- = CALL \ +-- / \ \ +-- CALL b \ +-- / \ CALL +-- f a / \ +-- CALL \ +-- / \ \ +-- where c \ +-- CALL +-- / \ +-- CALL \ +-- / \ \ +-- = c \ +-- CALL +-- / \ +-- CALL b +-- / \ +-- + a diff --git a/samples/currying_infix.vh b/samples/currying_infix.vh @@ -8,6 +8,8 @@ -- same as (+) 2 3 -- same as +(flip (+)) 3 2 +-- same as (2 +) 3 -- same as -(+ 3) 2- \ No newline at end of file +(+ 3) 2 diff --git a/samples/equ_solve.vh b/samples/equ_solve.vh @@ -0,0 +1,64 @@ +-- Notationally powerful set comprhension requiers some +-- sort of eqation solver / symbol manipulation to be +-- computationally understood and useful. + +-- e.g. finding the solutions to a quadratic is done as so: + +[ x : Real => x^2 - 3*x - 4 == 0 ] +--> [ -1; 4 ] + +-- x^2 + (1/2)x -3 = 0 has roots (x = -2) and (x = 1.5) +-- so, +[ x : Int => x^2 + 0.5*x == 3 ] +--> [ -2 ] + +-- Likewise +[ x : Nat => x^2 + 0.5*x == 3 ] +--> [] +-- or +--> Empty + + +-- Same for FizzBuzz + +Multiples3 = [ 3*n => n <- Nat ] +Multiples5 = [ 5*n => n <- Nat ] + +fizzbuzz : Nat -> String +fizzbuzz n = show n + +fizzbuzz : Multiples3 -> String +fizzbuzz _ = "Fizz" + +fizzbuzz : Multiples5 -> String +fizzbuzz _ = "Buzz" + +fizzbuzz : Mutiples3 & Multipes5 -> String +fizzbuzz _ = "FizzBuzz" + +image f [| n : Nat => 0 < n < 101 |] +-- or +image f (1..100) +-- or +map (1..100) fizzbuzz +-- or +(1..100).map fizzbuzz +-- or +1..100 |> map fizzbuzz + +-- a.b.c == (a.b).c == c (b a) +-- a |> b |> c == (a |> b) |> c == c (b a) + +-- s.t. +-- !infix operator precedenc associativity +!infix (.) 200 :left +!infix (|>) 50 :left + +(.) : 'A -> ('A -> 'B) -> 'B +(|>) : 'A -> ('A -> 'B) -> 'B + +-- 'X is a generic type. Last resort if overloaded. + +arg . f = f arg +arg |> f = f arg -- Different precedence. + diff --git a/samples/juxtaposition.vh b/samples/juxtaposition.vh @@ -0,0 +1,24 @@ +-- Alow for multiplication by juxtaposition + +jux : Real -> Real -> Real +jux n m = n * m + + +-- when the type resolver sees juxtaposition of two +-- values, and the first is not a function, it will look for +-- a definition of `jux` on both of them, if found, it will expand +-- (n m) into (jux n m), if not it will throw an error (e.g. +-- "juxtapostion not defined for `T' on `U'"). + +-- now +3 2 == 6 + +a : Nat +a = 3 + +b : Int +b = -3 + +assert <| a b == -9 + +(0.5)a == 1.5 diff --git a/samples/list.vh b/samples/list.vh @@ -0,0 +1,67 @@ +!allow redefinition + +-- Code blocks are a series of statements, these statements +-- can be treated as items in a list. After all, that is how it is +-- represented internally. + +stmts = { + a + b + c +} +stmts = { a; b; c } +stmts = (a, b, c) -- Warning: could not find definition for a, b or c. + +-- ^^ All the above are equivalent, +-- they are all code blocks, and are represented as functions +-- that take in *nothing* (i.e `()`), and execute a, b and c (returning c). + +[| a; b; c |] = [| 11; 22; 33 |] -- assignment to tuples of equal size. +:(a; b; c) = :(11; 22; 33) -- assignment to quotes/ASTs of equal size. +(a, b, c) = (11, 22, 33) -- assignemt to blocks of equal size. + +stmts -- is a function, with three statements. +stmts () -- is a function call, and executes a, b and c. +eval stmts -- same as above, calls the function. + +assert (!nth 2 stmts == :b) + +-- You can also write something similar +stmts_similar = :(a; b; c) -- This is, however, NOT a function. +stmts_similar -- is a quotation/AST of code, spanning three statements. +eval stmts_similar -- will execute the three stements, returning c. + +stmts' = { + j = i + 2 + 3 * j + j +} + +assert (!nth 2 stmts' == :(3 * j)) +assert (!nth 3 stmts' == :j) + + +items = !list { a; b; c } -- or +items = !list (a, b, c) + +-- !list is a macro that evaluates a, b and c and places +-- them in a 'list'. + +assert (items[2] == 22) + +assert (eval :a == 22) +assert (eval :(:a) == :a) -- :xyz is essentially a way to escape/quote code. + +-- :ident colon quote operator is slightly special on idents, but +-- works the same in every way. + +s = input "Please enter a variable: " +sym = to_symbol s + +eval sym |> IO::puts +-- This (unlike the rest) will only execute at +-- run-time (instead of compile-time), it may or may not +-- throw an error, depening on whether the input given as a variable +-- exists in this program. + + diff --git a/samples/match.vh b/samples/match.vh @@ -0,0 +1,32 @@ +--* Pattern matching into product types. *-- + +Product = Tagged Nat^2 * Int -- Makes a constructor. + +prod : Product +prod = [| 2; 7; -66 |] -- Casts implicitly. +--prod = Product [| 2; 7; -66 |] + +alpha : Boolean +alpha = match prod: + [| 0; 0; i |] => i == 0, + Product [| 2; n; _ |] => n == 2, -- Explicit, but not needed, type is know. + [| _; _; i |] => i <- Nat | [ -1 ], + [| n; m; i |] => do: + n + m == -j where: + j = i + m % 3 + + +beta : Number +beta = match prod { + [| _; _; i |] => { + j = i + 2 + j * m where { + k = 8 - i + m = k ** 3 + } + }, + [| _; n; _ |] => n * 8, + _ => 42 +} + +-- Yes, these are messy on purpose. diff --git a/samples/peano.vh b/samples/peano.vh @@ -0,0 +1,59 @@ +-- ZF Formulation. +Succ a = a | [ a ] +Zero = [] +-- one = [ [] ] = [ 0 ] +-- two = [ []; [ [] ] ] = [ 0; 1 ] +-- etc. + +Peano = Tag [ Zero ] | [ Succ n => n <- Peano ] + +-- Succ n forany n <- Peano, is also a Peano +assert <| all [ Succ n <- Peano => n <- Peano ] + +(+) : Peano -> Peano -> Peano +Zero + n = n +(Succ n) + m = Succ (n + m) + +(*) : Peano -> Peano -> Peano +n * Zero = Zero +n * (Succ m) = n + n * m + +-- Printing Example: +show : Peano -> String +show Zero = "0" +show (Succ n) = (show n) + "+1" + +one : Peano +one = succ zero + +two : Peano +two = one + one + +three : Peano +three = one + two + +IO::puts three + -- "0+1+1+1" + + +-- Indexing the Peano set is essentialy converting +-- between internal Nat to Peano. +index : Nat -> [S] -> S where S <: Peano +index 0 _ = Zero +index (n + 1) _ = Succ (index n) +--index n _ = Succ (index (n - 1)) +--index n _ = n - 1 |> index |> Succ + +assert (Peano[0] == zero) +assert (index 0 Peano == zero) +assert (Peano[1] == one) +assert (Peano[2] == one + one) +assert (Peano[3] == three) + +to_nat : Peano -> Nat +to_nat Zero = 0 +to_nat (Succ n) = to_nat n + 1 + +assert (Peano == [ Peano[n] => n <- Nat ]) +assert (Nat == [ to_nat p => p <- Peano ]) + diff --git a/samples/product_type.vh b/samples/product_type.vh @@ -0,0 +1,104 @@ +!allow redefinition +-- Consider the C code: +{- + #include <stdio.h> + + struct Prod { + int x, y; + char *name; + double r; + }; + + int main(void) + { + struct Prod prod = { 3, 9, "John", -2.6f }; + printf("%d, %d, %s, %f\n", + prod.x, prod.y, prod.name, prod.r); + return 0; + } +-} + +-- How do we write this in Valhalla? + +-- Product types are explicitly cartesian products. + +Prod = Int * Int * String * Real +-- or +Prod = Int^2 * String * Real +-- Prod is a type (set) of the cartesian product of: +-- Two Ints, String and Real. + +-- [| ... |] denotes an ordered set, or a 'vector' if you will. +prod : Prod -- prod is an element of Prod (prod has type Prod). +prod = [| 3; 9; "John"; -2.6 |] -- way to initialise, by just assigning. + +-- Now, to access elements of `prod`, we may index normally. +assert (prod[0] is 3) + +-- If we want names, we can do that with functions: +x : Prod -> Int +x p = p[0] + +-- Hence, +assert ((x prod) is 3) +-- or using the `.` composition operator. +assert (prod.x is 3) + +-- We can define `x` function like this too: +x p = index 0 p +-- since `p[0]` is the same as `index 0 p` +-- so, since this is Curried, we can also write: +x = index 0 + +-- So, lets define the rest. +x : Prod -> Int +y : Prod -> Int +name : Prod -> String +r : Prod -> Real + +x = index 0 +y = index 1 +name = index 2 +r = index 3 + +-- If we don't include the type signatures here, then +-- the functions (x, y, name, etc) would be defined generally +-- for any type overloading the `index` function. + +IO::put "${prod.x}, ${prod.y}, ${prod.name} ${prod.r}" + +-- And we've done the same as the C program, but +-- all this can be automated by macros, and we were +-- overly verbose here, for the sake of being educational. + + + +-- More examples: + +-- 2 dimensional vector type. +Vec2D = Real^2 -- or, Real * Real +x : Vec2D -> Real +y : Vec2D -> Real +x = index 0 +y = index 1 + + +v : Vec2D +v = [| 1; 2 |] -- Create a Vec2D. + +[| n; m |] : Real^2 +[| n; m |] : Vec2D -- these two type signatures mean the same thing, +-- Neither type signature are necessary, because we have type inference. +[| n; m |] = v -- pattern matching assignment. + +assert (v.x == n) +assert (v.y == m) + + +show : Vec2D -> String -- overload `show'. +show v = "(${v.x}, ${v.y})" + +IO::puts v -- this calls `show' impicitly, + -- (within the definition of `puts'). + + diff --git a/samples/raw_hello_world.vh b/samples/raw_hello_world.vh @@ -1,3 +1,3 @@ # Without using the prelude, or importing IO... -_write_stream _STDOUT "Hello, World.\n" +_write_stream :STDOUT "Hello, World.\n" diff --git a/samples/set_comp.vh b/samples/set_comp.vh @@ -1 +1,2 @@ -n : Nat => n mod 3 is 0- \ No newline at end of file +Fizz <: Nat +Fizz = [ n : Nat => n mod 3 is 0 ] diff --git a/samples/singleton_return.vh b/samples/singleton_return.vh @@ -0,0 +1,13 @@ +-- The `add' function return the sum of two inputs: + +add : Nat -> Nat -> Nat +add n m = n + m + +-- This can be rewritten as a singleton set return value: +add : (n : Nat) -> (m : Nat) -> [ n + m ] + +-- and so no direct definition is required, because it can +-- only possibly return one exact value, based on the inputs. +-- (The return set only holds one value). + +-- This is not always applicable, for pattern/case matches and such. diff --git a/samples/subsets.vh b/samples/subsets.vh @@ -1,3 +1,7 @@ -subset I Int +-- I is subset of the integers. +I <: Int +I : Power(Int) -- the same -(n, m) : I^2 +-- n and m are both integers. +[| n; m |] : I^2 +[| n; m |] : I*I -- the same diff --git a/samples/vect.vh b/samples/vect.vh @@ -0,0 +1,25 @@ +import :Prelude + +Vect : (n : Nat) -> ['A] -> 'Self where: + Nil : Vect 0 'A + Cons : 'A -> Vect n 'A -> Vect (n + 1) 'A + +extend : Vect 'N 'A -> Vect 'M 'A -> Vect ('N + 'M) 'A +extend Nil ys = ys +extend (Cons x xs) ys = Cons x (extend xs ys) + +zip_with : ('A -> 'B -> 'C) -> Vect 'N 'A -> Vect 'N 'B -> Vect 'N 'C +zip_with f Nil _ = Nil +zip_with f (Cons x xs) (Cons y ys) = Cons (f x y) (zip_with f xs ys) + +v : Vect 3 Int +v = Cons -7 (Cons -6 (Cons -5 Nil)) + +-- Or, since product type A^n is A +-- vector of type a with n elements + +Vect : (n : Nat) -> ['A] -> ['A^n] +-- i.e.: Vect n a = a^n +-- so, we can also just say: +Vect : Nat -> ['A] -> 'Self +Vect n a = a^n diff --git a/scripts/inspect.sh b/scripts/inspect.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +OD="$(command -v od)" +[ -z "$OD" ] && echo "Please install \`od'." && exit 1 + +"$OD" "$1" -x --endian=big diff --git a/src/bin.rs b/src/bin.rs @@ -1,19 +1,176 @@ -use valhallac; +use ::valhallac; +//use valhallac::err; + use std::env; +use std::{fs::File, path::Path}; +use std::io::Write; +use std::time::Instant; +use std::collections::HashMap; + +use lazy_static::lazy_static; + +use colored; +use colored::*; + fn is_vh_file(filename : &String) -> bool { filename.ends_with(".vh") + && Path::new(filename).exists() +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +enum Flags { + Verbose, Out, + Version } -pub fn main() -> Result<(), i32> { - let args = env::args(); +// TODO: Halt on urecognised options. +/// Collect flags and options passed to the executable. +fn collect_flags() -> HashMap<Flags, String> { + let mut map = HashMap::new(); + let dummy = String::new(); + + let mut maybe_argument : Option<Flags> = None; + + for arg in env::args() { + // Collect any chars after a dash (e.g. `-v` for verbose) + // and build a set of flags from the specific letters. + let arg_str = arg.to_string(); + + if let Some(argument) = maybe_argument { + // Key assuredly exists. + *map.get_mut(&argument).unwrap() = arg_str; + maybe_argument = None; + continue; // Not an option, but an argument. + } + + let mut singleton = |flag: Flags| map.insert(flag, dummy.clone()); + + if arg_str.starts_with("--") { + let name = arg_str.get(2..); + match name { + Some("verbose") => singleton(Flags::Verbose), + Some("version") => singleton(Flags::Version), + Some("out") => { + maybe_argument = Some(Flags::Out); + singleton(Flags::Out) + }, + Some(&_) | None => None + }; + } else if arg_str.starts_with('-') { + let chars = arg_str.split(""); + for c in chars { + if c == "-" { continue; } + if c == "v" { + singleton(Flags::Verbose); + } else if c == "o" { + maybe_argument = Some(Flags::Out); + singleton(Flags::Out); + } + } + } + } + map +} + +macro_rules! not_debug { + ($verbose:expr, $block:block) => { + #[cfg(not(feature="debug"))] { + if $verbose $block + } + }; +} + +fn argument_error(msg : &str) { + println!("{} {}", "[**]".red().bold(), msg.bold()); +} + +lazy_static! { + static ref INFO : String = format!("{}", " :: ".bold().white()); +} - let files = args.filter(is_vh_file); +pub fn main() -> Result<(), Box<dyn std::error::Error>> { + let mut args = env::args(); + args.next(); + + let flags = collect_flags(); + + if flags.contains_key(&Flags::Version) { + let (major, minor, tiny) = valhallac::VERSION; + println!("(valhallac): v{}.{}.{}", + major, minor, tiny); + return Ok(()); + } + + let verbose : bool = flags.contains_key(&Flags::Verbose); + #[cfg(feature="debug")] + let verbose = true; + + let mut files = args.filter(is_vh_file).peekable(); + + if files.peek().is_none() { + argument_error("No valid input file given."); + std::process::exit(1); + } + + let begin = Instant::now(); for file in files { + not_debug!(verbose, { + println!("\n{}{} `{}'...", *INFO, + "Parsing".bold().blue(), + file.underline().white()); + }); + // Parse source into tree. let root = valhallac::parse(&file); + // Then compile into series of instructions, + // stored as a code block. + not_debug!(verbose, { + println!("{}{}...", *INFO, + "Compiling".bold().blue()); + }); let block = valhallac::compile(&root); - valhallac::binary_gen(&block); + + // Pick name of outfile. + let out = if let Some(out_location) = flags.get(&Flags::Out) { + out_location.to_owned() + } else { + file[..file.len() - 3].to_owned() + ".out" + }; + + if out.is_empty() { + argument_error("Empty/invalid output file specified."); + std::process::exit(1); + } + + // Convert code block to byte-stream, which will be + // the file's contents. + let bytes = valhallac::binary_blob(&block); + + // Write blob to file. + let mut file = File::create(&out)?; + file.write_all(&bytes)?; + + not_debug!(verbose, { + println!("{}{} to `{}'.", *INFO, + "Binary written".bold().blue(), + out.underline().white()); + }); } + + let elapsed = begin.elapsed(); + let seconds = elapsed.as_secs_f64(); + + not_debug!(verbose, { + print!("{}{} ", *INFO, "Took".bold().blue()); + println!("{}", if seconds < 0.1f64 { + format!("{}ms.", begin.elapsed().as_millis()) + } else { + format!("{:0.5}s", seconds) + }.white()); + + println!(); + }); + Ok(()) -}- \ No newline at end of file +} diff --git a/src/compiler/block.rs b/src/compiler/block.rs @@ -5,7 +5,7 @@ use super::super::err; use super::super::syntax; use syntax::ast; -use syntax::ast::Nodes; +use syntax::ast::{Nodes, StaticTypes}; use super::element; use super::instructions; @@ -16,9 +16,9 @@ use num_traits::cast::FromPrimitive; use super::internal_functions; -fn append_unique<'a, T : Clone + PartialEq>(v : &mut Vec<T>, e : T) -> usize { +fn append_unique<T : Clone + PartialEq>(v : &mut Vec<T>, e : T) -> usize { let index = v.iter().position(|c| c == &e); - if index.is_none() { v.push(e.clone()); } + if index.is_none() { v.push(e); } index.unwrap_or(v.len() - 1) } @@ -44,12 +44,14 @@ pub struct LocalBlock<'a> { pub return_type : ast::StaticTypes, // Used only for compilation: - locals_map : HashMap<String, u16>, + pub locals_map : HashMap<String, u16>, types_to_check : VecDeque<IdentTypePair<'a>>, current_line : usize, current_depth : usize, - stack_depth : usize, - last_instruction : Instr + pub stack_depth : usize, + last_instruction : Instr, + last_const_push_index : u16, + last_depth_delta : isize, } impl<'a> PartialEq for LocalBlock<'a> { @@ -75,17 +77,28 @@ impl<'a> LocalBlock<'a> { current_line: 0, stack_depth: 0, current_depth: 0, - last_instruction: Instr::Operator(0) + last_instruction: Instr::Operator(0), + last_const_push_index: 0xffff, + last_depth_delta: 0, } } fn push_const_instr(&mut self, e : Element<'a>) { let index = append_unique(&mut self.constants, e) as u16; - self.push_operator(Operators::PUSH_CONST); - self.push_operand(index); + + // Don't push constant if: + // (already on stack) and (stack depth has stayed the same) + if !(index == self.last_const_push_index + && self.last_depth_delta == 0) { + self.push_operator(Operators::PUSH_CONST); + self.push_operand(index); + self.last_const_push_index = index; + } } fn change_stack_depth(&mut self, i : isize) { + assert!((self.current_depth as isize) + i >= 0); + self.last_depth_delta = i; self.current_depth = ( (self.current_depth as isize) + i ) as usize; @@ -94,6 +107,7 @@ impl<'a> LocalBlock<'a> { } } + /// Pushes an operator onto the _instruction stack_. fn push_operator(&mut self, o : Operators) { let instr = Instr::Operator(o as u8); if !o.takes_operand() { @@ -103,6 +117,7 @@ impl<'a> LocalBlock<'a> { self.instructions.push(instr); } + /// Pushes an operand onto the _instruction stack_. fn push_operand(&mut self, i : u16) { let operand = Instr::Operand(i); self.instructions.push(operand); @@ -119,12 +134,14 @@ impl<'a> LocalBlock<'a> { fn ident_assignment(&mut self, left : &'a ast::IdentNode, right : &'a Nodes) { if self.types_to_check.is_empty() { - issue!(err::Types::TypeError, &self.filename, err::NO_TOKEN, self.current_line, - "You must state what set `{}' is a member of. No type-annotation found.", left.value); + issue!(TypeError, &self.filename, err::LINE, self.current_line, + "You must state what set `{}' is a member of. + No type-annotation found.", left.value); } if self.locals_map.contains_key(&left.value) { - issue!(err::Types::CompError, &self.filename, err::NO_TOKEN, self.current_line, - "Cannot mutate value of `{}', as it is already bound.", left.value); + issue!(CompError, &self.filename, err::LINE, self.current_line, + "Cannot mutate value of `{}', + as it is already bound.", left.value); } let index = self.insert_local(left.value.to_owned()); @@ -145,7 +162,7 @@ impl<'a> LocalBlock<'a> { } fn function_assign(&mut self, left : &ast::CallNode, right : &'a Nodes) { - let mut arguments = left.collect(); + let mut arguments = left.collect_operands(); let base_node = arguments.remove(0); if let Nodes::Ident(ident) = base_node { @@ -167,7 +184,7 @@ impl<'a> LocalBlock<'a> { super_block.insert_local(arguments[i].ident().unwrap().value.to_owned()); let block_name = last_block.name.clone(); - super_block.push_const_instr(Element::ECode(last_block)); + super_block.push_const_instr(Element::ECode(Box::new(last_block))); super_block.push_const_instr(Element::ESymbol(Symbol::new(&block_name))); super_block.push_operator(Operators::MAKE_FUNC); super_block.yield_last(); @@ -176,7 +193,7 @@ impl<'a> LocalBlock<'a> { let index = self.insert_local(ident.value.to_owned()); - self.push_const_instr(Element::ECode(last_block)); + self.push_const_instr(Element::ECode(Box::new(last_block))); self.push_const_instr(Element::ESymbol(Symbol::new(&ident.value))); self.push_operator(Operators::MAKE_FUNC); self.push_operator(Operators::STORE_LOCAL); @@ -198,11 +215,11 @@ impl<'a> LocalBlock<'a> { let current_line = node.location().line as usize; if self.current_line != current_line { let len = self.instructions.len(); - if len > 1 { - if self.instructions[len - 2] == Instr::Operator(Operators::SET_LINE as u8) { - self.instructions.pop(); - self.instructions.pop(); - } + if len > 1 + && self.instructions[len - 2] + == Instr::Operator(Operators::SET_LINE as u8) { + self.instructions.pop(); + self.instructions.pop(); } self.current_line = current_line; self.push_operator(Operators::SET_LINE); @@ -222,6 +239,9 @@ impl<'a> LocalBlock<'a> { self.push_operator(Operators::PUSH_LOCAL); self.push_operand(self.locals_map[s]); }, + Nodes::Nil(_) => { + self.push_const_instr(Element::ENil); + }, Nodes::Num(num_node) => { self.push_const_instr(numerics_to_element(&num_node.value)); }, @@ -236,8 +256,22 @@ impl<'a> LocalBlock<'a> { let mut do_return = true; match ident_node.value.as_str() { "__raw_print" => { - self.emit(&call_node.operands[0]); + let arg = &call_node.operands[0]; + + let print_type : u16 = match arg.yield_type() { + StaticTypes::TNatural => 0x01, + StaticTypes::TInteger => 0x02, + StaticTypes::TReal => 0x03, + StaticTypes::TString => 0x04, + _ => issue!(CompError, &self.filename, + err::LINE, self.current_line, + "__raw_print cannot display `{}' types.", + arg.yield_type()) + }; + + self.emit(arg); self.push_operator(Operators::RAW_PRINT); + self.push_operand(print_type); } _ => do_return = false }; @@ -257,22 +291,22 @@ impl<'a> LocalBlock<'a> { if let Some(cast_name) = args[1].get_name() { let cast_to : u16 = match cast_name { - "Real" => 0b00000011, - "Int" => 0b00000010, - "Nat" => 0b00000001, - _ => issue!(err::Types::TypeError, &self.filename, err::NO_TOKEN, self.current_line, + "Real" => 0b0000_0011, + "Int" => 0b0000_0010, + "Nat" => 0b0000_0001, + _ => issue!(TypeError, &self.filename, err::LINE, self.current_line, "Compiler does not know how to cast to `{}'.", cast_name) }; let cast_from = match args[0].yield_type() { - ast::StaticTypes::TReal => 0b00000011, - ast::StaticTypes::TInteger => 0b00000010, - ast::StaticTypes::TNatural => 0b00000001, - _ => issue!(err::Types::TypeError, &self.filename, err::NO_TOKEN, self.current_line, + ast::StaticTypes::TReal => 0b0000_0011, + ast::StaticTypes::TInteger => 0b0000_0010, + ast::StaticTypes::TNatural => 0b0000_0001, + _ => issue!(TypeError, &self.filename, err::LINE, self.current_line, "Compiler does not know how to cast from `{}'.", args[0].yield_type()) }; self.push_operand(cast_from << 8 | cast_to); } else { - issue!(err::Types::CompError, &self.filename, err::NO_TOKEN, self.current_line, + issue!(CompError, &self.filename, err::LINE, self.current_line, "Cast-type provided to `cast' has to be a type-name.") } return; @@ -294,7 +328,7 @@ impl<'a> LocalBlock<'a> { // If the LHS is not an ident, it is not a // valid annotation. if args[0].ident().is_none() { - issue!(err::Types::CompError, &self.filename, err::NO_TOKEN, self.current_line, + issue!(CompError, &self.filename, err::LINE, self.current_line, "Left of `:` type annotator must be an identifier."); } let left = args[0].ident().unwrap(); @@ -326,10 +360,13 @@ impl<'a> LocalBlock<'a> { } fn yield_last(&mut self) { + if self.current_depth == 0usize { + self.push_const_instr(Element::ENil); + } self.push_operator(Operators::YIELD); } - pub fn generate(&mut self, nodes : &'a Vec<Nodes>) { + pub fn generate(&mut self, nodes : &'a [Nodes]) { for node in nodes { self.emit(node); } @@ -340,31 +377,31 @@ impl<'a> LocalBlock<'a> { impl<'a> fmt::Display for LocalBlock<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for c in &self.constants { - if let Element::ECode(local_block) = c { - write!(f, "{}", local_block)?; + if let Element::ECode(local_block_box) = c { + write!(f, "{}", *local_block_box)?; } } write!(f, "\n{}:", self.name)?; - write!(f," + writeln!(f," |[meta]: | stack-depth: {} - | file-name: {}\n", + | file-name: {}", self.stack_depth, self.filename)?; - write!(f, " |====Constants===============\n")?; + writeln!(f, " |====Constants===============")?; for (i, c) in self.constants.iter().enumerate() { - write!(f, " | {: >3} | {}\n", i, c)?; + writeln!(f, " | {: >3} | {}", i, c)?; } - write!(f, " |====Locals==================\n")?; + writeln!(f, " |====Locals==================")?; for key in self.locals_map.keys() { - write!(f, " | {: >3} | {}\n", self.locals_map[key], key)?; + writeln!(f, " | {: >3} | {}", self.locals_map[key], key)?; } - write!(f, " |====Globals=================\n")?; + writeln!(f, " |====Globals=================")?; for (i, c) in self.globals.iter().enumerate() { - write!(f, " | {: >3} | {}\n", i, c)?; + writeln!(f, " | {: >3} | {}", i, c)?; } - write!(f, " |====Bytecodes===============\n")?; + writeln!(f, " |====Bytecodes===============")?; for inst in &self.instructions { if let Instr::Operand(_) = inst { write!(f, "{}", inst)?; diff --git a/src/compiler/element.rs b/src/compiler/element.rs @@ -7,7 +7,7 @@ use snailquote::escape; use super::block; use super::types; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Symbol { hash : u64, string : String @@ -47,8 +47,8 @@ pub enum Element<'a> { EReal(f64), EString(&'a str), ESymbol(Symbol), - ECode(block::LocalBlock<'a>), - ESet(types::Set<'a>), + ECode(Box<block::LocalBlock<'a>>), + ESet(Box<types::Set<'a>>), ENil } @@ -74,7 +74,7 @@ impl<'a> fmt::Display for Element<'a> { Element::ESymbol(t) => format!("{: <13} (Sym) ", t.to_string()), Element::ECode(t) => format!("{: <13} (Code)", t.name), Element::ESet(t) => format!("{: <13p} (Set) ", t), - Element::ENil => format!("{: <13} (Nil) ", "nil"), + Element::ENil => format!("{: <13}(Empty) ", "()"), }; write!(f, "{}", s) } diff --git a/src/compiler/instructions.rs b/src/compiler/instructions.rs @@ -1,7 +1,7 @@ use std::fmt; use enum_primitive_derive::Primitive; -use num_traits::{FromPrimitive}; +use num_traits::FromPrimitive; #[derive(Debug, Clone, Copy, PartialEq)] pub enum Instr { @@ -10,9 +10,9 @@ pub enum Instr { } impl Instr { - pub fn depth_delta(&self, maybe_operand : Option<Instr>) -> isize { + pub fn depth_delta(self, maybe_operand : Option<Instr>) -> isize { if let Instr::Operand(_) = self - { panic!("An operand does not have an impact on stack depth."); } + { panic!("An operand does not have an effect on stack depth."); } if let Some(instr_operand) = maybe_operand { if let Instr::Operand(operand) = instr_operand { @@ -25,29 +25,27 @@ impl Instr { Operators::STORE_LOCAL => -1, Operators::DUP_N => operand as isize, Operators::CAST => 0, + Operators::RAW_PRINT => 0, Operators::SET_LINE => 0, _ => panic!("This type of opcode doesn't take operands.") }; }} - } else { - if let Instr::Operator(code) = self { - match code { - 40..=56 => return -1, - _ => () - } - return match Operators::from_u8(code.to_owned()).unwrap() { - Operators::POP => -1, - Operators::DUP => 1, - Operators::SWAP => 0, - Operators::CALL_1 => -1, - Operators::CHECK_TYPE => -2, - Operators::MAKE_FUNC => -1, - Operators::YIELD => -1, - Operators::RAW_PRINT => 0, - Operators::NOP => 0, - _ => panic!("This opcode must take an operand.") - }; + } else if let Instr::Operator(code) = self { + match code { + 40..=56 => return -1, + _ => () } + return match Operators::from_u8(code.to_owned()).unwrap() { + Operators::POP => -1, + Operators::DUP => 1, + Operators::SWAP => 0, + Operators::CALL_1 => -1, + Operators::CHECK_TYPE => -2, + Operators::MAKE_FUNC => -1, + Operators::YIELD => -1, + Operators::NOP => 0, + _ => panic!("This opcode must take an operand.") + }; } panic!("Uncovered opcode.") } @@ -59,7 +57,7 @@ impl fmt::Display for Instr { Instr::Operand(n) => format!("{: >4} (0x{:04x})\n", n, n), Instr::Operator(n) => { let op_str = &Operators::from_u8(*n).unwrap().to_string(); - if op_str.ends_with("\n") { + if op_str.ends_with('\n') { format!("(0x{:02x}):{}", n, op_str) } else { format!("(0x{:02x}):{: <16}", n, op_str) @@ -74,7 +72,8 @@ impl fmt::Display for Instr { #[allow(non_camel_case_types)] #[derive(Primitive, Clone, Copy)] pub enum Operators { - HALT = 0, // TAKES 1 OPERAND(s) + EOI = 0, // TAKES 0 OPERAND(s) (Not a proper operator) + PUSH_CONST = 1, // TAKES 1 OPERAND(s) PUSH_LOCAL = 2, // TAKES 1 OPERAND(s) PUSH_SUPER = 3, // TAKES 1 OPERAND(s) @@ -88,7 +87,7 @@ pub enum Operators { CAST = 11, // TAKES 2 OPERAND(s) (2 operands, 1 out of 2 bytes for each) MAKE_FUNC = 12, // TAKES 0 OPERAND(s) YIELD = 13, // TAKES 0 OPERAND(s) - RAW_PRINT = 14, // TAKES 0 OPERAND(s) + RAW_PRINT = 14, // TAKES 1 OPERAND(s) N_ADD = 40, // TAKES 0 OPERAND(s) I_ADD = 41, // TAKES 0 OPERAND(s) @@ -108,22 +107,25 @@ pub enum Operators { R_DIV = 55, // TAKES 0 OPERAND(s) U_DIV = 56, // TAKES 0 OPERAND(s) + HALT = 200, // TAKES 1 OPERAND(s) + // Misc- / Meta-codes SET_LINE = 254, // TAKES 1 OPERAND(s) NOP = 255, // TAKES 0 OPERAND(s) } impl Operators { - pub fn takes_operand(&self) -> bool { + pub fn takes_operand(self) -> bool { match self { - Operators::HALT - | Operators::PUSH_CONST - | Operators::PUSH_LOCAL - | Operators::PUSH_SUPER - | Operators::STORE_LOCAL - | Operators::DUP_N - | Operators::CAST - | Operators::SET_LINE => true, + Self::HALT + | Self::PUSH_CONST + | Self::PUSH_LOCAL + | Self::PUSH_SUPER + | Self::STORE_LOCAL + | Self::DUP_N + | Self::CAST + | Self::RAW_PRINT + | Self::SET_LINE => true, _ => false } } @@ -132,6 +134,8 @@ impl Operators { impl fmt::Display for Operators { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match &self { + Operators::EOI => "EOI", + Operators::HALT => "HALT", Operators::PUSH_CONST => "PUSH_CONST", Operators::PUSH_LOCAL => "PUSH_LOCAL", @@ -146,7 +150,7 @@ impl fmt::Display for Operators { Operators::CAST => "CAST", Operators::MAKE_FUNC => "MAKE_FUNC\n", Operators::YIELD => "YIELD\n", - Operators::RAW_PRINT => "RAW_PRINT\n", + Operators::RAW_PRINT => "RAW_PRINT", Operators::N_ADD => "N_ADD\n", Operators::I_ADD => "I_ADD\n", diff --git a/src/compiler/internal_functions.rs b/src/compiler/internal_functions.rs @@ -61,4 +61,4 @@ pub fn get_internal_op(ident : &str, args : Option<&Vec<&ast::Nodes>>) -> Option } _ => None } -}- \ No newline at end of file +} diff --git a/src/compiler/marshal.rs b/src/compiler/marshal.rs @@ -1,36 +1,191 @@ -use std::fs::File; -use std::io::{Write, Error}; +/*! + * # NOTES + * - No top level bytes should be `0x00`. + * This includes constant specifiers, operators (operands are OK), + * etc. 0x00 should be reserved as a terminator for certain + * strings and blocks. + * + * ## Compiled Bytecode Format + * ```ignore + * | VERSION [u8; 3] + * | MARSHALLED CODE BLOCK: + * | | source-filename [u8; x] (abs path, null terminated, utf8) + * | | module-name [u8; x] (null terminated, utf8) + * | | stack-depth [u8; 2] + * | | + * | | CONSTANTS [u8; x] (block begin: 0x11 byte) + * | | (can contain other marshalled code blocks) + * | | (block end: 0x00) + * | | LOCAL NAMES [u8; x] (block begin: 0x12) + * | | (contains null terminated strings) + * | | (block end: 0x00) + * | | INSTRUCTION CODES [u8; x] (block begin: 0x13) + * | | (contains stream of operators and operands) + * | | (block end: 0x00 (EOI)) + * ``` + */ + +use std::collections::HashMap; use super::element; use super::instructions; use super::block; -use element::{Element, Symbol}; -use instructions::{Instr, Operators}; +use element::Element; +use instructions::Instr; + + +/// Gives each type a specifier prefix to identify them. +fn constant_ident_prefix(element : &Element) -> u8 { + return match element { + Element::ENil => 0xff, // Nil is technically not a prefix. + Element::ENatural(_) => 0x01, + Element::EInteger(_) => 0x02, + Element::EReal(_) => 0x03, + Element::EString(_) => 0x04, + _ => panic!("No byte-ident for this constant type") + } as u8; +} -// This ain't gonna be fun. +macro_rules! num_marshal_append { + ($num:expr, $arr:expr) => { + { + // Split to big-endian byte-vector. + let mut split = $num.to_be_bytes().to_vec(); + // Ignore leading zeros. + let mut i = 0u8; + for byte in &split { + if *byte != 0 { break; } + i += 1; + } + split = split[i as usize..].to_vec(); -fn mk_bin_file(name : &str, bytes : Vec<u8>) -> File { - let mut file = File::create(name).expect("Could not create binary."); - file.write(&bytes.as_ref()); - file + $arr.push(split.len() as u8); + $arr.append(&mut split); + } + }; +} +/* + * Constant marshalling: + * + * [...] = one (1) byte. + * [TSP] = Type Specifier Prefix + * + * For numbers: + * `[TPS] [NUM OF BYTES (n)] [BYTE 1] [BYTE 2] ... [BYTE n]` + * \_______________________________________________/ + * For strings: | These are the same concept. + * / ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄\ + * `[TPS] [NUM OF SIZE BYTES (n)] [SIZE BYTE 1]...[SIZE BYTE n] [CHAR 1]...[CHAR m]` + * \_____size of string (m)____/ + */ +fn marshal_element(element : &Element) -> Vec<u8> { + let mut bytes : Vec<u8> = vec![]; + match element { + Element::ENil => { + bytes.push(constant_ident_prefix(element)); + }, + Element::ENatural(n) => { + bytes.push(constant_ident_prefix(element)); + num_marshal_append!(n, bytes); + }, + Element::EInteger(i) => { + bytes.push(constant_ident_prefix(element)); + num_marshal_append!(i, bytes); + }, + Element::EReal(r) => { + bytes.push(constant_ident_prefix(element)); + num_marshal_append!(r, bytes); + }, + Element::EString(s) => { + let s_bytes = s.as_bytes().to_vec(); + let s_bytes_len = s.len(); + bytes.push(constant_ident_prefix(element)); + num_marshal_append!(s_bytes_len, bytes); + bytes.extend(s_bytes); + } + _ => panic!("I do not know how to marshal type of `{}'.", + element) + }; + bytes } -fn marshal_instructions(instrs : &Vec<Instr>) -> Vec<u8> { +fn marshal_instructions(instrs : &[Instr]) -> Vec<u8> { let mut bytes : Vec<u8> = vec![]; for instr in instrs { match *instr { Instr::Operator(o) => bytes.push(o), - Instr::Operand(o) => bytes.append(&mut vec![(o >> 8) as u8, o as u8]) + Instr::Operand(o) => bytes.extend(vec![(o >> 8) as u8, o as u8]) }; } bytes } -pub fn make_binary(blk : &block::LocalBlock) -> String { +fn marshal_consts(consts : &[Element]) -> Vec<u8> { + let mut bytes : Vec<u8> = vec![]; + for element in consts { + bytes.extend(marshal_element(element)); + } + bytes +} + +fn marshal_locals(locals : &HashMap<String, u16>) -> Vec<u8> { + let mut strings : Vec<Vec<u8>> = vec![vec![0x00]; locals.len()]; + for key in locals.keys() { + let mut string = key.as_bytes().to_vec(); + string.push(0x00); + strings[locals[key] as usize] = string; + } + strings.into_iter().flatten().collect() +} + +pub fn marshal_block(blk : &block::LocalBlock) -> Vec<u8> { let instrs = marshal_instructions(&blk.instructions); - let file = mk_bin_file(&blk.name, instrs); + let consts = marshal_consts(&blk.constants); + let locals = marshal_locals(&blk.locals_map); + let source_name = blk.filename.to_owned(); + + let mut bytes : Vec<u8> = vec![]; + // Null-terminated file name. + bytes.extend(source_name.as_bytes()); + bytes.push(0x00); + // Null-terminated module name. + bytes.extend(blk.name.as_bytes()); + bytes.push(0x00); + // Stack depth [u8; 2]. + bytes.extend(&(blk.stack_depth as u16).to_be_bytes()); + // Constants. + bytes.push(0x11); // Begin constants block. + bytes.extend(consts); + bytes.push(0x00); + // Locals. + bytes.push(0x12); // Begin locals block. + bytes.extend(locals); + bytes.push(0x00); + // Instructions. + bytes.push(0x13); + bytes.extend(instrs); + bytes.push(0x00); + + bytes +} + +pub fn generate_binary(blk : &block::LocalBlock) -> Vec<u8> { + let (major, minor, tiny) = crate::VERSION; + let mut bytes : Vec<u8> = vec![major, minor, tiny]; + bytes.extend(marshal_block(blk)); + + #[cfg(feature="debug")] { + print!("Bytes:\n "); + let mut i = 1; + for byte in &bytes { + print!("{:02x} ", byte); + if i % 16 == 0 { print!("\n ") }; + i += 1; + } + println!(); + } - blk.name.to_owned() -}- \ No newline at end of file + bytes +} diff --git a/src/compiler/types.rs b/src/compiler/types.rs @@ -61,4 +61,4 @@ impl<'a> Set<'a> { } false } -}- \ No newline at end of file +} diff --git a/src/err.rs b/src/err.rs @@ -1,4 +1,7 @@ -use crate::syntax::token; +#![allow(non_camel_case_types)] +#![allow(clippy::pub_enum_variant_names)] + +use crate::syntax::{token::Token, location::Loc}; use std::fs; use std::fmt; @@ -9,49 +12,65 @@ use colored::*; use unindent::unindent; -#[allow(non_camel_case_types)] -pub struct NO_TOKEN; +pub struct LINE; +pub struct LOC; -pub enum Types { - LexError, - ParseError, - TypeError, - CompError, +pub enum IssueType { + LexError, LexWarn, + ParseError, ParseWarn, + TypeError, TypeWarn, + CompError, CompWarn } -impl fmt::Display for Types { +impl fmt::Display for IssueType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let printable = match *self { - Types::LexError => "Lexicographical Error", - Types::ParseError => "Grammar Error", - Types::TypeError => "Typing Error", - Types::CompError => "Compilation Error", + IssueType::LexError => "Lexicographical Error".red(), + IssueType::ParseError => "Grammar Error".red(), + IssueType::TypeError => "Typing Error".red(), + IssueType::CompError => "Compilation Error".red(), + IssueType::LexWarn => "Lexicographical Warning".yellow(), + IssueType::ParseWarn => "Grammar Warning".yellow(), + IssueType::TypeWarn => " Typing Warning".yellow(), + IssueType::CompWarn => " Compilation Warning".yellow(), }; write!(f, "{}", printable) } } -pub fn tissue(class : Types, filename : &str, token : &token::Token, message : &str) { - let file = fs::File::open(filename).expect("Invalid filename for error message."); - let line = BufReader::new(file).lines().nth((token.location.line - 1) as usize).unwrap().unwrap(); +pub fn loc_issue(class : IssueType, filename : &str, loc : &Loc, message : &str) { + eprintln!(); + let file = fs::File::open(filename) + .expect("Invalid filename for error message."); + let line = BufReader::new(file).lines().nth((loc.line - 1) as usize) + .expect(&format!("Line ({}) does not exist, file is too short.", loc.line)) + .expect("Could not get line."); let formatted = unindent(message).split('\n').collect::<Vec<&str>>().join("\n "); eprintln!("{}{} {}", "issue".bold().red(), ":".white(), formatted.bold()); eprint!("{}", "".clear()); eprintln!(" ==> {class} in (`{file}`:{line}:{col}):\n{space}|\n{line_str}| {stuff}", - class=class.to_string().bold(), file=filename, line=token.location.line, - col=token.location.col, space=" ".repeat(5), - line_str=format!("{: >4} ", token.location.line.to_string().bold()), stuff=line); + class=class.to_string().bold(), file=filename, line=loc.line, + col=loc.col, space=" ".repeat(5), + line_str=format!("{: >4} ", loc.line.to_string().bold()), stuff=line); eprintln!("{space}|{: >offset$}", - "^".repeat(token.location.span as usize), space=" ".repeat(5), - offset=((token.location.col + token.location.span) as usize)); + "^".repeat(loc.span as usize).yellow().bold(), space=" ".repeat(5), + offset=((loc.col + loc.span) as usize)); +} + +pub fn token_issue(class : IssueType, filename : &str, token : &Token, message : &str) { + loc_issue(class, filename, &token.location, message); } -pub fn lissue(class : Types, filename : &str, line_n : usize, message : &str) { - let file = fs::File::open(filename).expect("Invalid filename for error message."); - let line = BufReader::new(file).lines().nth((line_n - 1) as usize).unwrap().unwrap(); +pub fn line_issue(class : IssueType, filename : &str, line_n : usize, message : &str) { + eprintln!(); + let file = fs::File::open(filename) + .expect("Invalid filename for error message."); + let line = BufReader::new(file).lines().nth((line_n - 1) as usize) + .unwrap() + .unwrap(); - let formatted = unindent(message).split("\n").collect::<Vec<&str>>().join("\n "); + let formatted = unindent(message).split('\n').collect::<Vec<&str>>().join("\n "); eprintln!("{}{} {}", "issue".bold().red(), ":".white(), formatted.bold()); eprint!("{}", "".clear()); eprintln!(" ==> {class} in (`{file}`:{line}):\n{space}|\n{line_str}| {stuff}", @@ -62,30 +81,37 @@ pub fn lissue(class : Types, filename : &str, line_n : usize, message : &str) { } #[macro_export] -macro_rules! issue { - ($type:path, $file:expr, err::NO_TOKEN, $line:expr, $message:expr) => { - { - err::lissue($type, $file, $line, $message); - std::process::exit(1) - } - }; - ($type:path, $file:expr, err::NO_TOKEN, $line:expr, $message:expr, $($form:expr),*) => { - { - err::lissue($type, $file, $line, &format!($message, $($form),*)); - std::process::exit(1) - } - }; - ($type:path, $file:expr, $token:expr, $message:expr) => { - { - err::tissue($type, $file, $token, $message); - std::process::exit(1) - } - }; - ($type:path, $file:expr, $token:expr, $message:expr, $($form:expr),*) => { - { - err::tissue($type, $file, $token, &format!($message, $($form),*)); - std::process::exit(1) - } - }; +macro_rules! warn { + ($type:ident, $file:expr, err::LINE, $line:expr, $message:expr) => {{ + err::line_issue(err::IssueType::$type, + $file, $line, $message); + }}; + ($type:ident, $file:expr, err::LINE, $line:expr, $message:expr, $($form:expr),*) => {{ + err::line_issue(err::IssueType::$type, + $file, $line, &format!($message, $($form),*)); + }}; + ($type:ident, $file:expr, err::LOC, $loc:expr, $message:expr) => {{ + err::loc_issue(err::IssueType::$type, + $file, $loc, $message); + }}; + ($type:ident, $file:expr, err::LOC, $loc:expr, $message:expr, $($form:expr),*) => {{ + err::loc_issue(err::IssueType::$type, + $file, $loc, &format!($message, $($form),*)); + }}; + ($type:ident, $file:expr, $token:expr, $message:expr) => {{ + err::token_issue(err::IssueType::$type, + $file, $token, $message); + }}; + ($type:ident, $file:expr, $token:expr, $message:expr, $($form:expr),*) => {{ + err::token_issue(err::IssueType::$type, + $file, $token, &format!($message, $($form),*)); + }}; } +#[macro_export] +macro_rules! issue { + ($type:ident, $($args:tt)*) => {{ + warn!($type, $($args)*); + std::process::exit(1) + }}; +} diff --git a/src/lib.rs b/src/lib.rs @@ -1,6 +1,14 @@ //! Crate responsible for parsing and compiling //! the generated AST to Brokkr-bytecode for the //! Valhalla set theoretic programming language. +#![warn(clippy::all)] +#![allow(clippy::needless_return)] +#![allow(clippy::single_match)] +#![allow(clippy::new_ret_no_self)] + +include!(concat!(env!("OUT_DIR"), "/version.rs")); + +pub const VERSION : (u8, u8, u8) = read_version(); /// Error messages. #[macro_use] @@ -18,15 +26,16 @@ pub fn parse(filename : &str) -> syntax::ast::Root { syntax::parse_file(filename) } -pub fn compile<'a>(root : &'a syntax::ast::Root) -> compiler::block::LocalBlock<'a> { +pub fn compile(root : &syntax::ast::Root) -> compiler::block::LocalBlock { let mut code_block = compiler::block::LocalBlock::new("<main>", &root.filename); code_block.generate(&root.branches); + + #[cfg(feature="debug")] println!("Code Blocks:\n{}", code_block); code_block } -pub fn binary_gen(block : &compiler::block::LocalBlock) -> String { - compiler::marshal::make_binary(block); - block.name.to_owned() -}- \ No newline at end of file +pub fn binary_blob(block : &compiler::block::LocalBlock) -> Vec<u8> { + compiler::marshal::generate_binary(block) +} diff --git a/src/syntax/analyser.rs b/src/syntax/analyser.rs @@ -1,358 +0,0 @@ -use std::collections::HashMap; - -use crate::err; - -use super::ast; -use ast::Nodes; - -/// Constant folding. -/// A static optimisation that relieves the runtime of having to perform -/// pre-computable trivial calculations, by doing them at compile time -/// instead. This function takes a node and recurses down, looking -/// for arithmetic operations containing exactly two numeric type nodes -/// as operands, and performs the stated operation. -fn const_fold(node : &Nodes) -> Nodes { - if let Nodes::Call(call) = node { - if call.is_binary() { - let bin_op = call.callee.call().unwrap().callee.ident().unwrap(); - let left = const_fold(&call.callee.call().unwrap().operands[0]); - let right = const_fold(&call.operands[0]); - - let default = Nodes::Call(ast::CallNode { - callee: Box::new(Nodes::Call(ast::CallNode { - callee: Box::new(const_fold(&*call.callee.call().unwrap().callee)), - operands: vec![left.clone()], - return_type: call.callee.yield_type(), - location: call.callee.call().unwrap().location - })), - operands: vec![right.clone()], - return_type: call.return_type.clone(), - location: call.location - }); - - let is_num_left = left.num().is_some(); - let is_num_right = right.num().is_some(); - - if is_num_left && is_num_right { - let l_value = left.num().unwrap().value; - let r_value = right.num().unwrap().value; - let value = match bin_op.value.as_str() { - "+" => l_value + r_value, - "-" => l_value - r_value, - "*" => l_value * r_value, - "/" => { - if r_value == ast::Numerics::Natural(0) { - return default; - } - l_value / r_value - }, - _ => { - return default; - } - }; - return Nodes::Num(ast::NumNode { value, location: call.location }); - } else { - return default; - } - } - return Nodes::Call(ast::CallNode { - callee: Box::new(const_fold(&*call.callee)), - operands: vec![const_fold(&call.operands[0])], - return_type: call.return_type.clone(), - location: call.location - }); - } - return node.to_owned(); -} - - -fn create_cast(node : &Nodes, cast : &ast::StaticTypes) -> Nodes { - let to_type = match cast { - ast::StaticTypes::TReal => ":Real", - ast::StaticTypes::TInteger => ":Int", - ast::StaticTypes::TNatural => ":Nat", - _ => panic!(".is_number() must be broken.") - }; - - let mut cast_node = ast::CallNode::new( - ast::CallNode::new( - ast::IdentNode::new("cast", node.location()), - vec![node.clone()], - node.location()), - vec![ast::SymNode::new(to_type, node.location())], - node.location()); - if let Nodes::Call(ref mut call) = cast_node { - call.set_return_type(cast.clone()) - } - cast_node -} - -fn cast_strength(st : &ast::StaticTypes) -> i32 { - match st { - ast::StaticTypes::TReal => 4, - ast::StaticTypes::TInteger => 2, - ast::StaticTypes::TNatural => 0, - _ => -1, - } -} - -/// The type balancer is a static utility that checks if something -/// like an arithmetic operator has unequal types (e.g. 4.3 + 6 (Real + Natural)). -/// If it does, it balances the two sides of the expressions by injecting a type -/// cast call to one of the arguments. -/// We always cast up (without loss of information), so, 4.3 + 6 will cast the 6 -/// to be 6.0. i.e. 4.3 + 6 ==> 4.3 + (cast 6 :Real) <=> 4.3 + 6.0. -fn balance_types(node : &Nodes) -> Nodes { - if let Nodes::Call(call) = node { - if call.is_binary() { - let bin_op = call.callee.call().unwrap().callee.ident().unwrap(); - let left = balance_types(&call.callee.call().unwrap().operands[0]); - let right = balance_types(&call.operands[0].clone()); - - let left_yield = left.yield_type(); - let right_yield = right.yield_type(); - if ["+", "-", "*", "/"].contains(&bin_op.value.as_str()) { - if left_yield.is_number() && right_yield.is_number() { - if cast_strength(&left_yield) != cast_strength(&right_yield) { - - let casting_right = cast_strength(&left_yield) > cast_strength(&right_yield); - let cast_to = (if casting_right { &left } else { &right }).yield_type(); - - let mut new_call; - if casting_right { - new_call = ast::CallNode::new( - *call.callee.clone(), - vec![create_cast(&right, &cast_to)], - call.callee.location()); - } else { - new_call = ast::CallNode::new( - ast::CallNode::new( - *call.callee.call().unwrap().callee.clone(), - vec![create_cast(&left, &cast_to)], - call.callee.location()), - vec![right], - call.location); - } - if let Nodes::Call(ref mut c) = new_call { - c.set_return_type(cast_to); - } - return new_call; - } else { - let mut cloned_node = node.clone(); - if let Nodes::Call(ref mut c) = cloned_node { - c.set_return_type(right_yield); - } - return cloned_node; - } - } - } else if bin_op.value == "=" { - if left_yield.is_number() { - if cast_strength(&left_yield) > cast_strength(&right_yield) { - let mut new_call = ast::CallNode::new( - *call.callee.clone(), - vec![create_cast(&right, &left_yield)], - call.callee.location()); - if let Nodes::Call(ref mut c) = new_call { - c.set_return_type(left_yield); - } - return new_call; - } - } - } - } - let mut non_bi = ast::CallNode::new( - balance_types(&*call.callee), - vec![balance_types(&call.operands[0])], - call.callee.location()); - if let Nodes::Call(ref mut c) = non_bi { - c.set_return_type(call.return_type.clone()); - } - return non_bi; - } - return node.to_owned(); -} - -#[derive(Clone)] -struct TypeChecker { - pub source_line : usize, - pub source_file : String, - ident_map : HashMap<String, ast::StaticTypes>, -} - -impl TypeChecker { - pub fn new() -> Self { - Self { - source_line: 0, - source_file: String::from("UNANNOUNCED_FILE"), - ident_map: HashMap::new(), - } - } - - pub fn type_branch(&mut self, node : &Nodes) -> Nodes { - let mut clone = node.to_owned(); - self.source_line = clone.location().line as usize; - match clone { - Nodes::File(f) => self.source_file = f.filename.to_owned(), - Nodes::Ident(ref mut i) => { - if let Some(annotation) = self.ident_map.get(&i.value) { - if let ast::StaticTypes::TSet(class) = annotation.clone() { - i.static_type = *class; - } else { - i.static_type = annotation.clone(); - } - } - return Nodes::Ident(i.to_owned()); - } - Nodes::Call(ref mut call) => { - if let Nodes::Call(ref mut callee) = *call.callee { - if let Nodes::Ident(ref binary_ident) = *callee.callee { - match binary_ident.value.as_str() { - ":" => { - if let Nodes::Ident(ref mut annotatee) = callee.operands[0] { - let annotation = ( - annotatee.value.to_owned(), - self.type_branch(&call.operands[0]).yield_type() - ); - - self.ident_map.insert(annotation.0.clone(), annotation.1.clone()); - - if let ast::StaticTypes::TSet(class) = annotation.1 { - annotatee.static_type = *class; - } else { - // Error, can only be element of set. - } - - return clone; - } else { - // Error: We need the left to be an ident. - issue!(err::Types::ParseError, - self.source_file.as_str(), - err::NO_TOKEN, self.source_line, - "The left side of the member-of operator (`:`), must be an identifier. - You supplied a type of `{}'. - Only variable names can be declared as being members of sets.", - callee.operands[0].node_type()); - } - }, - "=" => { - // This is useful for checking variables in functions. - match &callee.operands[0] { - Nodes::Call(ref assignee) => { - // Check all the types in the annotation (A -> B -> C) - // and match them to the arguments found on the left side - // of the assignment (=). Compile these matches into a list - // and pass that list into a new TypeChecker object which checks - // the right hand side of the assignment, matching up the sub-scoped - // variables. - - // A -> B -> C -> D - // f a b c = d - // <=> - // (A -> (B -> (C -> D))) - // ( ((=) ( (((f a) b) c) )) d) - - let mut operands = assignee.collect(); - let mut func_checker = self.clone(); - - let base_node = operands.remove(0); - if base_node.ident().is_none() { - issue!(err::Types::ParseError, - &self.source_file, err::NO_TOKEN, self.source_line, - "Function definitions must have the defining function's base caller - be an identifier! You're trying to define a function that has - `{}' as base caller...", base_node.node_type()); - } - - let maybe_type = self.ident_map.get(&base_node.ident().unwrap().value); - if maybe_type.is_none() { - println!("{}", base_node); - println!("{:?}", self.ident_map); - issue!(err::Types::TypeError, - self.source_file.as_str(), - err::NO_TOKEN, self.source_line, - "Cannot find type annotation for the - function definition of `{}'.", - base_node.ident().unwrap().value); - } - let mut t = maybe_type.unwrap().clone(); - - for operand in operands { - if let Nodes::Ident(ident) = operand { - if let ast::StaticTypes::TSet(f) = &t { - if let ast::StaticTypes::TFunction(i, o) = *f.clone() { - func_checker.ident_map.insert(ident.value, *i.clone()); - t = *o.clone(); - } - } - } - } - - call.operands[0] = func_checker.type_branch(&call.operands[0]); - return clone; - } - Nodes::Ident(_assignee) => { - // TODO: - // Here, if the ident exists in the ident_map, that means - // we need to check if both sides of the `=`'s types match up. - // If it does not exist, we need to infer its type by looking at - // the RHS and statically determine the RHS's type, and adding that - // type to the ident_map for the assignee. - } - _ => () - } - } - _ => () - } - } - } - // TODO HERE: - // We need to check to see if the function being called - // has a statically determined type, and if so, check that - // the operand to that function call has the exact same - // static type. - // If there is a type-mismatch, just throw an `issue!`. - // (If the function is statically typed, so - // must all the arguments be as well). - // The call must have a yield of type `function` and the - // input part of the function (input |-> output), must match - // the type of the operand. :^) - call.callee = Box::new(self.type_branch(&*call.callee)); - call.operands = vec![self.type_branch(&call.operands[0])]; - - if let ast::StaticTypes::TFunction(_, o) = call.callee.yield_type() { - if let ast::StaticTypes::TSet(t) = *o { - call.return_type = *t.clone(); - } else { - call.return_type = *o.clone(); - } - } - - return Nodes::Call(call.to_owned()); - }, - _ => () - }; - node.to_owned() - } -} - -pub fn replace(root : &mut ast::Root) { - let mut type_checker = TypeChecker::new(); - - let length = root.branches.len(); - let mut i = 0; - while i < length { - { // START TOP-LEVEL TYPE-CHECKING - let new = type_checker.type_branch(&root.branches[i]); - root.branches[i] = new; - } // END TOP-LEVEL TYPE-CHECKING - { // START TOP-LEVEL CONSTANT FOLD - let new = const_fold(&root.branches[i]); - root.branches[i] = new; - } // END TOP-LEVEL CONSTANT FOLD - { // START TOP-LEVEL TYPE BALANCING - let new = balance_types(&root.branches[i]); - root.branches[i] = new; - } // END TOP-LEVEL TYPE BALANCING - i += 1; - } -}- \ No newline at end of file diff --git a/src/syntax/analysis/beta_reduction.rs b/src/syntax/analysis/beta_reduction.rs diff --git a/src/syntax/analysis/constant_fold.rs b/src/syntax/analysis/constant_fold.rs @@ -0,0 +1,69 @@ +/*! + * Constant folding. + * A static optimisation that relieves the runtime of having to perform + * pre-computable trivial calculations, by doing them at compile time + * instead. This function takes a node and recurses down, looking + * for arithmetic operations containing exactly two numeric type nodes + * as operands, and performs the stated operation. + */ + + +use super::ast; +use ast::Nodes; + +fn const_fold(node : &Nodes) -> Nodes { + if let Nodes::Call(call) = node { + if call.is_binary() { + let bin_op = call.callee.call().unwrap().callee.ident().unwrap(); + let left = const_fold(&call.callee.call().unwrap().operands[0]); + let right = const_fold(&call.operands[0]); + + let def = Nodes::Call(ast::CallNode { + callee: Box::new(Nodes::Call(ast::CallNode { + callee: Box::new(const_fold(&*call.callee.call().unwrap().callee)), + operands: vec![left.clone()], + return_type: call.callee.yield_type(), + location: call.callee.call().unwrap().location + })), + operands: vec![right.clone()], + return_type: call.return_type.clone(), + location: call.location + }); + + let is_num_left = left.num().is_some(); + let is_num_right = right.num().is_some(); + + if is_num_left && is_num_right { + let l_value = left.num().unwrap().value; + let r_value = right.num().unwrap().value; + let value = match bin_op.value.as_str() { + "+" => l_value + r_value, + "-" => l_value - r_value, + "*" => l_value * r_value, + "/" => { + if r_value == ast::Numerics::Natural(0) { + return def; + } + l_value / r_value + }, + _ => { + return def; + } + }; + return Nodes::Num(ast::NumNode { value, location: call.location }); + } else { + return def; + } + } + return Nodes::Call(ast::CallNode { + callee: Box::new(const_fold(&*call.callee)), + operands: vec![const_fold(&call.operands[0])], + return_type: call.return_type.clone(), + location: call.location + }); + } + return node.to_owned(); +} + +#[allow(non_upper_case_globals)] +pub static default : fn(&Nodes) -> Nodes = const_fold; diff --git a/src/syntax/analysis/mod.rs b/src/syntax/analysis/mod.rs @@ -0,0 +1,67 @@ +/*! + * Analyse the syntax tree, assign types, cast types, and + * perform a battery of optimisations. + */ + +use std::collections::HashSet; +use super::ast; + +mod type_resolver; +mod type_balancer; +mod type_checker; +mod constant_fold; + + +#[macro_export] +macro_rules! transformations { + ( $( $trans:ident ),* ) => { + { + let mut set = HashSet::new(); + $( + set.insert(crate::syntax::analysis::Transform::$trans); + )* + set + } + }; +} + +#[allow(non_camel_case_types)] +#[derive(PartialEq, Eq, Hash)] +pub enum Transform { + // Tree Typing + TYPE_RESOLUTION, TYPE_PROPAGATION, + TYPE_INFERENCE, TYPE_BALANCING, + TYPE_CHECKING, + // Simplification and Optimisation + BETA_REDUCTION, CONSTANT_FOLDING, + DEAD_CODE_ELIMINATION, TAIL_CALL, + LOOP_INVARIANT_CODE_MOTION, + COMMON_SUBEXPRESSION_ELIMINATION, + LOOP_FISSION, LOOP_FUSION, + LOOP_UNROLLING +} + +pub fn replace(root : &mut ast::Root, transforms : HashSet<Transform>) { + let mut checker_context = type_checker::TypeChecker::new(); + let mut resolution_context = type_resolver::ResolutionContext::new(); + + let length = root.branches.len(); + let mut i = 0; + + + while i < length { + if transforms.contains(&Transform::TYPE_RESOLUTION) { + let new = resolution_context.resolve_branch(&root.branches[i]); + root.branches[i] = new; + } + if transforms.contains(&Transform::TYPE_CHECKING) { + let new = checker_context.type_branch(&root.branches[i]); + root.branches[i] = new; + } + if transforms.contains(&Transform::CONSTANT_FOLDING) { + let new = constant_fold::default(&root.branches[i]); + root.branches[i] = new; + } + i += 1; + } +} diff --git a/src/syntax/analysis/type_balancer.rs b/src/syntax/analysis/type_balancer.rs @@ -0,0 +1,119 @@ +use super::ast; +use ast::Nodes; + +fn create_cast(node : &Nodes, cast : &ast::StaticTypes) -> Nodes { + let to_type = match cast { + ast::StaticTypes::TReal => ":Real", + ast::StaticTypes::TInteger => ":Int", + ast::StaticTypes::TNatural => ":Nat", + _ => panic!(".is_number() must be broken.") + }; + + let mut cast_node = ast::CallNode::new( + ast::CallNode::new( + ast::IdentNode::new("cast", node.location()), + vec![node.clone()], + node.location()), + vec![ast::SymNode::new(to_type, node.location())], + node.location()); + if let Nodes::Call(ref mut call) = cast_node { + call.set_return_type(cast.clone()) + } + cast_node +} + +fn cast_strength(st : &ast::StaticTypes) -> i32 { + match st { + ast::StaticTypes::TReal => 4, + ast::StaticTypes::TInteger => 2, + ast::StaticTypes::TNatural => 0, + _ => -1, + } +} + +/// The type balancer is a static utility that checks if something +/// like an arithmetic operator has unequal types (e.g. 4.3 + 6 (Real + Natural)). +/// If it does, it balances the two sides of the expressions by injecting a type +/// cast call to one of the arguments. +/// We always cast up (without loss of information), so, 4.3 + 6 will cast the 6 +/// to be 6.0. i.e. 4.3 + 6 ==> 4.3 + (cast 6 :Real) <=> 4.3 + 6.0. +fn balance_types(node : &Nodes) -> Nodes { + if let Nodes::Call(call) = node { + if call.is_binary() { + let bin_op = call.callee.call().unwrap().callee.ident().unwrap(); + let left = balance_types(&call.callee.call().unwrap().operands[0]); + let right = balance_types(&call.operands[0].clone()); + + let left_yield = left.yield_type(); + let right_yield = right.yield_type(); + if ["+", "-", "*", "/"].contains(&bin_op.value.as_str()) { + if left_yield.is_number() && right_yield.is_number() { + if cast_strength(&left_yield) != cast_strength(&right_yield) { + + let casting_right = cast_strength(&left_yield) > cast_strength(&right_yield); + let mut cast_to = (if casting_right { &left } else { &right }).yield_type(); + if cast_to == ast::StaticTypes::TNatural + && bin_op.value == "-" { + cast_to = ast::StaticTypes::TInteger; + } + + let mut new_call = if casting_right { + ast::CallNode::new( + *call.callee.clone(), + vec![create_cast(&right, &cast_to)], + call.callee.location()) + } else { + ast::CallNode::new( + ast::CallNode::new( + *call.callee.call().unwrap().callee.clone(), + vec![create_cast(&left, &cast_to)], + call.callee.location()), + vec![right], + call.location) + }; + if let Nodes::Call(ref mut c) = new_call { + c.set_return_type(cast_to); + } + return new_call; + } else { + let mut cloned_node = node.clone(); + let mut cast_to = right_yield; + + if cast_to == ast::StaticTypes::TNatural + && bin_op.value == "-" { + cast_to = ast::StaticTypes::TInteger; + } + + if let Nodes::Call(ref mut c) = cloned_node { + c.set_return_type(cast_to); + } + return cloned_node; + } + } + } else if bin_op.value == "=" + && left_yield.is_number() + && cast_strength(&left_yield) > cast_strength(&right_yield) { + let mut new_call = ast::CallNode::new( + *call.callee.clone(), + vec![create_cast(&right, &left_yield)], + call.callee.location()); + if let Nodes::Call(ref mut c) = new_call { + c.set_return_type(left_yield); + } + return new_call; + } + } + let mut non_bi = ast::CallNode::new( + balance_types(&*call.callee), + vec![balance_types(&call.operands[0])], + call.callee.location()); + if let Nodes::Call(ref mut c) = non_bi { + c.set_return_type(call.return_type.clone()); + } + return non_bi; + } + return node.to_owned(); +} + +#[allow(non_upper_case_globals)] +pub static default : fn(&Nodes) -> Nodes = balance_types; diff --git a/src/syntax/analysis/type_checker.rs b/src/syntax/analysis/type_checker.rs @@ -0,0 +1,171 @@ +use std::collections::HashMap; + +use crate::err; + +use super::ast; +use ast::Nodes; + +#[derive(Clone)] +pub struct TypeChecker { + pub source_line : usize, + pub source_file : String, + ident_map : HashMap<String, ast::StaticTypes>, +} + +impl TypeChecker { + pub fn new() -> Self { + Self { + source_line: 0, + source_file: String::from("UNANNOUNCED_FILE"), + ident_map: HashMap::new(), + } + } + + pub fn type_branch(&mut self, node : &Nodes) -> Nodes { + let mut clone = node.to_owned(); + self.source_line = clone.location().line as usize; + match clone { + Nodes::File(f) => self.source_file = f.filename, + Nodes::Ident(ref mut i) => { + if let Some(annotation) = self.ident_map.get(&i.value) { + if let ast::StaticTypes::TSet(class) = annotation.clone() { + i.static_type = *class; + } else { + i.static_type = annotation.clone(); + } + } + return Nodes::Ident(i.to_owned()); + } + Nodes::Call(ref mut call) => { + if let Nodes::Call(ref mut callee) = *call.callee { + if let Nodes::Ident(ref binary_ident) = *callee.callee { + match binary_ident.value.as_str() { + ":" => { + if let Nodes::Ident(ref mut annotatee) = callee.operands[0] { + let annotation = ( + annotatee.value.to_owned(), + self.type_branch(&call.operands[0]).yield_type() + ); + + self.ident_map.insert(annotation.0.clone(), annotation.1.clone()); + + if let ast::StaticTypes::TSet(class) = annotation.1 { + annotatee.static_type = *class; + } else { + // Error, can only be element of set. + } + + return clone; + } else { + // Error: We need the left to be an ident. + issue!(ParseError, + self.source_file.as_str(), + err::LINE, self.source_line, + "The left side of the member-of operator (`:`), must be an identifier. + You supplied a type of `{}'. + Only variable names can be declared as being members of sets.", + callee.operands[0].node_type()); + } + }, + "=" => { + // This is useful for checking variables in functions. + match &callee.operands[0] { + Nodes::Call(ref assignee) => { + // Check all the types in the annotation (A -> B -> C) + // and match them to the arguments found on the left side + // of the assignment (=). Compile these matches into a list + // and pass that list into a new TypeChecker object which checks + // the right hand side of the assignment, matching up the sub-scoped + // variables. + + // A -> B -> C -> D + // f a b c = d + // <=> + // (A -> (B -> (C -> D))) + // ( ((=) ( (((f a) b) c) )) d) + + let mut operands = assignee.collect_operands(); + let mut func_checker = self.clone(); + + let base_node = operands.remove(0); + if base_node.ident().is_none() { + issue!(ParseError, + &self.source_file, err::LINE, self.source_line, + "Function definitions must have the defining function's base caller + be an identifier! You're trying to define a function that has + `{}' as base caller...", base_node.node_type()); + } + + let maybe_type = self.ident_map.get(&base_node.ident().unwrap().value); + if maybe_type.is_none() { + #[cfg(feature="debug")] { + println!("{}", base_node); + println!("{:?}", self.ident_map); + } + issue!(TypeError, + self.source_file.as_str(), + err::LINE, self.source_line, + "Cannot find type annotation for the + function definition of `{}'.", + base_node.ident().unwrap().value); + } + let mut t = maybe_type.unwrap().clone(); + + for operand in operands { + if let Nodes::Ident(ident) = operand { + if let ast::StaticTypes::TSet(f) = &t { + if let ast::StaticTypes::TFunction(i, o) = *f.clone() { + func_checker.ident_map.insert(ident.value, *i.clone()); + t = *o.clone(); + } + } + } + } + + call.operands[0] = func_checker.type_branch(&call.operands[0]); + return clone; + } + Nodes::Ident(_assignee) => { + // TODO: + // Here, if the ident exists in the ident_map, that means + // we need to check if both sides of the `=`'s types match up. + // If it does not exist, we need to infer its type by looking at + // the RHS and statically determine the RHS's type, and adding that + // type to the ident_map for the assignee. + } + _ => () + } + } + _ => () + } + } + } + // TODO HERE: + // We need to check to see if the function being called + // has a statically determined type, and if so, check that + // the operand to that function call has the exact same + // static type. + // If there is a type-mismatch, just throw an `issue!`. + // (If the function is statically typed, so + // must all the arguments be as well). + // The call must have a yield of type `function` and the + // input part of the function (input |-> output), must match + // the type of the operand. :^) + call.callee = Box::new(self.type_branch(&*call.callee)); + call.operands = vec![self.type_branch(&call.operands[0])]; + + if let ast::StaticTypes::TFunction(_, o) = call.callee.yield_type() { + if let ast::StaticTypes::TSet(t) = *o { + call.return_type = *t; + } else { + call.return_type = *o; + } + } + + return Nodes::Call(call.to_owned()); + }, + _ => () + }; + node.to_owned() + } +} diff --git a/src/syntax/analysis/type_inference.rs b/src/syntax/analysis/type_inference.rs diff --git a/src/syntax/analysis/type_resolver.rs b/src/syntax/analysis/type_resolver.rs @@ -0,0 +1,424 @@ +use super::ast; +use ast::{Nodes, StaticTypes}; + +use super::type_balancer; + +use lazy_static::lazy_static; +use std::collections::HashSet; + +use crate::err; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct SymbolEntry { + pub identifier : String, + pub signature : StaticTypes, + pub defined : bool, +} + +impl SymbolEntry { + pub fn is_function(&self) -> bool { + if let StaticTypes::TFunction(_, _) = self.signature { + return true; + } + return false; + } + + pub fn was_defined(&mut self) { + self.defined = true; + } +} + +#[derive(Debug, Clone)] +struct SymbolTable { + table : Vec<SymbolEntry>, + pub scope : String +} + +impl SymbolTable { + pub fn new(scope : &str) -> Self { + Self { + table: vec![], + scope: String::from(scope) + } + } + + pub fn collect(self) -> Vec<SymbolEntry> { + self.table + } + + pub fn iter(&self) -> impl std::iter::Iterator<Item=&SymbolEntry> { + self.table.iter() + } + + pub fn iter_mut(&mut self) -> impl std::iter::Iterator<Item=&mut SymbolEntry> { + self.table.iter_mut() + } + + pub fn get(&self, i : usize) -> Option<&SymbolEntry> { + self.table.get(i) + } + + pub fn get_mut(&mut self, i : usize) -> Option<&mut SymbolEntry> { + self.table.get_mut(i) + } + + pub fn push(&mut self, ident : &str, sig : StaticTypes, def : bool) { + #[cfg(feature="debug")] { + println!("Type added to table `{}':", self.scope); + println!("\t`{}', is a: {}", &ident, &sig); + } + self.table.push(SymbolEntry { + identifier: String::from(ident), + signature: sig, + defined: def, + }); + } + + pub fn contains(&self, ident : &str) -> bool { + for elem in &self.table { + if elem.identifier == ident { + return true; + } + } + return false; + } + + pub fn collect_signatures(&self, ident : &str) -> HashSet<StaticTypes> { + self.table.iter() + .filter(|e| e.identifier == ident) + .map(|e| e.signature.to_owned()) + .collect() + } +} + + +pub struct ResolutionContext { + table_chain : Vec<SymbolTable>, + filename : String +} + +// TODO: Arithmetic operators will be properly defined +// in some sort of prelude lib. +lazy_static! { + static ref INTERNAL_IDENTS : HashSet<String> = vec![ + "=", ":", "->", "__raw_print", "+", "-", "*", "/", "^" + ].into_iter().map(String::from).collect(); +} + +// Rest is the implementation of the resolution context. +impl ResolutionContext { + +pub fn new() -> Self { + Self { + table_chain: vec![SymbolTable::new("GLOBAL")], + filename: String::from("unspecified") + } +} + +fn current_table(&mut self) -> &mut SymbolTable { + self.table_chain.last_mut() + .expect("Somehow there is no current scope. This is a bug.") +} + +fn search_chain(&mut self, ident : &str) -> Option<&mut SymbolTable> { + for table in self.table_chain.iter_mut().rev() { + if table.contains(ident) { + return Some(table); + } + } + return None; +} + +/// # Terminology +/// `appl_0` - refers to the the 0th (base) application (call). +/// `appl_n` - refers to any nested application n-levels deep. +/// # Function +/// Entry point for type resolution of AST branches. +/// Returns a clone of the branch but with added type-information. +pub fn resolve_branch(&mut self, branch : &Nodes) -> Nodes { + if let Nodes::File(file_node) = branch { + self.filename = file_node.filename.to_owned(); + return branch.to_owned(); + } + + let mut node = branch.to_owned(); + // Assign type to signatures, add to table. + + // If we have an ident (variable) not being used as a function + // call (i.e. it's type cannot be overloaded). + if let Nodes::Ident(ref mut ident) = node { + // Ignore certain variables (internals, not user declared). + if INTERNAL_IDENTS.contains(&ident.value) { + return node; + } + + // Search for variable in tables, to give it a type. + let maybe_table = self.search_chain(&ident.value); + if let Some(table) = maybe_table { // It is in the table. + // Get signatures. Variables cannot have multiple signatures. + let signatures = table.collect_signatures(&ident.value); + if signatures.len() > 1 { + // TODO: Partial application not considered. + issue!(ParseError, &self.filename, + err::LOC, &ident.location, + "Variable has multiple type signatures. Overloading \ + types is only possible with functions."); + } + // We can unwrap this because we know it contains exactly + // one (1) element. + let signature = signatures.iter().next().unwrap(); + // Give the identifier it's signature. + ident.static_type = signature.clone(); + } else { // Variable has not been declared. + issue!(ParseError, &self.filename, err::LOC, &ident.location, + "Variable `{}' is used, but has not been declared.", + &ident.value); + } + // What to do, if we have a call to resolve. + } else if let Nodes::Call(ref mut appl_0) = node { + let appl_0_clone = appl_0.clone(); + let mut skip_type_check = false; + + // Some day we'll have `let-chains'. + if let Nodes::Call(ref mut appl_1) = *appl_0.callee { + if let Nodes::Ident(ref ident_1) = *appl_1.callee { + match ident_1.value.as_ref() { + "->" => panic!("We should have prevented this."), + ":" => { + self.resolve_annotation(appl_0_clone, appl_1.clone()); + // FIXME: Should we really replace the annotation with a nil? + // I know it isn't useful anymore, but maybe we should keep it + // and just ignore it when compiling. Returning nil might + // add complexity to the rest of the type checking. + + // Return nil? + //return ast::NilNode::new(ident_1.location); + + // A type signature should evaluate to the type it has assigned. + // i.e. `T = (_ : Nat)` is the same as `T = Nat`. + // Pattern matching on signatures is also allowed: + // `f (n : Nat) = n + 2`, which matches on n that is natural. + + return node; + }, + "=" => { + *appl_0 = self.resolve_assignment(appl_0_clone, appl_1.clone()); + skip_type_check = true; + }, + // Internal functions, with internal + // types/definitions, etc. + "+" | "-" + | "*" | "/" + | "^" => { // Arithmetic operations typing. + // Resolve on both sides as much as possible. + if let Some(operand) = appl_0_clone.operand() { + appl_0.operands[0] = self.resolve_branch(operand); + } + if let Some(operand) = appl_1.operand() { + appl_1.operands[0] = self.resolve_branch(operand); + } + let cloned_node = node.clone(); + // This HAS to be rewritten. + return type_balancer::default(&cloned_node); + } + _ => () + } + }} + // Any call should resolve its callee type, and check if it is legal + // to apply an operand of such a (resolved) type. + // This entier call expression must thus also be typed, unrolling + // the type from the callee. + if skip_type_check { + return node; + } + // Recursively resolve both sides of the expression. + appl_0.callee = Box::new(self.resolve_branch(&*appl_0.callee)); + if let Some(operand) = appl_0.operand() { + appl_0.operands[0] = self.resolve_branch(operand); + } + // Check application is legal. + let appl_0_st = (*appl_0.callee).yield_type().to_owned(); + if let StaticTypes::TFunction(box_op_t, box_ret_t) = appl_0_st { + // Check if operand type checks out. + let op_0_st = appl_0.operands[0].yield_type(); + let maybe_op_inner_type = (*box_op_t).set_inner(); + + if maybe_op_inner_type.is_none() { + // Fatal, we should really never get here, + // because we _should_ check for this earlier. + issue!(TypeError, &self.filename, + err::LOC, &(*appl_0.callee).location(), + "Function should map from a set, it does not."); + } + + // Safe to unwrap, we've checked for none. + let op_inner_type = maybe_op_inner_type.unwrap(); + + if op_0_st != op_inner_type { + // TODO: If the types don't match, BUT, + // the type is a strict subset of the other, + // we may cast up the internal type (if possible). + // This should be done on 'Int' cast up to 'Real' + // (if a Real was expected, and got an Int), for + // example. + // We should alos emit a warning (always?) when + // an implicit cast has taken place. + issue!(TypeError, &self.filename, + err::LOC, &appl_0.operands[0].location(), + "Mismatching type in function call. + Expected argument of element \ + of `{}', instead got a `{}'.", + op_inner_type, op_0_st); + } + // If so, we can continue to unroll the type and + // assign it to this expression. + + // When applied, we end up with a value with + // a type of element of box_ret_t. + let return_type = (*box_ret_t).set_inner(); + if return_type.is_none() { + // Fatal, see simlar comment above. + issue!(TypeError, &self.filename, + err::LOC, &(*appl_0.callee).location(), + "Function should map to a set, it does not."); + } + appl_0.return_type = return_type.unwrap().clone(); + } else { + issue!(TypeError, &self.filename, + err::LOC, &appl_0.callee.location(), + "Function-application / juxtaposition is not \ + defined on type of `{}'.", + appl_0_st); + } + } + + node +} + +fn resolve_assignment(&mut self, + mut appl_0 : ast::CallNode, + appl_1 : ast::CallNode) -> ast::CallNode { + // TODO: Assignment means implicit type + // is given, if no type signature found, + // OR, it means we are defining a declared + // variable given the latest signature for it. + // Either way, we must say it is now 'defined' + // (as well as 'declared') in the table. + + // TODO: '=' with a type annotation should + // cast the value on the right if possible, to + // match the annotation. If not possible, throw + // an issue, saying types must match! + + // TODO: Handle if the assignment is defining + // a function (e.g. `f x = x + 1`). + + let filename = &self.filename.to_owned(); + let lhs = &appl_1.operands[0]; + // Handle variable (identifier) assignemnt: + if let Nodes::Ident(ident_op_1) = lhs { + // Recursively resolve RHS of assignment. + appl_0.operands[0] = self.resolve_branch(&appl_0.operands[0]); + // Check if an signature exists. + let maybe_table = self.search_chain(&ident_op_1.value); + if let Some(table) = maybe_table { + // TODO: Could be a function overload! + let mut entries : Vec<&mut SymbolEntry> = table + .iter_mut() + .filter(|entry| + entry.identifier == ident_op_1.value) + .collect(); + + // Search did not give `None`, so entries + // should never be empty! + assert!(entries.len() > 0); + #[cfg(feature="debug")] { + println!("Assignment of `{}':", ident_op_1.value); + println!("- RHS-type: {}", appl_0.operands[0].yield_type()); + println!("- Entries: {:#?}", entries); + } + + if entries.len() == 1 { // Not overloaded. + let ref mut entry = entries[0]; + // Check entry matches type of RHS + // of assigment. + + // TODO: Check if types can be coerced. + let rhs_type = appl_0.operands[0].yield_type(); + if rhs_type != entry.signature { + // TODO: Can cast? if so, do + // and don't throw an error. + issue!(TypeError, filename, + err::LOC, &appl_0.operands[0].location(), + "Signature does not match \ + right-hand-side of assignemnt. + Expected `{}', got `{}'.", + entry.signature, rhs_type); + } + // Otherwise, all is fine, + // and we can update whether it has + // been defined. + entry.was_defined(); + } else { // Overloaded. + // TODO: Check if it is valid to overload + // here. Non-functions cannot be overloaded + } + } else { + // Variable has implicit type, and + // adding the type to the symbol table + // is handled here. + } + } else if let Nodes::Call(call_op_1) = lhs { + let base_call = call_op_1.base_call(); + if !base_call.is_ident() { + // Fatal, we must define the call on some sort of ident. + issue!(ParseError, &self.filename, + err::LOC, &base_call.location(), + "You have to assign to a call on an identifer, + this identifier is the function you are defining. + Expected an `identifier', found `{}'!", + base_call.node_type()); + } + // We've checked, and we may unwrap it. + let base_call = base_call.ident().unwrap(); + // TODO Continue, collect the arguments too!!! + } else { + // TODO: Assigment to functions. + // TODO: Pattern matching etc. + issue!(ParseError, + &self.filename, err::LOC, &appl_1.operands[0].location(), + "Cannot assign to `{}' structure.", + appl_1.operands[0].node_type()); + } + + return appl_0; +} + +fn resolve_annotation(&mut self, appl_0 : ast::CallNode, appl_1 : ast::CallNode) { + let maybe_op_1 = appl_1.operand(); + if let Some(op_1) = maybe_op_1 { + if let Nodes::Ident(op_id_1) = op_1 { + let op_0 = appl_0.operands[0].clone(); + let set_signature = op_0.yield_type(); + if let StaticTypes::TSet(signature) = set_signature { + self.current_table().push( + &op_id_1.value, *signature, false); + } else { + issue!(TypeError, &self.filename, err::LOC, &op_0.location(), + "Right of type annotation must be a set. \ + Instead got `{}`.", set_signature); + } + } else { + issue!(ParseError, &self.filename, + err::LOC, &op_1.location(), + "Left of `:` type annotator must be \ + an identifier; found `{}'.", op_1.node_type()); + } + } else { + issue!(ParseError, + &self.filename, err::LOC, + &appl_1.location, + "No expression found left of `:`."); + } +} +} diff --git a/src/syntax/ast.rs b/src/syntax/ast.rs @@ -116,7 +116,7 @@ fn parse_with_radix(neg : bool, s : &str, radix : u32) -> Numerics { pub trait ToNumeric { fn to_numeric(&self) -> Numerics; } impl ToNumeric for &str { fn to_numeric(&self) -> Numerics { - let mut test_str = self.clone().to_ascii_lowercase(); + let mut test_str = <&str>::clone(self).to_ascii_lowercase(); let is_neg = self.starts_with('-'); if is_neg { test_str = test_str.get(1..).unwrap().to_string(); } @@ -258,17 +258,21 @@ pub struct FileNode { } #[derive(Clone)] -pub struct EmptyNode { +pub struct NilNode { /// Source location. pub location : Loc, } /// All base types, determined at compile time. -#[derive(Debug, Clone, PartialEq)] +/// The order the types are presented below, is +/// generally how we reference the types in the +/// compiled bytecode numerically (e.g. TReal => 3). +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum StaticTypes { TNatural, TInteger, TReal, TString, TSymbol, TSet(Box<StaticTypes>), + /// TFunction(boxed operand type, boxed return type) TFunction(Box<StaticTypes>, Box<StaticTypes>), TNil, @@ -276,6 +280,13 @@ pub enum StaticTypes { } impl StaticTypes { + pub fn set_inner(&self) -> Option<StaticTypes> { + if let StaticTypes::TSet(box_inner) = self { + return Some(*box_inner.clone()); + } + None + } + pub fn is_number(&self) -> bool { match self { StaticTypes::TNatural @@ -299,13 +310,13 @@ impl fmt::Display for StaticTypes { StaticTypes::TNatural => "Nat", StaticTypes::TInteger => "Int", StaticTypes::TReal => "Real", - StaticTypes::TString => "Str", + StaticTypes::TString => "String", StaticTypes::TSymbol => "Sym", StaticTypes::TFunction(o, r) => { ss = format!("({} \u{1f852} {})", o, r); ss.as_str() }, - StaticTypes::TNil => "Nil", + StaticTypes::TNil => "Empty", StaticTypes::TUnknown => "Any", _ => { ss = format!("Set {}", st); @@ -316,7 +327,7 @@ impl fmt::Display for StaticTypes { ss = format!("({} \u{21a6} {})", o, r); ss.as_str() }, - StaticTypes::TNil => "nil", + StaticTypes::TNil => "nothing", StaticTypes::TUnknown => "anything", }; write!(f, "{}", s) @@ -333,7 +344,7 @@ pub enum Nodes { Call(CallNode), Block(BlockNode), File(FileNode), - Empty(EmptyNode), + Nil(NilNode), } @@ -348,9 +359,14 @@ impl fmt::Display for Nodes { Nodes::Call(node) => format!( "%call{{\n :yield {}\n :callee ({})\n :operands [|\n {}\n |]\n}}", yt, node.callee, node.operands.iter().map(Nodes::to_string).collect::<Vec<String>>().join("\n ")), - Nodes::Block(_) => format!("%block{{ ... }}"), + Nodes::Block(node) => format!("%block{{ {} }}", + node.statements + .iter() + .map(Nodes::to_string) + .collect::<Vec<String>>() + .join("\n")), Nodes::File(node) => format!("%file{{ :filename {} }}", node.filename), - Nodes::Empty(_) => String::from("()"), + Nodes::Nil(_) => String::from("()"), }; write!(f, "{}", printable) } @@ -374,7 +390,7 @@ impl Nodes { Nodes::Num(n) => n.location, Nodes::Str(n) => n.location, Nodes::Sym(n) => n.location, - Nodes::Empty(n) => n.location, + Nodes::Nil(n) => n.location, Nodes::Block(n) => n.location, Nodes::File(n) => n.location, } @@ -397,10 +413,10 @@ impl Nodes { "Nat" => StaticTypes::TSet(Box::new(StaticTypes::TNatural)), "Int" => StaticTypes::TSet(Box::new(StaticTypes::TInteger)), "Real" => StaticTypes::TSet(Box::new(StaticTypes::TReal)), - "Str" => StaticTypes::TSet(Box::new(StaticTypes::TString)), - "Sym" => StaticTypes::TSet(Box::new(StaticTypes::TSymbol)), - "Nil" => StaticTypes::TSet(Box::new(StaticTypes::TNil)), - "Any" => StaticTypes::TSet(Box::new(StaticTypes::TUnknown)), + "Str" | "String" => StaticTypes::TSet(Box::new(StaticTypes::TString)), + "Sym" | "Symbol" => StaticTypes::TSet(Box::new(StaticTypes::TSymbol)), + "Empty" => StaticTypes::TSet(Box::new(StaticTypes::TNil)), + "Any" | "Anything" => StaticTypes::TSet(Box::new(StaticTypes::TUnknown)), _ => ident.static_type.to_owned() } }, @@ -431,7 +447,7 @@ impl Nodes { }, Nodes::Block(_) | Nodes::File(_) => StaticTypes::TUnknown, - Nodes::Empty(_) => StaticTypes::TNil, + Nodes::Nil(_) => StaticTypes::TNil, } } @@ -447,12 +463,12 @@ impl Nodes { match self { Nodes::Ident(_) => "identifier", Nodes::Num(_) => "numeric", - Nodes::Str(_) => "string", + Nodes::Str(_) => "string literal", Nodes::Sym(_) => "symbol", - Nodes::Empty(_) => "empty", - Nodes::Call(_) => "function-call", - Nodes::Block(_) => "code-block", - _ => "ungrammatical-meta-node" + Nodes::Nil(_) => "nothing", + Nodes::Call(_) => "function call", + Nodes::Block(_) => "code block", + _ => "ungrammatical meta node" } } @@ -472,7 +488,18 @@ impl Nodes { pub fn call(&self) -> Option<&CallNode> { unwrap_enum!(self, Nodes::Call) } pub fn block(&self) -> Option<&BlockNode> { unwrap_enum!(self, Nodes::Block) } pub fn file(&self) -> Option<&FileNode> { unwrap_enum!(self, Nodes::File) } - pub fn empty(&self) -> Option<&EmptyNode> { unwrap_enum!(self, Nodes::Empty) } + pub fn nil(&self) -> Option<&NilNode> { unwrap_enum!(self, Nodes::Nil) } + + pub fn is_ident(&self) -> bool { self.ident().is_some() } + pub fn is_num(&self) -> bool { self.num().is_some() } + pub fn is_str(&self) -> bool { self.str().is_some() } + pub fn is_sym(&self) -> bool { self.sym().is_some() } + pub fn is_call(&self) -> bool { self.call().is_some() } + pub fn is_block(&self) -> bool { self.block().is_some() } + pub fn is_file(&self) -> bool { self.file().is_some() } + pub fn is_nil(&self) -> bool { self.nil().is_some() } + + pub fn is_atomic(&self) -> bool { match self { @@ -480,7 +507,7 @@ impl Nodes { | Nodes::Num(_) | Nodes::Str(_) | Nodes::Sym(_) - | Nodes::Empty(_) => true, + | Nodes::Nil(_) => true, _ => false } } @@ -524,7 +551,7 @@ impl CallNode { pub fn new(callee : Nodes, operands : Vec<Nodes>, location : Loc) -> Nodes { Nodes::Call(CallNode { callee: Box::new(callee), - operands: operands, + operands, return_type: StaticTypes::TUnknown, location }) @@ -534,9 +561,23 @@ impl CallNode { self.return_type = new_type; } - pub fn collect(&self) -> Vec<Nodes> { - fn make_argument_vector(call_node : &Nodes, operands : VecDeque<Nodes>) -> VecDeque<Nodes> { - let mut pushable = operands.clone(); + /// The base (bottom-most) callee for a call chain. + pub fn base_call(&self) -> Nodes { + let mut last_call : &CallNode = self; + loop { + if let Nodes::Call(call) = &*last_call.callee { + last_call = call; + } else { + return (*last_call.callee).clone(); + } + } + } + + /// Collect arguments to a call. + pub fn collect_operands(&self) -> Vec<Nodes> { + fn make_argument_vector(call_node : &Nodes, + operands : VecDeque<Nodes>) -> VecDeque<Nodes> { + let mut pushable = operands; if let Nodes::Call(call) = call_node { pushable.push_front(call.operands[0].clone()); @@ -550,6 +591,13 @@ impl CallNode { Vec::from(q) } + /// List of callee and operands, lisp call style list. + pub fn collect(&self) -> Vec<Nodes> { + let mut list = vec![self.base_call()]; + list.extend_from_slice(&self.collect_operands()); + list + } + pub fn is_unary(&self) -> bool { self.callee.ident().is_some() && !self.operands.is_empty() } @@ -558,6 +606,10 @@ impl CallNode { let sub_call = self.callee.call(); sub_call.is_some() && !self.operands.is_empty() && sub_call.unwrap().is_unary() } + + pub fn operand(&self) -> Option<&Nodes> { + self.operands.last() + } } impl FileNode { @@ -565,8 +617,8 @@ impl FileNode { { Nodes::File(FileNode { filename, location }) } } -impl EmptyNode { - pub fn new(location : Loc) -> Nodes { Nodes::Empty(EmptyNode { location }) } +impl NilNode { + pub fn new(location : Loc) -> Nodes { Nodes::Nil(NilNode { location }) } } /// Root branch of the AST. @@ -585,27 +637,27 @@ const TAB : &str = " "; pub fn pretty_print(node : &Nodes, depth : usize) -> String { let tab = TAB.repeat(depth); - let printable = match node { - Nodes::Call(n) => format!( - "{tab}%call{{\n{tab}{T}:yield {yt}\n{tab}{T}:callee (\n{calling}\n{tab}{T})\n{tab}{T}:operand [|{op}|]\n{tab}}}", - tab=tab, T=TAB, - yt=n.return_type, - calling=pretty_print(&*n.callee, depth + 2), - op=(if n.operands.is_empty() { String::from(" ") } else { format!( - "\n{ops}\n{tab}{T}", - ops=pretty_print(&n.operands[0], depth + 2), - tab=tab, T=TAB) }) - ), - Nodes::Block(_) => format!("%block{{ ... }}"), - _ => format!("{}{}", tab, node) - }; - printable + match node { + Nodes::Call(n) => format!( + "{tab}%call{{\n{tab}{T}:yield {yt}\n{tab}{T}:callee (\n{calling}\n{tab}{T})\n{tab}{T}:operand [|{op}|]\n{tab}}}", + tab=tab, T=TAB, + yt=n.return_type, + calling=pretty_print(&*n.callee, depth + 2), + op=(if n.operands.is_empty() { String::from(" ") } else { format!( + "\n{ops}\n{tab}{T}", + ops=pretty_print(&n.operands[0], depth + 2), + tab=tab, T=TAB) }) + ), + // TODO: Pretty Print Blocks. + Nodes::Block(_) => node.to_string(), + _ => format!("{}{}", tab, node) + } } impl fmt::Display for Root { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let str_mapped : Vec<String> = self.branches.iter().map(|n| pretty_print(n, 0)).collect(); - write!(f, "[|\n {}\n|]", str_mapped.join("\n").split("\n").collect::<Vec<&str>>().join("\n ")) + write!(f, "[|\n {}\n|]", str_mapped.join("\n").split('\n').collect::<Vec<&str>>().join("\n ")) } -}- \ No newline at end of file +} diff --git a/src/syntax/lexer.rs b/src/syntax/lexer.rs @@ -1,3 +1,5 @@ +use crate::err; + use super::token; use token::{Token, TokenType}; @@ -40,6 +42,7 @@ impl RegexExt for Regex { /// All chars that may constitue an ident. const IDENT_CHARS : &str = r"\p{L}\?!'\-_"; +// TODO: Parse symbols with spaces? `:"..."` syntax. lazy_static! { static ref OP : Regex = re!(r"\A([,\+\.\*\|\\/\&%\$\^\~<¬=@>\-]+|:{2,})"); static ref IDENT : Regex = re!(&format!(r"\A([{id}][{id}\p{{N}}]*)", id=IDENT_CHARS)); @@ -58,14 +61,16 @@ macro_rules! try_match { location::new($line, $col, span))); $current_char_ptr += matched.len(); $col += span; - continue; + $stream.back() + } else { + None } }; } /// Takes a piece of code (as a &str) and returns /// the generated token-stream (as a VecDeque<Token>). -pub fn lex(string : &str) -> VecDeque<Token> { +pub fn lex(string : &str, filename : &str) -> VecDeque<Token> { let mut token_stream : VecDeque<Token> = VecDeque::new(); let mut current_char_ptr = 0; @@ -218,28 +223,45 @@ pub fn lex(string : &str) -> VecDeque<Token> { continue; } - try_match!(token_stream, partial, + let matched = try_match!(token_stream, partial, NUM, TokenType::Num, current_char_ptr, line, col); + if matched.is_some() { continue; } - try_match!(token_stream, partial, + let matched = try_match!(token_stream, partial, OP, TokenType::Op, current_char_ptr, line, col); + if matched.is_some() { continue; } - try_match!(token_stream, partial, + let matched = try_match!(token_stream, partial, IDENT, TokenType::Ident, current_char_ptr, line, col); + if matched.is_some() { continue; } - try_match!(token_stream, partial, + let matched = try_match!(token_stream, partial, SYM, TokenType::Sym, current_char_ptr, line, col); + if let Some(token) = matched { + if two_chars == ":)" { + warn!(LexWarn, filename, token, + "Nice smiley-face, but are you sure you wanted to \ + use a `Symbol' here? Use `:\")\"` to be more explicit."); + } + continue; + } current_char_ptr += 1; if partial.is_char_boundary(0) { col += 1 } } + let mut last_location = location::new(0, 0, 1); + if let Some(last_token) = token_stream.back() { + last_location = last_token.location; + } + token_stream.push_back(Token::new( TokenType::EOF, "\0", - location::new(line, col, 1))); + last_location)); + token_stream -}- \ No newline at end of file +} diff --git a/src/syntax/location.rs b/src/syntax/location.rs @@ -1,5 +1,5 @@ /// Holds line, column and span of a lexical token. -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Loc { /// Line number. pub line : u32, diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs @@ -22,9 +22,13 @@ pub mod lexer; pub mod parser; /// Tree static analysis. -pub mod analyser; +#[macro_use] +pub mod analysis; use std::fs; +use std::collections::HashSet; + +#[cfg(feature="debug")] use token::ShowStream; /// Parses a given file, calling various methods from @@ -32,13 +36,31 @@ use token::ShowStream; pub fn parse_file(filename : &str) -> ast::Root { let code = fs::read_to_string(filename) .expect("Could not open file for reading."); + + #[cfg(feature="debug")] println!("Code:\n{}\n", code); - let stream = lexer::lex(&code); + let stream = lexer::lex(&code, filename); + + #[cfg(feature="debug")] println!("Stream:\n{}\n", stream.to_string()); let mut tree = parser::parse(stream, filename); - analyser::replace(&mut tree); + let transformations = transformations![ + TYPE_RESOLUTION, + CONSTANT_FOLDING + ]; + + // No optimisations in debug. + #[cfg(feature="debug")] + let transformations = transformations![ + TYPE_RESOLUTION + ]; + + analysis::replace(&mut tree, transformations); + + #[cfg(feature="debug")] println!("AST:\n{}\n", tree); + tree -}- \ No newline at end of file +} diff --git a/src/syntax/operators.rs b/src/syntax/operators.rs @@ -21,7 +21,7 @@ pub struct Operator<'a> { impl<'a> Operator<'a> { pub fn new(name : &'a str, precedence : i32, associativity : Side, arity : i32) -> Self { Operator { - name: name.clone(), + name, precedence, associativity, arity, @@ -53,8 +53,38 @@ macro_rules! push_op { impl<'a> PrecedenceTable<'a> { pub fn new() -> Self { + Self::default() + } + + pub fn new_op(&mut self, name : &'a str, prec : i32, assoc : Side, arity : i32) -> Operator { + let op = Operator::new(name, prec, assoc, arity); + self.table.push(op); + op + } + + pub fn new_fun(&mut self, name : &'a str, max_arity : i32) -> Operator { + self.new_op(name, 19, Side::Neither, max_arity) + } + + pub fn lookup(&self, name : &str, arity : i32) -> Option<&Operator> { + self.table.iter().filter(|o| o.name == name && o.arity == arity).nth(0) + } + + pub fn exists(&self, name : &str) -> bool { + self.table.iter().filter(|o| o.name == name).nth(0).is_some() + } + + pub fn precedence(&self, name : &str) -> Option<i32> { + let maybe_op = self.lookup(name, 2); + if let Some(op) = maybe_op { return Some(op.precedence) } + return None; + } +} + +impl<'a> Default for PrecedenceTable<'a> { + fn default() -> Self { let op = Operator::new; - let table = PrecedenceTable { table: vec![ + PrecedenceTable { table: vec![ op( "::",210, Side::Left, 2), op( "<>",200, Side::Right, 2), // Function calls have precedence 190, i.e. very high. @@ -96,32 +126,6 @@ impl<'a> PrecedenceTable<'a> { op( "=>", 1, Side::Neither, 2), op( "(", 0, Side::Neither, 1), op( ")", 0, Side::Neither, 1), - ]}; - - table - } - - pub fn new_op(&mut self, name : &'a str, prec : i32, assoc : Side, arity : i32) -> Operator { - let op = Operator::new(name, prec, assoc, arity); - self.table.push(op); - op - } - - pub fn new_fun(&mut self, name : &'a str, max_arity : i32) -> Operator { - self.new_op(name, 19, Side::Neither, max_arity) - } - - pub fn lookup(&self, name : &str, arity : i32) -> Option<&Operator> { - self.table.iter().filter(|o| o.name == name && o.arity == arity).nth(0) - } - - pub fn exists(&self, name : &str) -> bool { - self.table.iter().filter(|o| o.name == name).nth(0).is_some() - } - - pub fn precedence(&self, name : &str) -> Option<i32> { - let op = self.lookup(name, 2); - if op.is_some() { return Some(op.unwrap().precedence) } - return None; + ]} } -}- \ No newline at end of file +} diff --git a/src/syntax/parser.rs b/src/syntax/parser.rs @@ -12,6 +12,22 @@ use location::Loc; use token::{Token, TokenType}; use ast::Nodes; +fn location_range(loc_begin : &Loc, loc_end : &Loc) -> Loc { + let mut loc_final = loc_end.clone(); + + loc_final.lines += loc_end.line - loc_begin.line; + loc_final.line = loc_begin.line; + + // TODO: Location should record character count from the + // beginning of the file, this way we can give a proper multi line + // span length. :) + if loc_final.lines == 1 { + loc_final.span += loc_end.col - loc_begin.col; + loc_final.col = loc_begin.col; + } + + loc_final +} pub fn parse(stream : VecDeque<Token>, file : &str) -> ast::Root { let mut environment = ParseEnvironment::new(stream, file); @@ -76,6 +92,9 @@ impl<'a> ParseEnvironment<'a> { } } + // TODO: Generate call nodes with accurate location data. + // Currently this is only done in `func_apply`. + fn null_den(&mut self, token : &Token) -> Nodes { let loc = token.location; match token.class { @@ -117,15 +136,20 @@ impl<'a> ParseEnvironment<'a> { TokenType::Str => ast::StrNode::new( &token.string, loc), TokenType::Sym => ast::SymNode::new( &token.string, loc), TokenType::LParen => { - let current = self.stream.get(0); - if current.is_none() || current.unwrap().class == TokenType::EOF { - self.expect(TokenType::RParen, current) - } else if current.unwrap().class == TokenType::RParen { - self.shift(); - return ast::EmptyNode::new(loc); + let maybe_current = self.stream.get(0); + if let Some(current) = maybe_current { + if current.class == TokenType::RParen { + self.shift(); + let mut nil_loc = loc.clone(); + nil_loc.span += 1; + return ast::NilNode::new(nil_loc); + } else if current.class == TokenType::EOF { + self.expect(TokenType::RParen, maybe_current); + } + } else { + self.expect(TokenType::RParen, None); } - self.ignore_newline = true; self.skip_newlines(); let expr = self.expr(0); @@ -135,8 +159,9 @@ impl<'a> ParseEnvironment<'a> { self.shift(); expr } - _ => issue!(err::Types::ParseError, self.file, token, - "`{}` has no null-denotation.", token.class) + _ => issue!(ParseError, self.file, token, + "`{}` has no null-denotation (cannot be used as a prefix).", + token.class) } } @@ -176,21 +201,33 @@ impl<'a> ParseEnvironment<'a> { } fn func_apply(&mut self, mut left : Nodes) -> Nodes { + // What are `first_loc` & `final_loc` for? + // They update location of function call nodes to span + // a correct number of columns (store first and last column). + // Also, update `lines` to specify how many lines the function + // call spans. + let first_loc = left.location(); + let mut pushed = false; - match left { - Nodes::Call(ref mut call) => { - if call.operands.is_empty() { - call.operands.push(self.expr(190)); - pushed = true; - } - }, - _ => () - }; + if let Nodes::Call(ref mut call) = left { + if call.operands.is_empty() { + let operand_node = self.expr(190); + call.operands.push(operand_node); + pushed = true; + } + } + if pushed { return left; } - ast::CallNode::new(left, vec![self.expr(190)], self.location) + + let operand_node = self.expr(190); + let last_loc = operand_node.location(); + let final_loc = location_range(&first_loc, &last_loc); + + ast::CallNode::new(left, vec![operand_node], final_loc) } fn left_den(&mut self, left : Nodes, op : operators::Operator) -> Nodes { + let left_loc = left.location(); let first_apply = ast::CallNode::new( ast::IdentNode::new(op.name, self.location), vec![left], @@ -198,21 +235,22 @@ impl<'a> ParseEnvironment<'a> { if self.stream[0].class == TokenType::RParen { return first_apply; } - let right = self.expr( - op.precedence - (if op.is_right() { 1 } else { 0 })); - ast::CallNode::new(first_apply, vec![right], self.location) + let right = self.expr(op.precedence + - (if op.is_right() { 1 } else { 0 })); + + let call_loc = location_range(&left_loc, &right.location()); + ast::CallNode::new(first_apply, vec![right], call_loc) } fn expect(&self, tt : TokenType, maybe_t : Option<&Token>) { if maybe_t.is_none() { - issue!(err::Types::ParseError, self.file, - self.stream.iter().last().unwrap(), + issue!(ParseError, self.file, self.stream.iter().last().unwrap(), "Unexpected end of stream."); } let t = maybe_t.unwrap(); if t.class != tt { - issue!(err::Types::ParseError, self.file, t, + issue!(ParseError, self.file, t, "Unexpected token type: `{}`, expected: `{}`.", t.class, tt); } } diff --git a/src/syntax/token.rs b/src/syntax/token.rs @@ -4,6 +4,14 @@ use super::location; use snailquote::escape; use unicode_width::UnicodeWidthStr; + +/// # TODO: Use this. +/// Way of representing a level of indentation. +enum Indent { + Tab, + Spaces(u32), +} + /// Contains all possible types/classes of /// lexiacal tokens. #[derive(PartialEq, Clone)] @@ -44,21 +52,21 @@ pub enum TokenType { impl fmt::Display for TokenType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let printable = match *self { - TokenType::Ident => "Identifier", - TokenType::Num => "Numeric", - TokenType::Op => "Operator", - TokenType::Sym => "Symbol", - TokenType::Str => "String", - TokenType::LParen => "L-Paren", - TokenType::RParen => "R-Paren", - TokenType::LBrack => "L-Bracket", - TokenType::RBrack => "R-Bracket", - TokenType::LBrace => "L-Brace", - TokenType::RBrace => "R-Brace", - TokenType::LVec => "L-Vector", - TokenType::RVec => "R-Vector", - TokenType::Term => "Terminator", - TokenType::EOF => "End-Of-File", + Self::Ident => "Identifier", + Self::Num => "Numeric", + Self::Op => "Operator", + Self::Sym => "Symbol", + Self::Str => "String", + Self::LParen => "L-Paren", + Self::RParen => "R-Paren", + Self::LBrack => "L-Bracket", + Self::RBrack => "R-Bracket", + Self::LBrace => "L-Brace", + Self::RBrace => "R-Brace", + Self::LVec => "L-Vector", + Self::RVec => "R-Vector", + Self::Term => "Terminator", + Self::EOF => "End-Of-File", }; write!(f, "{}", printable) } @@ -94,18 +102,20 @@ impl Token { _ => false, } } +} - /// String representation of the token. - pub fn to_string(&self) -> String { +/// String representation of the token. +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut escaped = escape(&self.string.to_string()).into_owned(); if !escaped.ends_with('"') { escaped = format!("\"{}\"", escaped); } - format!("[ {class}:{spaces1}{rep}{spaces2}({l}, {c}):{span} ]", + write!(f, "[ {class}:{spaces1}{rep}{spaces2}{l}:{c} ({span}) ]", class=self.class, rep=escaped, spaces1=" ".repeat(12 - self.class.to_string().width()), - spaces2=" ".repeat(50 - escaped.width()), + spaces2=" ".repeat(30 - escaped.width()), l=self.location.line, c=self.location.col, span=self.location.span) } diff --git a/test.vh b/test.vh @@ -1,5 +0,0 @@ -a : Nat -a = 3 - -__raw_print (a + 2.5) - diff --git a/test_source.vh b/test_source.vh @@ -0,0 +1,16 @@ +-- TODO: Test overloading with `f`. +-- TODO: Test casting from subsets of Real, upto Real. + +f : Real -> Real -> Real + +f a b = (1 + 1) * a + b -- 2a + b + +a : Nat +a = 3 + +( : ) b Int +b = 1 - 8 -- -7 + +c : Real +c = f (a + 1.0) (b - 0.0) +