Initial commit
This commit is contained in:
		
						commit
						80f8ede0bb
					
				
					 24 changed files with 2316 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								.envrc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.envrc
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | eval "$(lorri direnv)" | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | /target | ||||||
							
								
								
									
										789
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										789
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,789 @@ | ||||||
|  | # This file is automatically @generated by Cargo. | ||||||
|  | # It is not intended for manual editing. | ||||||
|  | [[package]] | ||||||
|  | name = "achilles" | ||||||
|  | version = "0.1.0" | ||||||
|  | dependencies = [ | ||||||
|  |  "anyhow", | ||||||
|  |  "clap", | ||||||
|  |  "derive_more", | ||||||
|  |  "inkwell", | ||||||
|  |  "llvm-sys", | ||||||
|  |  "nom", | ||||||
|  |  "nom-trace", | ||||||
|  |  "pratt", | ||||||
|  |  "proptest", | ||||||
|  |  "test-strategy", | ||||||
|  |  "thiserror", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "aho-corasick" | ||||||
|  | version = "0.7.15" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" | ||||||
|  | dependencies = [ | ||||||
|  |  "memchr", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "anyhow" | ||||||
|  | version = "1.0.38" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "arrayvec" | ||||||
|  | version = "0.5.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "atty" | ||||||
|  | version = "0.2.14" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" | ||||||
|  | dependencies = [ | ||||||
|  |  "hermit-abi", | ||||||
|  |  "libc", | ||||||
|  |  "winapi", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "autocfg" | ||||||
|  | version = "1.0.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "bit-set" | ||||||
|  | version = "0.5.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" | ||||||
|  | dependencies = [ | ||||||
|  |  "bit-vec", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "bit-vec" | ||||||
|  | version = "0.6.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "bitflags" | ||||||
|  | version = "1.2.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "bitvec" | ||||||
|  | version = "0.19.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" | ||||||
|  | dependencies = [ | ||||||
|  |  "funty", | ||||||
|  |  "radium", | ||||||
|  |  "tap", | ||||||
|  |  "wyz", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "byteorder" | ||||||
|  | version = "1.4.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "cc" | ||||||
|  | version = "1.0.67" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "cfg-if" | ||||||
|  | version = "1.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "clap" | ||||||
|  | version = "3.0.0-beta.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" | ||||||
|  | dependencies = [ | ||||||
|  |  "atty", | ||||||
|  |  "bitflags", | ||||||
|  |  "clap_derive", | ||||||
|  |  "indexmap", | ||||||
|  |  "lazy_static", | ||||||
|  |  "os_str_bytes", | ||||||
|  |  "strsim", | ||||||
|  |  "termcolor", | ||||||
|  |  "textwrap", | ||||||
|  |  "unicode-width", | ||||||
|  |  "vec_map", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "clap_derive" | ||||||
|  | version = "3.0.0-beta.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" | ||||||
|  | dependencies = [ | ||||||
|  |  "heck", | ||||||
|  |  "proc-macro-error", | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "derive_more" | ||||||
|  | version = "0.99.11" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "either" | ||||||
|  | version = "1.6.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "fnv" | ||||||
|  | version = "1.0.7" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "funty" | ||||||
|  | version = "1.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "getrandom" | ||||||
|  | version = "0.2.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" | ||||||
|  | dependencies = [ | ||||||
|  |  "cfg-if", | ||||||
|  |  "libc", | ||||||
|  |  "wasi", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "hashbrown" | ||||||
|  | version = "0.9.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "heck" | ||||||
|  | version = "0.3.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" | ||||||
|  | dependencies = [ | ||||||
|  |  "unicode-segmentation", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "hermit-abi" | ||||||
|  | version = "0.1.18" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "indexmap" | ||||||
|  | version = "1.6.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" | ||||||
|  | dependencies = [ | ||||||
|  |  "autocfg", | ||||||
|  |  "hashbrown", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "inkwell" | ||||||
|  | version = "0.1.0" | ||||||
|  | source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c" | ||||||
|  | dependencies = [ | ||||||
|  |  "either", | ||||||
|  |  "inkwell_internals", | ||||||
|  |  "libc", | ||||||
|  |  "llvm-sys", | ||||||
|  |  "once_cell", | ||||||
|  |  "parking_lot", | ||||||
|  |  "regex", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "inkwell_internals" | ||||||
|  | version = "0.3.0" | ||||||
|  | source = "git+https://github.com/TheDan64/inkwell?branch=master#a2db15b0bd1c06d71763585ae10d9ea4e775da0c" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "instant" | ||||||
|  | version = "0.1.9" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" | ||||||
|  | dependencies = [ | ||||||
|  |  "cfg-if", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "lazy_static" | ||||||
|  | version = "1.4.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "lexical-core" | ||||||
|  | version = "0.7.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "21f866863575d0e1d654fbeeabdc927292fdf862873dc3c96c6f753357e13374" | ||||||
|  | dependencies = [ | ||||||
|  |  "arrayvec", | ||||||
|  |  "bitflags", | ||||||
|  |  "cfg-if", | ||||||
|  |  "ryu", | ||||||
|  |  "static_assertions", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "libc" | ||||||
|  | version = "0.2.88" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "llvm-sys" | ||||||
|  | version = "110.0.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "21ede189444b8c78907e5d36da5dabcf153170fcff9c1dba48afc4b33c7e19f0" | ||||||
|  | dependencies = [ | ||||||
|  |  "cc", | ||||||
|  |  "lazy_static", | ||||||
|  |  "libc", | ||||||
|  |  "regex", | ||||||
|  |  "semver", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "lock_api" | ||||||
|  | version = "0.4.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" | ||||||
|  | dependencies = [ | ||||||
|  |  "scopeguard", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "memchr" | ||||||
|  | version = "2.3.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "nom" | ||||||
|  | version = "6.1.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitvec", | ||||||
|  |  "funty", | ||||||
|  |  "lexical-core", | ||||||
|  |  "memchr", | ||||||
|  |  "version_check", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "nom-trace" | ||||||
|  | version = "0.2.1" | ||||||
|  | source = "git+https://github.com/glittershark/nom-trace?branch=nom-6#6168d2e15cc51efd12d80260159b76a764dba138" | ||||||
|  | dependencies = [ | ||||||
|  |  "nom", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "num-traits" | ||||||
|  | version = "0.2.14" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" | ||||||
|  | dependencies = [ | ||||||
|  |  "autocfg", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "once_cell" | ||||||
|  | version = "1.7.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "os_str_bytes" | ||||||
|  | version = "2.4.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "parking_lot" | ||||||
|  | version = "0.11.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" | ||||||
|  | dependencies = [ | ||||||
|  |  "instant", | ||||||
|  |  "lock_api", | ||||||
|  |  "parking_lot_core", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "parking_lot_core" | ||||||
|  | version = "0.8.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" | ||||||
|  | dependencies = [ | ||||||
|  |  "cfg-if", | ||||||
|  |  "instant", | ||||||
|  |  "libc", | ||||||
|  |  "redox_syscall", | ||||||
|  |  "smallvec", | ||||||
|  |  "winapi", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "pest" | ||||||
|  | version = "2.1.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" | ||||||
|  | dependencies = [ | ||||||
|  |  "ucd-trie", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ppv-lite86" | ||||||
|  | version = "0.2.10" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "pratt" | ||||||
|  | version = "0.3.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e31bbc12f7936a7b195790dd6d9b982b66c54f45ff6766decf25c44cac302dce" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "proc-macro-error" | ||||||
|  | version = "1.0.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro-error-attr", | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  |  "version_check", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "proc-macro-error-attr" | ||||||
|  | version = "1.0.4" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "version_check", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "proc-macro2" | ||||||
|  | version = "1.0.24" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" | ||||||
|  | dependencies = [ | ||||||
|  |  "unicode-xid", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "proptest" | ||||||
|  | version = "1.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" | ||||||
|  | dependencies = [ | ||||||
|  |  "bit-set", | ||||||
|  |  "bitflags", | ||||||
|  |  "byteorder", | ||||||
|  |  "lazy_static", | ||||||
|  |  "num-traits", | ||||||
|  |  "quick-error 2.0.0", | ||||||
|  |  "rand", | ||||||
|  |  "rand_chacha", | ||||||
|  |  "rand_xorshift", | ||||||
|  |  "regex-syntax", | ||||||
|  |  "rusty-fork", | ||||||
|  |  "tempfile", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "quick-error" | ||||||
|  | version = "1.2.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "quick-error" | ||||||
|  | version = "2.0.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "quote" | ||||||
|  | version = "1.0.9" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "radium" | ||||||
|  | version = "0.5.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rand" | ||||||
|  | version = "0.8.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  |  "rand_chacha", | ||||||
|  |  "rand_core", | ||||||
|  |  "rand_hc", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rand_chacha" | ||||||
|  | version = "0.3.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" | ||||||
|  | dependencies = [ | ||||||
|  |  "ppv-lite86", | ||||||
|  |  "rand_core", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rand_core" | ||||||
|  | version = "0.6.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" | ||||||
|  | dependencies = [ | ||||||
|  |  "getrandom", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rand_hc" | ||||||
|  | version = "0.3.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" | ||||||
|  | dependencies = [ | ||||||
|  |  "rand_core", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rand_xorshift" | ||||||
|  | version = "0.3.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" | ||||||
|  | dependencies = [ | ||||||
|  |  "rand_core", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "redox_syscall" | ||||||
|  | version = "0.2.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" | ||||||
|  | dependencies = [ | ||||||
|  |  "bitflags", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "regex" | ||||||
|  | version = "1.4.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" | ||||||
|  | dependencies = [ | ||||||
|  |  "aho-corasick", | ||||||
|  |  "memchr", | ||||||
|  |  "regex-syntax", | ||||||
|  |  "thread_local", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "regex-syntax" | ||||||
|  | version = "0.6.22" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "remove_dir_all" | ||||||
|  | version = "0.5.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" | ||||||
|  | dependencies = [ | ||||||
|  |  "winapi", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "rusty-fork" | ||||||
|  | version = "0.3.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" | ||||||
|  | dependencies = [ | ||||||
|  |  "fnv", | ||||||
|  |  "quick-error 1.2.3", | ||||||
|  |  "tempfile", | ||||||
|  |  "wait-timeout", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ryu" | ||||||
|  | version = "1.0.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "scopeguard" | ||||||
|  | version = "1.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "semver" | ||||||
|  | version = "0.11.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" | ||||||
|  | dependencies = [ | ||||||
|  |  "semver-parser", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "semver-parser" | ||||||
|  | version = "0.10.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" | ||||||
|  | dependencies = [ | ||||||
|  |  "pest", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "smallvec" | ||||||
|  | version = "1.6.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "static_assertions" | ||||||
|  | version = "1.1.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "strsim" | ||||||
|  | version = "0.10.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "syn" | ||||||
|  | version = "1.0.61" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "ed22b90a0e734a23a7610f4283ac9e5acfb96cbb30dfefa540d66f866f1c09c5" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "unicode-xid", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "tap" | ||||||
|  | version = "1.0.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "tempfile" | ||||||
|  | version = "3.2.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" | ||||||
|  | dependencies = [ | ||||||
|  |  "cfg-if", | ||||||
|  |  "libc", | ||||||
|  |  "rand", | ||||||
|  |  "redox_syscall", | ||||||
|  |  "remove_dir_all", | ||||||
|  |  "winapi", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "termcolor" | ||||||
|  | version = "1.1.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" | ||||||
|  | dependencies = [ | ||||||
|  |  "winapi-util", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "test-strategy" | ||||||
|  | version = "0.1.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "2328963c69243416e811c88066d18f670792b2e36e17fa57f4b1a124f85d18a8" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "textwrap" | ||||||
|  | version = "0.12.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" | ||||||
|  | dependencies = [ | ||||||
|  |  "unicode-width", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "thiserror" | ||||||
|  | version = "1.0.24" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" | ||||||
|  | dependencies = [ | ||||||
|  |  "thiserror-impl", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "thiserror-impl" | ||||||
|  | version = "1.0.24" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" | ||||||
|  | dependencies = [ | ||||||
|  |  "proc-macro2", | ||||||
|  |  "quote", | ||||||
|  |  "syn", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "thread_local" | ||||||
|  | version = "1.1.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" | ||||||
|  | dependencies = [ | ||||||
|  |  "once_cell", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "ucd-trie" | ||||||
|  | version = "0.1.3" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "unicode-segmentation" | ||||||
|  | version = "1.7.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "unicode-width" | ||||||
|  | version = "0.1.8" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "unicode-xid" | ||||||
|  | version = "0.2.1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "vec_map" | ||||||
|  | version = "0.8.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "version_check" | ||||||
|  | version = "0.9.2" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "wait-timeout" | ||||||
|  | version = "0.2.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" | ||||||
|  | dependencies = [ | ||||||
|  |  "libc", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "wasi" | ||||||
|  | version = "0.10.2+wasi-snapshot-preview1" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "winapi" | ||||||
|  | version = "0.3.9" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||||||
|  | dependencies = [ | ||||||
|  |  "winapi-i686-pc-windows-gnu", | ||||||
|  |  "winapi-x86_64-pc-windows-gnu", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "winapi-i686-pc-windows-gnu" | ||||||
|  | version = "0.4.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "winapi-util" | ||||||
|  | version = "0.1.5" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" | ||||||
|  | dependencies = [ | ||||||
|  |  "winapi", | ||||||
|  | ] | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "winapi-x86_64-pc-windows-gnu" | ||||||
|  | version = "0.4.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||||||
|  | 
 | ||||||
|  | [[package]] | ||||||
|  | name = "wyz" | ||||||
|  | version = "0.2.0" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" | ||||||
							
								
								
									
										18
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Cargo.toml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | [package] | ||||||
|  | name = "achilles" | ||||||
|  | version = "0.1.0" | ||||||
|  | authors = ["Griffin Smith <root@gws.fyi>"] | ||||||
|  | edition = "2018" | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
|  | anyhow = "1.0.38" | ||||||
|  | clap = "3.0.0-beta.2" | ||||||
|  | derive_more = "0.99.11" | ||||||
|  | inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm11-0"] } | ||||||
|  | llvm-sys = "110.0.1" | ||||||
|  | nom = "6.1.2" | ||||||
|  | nom-trace = { git = "https://github.com/glittershark/nom-trace", branch = "nom-6" } | ||||||
|  | pratt = "0.3.0" | ||||||
|  | proptest = "1.0.0" | ||||||
|  | test-strategy = "0.1.1" | ||||||
|  | thiserror = "1.0.24" | ||||||
							
								
								
									
										2
									
								
								ach/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								ach/.gitignore
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,2 @@ | ||||||
|  | *.ll | ||||||
|  | *.o | ||||||
							
								
								
									
										15
									
								
								ach/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								ach/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | default: simple | ||||||
|  | 
 | ||||||
|  | %.ll: %.ach | ||||||
|  | 	cargo run -- compile $< -o $@ -f llvm | ||||||
|  | 
 | ||||||
|  | %.o: %.ll | ||||||
|  | 	llc $< -o $@ -filetype=obj | ||||||
|  | 
 | ||||||
|  | %: %.o | ||||||
|  | 	clang $< -o $@ | ||||||
|  | 
 | ||||||
|  | .PHONY: clean | ||||||
|  | 
 | ||||||
|  | clean: | ||||||
|  | 	@rm -f *.ll *.o simple | ||||||
							
								
								
									
										3
									
								
								ach/functions.ach
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								ach/functions.ach
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | fn id x = x | ||||||
|  | fn plus x y = x + y | ||||||
|  | fn main = plus (id 2) 7 | ||||||
							
								
								
									
										1
									
								
								ach/simple.ach
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ach/simple.ach
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | fn main = let x = 2; y = 3 in x + y | ||||||
							
								
								
									
										20
									
								
								shell.nix
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								shell.nix
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | with import (builtins.fetchTarball { | ||||||
|  |   url = "https://github.com/nixos/nixpkgs/archive/93a812bb9f9c398bd5b9636ab3674dcfe8cfb884.tar.gz"; | ||||||
|  |   sha256 = "14zzsgnigd7vjbrpzm1s4qsknm73sci38ss00x96wamz6psaxyah"; | ||||||
|  | }) {}; | ||||||
|  | 
 | ||||||
|  | mkShell { | ||||||
|  |   buildInputs = [ | ||||||
|  |     clang_11 | ||||||
|  |     llvm_11.lib | ||||||
|  |     llvmPackages_11.bintools | ||||||
|  |     llvmPackages_11.clang | ||||||
|  |     llvmPackages_11.libclang.lib | ||||||
|  |     zlib | ||||||
|  |     ncurses | ||||||
|  |     libxml2 | ||||||
|  |     libffi | ||||||
|  |     pkg-config | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										166
									
								
								src/ast/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/ast/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,166 @@ | ||||||
|  | use std::borrow::Cow; | ||||||
|  | use std::convert::TryFrom; | ||||||
|  | use std::fmt::{self, Display, Formatter}; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub struct InvalidIdentifier<'a>(Cow<'a, str>); | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Hash)] | ||||||
|  | pub struct Ident<'a>(pub Cow<'a, str>); | ||||||
|  | 
 | ||||||
|  | impl<'a> From<&'a Ident<'a>> for &'a str { | ||||||
|  |     fn from(id: &'a Ident<'a>) -> Self { | ||||||
|  |         id.0.as_ref() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Display for Ident<'a> { | ||||||
|  |     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { | ||||||
|  |         write!(f, "{}", self.0) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Ident<'a> { | ||||||
|  |     pub fn to_owned(&self) -> Ident<'static> { | ||||||
|  |         Ident(Cow::Owned(self.0.clone().into_owned())) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn from_str_unchecked(s: &'a str) -> Self { | ||||||
|  |         debug_assert!(is_valid_identifier(s)); | ||||||
|  |         Self(Cow::Borrowed(s)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn from_string_unchecked(s: String) -> Self { | ||||||
|  |         debug_assert!(is_valid_identifier(&s)); | ||||||
|  |         Self(Cow::Owned(s)) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn is_valid_identifier<S>(s: &S) -> bool | ||||||
|  | where | ||||||
|  |     S: AsRef<str> + ?Sized, | ||||||
|  | { | ||||||
|  |     s.as_ref() | ||||||
|  |         .chars() | ||||||
|  |         .any(|c| !c.is_alphanumeric() || !"_".contains(c)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> TryFrom<&'a str> for Ident<'a> { | ||||||
|  |     type Error = InvalidIdentifier<'a>; | ||||||
|  | 
 | ||||||
|  |     fn try_from(s: &'a str) -> Result<Self, Self::Error> { | ||||||
|  |         if is_valid_identifier(s) { | ||||||
|  |             Ok(Ident(Cow::Borrowed(s))) | ||||||
|  |         } else { | ||||||
|  |             Err(InvalidIdentifier(Cow::Borrowed(s))) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> TryFrom<String> for Ident<'a> { | ||||||
|  |     type Error = InvalidIdentifier<'static>; | ||||||
|  | 
 | ||||||
|  |     fn try_from(s: String) -> Result<Self, Self::Error> { | ||||||
|  |         if is_valid_identifier(&s) { | ||||||
|  |             Ok(Ident(Cow::Owned(s))) | ||||||
|  |         } else { | ||||||
|  |             Err(InvalidIdentifier(Cow::Owned(s))) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub enum BinaryOperator { | ||||||
|  |     /// `+`
 | ||||||
|  |     Add, | ||||||
|  | 
 | ||||||
|  |     /// `-`
 | ||||||
|  |     Sub, | ||||||
|  | 
 | ||||||
|  |     /// `*`
 | ||||||
|  |     Mul, | ||||||
|  | 
 | ||||||
|  |     /// `/`
 | ||||||
|  |     Div, | ||||||
|  | 
 | ||||||
|  |     /// `^`
 | ||||||
|  |     Pow, | ||||||
|  | 
 | ||||||
|  |     /// `==`
 | ||||||
|  |     Equ, | ||||||
|  | 
 | ||||||
|  |     /// `!=`
 | ||||||
|  |     Neq, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub enum UnaryOperator { | ||||||
|  |     /// !
 | ||||||
|  |     Not, | ||||||
|  | 
 | ||||||
|  |     /// -
 | ||||||
|  |     Neg, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub enum Literal { | ||||||
|  |     Int(u64), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub enum Expr<'a> { | ||||||
|  |     Ident(Ident<'a>), | ||||||
|  | 
 | ||||||
|  |     Literal(Literal), | ||||||
|  | 
 | ||||||
|  |     UnaryOp { | ||||||
|  |         op: UnaryOperator, | ||||||
|  |         rhs: Box<Expr<'a>>, | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     BinaryOp { | ||||||
|  |         lhs: Box<Expr<'a>>, | ||||||
|  |         op: BinaryOperator, | ||||||
|  |         rhs: Box<Expr<'a>>, | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     Let { | ||||||
|  |         bindings: Vec<(Ident<'a>, Expr<'a>)>, | ||||||
|  |         body: Box<Expr<'a>>, | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     If { | ||||||
|  |         condition: Box<Expr<'a>>, | ||||||
|  |         then: Box<Expr<'a>>, | ||||||
|  |         else_: Box<Expr<'a>>, | ||||||
|  |     }, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub struct Fun<'a> { | ||||||
|  |     pub name: Ident<'a>, | ||||||
|  |     pub args: Vec<Ident<'a>>, | ||||||
|  |     pub body: Expr<'a>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub enum Decl<'a> { | ||||||
|  |     Fun(Fun<'a>), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||||||
|  | pub enum Type { | ||||||
|  |     Int, | ||||||
|  |     Float, | ||||||
|  |     Bool, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for Type { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             Self::Int => f.write_str("int"), | ||||||
|  |             Self::Float => f.write_str("float"), | ||||||
|  |             Self::Bool => f.write_str("bool"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										282
									
								
								src/codegen/llvm.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								src/codegen/llvm.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,282 @@ | ||||||
|  | use std::path::Path; | ||||||
|  | use std::result; | ||||||
|  | 
 | ||||||
|  | use inkwell::basic_block::BasicBlock; | ||||||
|  | use inkwell::builder::Builder; | ||||||
|  | pub use inkwell::context::Context; | ||||||
|  | use inkwell::module::Module; | ||||||
|  | use inkwell::support::LLVMString; | ||||||
|  | use inkwell::types::FunctionType; | ||||||
|  | use inkwell::values::{BasicValueEnum, FunctionValue}; | ||||||
|  | use inkwell::IntPredicate; | ||||||
|  | use thiserror::Error; | ||||||
|  | 
 | ||||||
|  | use crate::ast::{BinaryOperator, Decl, Expr, Fun, Ident, Literal, UnaryOperator}; | ||||||
|  | use crate::common::env::Env; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Error)] | ||||||
|  | pub enum Error { | ||||||
|  |     #[error("Undefined variable {0}")] | ||||||
|  |     UndefinedVariable(Ident<'static>), | ||||||
|  | 
 | ||||||
|  |     #[error("LLVM Error: {0}")] | ||||||
|  |     LLVMError(String), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<LLVMString> for Error { | ||||||
|  |     fn from(s: LLVMString) -> Self { | ||||||
|  |         Self::LLVMError(s.to_string()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub type Result<T> = result::Result<T, Error>; | ||||||
|  | 
 | ||||||
|  | pub struct Codegen<'ctx, 'ast> { | ||||||
|  |     context: &'ctx Context, | ||||||
|  |     pub module: Module<'ctx>, | ||||||
|  |     builder: Builder<'ctx>, | ||||||
|  |     env: Env<'ast, BasicValueEnum<'ctx>>, | ||||||
|  |     function: Option<FunctionValue<'ctx>>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'ctx, 'ast> Codegen<'ctx, 'ast> { | ||||||
|  |     pub fn new(context: &'ctx Context, module_name: &str) -> Self { | ||||||
|  |         let module = context.create_module(module_name); | ||||||
|  |         let builder = context.create_builder(); | ||||||
|  |         Self { | ||||||
|  |             context, | ||||||
|  |             module, | ||||||
|  |             builder, | ||||||
|  |             env: Default::default(), | ||||||
|  |             function: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn new_function<'a>( | ||||||
|  |         &'a mut self, | ||||||
|  |         name: &str, | ||||||
|  |         ty: FunctionType<'ctx>, | ||||||
|  |     ) -> &'a FunctionValue<'ctx> { | ||||||
|  |         self.function = Some(self.module.add_function(name, ty, None)); | ||||||
|  |         let basic_block = self.append_basic_block("entry"); | ||||||
|  |         self.builder.position_at_end(basic_block); | ||||||
|  |         self.function.as_ref().unwrap() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn finish_function(&self, res: &BasicValueEnum<'ctx>) { | ||||||
|  |         self.builder.build_return(Some(res)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn append_basic_block(&self, name: &str) -> BasicBlock<'ctx> { | ||||||
|  |         self.context | ||||||
|  |             .append_basic_block(self.function.unwrap(), name) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn codegen_expr(&mut self, expr: &'ast Expr<'ast>) -> Result<BasicValueEnum<'ctx>> { | ||||||
|  |         match expr { | ||||||
|  |             Expr::Ident(id) => self | ||||||
|  |                 .env | ||||||
|  |                 .resolve(id) | ||||||
|  |                 .cloned() | ||||||
|  |                 .ok_or_else(|| Error::UndefinedVariable(id.to_owned())), | ||||||
|  |             Expr::Literal(Literal::Int(i)) => { | ||||||
|  |                 let ty = self.context.i64_type(); | ||||||
|  |                 Ok(BasicValueEnum::IntValue(ty.const_int(*i, false))) | ||||||
|  |             } | ||||||
|  |             Expr::UnaryOp { op, rhs } => { | ||||||
|  |                 let rhs = self.codegen_expr(rhs)?; | ||||||
|  |                 match op { | ||||||
|  |                     UnaryOperator::Not => unimplemented!(), | ||||||
|  |                     UnaryOperator::Neg => Ok(BasicValueEnum::IntValue( | ||||||
|  |                         self.builder.build_int_neg(rhs.into_int_value(), "neg"), | ||||||
|  |                     )), | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Expr::BinaryOp { lhs, op, rhs } => { | ||||||
|  |                 let lhs = self.codegen_expr(lhs)?; | ||||||
|  |                 let rhs = self.codegen_expr(rhs)?; | ||||||
|  |                 match op { | ||||||
|  |                     BinaryOperator::Add => { | ||||||
|  |                         Ok(BasicValueEnum::IntValue(self.builder.build_int_add( | ||||||
|  |                             lhs.into_int_value(), | ||||||
|  |                             rhs.into_int_value(), | ||||||
|  |                             "add", | ||||||
|  |                         ))) | ||||||
|  |                     } | ||||||
|  |                     BinaryOperator::Sub => { | ||||||
|  |                         Ok(BasicValueEnum::IntValue(self.builder.build_int_sub( | ||||||
|  |                             lhs.into_int_value(), | ||||||
|  |                             rhs.into_int_value(), | ||||||
|  |                             "add", | ||||||
|  |                         ))) | ||||||
|  |                     } | ||||||
|  |                     BinaryOperator::Mul => { | ||||||
|  |                         Ok(BasicValueEnum::IntValue(self.builder.build_int_sub( | ||||||
|  |                             lhs.into_int_value(), | ||||||
|  |                             rhs.into_int_value(), | ||||||
|  |                             "add", | ||||||
|  |                         ))) | ||||||
|  |                     } | ||||||
|  |                     BinaryOperator::Div => { | ||||||
|  |                         Ok(BasicValueEnum::IntValue(self.builder.build_int_signed_div( | ||||||
|  |                             lhs.into_int_value(), | ||||||
|  |                             rhs.into_int_value(), | ||||||
|  |                             "add", | ||||||
|  |                         ))) | ||||||
|  |                     } | ||||||
|  |                     BinaryOperator::Pow => unimplemented!(), | ||||||
|  |                     BinaryOperator::Equ => { | ||||||
|  |                         Ok(BasicValueEnum::IntValue(self.builder.build_int_compare( | ||||||
|  |                             IntPredicate::EQ, | ||||||
|  |                             lhs.into_int_value(), | ||||||
|  |                             rhs.into_int_value(), | ||||||
|  |                             "eq", | ||||||
|  |                         ))) | ||||||
|  |                     } | ||||||
|  |                     BinaryOperator::Neq => todo!(), | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Expr::Let { bindings, body } => { | ||||||
|  |                 self.env.push(); | ||||||
|  |                 for (id, val) in bindings { | ||||||
|  |                     let val = self.codegen_expr(val)?; | ||||||
|  |                     self.env.set(id, val); | ||||||
|  |                 } | ||||||
|  |                 let res = self.codegen_expr(body); | ||||||
|  |                 self.env.pop(); | ||||||
|  |                 res | ||||||
|  |             } | ||||||
|  |             Expr::If { | ||||||
|  |                 condition, | ||||||
|  |                 then, | ||||||
|  |                 else_, | ||||||
|  |             } => { | ||||||
|  |                 let then_block = self.append_basic_block("then"); | ||||||
|  |                 let else_block = self.append_basic_block("else"); | ||||||
|  |                 let join_block = self.append_basic_block("join"); | ||||||
|  |                 let condition = self.codegen_expr(condition)?; | ||||||
|  |                 self.builder.build_conditional_branch( | ||||||
|  |                     condition.into_int_value(), | ||||||
|  |                     then_block, | ||||||
|  |                     else_block, | ||||||
|  |                 ); | ||||||
|  |                 self.builder.position_at_end(then_block); | ||||||
|  |                 let then_res = self.codegen_expr(then)?; | ||||||
|  |                 self.builder.build_unconditional_branch(join_block); | ||||||
|  | 
 | ||||||
|  |                 self.builder.position_at_end(else_block); | ||||||
|  |                 let else_res = self.codegen_expr(else_)?; | ||||||
|  |                 self.builder.build_unconditional_branch(join_block); | ||||||
|  | 
 | ||||||
|  |                 self.builder.position_at_end(join_block); | ||||||
|  |                 let phi = self.builder.build_phi(self.context.i64_type(), "join"); | ||||||
|  |                 phi.add_incoming(&[(&then_res, then_block), (&else_res, else_block)]); | ||||||
|  |                 Ok(phi.as_basic_value()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn codegen_decl(&mut self, decl: &'ast Decl<'ast>) -> Result<()> { | ||||||
|  |         match decl { | ||||||
|  |             Decl::Fun(Fun { name, args, body }) => { | ||||||
|  |                 let i64_type = self.context.i64_type(); | ||||||
|  |                 self.new_function( | ||||||
|  |                     name.into(), | ||||||
|  |                     i64_type.fn_type( | ||||||
|  |                         args.iter() | ||||||
|  |                             .map(|_| i64_type.into()) | ||||||
|  |                             .collect::<Vec<_>>() | ||||||
|  |                             .as_slice(), | ||||||
|  |                         false, | ||||||
|  |                     ), | ||||||
|  |                 ); | ||||||
|  |                 self.env.push(); | ||||||
|  |                 for (i, arg) in args.iter().enumerate() { | ||||||
|  |                     self.env | ||||||
|  |                         .set(arg, self.function.unwrap().get_nth_param(i as u32).unwrap()); | ||||||
|  |                 } | ||||||
|  |                 let res = self.codegen_expr(body)?; | ||||||
|  |                 self.env.pop(); | ||||||
|  |                 self.finish_function(&res); | ||||||
|  |                 Ok(()) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn codegen_main(&mut self, expr: &'ast Expr<'ast>) -> Result<()> { | ||||||
|  |         self.new_function("main", self.context.i64_type().fn_type(&[], false)); | ||||||
|  |         let res = self.codegen_expr(expr)?; | ||||||
|  |         self.finish_function(&res); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn print_to_file<P>(&self, path: P) -> Result<()> | ||||||
|  |     where | ||||||
|  |         P: AsRef<Path>, | ||||||
|  |     { | ||||||
|  |         Ok(self.module.print_to_file(path)?) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn binary_to_file<P>(&self, path: P) -> Result<()> | ||||||
|  |     where | ||||||
|  |         P: AsRef<Path>, | ||||||
|  |     { | ||||||
|  |         if self.module.write_bitcode_to_path(path.as_ref()) { | ||||||
|  |             Ok(()) | ||||||
|  |         } else { | ||||||
|  |             Err(Error::LLVMError( | ||||||
|  |                 "Error writing bitcode to output path".to_owned(), | ||||||
|  |             )) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use inkwell::execution_engine::JitFunction; | ||||||
|  |     use inkwell::OptimizationLevel; | ||||||
|  | 
 | ||||||
|  |     use super::*; | ||||||
|  | 
 | ||||||
|  |     fn jit_eval<T>(expr: &str) -> anyhow::Result<T> { | ||||||
|  |         let expr = crate::parser::expr(expr).unwrap().1; | ||||||
|  | 
 | ||||||
|  |         let context = Context::create(); | ||||||
|  |         let mut codegen = Codegen::new(&context, "test"); | ||||||
|  |         let execution_engine = codegen | ||||||
|  |             .module | ||||||
|  |             .create_jit_execution_engine(OptimizationLevel::None) | ||||||
|  |             .unwrap(); | ||||||
|  | 
 | ||||||
|  |         codegen.new_function("test", context.i64_type().fn_type(&[], false)); | ||||||
|  |         let res = codegen.codegen_expr(&expr)?; | ||||||
|  |         codegen.finish_function(&res); | ||||||
|  | 
 | ||||||
|  |         unsafe { | ||||||
|  |             let fun: JitFunction<unsafe extern "C" fn() -> T> = | ||||||
|  |                 execution_engine.get_function("test")?; | ||||||
|  |             Ok(fun.call()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn add_literals() { | ||||||
|  |         assert_eq!(jit_eval::<i64>("1 + 2").unwrap(), 3); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn variable_shadowing() { | ||||||
|  |         assert_eq!( | ||||||
|  |             jit_eval::<i64>("let x = 1 in (let x = 2 in x) + x").unwrap(), | ||||||
|  |             3 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn eq() { | ||||||
|  |         assert_eq!( | ||||||
|  |             jit_eval::<i64>("let x = 1 in if x == 1 then 2 else 4").unwrap(), | ||||||
|  |             2 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								src/codegen/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/codegen/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | pub mod llvm; | ||||||
|  | 
 | ||||||
|  | use inkwell::execution_engine::JitFunction; | ||||||
|  | use inkwell::OptimizationLevel; | ||||||
|  | pub use llvm::*; | ||||||
|  | 
 | ||||||
|  | use crate::ast::Expr; | ||||||
|  | use crate::common::Result; | ||||||
|  | 
 | ||||||
|  | pub fn jit_eval<T>(expr: &Expr) -> Result<T> { | ||||||
|  |     let context = Context::create(); | ||||||
|  |     let mut codegen = Codegen::new(&context, "eval"); | ||||||
|  |     let execution_engine = codegen | ||||||
|  |         .module | ||||||
|  |         .create_jit_execution_engine(OptimizationLevel::None) | ||||||
|  |         .map_err(Error::from)?; | ||||||
|  |     codegen.new_function("eval", context.i64_type().fn_type(&[], false)); | ||||||
|  |     let res = codegen.codegen_expr(&expr)?; | ||||||
|  |     codegen.finish_function(&res); | ||||||
|  | 
 | ||||||
|  |     unsafe { | ||||||
|  |         let fun: JitFunction<unsafe extern "C" fn() -> T> = | ||||||
|  |             execution_engine.get_function("eval").unwrap(); | ||||||
|  |         Ok(fun.call()) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								src/commands/compile.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/commands/compile.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | ||||||
|  | use std::path::PathBuf; | ||||||
|  | 
 | ||||||
|  | use clap::Clap; | ||||||
|  | 
 | ||||||
|  | use crate::common::Result; | ||||||
|  | use crate::compiler::{self, CompilerOptions}; | ||||||
|  | 
 | ||||||
|  | #[derive(Clap)] | ||||||
|  | pub struct Compile { | ||||||
|  |     file: PathBuf, | ||||||
|  | 
 | ||||||
|  |     #[clap(short = 'o')] | ||||||
|  |     out_file: PathBuf, | ||||||
|  | 
 | ||||||
|  |     #[clap(flatten)] | ||||||
|  |     options: CompilerOptions, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Compile { | ||||||
|  |     pub fn run(self) -> Result<()> { | ||||||
|  |         eprintln!( | ||||||
|  |             ">>> {} -> {}", | ||||||
|  |             &self.file.to_string_lossy(), | ||||||
|  |             self.out_file.to_string_lossy() | ||||||
|  |         ); | ||||||
|  |         compiler::compile_file(&self.file, &self.out_file, &self.options) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								src/commands/eval.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/commands/eval.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | ||||||
|  | use clap::Clap; | ||||||
|  | 
 | ||||||
|  | use crate::codegen; | ||||||
|  | use crate::interpreter; | ||||||
|  | use crate::parser; | ||||||
|  | use crate::Result; | ||||||
|  | 
 | ||||||
|  | /// Evaluate an expression and print its result
 | ||||||
|  | #[derive(Clap)] | ||||||
|  | pub struct Eval { | ||||||
|  |     /// JIT-compile with LLVM instead of interpreting
 | ||||||
|  |     #[clap(long)] | ||||||
|  |     jit: bool, | ||||||
|  | 
 | ||||||
|  |     /// Expression to evaluate
 | ||||||
|  |     expr: String, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Eval { | ||||||
|  |     pub fn run(self) -> Result<()> { | ||||||
|  |         let (_, parsed) = parser::expr(&self.expr)?; | ||||||
|  |         let result = if self.jit { | ||||||
|  |             codegen::jit_eval::<i64>(&parsed)?.into() | ||||||
|  |         } else { | ||||||
|  |             interpreter::eval(&parsed)? | ||||||
|  |         }; | ||||||
|  |         println!("{}", result); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								src/commands/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/commands/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | pub mod compile; | ||||||
|  | pub mod eval; | ||||||
|  | 
 | ||||||
|  | pub use compile::Compile; | ||||||
|  | pub use eval::Eval; | ||||||
							
								
								
									
										40
									
								
								src/common/env.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/common/env.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | ||||||
|  | use std::collections::HashMap; | ||||||
|  | 
 | ||||||
|  | use crate::ast::Ident; | ||||||
|  | 
 | ||||||
|  | /// A lexical environment
 | ||||||
|  | #[derive(Debug, PartialEq, Eq)] | ||||||
|  | pub struct Env<'ast, V>(Vec<HashMap<&'ast Ident<'ast>, V>>); | ||||||
|  | 
 | ||||||
|  | impl<'ast, V> Default for Env<'ast, V> { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self::new() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'ast, V> Env<'ast, V> { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         Self(vec![Default::default()]) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn push(&mut self) { | ||||||
|  |         self.0.push(Default::default()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn pop(&mut self) { | ||||||
|  |         self.0.pop(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn set(&mut self, k: &'ast Ident<'ast>, v: V) { | ||||||
|  |         self.0.last_mut().unwrap().insert(k, v); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn resolve<'a>(&'a self, k: &'ast Ident<'ast>) -> Option<&'a V> { | ||||||
|  |         for ctx in self.0.iter().rev() { | ||||||
|  |             if let Some(res) = ctx.get(k) { | ||||||
|  |                 return Some(res); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         None | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								src/common/error.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/common/error.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | use std::{io, result}; | ||||||
|  | 
 | ||||||
|  | use thiserror::Error; | ||||||
|  | 
 | ||||||
|  | use crate::{codegen, interpreter, parser}; | ||||||
|  | 
 | ||||||
|  | #[derive(Error, Debug)] | ||||||
|  | pub enum Error { | ||||||
|  |     #[error(transparent)] | ||||||
|  |     IOError(#[from] io::Error), | ||||||
|  | 
 | ||||||
|  |     #[error("Error parsing input: {0}")] | ||||||
|  |     ParseError(#[from] parser::Error), | ||||||
|  | 
 | ||||||
|  |     #[error("Error evaluating expression: {0}")] | ||||||
|  |     EvalError(#[from] interpreter::Error), | ||||||
|  | 
 | ||||||
|  |     #[error("Compile error: {0}")] | ||||||
|  |     CodegenError(#[from] codegen::Error), | ||||||
|  | 
 | ||||||
|  |     #[error("{0}")] | ||||||
|  |     Message(String), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<String> for Error { | ||||||
|  |     fn from(s: String) -> Self { | ||||||
|  |         Self::Message(s) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> From<nom::Err<nom::error::Error<&'a str>>> for Error { | ||||||
|  |     fn from(e: nom::Err<nom::error::Error<&'a str>>) -> Self { | ||||||
|  |         use nom::error::Error as NomError; | ||||||
|  |         use nom::Err::*; | ||||||
|  | 
 | ||||||
|  |         Self::ParseError(match e { | ||||||
|  |             Incomplete(i) => Incomplete(i), | ||||||
|  |             Error(NomError { input, code }) => Error(NomError { | ||||||
|  |                 input: input.to_owned(), | ||||||
|  |                 code, | ||||||
|  |             }), | ||||||
|  |             Failure(NomError { input, code }) => Failure(NomError { | ||||||
|  |                 input: input.to_owned(), | ||||||
|  |                 code, | ||||||
|  |             }), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub type Result<T> = result::Result<T, Error>; | ||||||
							
								
								
									
										4
									
								
								src/common/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/common/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | pub(crate) mod env; | ||||||
|  | pub(crate) mod error; | ||||||
|  | 
 | ||||||
|  | pub use error::{Error, Result}; | ||||||
							
								
								
									
										84
									
								
								src/compiler.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/compiler.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | ||||||
|  | use std::fmt::{self, Display}; | ||||||
|  | use std::path::Path; | ||||||
|  | use std::str::FromStr; | ||||||
|  | use std::{fs, result}; | ||||||
|  | 
 | ||||||
|  | use clap::Clap; | ||||||
|  | use test_strategy::Arbitrary; | ||||||
|  | 
 | ||||||
|  | use crate::codegen::{self, Codegen}; | ||||||
|  | use crate::common::Result; | ||||||
|  | use crate::parser; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Arbitrary)] | ||||||
|  | pub enum OutputFormat { | ||||||
|  |     LLVM, | ||||||
|  |     Bitcode, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for OutputFormat { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self::Bitcode | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl FromStr for OutputFormat { | ||||||
|  |     type Err = String; | ||||||
|  | 
 | ||||||
|  |     fn from_str(s: &str) -> result::Result<Self, Self::Err> { | ||||||
|  |         match s { | ||||||
|  |             "llvm" => Ok(Self::LLVM), | ||||||
|  |             "binary" => Ok(Self::Bitcode), | ||||||
|  |             _ => Err(format!( | ||||||
|  |                 "Invalid output format {}, expected one of {{llvm, binary}}", | ||||||
|  |                 s | ||||||
|  |             )), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for OutputFormat { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             OutputFormat::LLVM => f.write_str("llvm"), | ||||||
|  |             OutputFormat::Bitcode => f.write_str("binary"), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clap, Debug, PartialEq, Eq, Default)] | ||||||
|  | pub struct CompilerOptions { | ||||||
|  |     #[clap(long, short = 'f', default_value)] | ||||||
|  |     format: OutputFormat, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn compile_file(input: &Path, output: &Path, options: &CompilerOptions) -> Result<()> { | ||||||
|  |     let src = fs::read_to_string(input)?; | ||||||
|  |     let (_, decls) = parser::toplevel(&src)?; // TODO: statements
 | ||||||
|  |     let context = codegen::Context::create(); | ||||||
|  |     let mut codegen = Codegen::new( | ||||||
|  |         &context, | ||||||
|  |         &input | ||||||
|  |             .file_stem() | ||||||
|  |             .map_or("UNKNOWN".to_owned(), |s| s.to_string_lossy().into_owned()), | ||||||
|  |     ); | ||||||
|  |     for decl in &decls { | ||||||
|  |         codegen.codegen_decl(decl)?; | ||||||
|  |     } | ||||||
|  |     match options.format { | ||||||
|  |         OutputFormat::LLVM => codegen.print_to_file(output)?, | ||||||
|  |         OutputFormat::Bitcode => codegen.binary_to_file(output)?, | ||||||
|  |     } | ||||||
|  |     Ok(()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use super::*; | ||||||
|  |     use test_strategy::proptest; | ||||||
|  | 
 | ||||||
|  |     #[proptest] | ||||||
|  |     fn output_format_display_from_str_round_trip(of: OutputFormat) { | ||||||
|  |         assert_eq!(OutputFormat::from_str(&of.to_string()), Ok(of)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								src/interpreter/error.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/interpreter/error.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | ||||||
|  | use std::result; | ||||||
|  | 
 | ||||||
|  | use thiserror::Error; | ||||||
|  | 
 | ||||||
|  | use crate::ast::{Ident, Type}; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Eq, Error)] | ||||||
|  | pub enum Error { | ||||||
|  |     #[error("Undefined variable {0}")] | ||||||
|  |     UndefinedVariable(Ident<'static>), | ||||||
|  | 
 | ||||||
|  |     #[error("Unexpected type {actual}, expected type {expected}")] | ||||||
|  |     InvalidType { actual: Type, expected: Type }, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub type Result<T> = result::Result<T, Error>; | ||||||
							
								
								
									
										125
									
								
								src/interpreter/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/interpreter/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,125 @@ | ||||||
|  | mod error; | ||||||
|  | mod value; | ||||||
|  | 
 | ||||||
|  | pub use self::error::{Error, Result}; | ||||||
|  | pub use self::value::Value; | ||||||
|  | use crate::ast::{BinaryOperator, Expr, Ident, Literal, UnaryOperator}; | ||||||
|  | use crate::common::env::Env; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, Default)] | ||||||
|  | pub struct Interpreter<'a> { | ||||||
|  |     env: Env<'a, Value>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a> Interpreter<'a> { | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         Self::default() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn resolve(&self, var: &'a Ident<'a>) -> Result<Value> { | ||||||
|  |         self.env | ||||||
|  |             .resolve(var) | ||||||
|  |             .cloned() | ||||||
|  |             .ok_or_else(|| Error::UndefinedVariable(var.to_owned())) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn eval(&mut self, expr: &'a Expr<'a>) -> Result<Value> { | ||||||
|  |         match expr { | ||||||
|  |             Expr::Ident(id) => self.resolve(id), | ||||||
|  |             Expr::Literal(Literal::Int(i)) => Ok((*i).into()), | ||||||
|  |             Expr::UnaryOp { op, rhs } => { | ||||||
|  |                 let rhs = self.eval(rhs)?; | ||||||
|  |                 match op { | ||||||
|  |                     UnaryOperator::Neg => -rhs, | ||||||
|  |                     _ => unimplemented!(), | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Expr::BinaryOp { lhs, op, rhs } => { | ||||||
|  |                 let lhs = self.eval(lhs)?; | ||||||
|  |                 let rhs = self.eval(rhs)?; | ||||||
|  |                 match op { | ||||||
|  |                     BinaryOperator::Add => lhs + rhs, | ||||||
|  |                     BinaryOperator::Sub => lhs - rhs, | ||||||
|  |                     BinaryOperator::Mul => lhs * rhs, | ||||||
|  |                     BinaryOperator::Div => lhs / rhs, | ||||||
|  |                     BinaryOperator::Pow => todo!(), | ||||||
|  |                     BinaryOperator::Equ => Ok(lhs.eq(&rhs).into()), | ||||||
|  |                     BinaryOperator::Neq => todo!(), | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Expr::Let { bindings, body } => { | ||||||
|  |                 self.env.push(); | ||||||
|  |                 for (id, val) in bindings { | ||||||
|  |                     let val = self.eval(val)?; | ||||||
|  |                     self.env.set(id, val); | ||||||
|  |                 } | ||||||
|  |                 let res = self.eval(body)?; | ||||||
|  |                 self.env.pop(); | ||||||
|  |                 Ok(res) | ||||||
|  |             } | ||||||
|  |             Expr::If { | ||||||
|  |                 condition, | ||||||
|  |                 then, | ||||||
|  |                 else_, | ||||||
|  |             } => { | ||||||
|  |                 let condition = self.eval(condition)?; | ||||||
|  |                 if *(condition.into_type::<bool>()?) { | ||||||
|  |                     self.eval(then) | ||||||
|  |                 } else { | ||||||
|  |                     self.eval(else_) | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn eval<'a>(expr: &'a Expr<'a>) -> Result<Value> { | ||||||
|  |     let mut interpreter = Interpreter::new(); | ||||||
|  |     interpreter.eval(expr) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use std::convert::TryFrom; | ||||||
|  | 
 | ||||||
|  |     use super::value::{TypeOf, Val}; | ||||||
|  |     use super::*; | ||||||
|  |     use BinaryOperator::*; | ||||||
|  | 
 | ||||||
|  |     fn int_lit(i: u64) -> Box<Expr<'static>> { | ||||||
|  |         Box::new(Expr::Literal(Literal::Int(i))) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn parse_eval<T>(src: &str) -> T | ||||||
|  |     where | ||||||
|  |         for<'a> &'a T: TryFrom<&'a Val>, | ||||||
|  |         T: Clone + TypeOf, | ||||||
|  |     { | ||||||
|  |         let expr = crate::parser::expr(src).unwrap().1; | ||||||
|  |         let res = eval(&expr).unwrap(); | ||||||
|  |         res.into_type::<T>().unwrap().clone() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn simple_addition() { | ||||||
|  |         let expr = Expr::BinaryOp { | ||||||
|  |             lhs: int_lit(1), | ||||||
|  |             op: Mul, | ||||||
|  |             rhs: int_lit(2), | ||||||
|  |         }; | ||||||
|  |         let res = eval(&expr).unwrap(); | ||||||
|  |         assert_eq!(*res.into_type::<i64>().unwrap(), 2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn variable_shadowing() { | ||||||
|  |         let res = parse_eval::<i64>("let x = 1 in (let x = 2 in x) + x"); | ||||||
|  |         assert_eq!(res, 3); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn conditional_with_equals() { | ||||||
|  |         let res = parse_eval::<i64>("let x = 1 in if x == 1 then 2 else 4"); | ||||||
|  |         assert_eq!(res, 2); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										134
									
								
								src/interpreter/value.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/interpreter/value.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,134 @@ | ||||||
|  | use std::convert::TryFrom; | ||||||
|  | use std::fmt::{self, Display}; | ||||||
|  | use std::ops::{Add, Div, Mul, Neg, Sub}; | ||||||
|  | use std::rc::Rc; | ||||||
|  | 
 | ||||||
|  | use derive_more::{Deref, From, TryInto}; | ||||||
|  | 
 | ||||||
|  | use super::{Error, Result}; | ||||||
|  | use crate::ast::Type; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, From, TryInto)] | ||||||
|  | #[try_into(owned, ref)] | ||||||
|  | pub enum Val { | ||||||
|  |     Int(i64), | ||||||
|  |     Float(f64), | ||||||
|  |     Bool(bool), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl From<u64> for Val { | ||||||
|  |     fn from(i: u64) -> Self { | ||||||
|  |         Self::from(i as i64) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Display for Val { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         match self { | ||||||
|  |             Val::Int(x) => x.fmt(f), | ||||||
|  |             Val::Float(x) => x.fmt(f), | ||||||
|  |             Val::Bool(x) => x.fmt(f), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Val { | ||||||
|  |     pub fn type_(&self) -> Type { | ||||||
|  |         match self { | ||||||
|  |             Val::Int(_) => Type::Int, | ||||||
|  |             Val::Float(_) => Type::Float, | ||||||
|  |             Val::Bool(_) => Type::Bool, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn into_type<'a, T>(&'a self) -> Result<&'a T> | ||||||
|  |     where | ||||||
|  |         T: TypeOf + 'a + Clone, | ||||||
|  |         &'a T: TryFrom<&'a Self>, | ||||||
|  |     { | ||||||
|  |         <&T>::try_from(self).map_err(|_| Error::InvalidType { | ||||||
|  |             actual: self.type_(), | ||||||
|  |             expected: <T as TypeOf>::type_of(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Debug, PartialEq, Clone, Deref)] | ||||||
|  | pub struct Value(Rc<Val>); | ||||||
|  | 
 | ||||||
|  | impl Display for Value { | ||||||
|  |     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||||
|  |         self.0.fmt(f) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> From<T> for Value | ||||||
|  | where | ||||||
|  |     Val: From<T>, | ||||||
|  | { | ||||||
|  |     fn from(x: T) -> Self { | ||||||
|  |         Self(Rc::new(x.into())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Neg for Value { | ||||||
|  |     type Output = Result<Value>; | ||||||
|  | 
 | ||||||
|  |     fn neg(self) -> Self::Output { | ||||||
|  |         Ok((-self.into_type::<i64>()?).into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Add for Value { | ||||||
|  |     type Output = Result<Value>; | ||||||
|  | 
 | ||||||
|  |     fn add(self, rhs: Self) -> Self::Output { | ||||||
|  |         Ok((self.into_type::<i64>()? + rhs.into_type::<i64>()?).into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Sub for Value { | ||||||
|  |     type Output = Result<Value>; | ||||||
|  | 
 | ||||||
|  |     fn sub(self, rhs: Self) -> Self::Output { | ||||||
|  |         Ok((self.into_type::<i64>()? - rhs.into_type::<i64>()?).into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Mul for Value { | ||||||
|  |     type Output = Result<Value>; | ||||||
|  | 
 | ||||||
|  |     fn mul(self, rhs: Self) -> Self::Output { | ||||||
|  |         Ok((self.into_type::<i64>()? * rhs.into_type::<i64>()?).into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Div for Value { | ||||||
|  |     type Output = Result<Value>; | ||||||
|  | 
 | ||||||
|  |     fn div(self, rhs: Self) -> Self::Output { | ||||||
|  |         Ok((self.into_type::<f64>()? / rhs.into_type::<f64>()?).into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub trait TypeOf { | ||||||
|  |     fn type_of() -> Type; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl TypeOf for i64 { | ||||||
|  |     fn type_of() -> Type { | ||||||
|  |         Type::Int | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl TypeOf for bool { | ||||||
|  |     fn type_of() -> Type { | ||||||
|  |         Type::Bool | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl TypeOf for f64 { | ||||||
|  |     fn type_of() -> Type { | ||||||
|  |         Type::Float | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/main.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,31 @@ | ||||||
|  | use clap::Clap; | ||||||
|  | 
 | ||||||
|  | pub mod ast; | ||||||
|  | pub mod codegen; | ||||||
|  | pub(crate) mod commands; | ||||||
|  | pub(crate) mod common; | ||||||
|  | pub mod compiler; | ||||||
|  | pub mod interpreter; | ||||||
|  | pub mod parser; | ||||||
|  | 
 | ||||||
|  | pub use common::{Error, Result}; | ||||||
|  | 
 | ||||||
|  | #[derive(Clap)] | ||||||
|  | struct Opts { | ||||||
|  |     #[clap(subcommand)] | ||||||
|  |     subcommand: Command, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[derive(Clap)] | ||||||
|  | enum Command { | ||||||
|  |     Eval(commands::Eval), | ||||||
|  |     Compile(commands::Compile), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn main() -> anyhow::Result<()> { | ||||||
|  |     let opts = Opts::parse(); | ||||||
|  |     match opts.subcommand { | ||||||
|  |         Command::Eval(eval) => Ok(eval.run()?), | ||||||
|  |         Command::Compile(compile) => Ok(compile.run()?), | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										445
									
								
								src/parser/mod.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										445
									
								
								src/parser/mod.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,445 @@ | ||||||
|  | use nom::character::complete::{digit1, multispace0, multispace1}; | ||||||
|  | use nom::error::{ErrorKind, ParseError}; | ||||||
|  | use nom::{ | ||||||
|  |     alt, char, complete, delimited, do_parse, flat_map, many0, map, named, parse_to, | ||||||
|  |     separated_list0, separated_list1, tag, tuple, | ||||||
|  | }; | ||||||
|  | use pratt::{Affix, Associativity, PrattParser, Precedence}; | ||||||
|  | 
 | ||||||
|  | use crate::ast::{BinaryOperator, Decl, Expr, Fun, Ident, Literal, UnaryOperator}; | ||||||
|  | 
 | ||||||
|  | pub type Error = nom::Err<nom::error::Error<String>>; | ||||||
|  | 
 | ||||||
|  | #[derive(Debug)] | ||||||
|  | enum TokenTree<'a> { | ||||||
|  |     Prefix(UnaryOperator), | ||||||
|  |     // Postfix(char),
 | ||||||
|  |     Infix(BinaryOperator), | ||||||
|  |     Primary(Expr<'a>), | ||||||
|  |     Group(Vec<TokenTree<'a>>), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | named!(prefix(&str) -> TokenTree, map!(alt!( | ||||||
|  |     complete!(char!('-')) => { |_| UnaryOperator::Neg } | | ||||||
|  |     complete!(char!('!')) => { |_| UnaryOperator::Not } | ||||||
|  | ), TokenTree::Prefix)); | ||||||
|  | 
 | ||||||
|  | named!(infix(&str) -> TokenTree, map!(alt!( | ||||||
|  |     complete!(tag!("==")) => { |_| BinaryOperator::Equ } | | ||||||
|  |     complete!(tag!("!=")) => { |_| BinaryOperator::Neq } | | ||||||
|  |     complete!(char!('+')) => { |_| BinaryOperator::Add } | | ||||||
|  |     complete!(char!('-')) => { |_| BinaryOperator::Sub } | | ||||||
|  |     complete!(char!('*')) => { |_| BinaryOperator::Mul } | | ||||||
|  |     complete!(char!('/')) => { |_| BinaryOperator::Div } | | ||||||
|  |     complete!(char!('^')) => { |_| BinaryOperator::Pow } | ||||||
|  | ), TokenTree::Infix)); | ||||||
|  | 
 | ||||||
|  | named!(primary(&str) -> TokenTree, alt!( | ||||||
|  |     do_parse!( | ||||||
|  |         multispace0 >> | ||||||
|  |         char!('(') >> | ||||||
|  |         multispace0 >> | ||||||
|  |         group: group >> | ||||||
|  |         multispace0 >> | ||||||
|  |         char!(')') >> | ||||||
|  |         multispace0 >> | ||||||
|  |             (TokenTree::Group(group)) | ||||||
|  |     ) | | ||||||
|  |     delimited!(multispace0, simple_expr, multispace0) => { |s| TokenTree::Primary(s) } | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | named!( | ||||||
|  |     rest(&str) -> Vec<(TokenTree, Vec<TokenTree>, TokenTree)>, | ||||||
|  |     many0!(tuple!( | ||||||
|  |         infix, | ||||||
|  |         delimited!(multispace0, many0!(prefix), multispace0), | ||||||
|  |         primary | ||||||
|  |         // many0!(postfix)
 | ||||||
|  |     )) | ||||||
|  | ); | ||||||
|  | 
 | ||||||
|  | named!(group(&str) -> Vec<TokenTree>, do_parse!( | ||||||
|  |     prefix: many0!(prefix) | ||||||
|  |         >> primary: primary | ||||||
|  |         // >> postfix: many0!(postfix)
 | ||||||
|  |         >> rest: rest | ||||||
|  |         >> ({ | ||||||
|  |             let mut res = prefix; | ||||||
|  |             res.push(primary); | ||||||
|  |             // res.append(&mut postfix);
 | ||||||
|  |             for (infix, mut prefix, primary/*, mut postfix*/) in rest { | ||||||
|  |                 res.push(infix); | ||||||
|  |                 res.append(&mut prefix); | ||||||
|  |                 res.push(primary); | ||||||
|  |                 // res.append(&mut postfix);
 | ||||||
|  |             } | ||||||
|  |             res | ||||||
|  |         }) | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | fn token_tree(i: &str) -> nom::IResult<&str, Vec<TokenTree>> { | ||||||
|  |     group(i) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct ExprParser; | ||||||
|  | 
 | ||||||
|  | impl<'a, I> PrattParser<I> for ExprParser | ||||||
|  | where | ||||||
|  |     I: Iterator<Item = TokenTree<'a>>, | ||||||
|  | { | ||||||
|  |     type Error = pratt::NoError; | ||||||
|  |     type Input = TokenTree<'a>; | ||||||
|  |     type Output = Expr<'a>; | ||||||
|  | 
 | ||||||
|  |     fn query(&mut self, input: &Self::Input) -> Result<Affix, Self::Error> { | ||||||
|  |         use BinaryOperator::*; | ||||||
|  |         use UnaryOperator::*; | ||||||
|  | 
 | ||||||
|  |         Ok(match input { | ||||||
|  |             TokenTree::Infix(Add) => Affix::Infix(Precedence(6), Associativity::Left), | ||||||
|  |             TokenTree::Infix(Sub) => Affix::Infix(Precedence(6), Associativity::Left), | ||||||
|  |             TokenTree::Infix(Mul) => Affix::Infix(Precedence(7), Associativity::Left), | ||||||
|  |             TokenTree::Infix(Div) => Affix::Infix(Precedence(7), Associativity::Left), | ||||||
|  |             TokenTree::Infix(Pow) => Affix::Infix(Precedence(8), Associativity::Right), | ||||||
|  |             TokenTree::Infix(Equ) => Affix::Infix(Precedence(4), Associativity::Right), | ||||||
|  |             TokenTree::Infix(Neq) => Affix::Infix(Precedence(4), Associativity::Right), | ||||||
|  |             TokenTree::Prefix(Neg) => Affix::Prefix(Precedence(6)), | ||||||
|  |             TokenTree::Prefix(Not) => Affix::Prefix(Precedence(6)), | ||||||
|  |             TokenTree::Primary(_) => Affix::Nilfix, | ||||||
|  |             TokenTree::Group(_) => Affix::Nilfix, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn primary(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> { | ||||||
|  |         Ok(match input { | ||||||
|  |             TokenTree::Primary(expr) => expr, | ||||||
|  |             TokenTree::Group(group) => self.parse(&mut group.into_iter()).unwrap(), | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn infix( | ||||||
|  |         &mut self, | ||||||
|  |         lhs: Self::Output, | ||||||
|  |         op: Self::Input, | ||||||
|  |         rhs: Self::Output, | ||||||
|  |     ) -> Result<Self::Output, Self::Error> { | ||||||
|  |         let op = match op { | ||||||
|  |             TokenTree::Infix(op) => op, | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         }; | ||||||
|  |         Ok(Expr::BinaryOp { | ||||||
|  |             lhs: Box::new(lhs), | ||||||
|  |             op, | ||||||
|  |             rhs: Box::new(rhs), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> Result<Self::Output, Self::Error> { | ||||||
|  |         let op = match op { | ||||||
|  |             TokenTree::Prefix(op) => op, | ||||||
|  |             _ => unreachable!(), | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         Ok(Expr::UnaryOp { | ||||||
|  |             op, | ||||||
|  |             rhs: Box::new(rhs), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn postfix( | ||||||
|  |         &mut self, | ||||||
|  |         _lhs: Self::Output, | ||||||
|  |         _op: Self::Input, | ||||||
|  |     ) -> Result<Self::Output, Self::Error> { | ||||||
|  |         unreachable!() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E> | ||||||
|  | where | ||||||
|  |     E: ParseError<&'a str>, | ||||||
|  | { | ||||||
|  |     let mut chars = i.chars(); | ||||||
|  |     if let Some(f) = chars.next() { | ||||||
|  |         if f.is_alphabetic() || f == '_' { | ||||||
|  |             let mut idx = 1; | ||||||
|  |             for c in chars { | ||||||
|  |                 if !(c.is_alphanumeric() || c == '_') { | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 idx += 1; | ||||||
|  |             } | ||||||
|  |             Ok((&i[idx..], Ident::from_str_unchecked(&i[..idx]))) | ||||||
|  |         } else { | ||||||
|  |             Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy))) | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Eof))) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::Int)); | ||||||
|  | 
 | ||||||
|  | named!(literal(&str) -> Expr, map!(alt!(int), Expr::Literal)); | ||||||
|  | 
 | ||||||
|  | named!(binding(&str) -> (Ident, Expr), do_parse!( | ||||||
|  |     multispace0 | ||||||
|  |         >> ident: ident | ||||||
|  |         >> multispace0 | ||||||
|  |         >> char!('=') | ||||||
|  |         >> multispace0 | ||||||
|  |         >> expr: expr | ||||||
|  |         >> (ident, expr) | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | named!(let_(&str) -> Expr, do_parse!( | ||||||
|  |     tag!("let") | ||||||
|  |         >> multispace0 | ||||||
|  |         >> bindings: separated_list1!(alt!(char!(';') | char!('\n')), binding) | ||||||
|  |         >> multispace0 | ||||||
|  |         >> tag!("in") | ||||||
|  |         >> multispace0 | ||||||
|  |         >> body: expr | ||||||
|  |         >> (Expr::Let { | ||||||
|  |             bindings, | ||||||
|  |             body: Box::new(body) | ||||||
|  |         }) | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | named!(if_(&str) -> Expr, do_parse! ( | ||||||
|  |     tag!("if") | ||||||
|  |         >> multispace0 | ||||||
|  |         >> condition: expr | ||||||
|  |         >> multispace0 | ||||||
|  |         >> tag!("then") | ||||||
|  |         >> multispace0 | ||||||
|  |         >> then: expr | ||||||
|  |         >> multispace0 | ||||||
|  |         >> tag!("else") | ||||||
|  |         >> multispace0 | ||||||
|  |         >> else_: expr | ||||||
|  |         >> (Expr::If { | ||||||
|  |             condition: Box::new(condition), | ||||||
|  |             then: Box::new(then), | ||||||
|  |             else_: Box::new(else_) | ||||||
|  |         }) | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident)); | ||||||
|  | 
 | ||||||
|  | named!(simple_expr(&str) -> Expr, alt!( | ||||||
|  |     let_ | | ||||||
|  |     if_ | | ||||||
|  |     literal | | ||||||
|  |     ident_expr | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | named!(pub expr(&str) -> Expr, alt!( | ||||||
|  |     map!(token_tree, |tt| { | ||||||
|  |         ExprParser.parse(&mut tt.into_iter()).unwrap() | ||||||
|  |     }) | | ||||||
|  |     simple_expr)); | ||||||
|  | 
 | ||||||
|  | //////
 | ||||||
|  | 
 | ||||||
|  | named!(fun(&str) -> Fun, do_parse!( | ||||||
|  |     tag!("fn") | ||||||
|  |         >> multispace0 | ||||||
|  |         >> name: ident | ||||||
|  |         >> multispace1 | ||||||
|  |         >> args: separated_list0!(multispace1, ident) | ||||||
|  |         >> multispace0 | ||||||
|  |         >> char!('=') | ||||||
|  |         >> multispace0 | ||||||
|  |         >> body: expr | ||||||
|  |         >> (Fun { | ||||||
|  |             name, | ||||||
|  |             args, | ||||||
|  |             body | ||||||
|  |         }) | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | named!(pub decl(&str) -> Decl, alt!( | ||||||
|  |     fun => { |f| Decl::Fun(f) } | ||||||
|  | )); | ||||||
|  | 
 | ||||||
|  | named!(pub toplevel(&str) -> Vec<Decl>, separated_list0!(multispace1, decl)); | ||||||
|  | 
 | ||||||
|  | #[cfg(test)] | ||||||
|  | mod tests { | ||||||
|  |     use std::convert::{TryFrom, TryInto}; | ||||||
|  | 
 | ||||||
|  |     use super::*; | ||||||
|  |     use BinaryOperator::*; | ||||||
|  |     use Expr::{BinaryOp, If, Let, UnaryOp}; | ||||||
|  |     use UnaryOperator::*; | ||||||
|  | 
 | ||||||
|  |     fn ident_expr(s: &str) -> Box<Expr> { | ||||||
|  |         Box::new(Expr::Ident(Ident::try_from(s).unwrap())) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     macro_rules! test_parse { | ||||||
|  |         ($parser: ident, $src: expr) => {{ | ||||||
|  |             let (rem, res) = $parser($src).unwrap(); | ||||||
|  |             assert!( | ||||||
|  |                 rem.is_empty(), | ||||||
|  |                 "non-empty remainder: \"{}\", parsed: {:?}", | ||||||
|  |                 rem, | ||||||
|  |                 res | ||||||
|  |             ); | ||||||
|  |             res | ||||||
|  |         }}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     mod operators { | ||||||
|  |         use super::*; | ||||||
|  | 
 | ||||||
|  |         #[test] | ||||||
|  |         fn mul_plus() { | ||||||
|  |             let (rem, res) = expr("x*y+z").unwrap(); | ||||||
|  |             assert!(rem.is_empty()); | ||||||
|  |             assert_eq!( | ||||||
|  |                 res, | ||||||
|  |                 BinaryOp { | ||||||
|  |                     lhs: Box::new(BinaryOp { | ||||||
|  |                         lhs: ident_expr("x"), | ||||||
|  |                         op: Mul, | ||||||
|  |                         rhs: ident_expr("y") | ||||||
|  |                     }), | ||||||
|  |                     op: Add, | ||||||
|  |                     rhs: ident_expr("z") | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #[test] | ||||||
|  |         fn mul_plus_ws() { | ||||||
|  |             let (rem, res) = expr("x * y    +    z").unwrap(); | ||||||
|  |             assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem); | ||||||
|  |             assert_eq!( | ||||||
|  |                 res, | ||||||
|  |                 BinaryOp { | ||||||
|  |                     lhs: Box::new(BinaryOp { | ||||||
|  |                         lhs: ident_expr("x"), | ||||||
|  |                         op: Mul, | ||||||
|  |                         rhs: ident_expr("y") | ||||||
|  |                     }), | ||||||
|  |                     op: Add, | ||||||
|  |                     rhs: ident_expr("z") | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #[test] | ||||||
|  |         fn unary() { | ||||||
|  |             let (rem, res) = expr("x * -z").unwrap(); | ||||||
|  |             assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem); | ||||||
|  |             assert_eq!( | ||||||
|  |                 res, | ||||||
|  |                 BinaryOp { | ||||||
|  |                     lhs: ident_expr("x"), | ||||||
|  |                     op: Mul, | ||||||
|  |                     rhs: Box::new(UnaryOp { | ||||||
|  |                         op: Neg, | ||||||
|  |                         rhs: ident_expr("z"), | ||||||
|  |                     }) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #[test] | ||||||
|  |         fn mul_literal() { | ||||||
|  |             let (rem, res) = expr("x * 3").unwrap(); | ||||||
|  |             assert!(rem.is_empty()); | ||||||
|  |             assert_eq!( | ||||||
|  |                 res, | ||||||
|  |                 BinaryOp { | ||||||
|  |                     lhs: ident_expr("x"), | ||||||
|  |                     op: Mul, | ||||||
|  |                     rhs: Box::new(Expr::Literal(Literal::Int(3))), | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #[test] | ||||||
|  |         fn equ() { | ||||||
|  |             let res = test_parse!(expr, "x * 7 == 7"); | ||||||
|  |             assert_eq!( | ||||||
|  |                 res, | ||||||
|  |                 BinaryOp { | ||||||
|  |                     lhs: Box::new(BinaryOp { | ||||||
|  |                         lhs: ident_expr("x"), | ||||||
|  |                         op: Mul, | ||||||
|  |                         rhs: Box::new(Expr::Literal(Literal::Int(7))) | ||||||
|  |                     }), | ||||||
|  |                     op: Equ, | ||||||
|  |                     rhs: Box::new(Expr::Literal(Literal::Int(7))) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn let_complex() { | ||||||
|  |         let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4"); | ||||||
|  |         assert_eq!( | ||||||
|  |             res, | ||||||
|  |             Let { | ||||||
|  |                 bindings: vec![ | ||||||
|  |                     ( | ||||||
|  |                         Ident::try_from("x").unwrap(), | ||||||
|  |                         Expr::Literal(Literal::Int(1)) | ||||||
|  |                     ), | ||||||
|  |                     ( | ||||||
|  |                         Ident::try_from("y").unwrap(), | ||||||
|  |                         Expr::BinaryOp { | ||||||
|  |                             lhs: ident_expr("x"), | ||||||
|  |                             op: Mul, | ||||||
|  |                             rhs: Box::new(Expr::Literal(Literal::Int(7))) | ||||||
|  |                         } | ||||||
|  |                     ) | ||||||
|  |                 ], | ||||||
|  |                 body: Box::new(Expr::BinaryOp { | ||||||
|  |                     lhs: Box::new(Expr::BinaryOp { | ||||||
|  |                         lhs: ident_expr("x"), | ||||||
|  |                         op: Add, | ||||||
|  |                         rhs: ident_expr("y"), | ||||||
|  |                     }), | ||||||
|  |                     op: Mul, | ||||||
|  |                     rhs: Box::new(Expr::Literal(Literal::Int(4))), | ||||||
|  |                 }) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn if_simple() { | ||||||
|  |         let res = test_parse!(expr, "if x == 8 then 9 else 20"); | ||||||
|  |         assert_eq!( | ||||||
|  |             res, | ||||||
|  |             If { | ||||||
|  |                 condition: Box::new(BinaryOp { | ||||||
|  |                     lhs: ident_expr("x"), | ||||||
|  |                     op: Equ, | ||||||
|  |                     rhs: Box::new(Expr::Literal(Literal::Int(8))), | ||||||
|  |                 }), | ||||||
|  |                 then: Box::new(Expr::Literal(Literal::Int(9))), | ||||||
|  |                 else_: Box::new(Expr::Literal(Literal::Int(20))) | ||||||
|  |             } | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[test] | ||||||
|  |     fn fn_decl() { | ||||||
|  |         let res = test_parse!(decl, "fn id x = x"); | ||||||
|  |         assert_eq!( | ||||||
|  |             res, | ||||||
|  |             Decl::Fun(Fun { | ||||||
|  |                 name: "id".try_into().unwrap(), | ||||||
|  |                 args: vec!["x".try_into().unwrap()], | ||||||
|  |                 body: *ident_expr("x"), | ||||||
|  |             }) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue