diff --git a/Cargo.lock b/Cargo.lock index bbac391..1b692aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,7 +89,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -99,7 +99,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -132,6 +132,19 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.0", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-executor" version = "1.5.1" @@ -152,7 +165,7 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" dependencies = [ - "async-channel", + "async-channel 1.9.0", "async-executor", "async-io 1.13.0", "async-lock 2.8.0", @@ -183,22 +196,21 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9d5715c2d329bf1b4da8d60455b99b187f27ba726df2883799af9af60997" +checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" dependencies = [ - "async-lock 3.0.0", + "async-lock 3.1.2", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.0.1", "parking", - "polling 3.3.0", - "rustix 0.38.21", + "polling 3.3.1", + "rustix 0.38.25", "slab", "tracing", - "waker-fn", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -212,11 +224,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.0.0" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e900cdcd39bb94a14487d3f7ef92ca222162e6c7c3fe7cb3550ea75fb486ed" +checksum = "dea8b3453dd7cc96711834b75400d671b73e3656975fa68d9f277163b7f7e316" dependencies = [ - "event-listener 3.0.1", + "event-listener 4.0.0", "event-listener-strategy", "pin-project-lite", ] @@ -232,10 +244,10 @@ dependencies = [ "async-signal", "blocking", "cfg-if", - "event-listener 3.0.1", + "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.21", - "windows-sys", + "rustix 0.38.25", + "windows-sys 0.48.0", ] [[package]] @@ -244,16 +256,16 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io 2.2.0", + "async-io 2.2.1", "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.21", + "rustix 0.38.25", "signal-hook-registry", "slab", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -263,7 +275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", - "async-channel", + "async-channel 1.9.0", "async-global-executor", "async-io 1.13.0", "async-lock 2.8.0", @@ -429,16 +441,16 @@ dependencies = [ [[package]] name = "blocking" -version = "1.4.1" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel", - "async-lock 2.8.0", + "async-channel 2.1.1", + "async-lock 3.1.2", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite 1.13.0", + "futures-lite 2.0.1", "piper", "tracing", ] @@ -507,14 +519,14 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] name = "clap" -version = "4.4.7" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" +checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", @@ -522,9 +534,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.7" +version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", @@ -562,12 +574,16 @@ version = "0.1.0" dependencies = [ "bitflags 2.4.1", "clap", + "derive-name", "derive_more", + "enum-display", "functor_derive", + "indenter", "itertools 0.11.0", "lalrpop", "lalrpop-util", "miette", + "once_cell", "petgraph", "serde", "tempfile", @@ -591,6 +607,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -668,9 +693,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "deranged" @@ -692,13 +717,32 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-name" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19af109a7f1118ab96e1fd2a0c87310787f791680fa71b4bbfa5dffbc358b08" +dependencies = [ + "derive-name-macros", +] + +[[package]] +name = "derive-name-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa60999fb9292247d7c7eec5dada22b4ba337394612b0d935616bf49964c8bfd" +dependencies = [ + "quote", + "syn 2.0.39", +] + [[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version 0.4.0", @@ -770,6 +814,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "enum-display" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d4df33d54dd1959d177a0e2c2f4e5a8637a3054aa56861ed7e173ad2043fe2" +dependencies = [ + "enum-display-macro", +] + +[[package]] +name = "enum-display-macro" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ce3a36047ede676eb0d2721d065beed8410cf4f113f489604d2971331cb378" +dependencies = [ + "convert_case 0.6.0", + "quote", + "syn 1.0.109", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -778,12 +842,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" +checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -794,9 +858,20 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" -version = "3.0.1" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cec0252c2afff729ee6f00e903d479fba81784c8e2bd77447673471fdfaea1" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" dependencies = [ "concurrent-queue", "parking", @@ -805,11 +880,11 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 3.0.1", + "event-listener 4.0.0", "pin-project-lite", ] @@ -848,9 +923,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -995,9 +1070,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", @@ -1006,9 +1081,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "git2" @@ -1129,14 +1204,20 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "1.9.3" @@ -1174,7 +1255,7 @@ checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1185,7 +1266,7 @@ checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ "socket2 0.5.5", "widestring", - "windows-sys", + "windows-sys 0.48.0", "winreg", ] @@ -1202,8 +1283,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.21", - "windows-sys", + "rustix 0.38.25", + "windows-sys 0.48.0", ] [[package]] @@ -1372,9 +1453,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lock_api" @@ -1481,7 +1562,7 @@ checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1581,9 +1662,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.95" +version = "0.9.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9" +checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" dependencies = [ "cc", "libc", @@ -1623,7 +1704,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1643,9 +1724,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" @@ -1714,21 +1795,21 @@ dependencies = [ "libc", "log", "pin-project-lite", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "polling" -version = "3.3.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.21", + "rustix 0.38.25", "tracing", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1955,7 +2036,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2003,27 +2084,27 @@ dependencies = [ "io-lifetimes", "libc", "linux-raw-sys 0.3.8", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "rustix" -version = "0.38.21" +version = "0.38.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" +checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" dependencies = [ "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.10", - "windows-sys", + "linux-raw-sys 0.4.11", + "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.21.8" +version = "0.21.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" +checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", "ring", @@ -2033,9 +2114,9 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ "base64 0.21.5", ] @@ -2110,9 +2191,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -2128,9 +2209,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.192" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -2219,9 +2300,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "smawk" @@ -2246,7 +2327,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2372,8 +2453,8 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall", - "rustix 0.38.21", - "windows-sys", + "rustix 0.38.25", + "windows-sys 0.48.0", ] [[package]] @@ -2399,11 +2480,10 @@ dependencies = [ [[package]] name = "test_each_file" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d2b9676fc7f7fa8f8426cd82ff8c0db19359a356c3c837969acb1baa5eab31e" +checksum = "59655d1577b8984dc71241b628738ada4ba23ac2f05085eed42542f2c1c7e93b" dependencies = [ - "pathdiff", "proc-macro-error", "proc-macro2", "quote", @@ -2496,9 +2576,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.33.0" +version = "1.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", "bytes", @@ -2508,14 +2588,14 @@ dependencies = [ "pin-project-lite", "socket2 0.5.5", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", @@ -2651,6 +2731,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "unicode-width" version = "0.1.11" @@ -2671,12 +2757,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.4.0", + "idna 0.5.0", "percent-encoding", ] @@ -2688,9 +2774,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" dependencies = [ "getrandom", "serde", @@ -2814,9 +2900,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.2" +version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" [[package]] name = "widestring" @@ -2861,7 +2947,7 @@ version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -2870,7 +2956,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", ] [[package]] @@ -2879,13 +2974,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -2894,42 +3004,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winreg" version = "0.50.0" @@ -2937,7 +3089,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ "cfg-if", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2951,9 +3103,9 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.25" +version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" dependencies = [ "byteorder", "zerocopy-derive", @@ -2961,9 +3113,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.7.25" +version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" dependencies = [ "proc-macro2", "quote", diff --git a/README.md b/README.md index 144e794..b73d6c8 100644 --- a/README.md +++ b/README.md @@ -31,14 +31,16 @@ cargo run -- input.jj -o output && ./output ; echo $? # Fixes * [ ] Updated README, with 3 new colors! * [ ] Add documentation where necessary. -* [ ] Improve error handling for parsing pass. -* [ ] Improve error handling for type checking pass. +* [x] Improve error handling for parsing pass. +* [x] Improve error handling for type checking pass. + * [ ] Make errors prettier. * [ ] Improve algorithm for colouring the interference graph. * [ ] Add read and write functionality to the bencher to update locally. * [x] Lots, and lots, of refactoring! * [x] Write test input in comments. # Upcoming Language Features +* [x] Type inference. * [x] Implement comments in code. * [ ] Algebraic Data Types. * [x] Structs. diff --git a/bencher/src/main.rs b/bencher/src/main.rs index 3f652c2..c263eb4 100644 --- a/bencher/src/main.rs +++ b/bencher/src/main.rs @@ -1,5 +1,6 @@ use clap::Parser; use compiler::interpreter::TestIO; +use compiler::passes::parse::parse::parse_program; use compiler::passes::select::interpreter::IStats; use compiler::utils::split_test::split_test; use git2::{Commit, Repository}; @@ -263,25 +264,22 @@ fn write_commit(benches: &Collection, commit: &Commit<'_>, test_data: impl Stats { fn from_program(program: &str) -> Self { - let (input, _, _, program) = split_test(program); + let (input, _, _, _) = split_test(program); let mut io = TestIO::new(input); let tempdir = TempDir::new("cc-bench").unwrap(); let output = tempdir.path().join("output"); - let prg_concluded = program + let prg_concluded = parse_program(program) + .unwrap() .validate() .unwrap() - .uniquify() .reveal() .atomize() .explicate() .eliminate() .select() - .add_liveness() - .compute_interference() - .color_interference() - .assign_homes() + .assign() .patch() .conclude(); diff --git a/compiler/Cargo.toml b/compiler/Cargo.toml index fe608eb..437beef 100644 --- a/compiler/Cargo.toml +++ b/compiler/Cargo.toml @@ -16,10 +16,14 @@ zerocopy = { version = "0.7.25", features = ["derive"] } lalrpop-util = { version = "0.20.0", features = ["lexer", "unicode"] } derive_more = "0.99.17" functor_derive = "0.2.3" +indenter = "0.3.3" +once_cell = "1.18.0" [build-dependencies] lalrpop = "0.20.0" [dev-dependencies] -test_each_file = "0.1.1" +test_each_file = "0.2.0" tempfile = "3.8.1" +enum-display = "0.1.3" +derive-name = "1.1.0" diff --git a/compiler/src/interpreter.rs b/compiler/src/interpreter.rs index 99cc621..661e9ac 100644 --- a/compiler/src/interpreter.rs +++ b/compiler/src/interpreter.rs @@ -1,8 +1,8 @@ use crate::passes::validate::TLit; +use crate::utils::gen_sym::UniqueSym; use derive_more::Display; use std::collections::HashMap; -use std::fmt::Display; -use std::hash::Hash; + use std::vec::IntoIter; pub trait IO { @@ -41,7 +41,7 @@ impl IO for TestIO { } #[derive(Eq, PartialEq, Clone, Debug, Display)] -pub enum Val<'p, A: Copy + Hash + Eq + Display> { +pub enum Val<'p> { #[display(fmt = "{val}")] Int { val: i64 }, #[display(fmt = "{}", r#"if *val { "true" } else { "false" }"#)] @@ -49,24 +49,25 @@ pub enum Val<'p, A: Copy + Hash + Eq + Display> { #[display(fmt = "unit")] Unit, #[display(fmt = "fn pointer `{sym}`")] - Function { sym: A }, + Function { sym: UniqueSym<'p> }, + #[display(fmt = "stdlib function `{sym}`")] + StdlibFunction { sym: &'p str }, #[display(fmt = "struct instance")] - StructInstance { - fields: HashMap<&'p str, Val<'p, A>>, - }, + StructInstance { fields: HashMap<&'p str, Val<'p>> }, } -impl<'p, A: Copy + Hash + Eq + Display> From for Val<'p, A> { +impl<'p> From for Val<'p> { fn from(value: TLit) -> Self { match value { - TLit::Int { val } => Val::Int { val: val as i64 }, + TLit::I64 { val } => Val::Int { val }, + TLit::U64 { val } => Val::Int { val: val as i64 }, TLit::Bool { val } => Val::Bool { val }, TLit::Unit => Val::Unit, } } } -impl<'p, A: Copy + Hash + Eq + Display> Val<'p, A> { +impl<'p> Val<'p> { pub fn int(&self) -> i64 { match self { Val::Int { val } => *val, @@ -81,14 +82,14 @@ impl<'p, A: Copy + Hash + Eq + Display> Val<'p, A> { } } - pub fn fun(&self) -> A { + pub fn fun(&self) -> UniqueSym<'p> { match self { Val::Function { sym } => *sym, _ => panic!(), } } - pub fn strct(&self) -> &HashMap<&'p str, Val<'p, A>> { + pub fn strct(&self) -> &HashMap<&'p str, Val<'p>> { match self { Val::StructInstance { fields } => fields, _ => panic!(), diff --git a/compiler/src/lib.rs b/compiler/src/lib.rs index a3e72cf..83c0b50 100644 --- a/compiler/src/lib.rs +++ b/compiler/src/lib.rs @@ -23,16 +23,12 @@ pub fn compile(program: &str, filename: &str, output: &Path) -> miette::Result<( .validate() .map_err(Into::into) .map_err(add_source)? - .uniquify() .reveal() .atomize() .explicate() .eliminate() .select() - .add_liveness() - .compute_interference() - .color_interference() - .assign_homes() + .assign() .patch() .conclude() .emit() diff --git a/compiler/src/main.rs b/compiler/src/main.rs index fcc3bdd..4ab59fe 100644 --- a/compiler/src/main.rs +++ b/compiler/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; use compiler::compile; use compiler::passes::parse::parse::PrettyParseError; -use compiler::passes::validate::ValidateError; +use compiler::passes::validate::error::TypeError; use miette::{Diagnostic, IntoDiagnostic}; use std::fs; use std::io::Read; @@ -15,7 +15,7 @@ enum MainError { ParseError(#[from] PrettyParseError), #[error(transparent)] #[diagnostic(transparent)] - ValidateError(#[from] ValidateError), + ValidateError(#[from] TypeError), } #[derive(Parser, Debug)] diff --git a/compiler/src/passes/assign/assign.rs b/compiler/src/passes/assign/assign.rs new file mode 100644 index 0000000..ea3400e --- /dev/null +++ b/compiler/src/passes/assign/assign.rs @@ -0,0 +1,78 @@ +use crate::passes::assign::{Arg, X86Assigned}; +use crate::passes::select::{Block, Instr, VarArg, X86Selected}; +use crate::utils::gen_sym::UniqueSym; +use crate::*; +use std::collections::HashMap; + +impl<'p> X86Selected<'p> { + #[must_use] + pub fn assign(self) -> X86Assigned<'p> { + let program = self.include_liveness(); + let interference = program.compute_interference(); + let (color_map, stack_space) = interference.color(); + + let blocks = program + .blocks + .into_iter() + .map(|(lbl, block)| (lbl, assign_block(block.into(), &color_map))) + .collect(); + + X86Assigned { + blocks, + entry: program.entry, + stack_space, + std: program.std, + } + } +} + +fn assign_block<'p>( + block: Block<'p, VarArg>, + color_map: &HashMap, +) -> Block<'p, Arg> { + Block { + instrs: block + .instrs + .into_iter() + .map(|instr| assign_instr(instr, color_map)) + .collect(), + } +} + +fn assign_instr<'p>( + instr: Instr<'p, VarArg>, + color_map: &HashMap, +) -> Instr<'p, Arg> { + let map = |arg: VarArg| -> Arg { + match arg { + VarArg::Imm { val } => Arg::Imm { val }, + VarArg::Reg { reg } => Arg::Reg { reg }, + VarArg::Deref { reg, off } => Arg::Deref { reg, off }, + VarArg::XVar { sym } => color_map[&sym].clone(), + } + }; + + match instr { + Instr::Addq { src, dst } => addq!(map(src), map(dst)), + Instr::Subq { src, dst } => subq!(map(src), map(dst)), + Instr::Divq { divisor } => divq!(map(divisor)), + Instr::Mulq { src } => mulq!(map(src)), + Instr::Negq { dst } => negq!(map(dst)), + Instr::Movq { src, dst } => movq!(map(src), map(dst)), + Instr::Pushq { src } => pushq!(map(src)), + Instr::Popq { dst } => popq!(map(dst)), + Instr::CallqDirect { lbl, arity } => callq_direct!(lbl, arity), + Instr::Retq => retq!(), + Instr::Syscall { arity } => syscall!(arity), + Instr::Jmp { lbl } => jmp!(lbl), + Instr::Jcc { lbl, cnd } => jcc!(lbl, cnd), + Instr::Cmpq { src, dst } => cmpq!(map(src), map(dst)), + Instr::Andq { src, dst } => andq!(map(src), map(dst)), + Instr::Orq { src, dst } => orq!(map(src), map(dst)), + Instr::Xorq { src, dst } => xorq!(map(src), map(dst)), + Instr::Notq { dst } => notq!(map(dst)), + Instr::Setcc { cnd } => setcc!(cnd), + Instr::LoadLbl { sym, dst } => load_lbl!(sym, map(dst)), + Instr::CallqIndirect { src, arity } => callq_indirect!(map(src), arity), + } +} diff --git a/compiler/src/passes/assign/color_interference.rs b/compiler/src/passes/assign/color_interference.rs new file mode 100644 index 0000000..4ec7197 --- /dev/null +++ b/compiler/src/passes/assign/color_interference.rs @@ -0,0 +1,114 @@ +use crate::passes::assign::{Arg, InterferenceGraph, LArg}; +use crate::passes::select::Reg; +use crate::utils::gen_sym::UniqueSym; +use itertools::Itertools; +use std::collections::{HashMap, HashSet}; + +impl<'p> InterferenceGraph<'p> { + #[must_use] + pub fn color(self) -> (HashMap, Arg>, usize) { + let graph = self.0; + let mut queue = Vec::new(); + let mut node_map = HashMap::::new(); + + for node in graph.nodes() { + match node { + LArg::Var { .. } => { + queue.push(node); + } + LArg::Reg { reg } => { + let node_weight = match reg { + Reg::RCX => 0, + Reg::RDX => 1, + Reg::RSI => 2, + Reg::RDI => 3, + Reg::R8 => 4, + Reg::R9 => 5, + Reg::R10 => 6, + Reg::RBX => 7, + Reg::R12 => 8, + Reg::R13 => 9, + Reg::R14 => 10, + Reg::RAX => -1, + Reg::RSP => -2, + Reg::RBP => -3, + Reg::R11 => -4, + Reg::R15 => -5, + }; + node_map.insert(node, node_weight); + } + } + } + + while let Some(node) = queue.pop() { + let used_colors = graph + .neighbors(node) + .filter_map(|nb| node_map.get(&nb)) + .collect::>(); + + let chosen_color = (0..) + .find(|i| !used_colors.contains(i)) + .expect("there are infinite numbers, lol"); + + node_map.insert(node, chosen_color); + + queue.sort_by_key(|node| { + graph + .neighbors(*node) + .filter_map(|nb| node_map.get(&nb)) + .unique() + .count() + }); + } + + let used_vars = node_map + .values() + .filter(|&&n| n >= 10) + .map(|&n| n - 10) + .max() + .unwrap_or_default() as usize; + + let stack_space = (8 * used_vars).div_ceil(16) * 16; + + let colors = node_map + .into_iter() + .filter_map(|(node, color)| match node { + LArg::Var { sym } => Some((sym, arg_from_color(color))), + LArg::Reg { .. } => None, + }) + .collect(); + + (colors, stack_space) + } +} + +fn arg_from_color(i: isize) -> Arg { + match i { + -5 => Arg::Reg { reg: Reg::R15 }, + -4 => Arg::Reg { reg: Reg::R11 }, + -3 => Arg::Reg { reg: Reg::RBP }, + -2 => Arg::Reg { reg: Reg::RSP }, + -1 => Arg::Reg { reg: Reg::RAX }, + 0 => Arg::Reg { reg: Reg::RCX }, + 1 => Arg::Reg { reg: Reg::RDX }, + 2 => Arg::Reg { reg: Reg::RSI }, + 3 => Arg::Reg { reg: Reg::RDI }, + 4 => Arg::Reg { reg: Reg::R8 }, + 5 => Arg::Reg { reg: Reg::R9 }, + 6 => Arg::Reg { reg: Reg::R10 }, + 7 => Arg::Reg { reg: Reg::RBX }, + 8 => Arg::Reg { reg: Reg::R12 }, + 9 => Arg::Reg { reg: Reg::R13 }, + 10 => Arg::Reg { reg: Reg::R14 }, + i => { + assert!( + i > 10, + "Something went wrong while coloring the assign graph." + ); + Arg::Deref { + reg: Reg::RBP, + off: (-8 * (i - 10)) as i64, + } + } + } +} diff --git a/compiler/src/passes/interference/compute_interference.rs b/compiler/src/passes/assign/compute_interference.rs similarity index 69% rename from compiler/src/passes/interference/compute_interference.rs rename to compiler/src/passes/assign/compute_interference.rs index 34cb74c..36b0eda 100644 --- a/compiler/src/passes/interference/compute_interference.rs +++ b/compiler/src/passes/assign/compute_interference.rs @@ -1,25 +1,14 @@ -use crate::passes::interference::liveness_analysis::{handle_instr, ReadWriteOp}; -use crate::passes::interference::{InterferenceGraph, LArg, LX86VarProgram, X86WithInterference}; +use crate::passes::assign::include_liveness::{handle_instr, ReadWriteOp}; +use crate::passes::assign::{InterferenceGraph, LArg, LX86VarProgram}; use crate::passes::select::VarArg; +use petgraph::graphmap::GraphMap; +use petgraph::Undirected; use std::collections::HashMap; impl<'p> LX86VarProgram<'p> { #[must_use] - pub fn compute_interference(self) -> X86WithInterference<'p> { - X86WithInterference { - interference: self.build_graph(), - blocks: self - .blocks - .into_iter() - .map(|(name, block)| (name, block.into())) - .collect(), - entry: self.entry, - std: self.std, - } - } - - fn build_graph(&self) -> InterferenceGraph<'p> { - let mut graph = InterferenceGraph::new(); + pub fn compute_interference(&self) -> InterferenceGraph<'p> { + let mut graph = GraphMap::, (), Undirected>::new(); for block in self.blocks.values() { for (instr, live_after) in &block.instrs { @@ -51,6 +40,6 @@ impl<'p> LX86VarProgram<'p> { } } - graph + InterferenceGraph(graph) } } diff --git a/compiler/src/passes/interference/liveness_analysis.rs b/compiler/src/passes/assign/include_liveness.rs similarity index 88% rename from compiler/src/passes/interference/liveness_analysis.rs rename to compiler/src/passes/assign/include_liveness.rs index faa3dde..f57fd65 100644 --- a/compiler/src/passes/interference/liveness_analysis.rs +++ b/compiler/src/passes/assign/include_liveness.rs @@ -1,15 +1,13 @@ use crate::utils::gen_sym::UniqueSym; -use crate::passes::interference::{LArg, LBlock, LX86VarProgram}; +use crate::passes::assign::{LArg, LBlock, LX86VarProgram}; use crate::passes::select::{Block, Instr, Reg, VarArg, X86Selected, CALLER_SAVED, SYSCALL_REGS}; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; impl<'p> X86Selected<'p> { #[must_use] - pub fn add_liveness(self) -> LX86VarProgram<'p> { - // let graph = create_graph(&self.blocks); - + pub(super) fn include_liveness(self) -> LX86VarProgram<'p> { // maps block names to what is live before the block let mut before_map = HashMap::>::new(); // maps block names to blocks with liveness info added @@ -59,25 +57,6 @@ impl<'p> X86Selected<'p> { } } -// todo: implement more efficient way to update blocks -// fn create_graph<'p>(blocks: &HashMap, Block<'p, VarArg<'p>>>) -> GraphMap, (), Directed> { -// let mut graph = GraphMap::default(); -// -// for (src, block) in blocks{ -// graph.add_node(*src); -// for instr in &block.instrs { -// match instr { -// Instr::Jmp { lbl } | Instr::Jcc { lbl, .. } => { -// graph.add_edge(*src, *lbl, ()); -// } -// _ => {} -// } -// } -// } -// -// graph -// } - fn block_liveness<'p>( block: &Block<'p, VarArg<'p>>, before_map: &HashMap, HashSet>>, diff --git a/compiler/src/passes/interference/mod.rs b/compiler/src/passes/assign/mod.rs similarity index 68% rename from compiler/src/passes/interference/mod.rs rename to compiler/src/passes/assign/mod.rs index a686ba0..a1cafe9 100644 --- a/compiler/src/passes/interference/mod.rs +++ b/compiler/src/passes/assign/mod.rs @@ -1,4 +1,11 @@ -use crate::passes::select::io::Std; +mod assign; +mod color_interference; +mod compute_interference; +mod include_liveness; +#[cfg(test)] +mod tests; + +use crate::passes::select::std_lib::Std; use crate::passes::select::{Block, Instr, Reg, VarArg, X86Selected}; use crate::utils::gen_sym::UniqueSym; use derive_more::Display; @@ -7,12 +14,6 @@ use petgraph::graphmap::GraphMap; use petgraph::Undirected; use std::collections::{HashMap, HashSet}; -pub mod assign_homes; -pub mod coloring_interference; -pub mod compute_interference; -pub mod liveness_analysis; - -#[derive(Debug, PartialEq)] pub struct X86Assigned<'p> { pub blocks: HashMap, Block<'p, Arg>>, pub entry: UniqueSym<'p>, @@ -20,48 +21,30 @@ pub struct X86Assigned<'p> { pub std: Std<'p>, } -#[derive(Debug, PartialEq)] -pub struct LX86VarProgram<'p> { - pub blocks: HashMap, LBlock<'p>>, - pub entry: UniqueSym<'p>, - pub std: Std<'p>, +#[derive(Clone, Display)] +pub enum Arg { + #[display(fmt = "${val}")] + Imm { val: i64 }, + #[display(fmt = "%{reg}")] + Reg { reg: Reg }, + #[display(fmt = "[%{reg} + ${off}]")] + Deref { reg: Reg, off: i64 }, } -#[derive(Debug)] -pub struct X86WithInterference<'p> { - pub blocks: HashMap, Block<'p, VarArg<'p>>>, - pub entry: UniqueSym<'p>, - pub interference: InterferenceGraph<'p>, - pub std: Std<'p>, -} +pub struct InterferenceGraph<'p>(GraphMap, (), Undirected>); -#[derive(Debug)] -pub struct X86Colored<'p> { - pub blocks: HashMap, Block<'p, VarArg<'p>>>, +pub struct LX86VarProgram<'p> { + pub blocks: HashMap, LBlock<'p>>, pub entry: UniqueSym<'p>, - pub color_map: HashMap, Arg>, - pub stack_space: usize, pub std: Std<'p>, } -pub type InterferenceGraph<'p> = GraphMap, (), Undirected>; - -#[derive(Debug, PartialEq)] +#[derive(PartialEq)] pub struct LBlock<'p> { pub instrs: Vec<(Instr<'p, VarArg<'p>>, HashSet>)>, } -#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq, Display)] -pub enum Arg { - #[display(fmt = "${val}")] - Imm { val: i64 }, - #[display(fmt = "%{reg}")] - Reg { reg: Reg }, - #[display(fmt = "[%{reg} + ${off}]")] - Deref { reg: Reg, off: i64 }, -} - -#[derive(Debug, PartialEq, Clone, Copy, Hash, Eq, Ord, PartialOrd)] +#[derive(Hash, Clone, Copy, Eq, Ord, PartialEq, PartialOrd)] pub enum LArg<'p> { Var { sym: UniqueSym<'p> }, Reg { reg: Reg }, diff --git a/compiler/src/passes/assign/tests.rs b/compiler/src/passes/assign/tests.rs new file mode 100644 index 0000000..2f0dbb3 --- /dev/null +++ b/compiler/src/passes/assign/tests.rs @@ -0,0 +1,43 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::passes::select::X86Selected; +use crate::utils::gen_sym::gen_sym; +use crate::utils::split_test::split_test; +use crate::{block, callq_direct, movq, reg}; +use test_each_file::test_each_file; + +fn assign([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let mut program: X86Selected = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .atomize() + .explicate() + .eliminate() + .select() + .assign() + .into(); + + // Redirect program to exit + let new_entry = gen_sym("tmp"); + program.blocks.insert( + new_entry, + block!( + callq_direct!(program.entry, 0), + movq!(reg!(RAX), reg!(RDI)), + callq_direct!(program.std["exit"], 1) + ), + ); + program.entry = new_entry; + + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as assign => assign } diff --git a/compiler/src/passes/atomize/atomize.rs b/compiler/src/passes/atomize/atomize.rs index 6afeb6e..de6774e 100644 --- a/compiler/src/passes/atomize/atomize.rs +++ b/compiler/src/passes/atomize/atomize.rs @@ -1,7 +1,7 @@ -use crate::passes::atomize::{AExpr, Atom, PrgAtomized}; +use crate::passes::atomize::{AExpr, Atom, DefAtomized, PrgAtomized}; use crate::passes::parse::types::Type; -use crate::passes::parse::Def; -use crate::passes::reveal_functions::{PrgRevealed, RExpr}; +use crate::passes::parse::{Meta, Typed}; +use crate::passes::reveal::{DefRevealed, PrgRevealed, RExpr}; use crate::utils::gen_sym::{gen_sym, UniqueSym}; impl<'p> PrgRevealed<'p> { @@ -14,154 +14,140 @@ impl<'p> PrgRevealed<'p> { .map(|(sym, def)| (sym, atomize_def(def))) .collect(), entry: self.entry, + std: self.std, } } } -fn atomize_def<'p>(def: Def<'p, UniqueSym<'p>, RExpr<'p>>) -> Def<'p, UniqueSym<'p>, AExpr<'p>> { +fn atomize_def(def: DefRevealed) -> DefAtomized { match def { - Def::Fn { + DefRevealed::Fn { sym, params, typ, bdy, - } => Def::Fn { + } => DefAtomized::Fn { sym, params, typ, bdy: atomize_expr(bdy), }, - Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, + DefRevealed::TypeDef { sym, def } => DefAtomized::TypeDef { sym, def }, } } -fn atomize_expr(expr: RExpr) -> AExpr { - match expr { - RExpr::Lit { val, typ } => AExpr::Atom { +fn atomize_expr<'p>(expr: Typed<'p, RExpr<'p>>) -> Typed<'p, AExpr<'p>> { + // Keep track of all the priors. These are bindings that should come before evaluating the expression. + let mut priors = Vec::new(); + + // Returns the atomized expression. This can hold `Var`s that must be bound by the priors. + let inner = match expr.inner { + RExpr::Lit { val } => AExpr::Atom { atm: Atom::Val { val }, - typ, }, - RExpr::Var { sym, typ } => AExpr::Atom { + RExpr::Var { sym } => AExpr::Atom { atm: Atom::Var { sym }, - typ, }, - RExpr::Prim { op, args, typ } => { - let (args, extras): (Vec<_>, Vec<_>) = args.into_iter().map(atomize_atom).unzip(); - - extras - .into_iter() - .flatten() - .rfold(AExpr::Prim { op, args, typ }, |bdy, (sym, bnd)| { - AExpr::Let { - sym, - typ: bdy.typ().clone(), - bnd: Box::new(bnd), - bdy: Box::new(bdy), - } - }) - } - RExpr::Let { sym, bnd, bdy, typ } => AExpr::Let { + RExpr::BinaryOp { + op, + exprs: [lhs, rhs], + } => AExpr::BinaryOp { + op, + exprs: [ + atomize_atom(*lhs, &mut priors), + atomize_atom(*rhs, &mut priors), + ], + }, + RExpr::UnaryOp { op, expr: arg } => AExpr::UnaryOp { + op, + expr: atomize_atom(*arg, &mut priors), + }, + RExpr::Let { sym, bnd, bdy } => AExpr::Let { sym, bnd: Box::new(atomize_expr(*bnd)), bdy: Box::new(atomize_expr(*bdy)), - typ, }, - RExpr::If { cnd, thn, els, typ } => AExpr::If { + RExpr::If { cnd, thn, els } => AExpr::If { cnd: Box::new(atomize_expr(*cnd)), thn: Box::new(atomize_expr(*thn)), els: Box::new(atomize_expr(*els)), - typ, }, - RExpr::Apply { fun, args, typ } => { - let Type::Fn { params, .. } = fun.typ().clone() else { + RExpr::Apply { fun, args } => { + let Type::Fn { params, .. } = fun.meta.clone() else { unreachable!() }; - let (args, extras): (Vec<_>, Vec<_>) = args - .into_iter() - .map(atomize_atom) - .zip(params) - .map(|((arg, extra), arg_typ)| ((arg, arg_typ), extra)) - .unzip(); - - let (fun, fun_expr) = atomize_atom(*fun); - - fun_expr - .into_iter() - .chain(extras.into_iter().flatten()) - .rfold(AExpr::Apply { fun, args, typ }, |bdy, (sym, bnd)| { - AExpr::Let { - typ: bdy.typ().clone(), - sym, - bnd: Box::new(bnd), - bdy: Box::new(bdy), - } - }) + AExpr::Apply { + fun: atomize_atom(*fun, &mut priors), + args: args + .into_iter() + .map(|arg| atomize_atom(arg, &mut priors)) + .zip(params) + .collect(), + } } - RExpr::FunRef { sym, typ } => AExpr::FunRef { sym, typ }, - RExpr::Loop { bdy, typ } => AExpr::Loop { + RExpr::FunRef { sym } => AExpr::FunRef { sym }, + RExpr::Loop { bdy } => AExpr::Loop { bdy: Box::new(atomize_expr(*bdy)), - typ, }, - RExpr::Break { bdy, typ } => AExpr::Break { + RExpr::Break { bdy } => AExpr::Break { bdy: Box::new(atomize_expr(*bdy)), - typ, }, - RExpr::Seq { stmt, cnt, typ } => AExpr::Seq { + RExpr::Seq { stmt, cnt } => AExpr::Seq { stmt: Box::new(atomize_expr(*stmt)), cnt: Box::new(atomize_expr(*cnt)), - typ, }, - RExpr::Assign { sym, bnd, typ } => AExpr::Assign { + RExpr::Assign { sym, bnd } => AExpr::Assign { sym, bnd: Box::new(atomize_expr(*bnd)), - typ, }, - RExpr::Continue { typ } => AExpr::Continue { typ }, - RExpr::Return { bdy, typ } => AExpr::Return { + RExpr::Continue => AExpr::Continue, + RExpr::Return { bdy } => AExpr::Return { bdy: Box::new(atomize_expr(*bdy)), - typ, }, - RExpr::Struct { sym, fields, typ } => { - let (fields, extras): (Vec<_>, Vec<_>) = fields + RExpr::Struct { sym, fields } => AExpr::Struct { + sym, + fields: fields .into_iter() .map(|(sym, expr)| { - let (field, extra) = atomize_atom(expr); - ((sym, field), extra) + let field = atomize_atom(expr, &mut priors); + (sym, field) }) - .unzip(); + .collect(), + }, + RExpr::AccessField { strct, field } => AExpr::AccessField { + strct: atomize_atom(*strct, &mut priors), + field, + }, + }; - extras.into_iter().flatten().rfold( - AExpr::Struct { sym, fields, typ }, - |bdy, (sym, bnd)| AExpr::Let { - sym, - typ: bdy.typ().clone(), - bnd: Box::new(bnd), - bdy: Box::new(bdy), - }, - ) - } - RExpr::AccessField { strct, field, typ } => { - let (strct, extra) = atomize_atom(*strct); + // Chains all the priors with the atomized expression as the body. + let inner = priors + .into_iter() + .rfold(inner, |bdy, (sym, bnd)| AExpr::Let { + sym, + bnd: Box::new(bnd), + bdy: Box::new(Meta { + inner: bdy, + meta: expr.meta.clone(), + }), + }); - extra.into_iter().rfold( - AExpr::AccessField { strct, field, typ }, - |bdy, (sym, bnd)| AExpr::Let { - sym, - typ: bdy.typ().clone(), - bnd: Box::new(bnd), - bdy: Box::new(bdy), - }, - ) - } + Meta { + inner, + meta: expr.meta, } } -fn atomize_atom(expr: RExpr) -> (Atom, Option<(UniqueSym, AExpr)>) { - if let RExpr::Lit { val, typ: _ } = expr { - (Atom::Val { val }, None) +fn atomize_atom<'p>( + expr: Typed<'p, RExpr<'p>>, + priors: &mut Vec<(UniqueSym<'p>, Typed<'p, AExpr<'p>>)>, +) -> Atom<'p> { + if let RExpr::Lit { val } = expr.inner { + Atom::Val { val } } else { let tmp = gen_sym("tmp"); - (Atom::Var { sym: tmp }, Some((tmp, atomize_expr(expr)))) + priors.push((tmp, atomize_expr(expr))); + Atom::Var { sym: tmp } } } diff --git a/compiler/src/passes/atomize/mod.rs b/compiler/src/passes/atomize/mod.rs index 62aed8a..09a668c 100644 --- a/compiler/src/passes/atomize/mod.rs +++ b/compiler/src/passes/atomize/mod.rs @@ -1,109 +1,80 @@ pub mod atomize; +#[cfg(test)] +mod tests; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Op}; -use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::{TExpr, TLit}; +use crate::passes::parse::{BinaryOp, Def, Meta, Typed, UnaryOp}; +use crate::passes::select::std_lib::Std; +use crate::passes::validate::{DefValidated, ExprValidated, PrgValidated, TLit}; use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; -#[derive(Debug, PartialEq)] pub struct PrgAtomized<'p> { - pub defs: HashMap, Def<'p, UniqueSym<'p>, AExpr<'p>>>, + pub defs: HashMap, DefAtomized<'p>>, pub entry: UniqueSym<'p>, + pub std: Std<'p>, } -#[derive(Debug, PartialEq)] +pub type DefAtomized<'p> = Def, &'p str, Typed<'p, AExpr<'p>>>; + pub enum AExpr<'p> { Atom { atm: Atom<'p>, - typ: Type>, }, - Prim { - op: Op, - args: Vec>, - typ: Type>, + BinaryOp { + op: BinaryOp, + exprs: [Atom<'p>; 2], + }, + UnaryOp { + op: UnaryOp, + expr: Atom<'p>, }, Let { sym: UniqueSym<'p>, - bnd: Box>, - bdy: Box>, - typ: Type>, + bnd: Box>>, + bdy: Box>>, }, If { - cnd: Box>, - thn: Box>, - els: Box>, - typ: Type>, + cnd: Box>>, + thn: Box>>, + els: Box>>, }, Apply { fun: Atom<'p>, args: Vec<(Atom<'p>, Type>)>, - typ: Type>, }, FunRef { sym: UniqueSym<'p>, - typ: Type>, }, Loop { - bdy: Box>, - typ: Type>, + bdy: Box>>, }, Break { - bdy: Box>, - typ: Type>, - }, - Continue { - typ: Type>, + bdy: Box>>, }, + Continue, Seq { - stmt: Box>, - cnt: Box>, - typ: Type>, + stmt: Box>>, + cnt: Box>>, }, Assign { sym: UniqueSym<'p>, - bnd: Box>, - typ: Type>, + bnd: Box>>, }, Return { - bdy: Box>, - typ: Type>, + bdy: Box>>, }, Struct { sym: UniqueSym<'p>, fields: Vec<(&'p str, Atom<'p>)>, - typ: Type>, }, AccessField { strct: Atom<'p>, field: &'p str, - typ: Type>, }, } -impl<'p> AExpr<'p> { - pub fn typ(&self) -> &Type> { - match self { - AExpr::Atom { typ, .. } - | AExpr::Prim { typ, .. } - | AExpr::Let { typ, .. } - | AExpr::If { typ, .. } - | AExpr::Apply { typ, .. } - | AExpr::Loop { typ, .. } - | AExpr::Break { typ, .. } - | AExpr::Continue { typ, .. } - | AExpr::Return { typ, .. } - | AExpr::Seq { typ, .. } - | AExpr::Assign { typ, .. } - | AExpr::Struct { typ, .. } - | AExpr::AccessField { typ, .. } - | AExpr::FunRef { typ, .. } => typ, - } - } -} - -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Copy, Clone)] pub enum Atom<'p> { Val { val: TLit }, Var { sym: UniqueSym<'p> }, @@ -119,146 +90,115 @@ impl<'p> Atom<'p> { } } -impl<'p> From> for PrgUniquified<'p> { +impl<'p> From> for PrgValidated<'p> { fn from(value: PrgAtomized<'p>) -> Self { - PrgUniquified { + PrgValidated { defs: value .defs .into_iter() .map(|(sym, def)| (sym, def.into())) .collect(), entry: value.entry, + std: value.std, } } } -// TODO functor time -impl<'p> From, AExpr<'p>>> - for Def<'p, UniqueSym<'p>, TExpr<'p, UniqueSym<'p>>> -{ - fn from(value: Def<'p, UniqueSym<'p>, AExpr<'p>>) -> Self { +impl<'p> From> for DefValidated<'p> { + fn from(value: DefAtomized<'p>) -> Self { match value { - Def::Fn { + DefAtomized::Fn { sym, params, typ, bdy, - } => Def::Fn { + } => DefValidated::Fn { sym, params, typ, bdy: bdy.into(), }, - Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, + DefAtomized::TypeDef { sym, def } => DefValidated::TypeDef { sym, def }, } } } -impl<'p> From> for TExpr<'p, UniqueSym<'p>> { - fn from(value: AExpr<'p>) -> Self { - match value { - AExpr::Atom { atm, .. } => atm.into(), - AExpr::Prim { op, args, typ } => TExpr::Prim { +impl<'p> From>> for Typed<'p, ExprValidated<'p>> { + fn from(value: Typed<'p, AExpr<'p>>) -> Self { + let inner = match value.inner { + AExpr::Atom { atm, .. } => return atm.into(), + AExpr::BinaryOp { op, exprs } => ExprValidated::BinaryOp { op, - args: args.into_iter().map(Into::into).collect(), - typ, + exprs: exprs.map(|a| Box::new(a.into())), + }, + AExpr::UnaryOp { op, expr } => ExprValidated::UnaryOp { + op, + expr: Box::new(expr.into()), }, - AExpr::Let { sym, bnd, bdy, typ } => TExpr::Let { + AExpr::Let { sym, bnd, bdy } => ExprValidated::Let { sym, + mutable: true, + typ: None, bnd: Box::new((*bnd).into()), bdy: Box::new((*bdy).into()), - typ, }, - AExpr::If { cnd, thn, els, typ } => TExpr::If { + AExpr::If { cnd, thn, els } => ExprValidated::If { cnd: Box::new((*cnd).into()), thn: Box::new((*thn).into()), els: Box::new((*els).into()), - typ, }, - AExpr::Apply { fun, args, typ, .. } => TExpr::Apply { + AExpr::Apply { fun, args, .. } => ExprValidated::Apply { fun: Box::new(fun.into()), args: args.into_iter().map(|(a, _)| a.into()).collect(), - typ, }, - AExpr::FunRef { sym, typ } => TExpr::Var { sym, typ }, - AExpr::Loop { bdy, typ } => TExpr::Loop { + AExpr::FunRef { sym } => ExprValidated::Var { sym }, + AExpr::Loop { bdy } => ExprValidated::Loop { bdy: Box::new((*bdy).into()), - typ, }, - AExpr::Break { bdy, typ } => TExpr::Break { + AExpr::Break { bdy } => ExprValidated::Break { bdy: Box::new((*bdy).into()), - typ, }, - AExpr::Seq { stmt, cnt, typ } => TExpr::Seq { + AExpr::Seq { stmt, cnt } => ExprValidated::Seq { stmt: Box::new((*stmt).into()), cnt: Box::new((*cnt).into()), - typ, }, - AExpr::Assign { sym, bnd, typ } => TExpr::Assign { + AExpr::Assign { sym, bnd } => ExprValidated::Assign { sym, bnd: Box::new((*bnd).into()), - typ, }, - AExpr::Continue { typ } => TExpr::Continue { typ }, - AExpr::Return { bdy, typ } => TExpr::Return { + AExpr::Continue => ExprValidated::Continue, + AExpr::Return { bdy } => ExprValidated::Return { bdy: Box::new((*bdy).into()), - typ, }, - AExpr::Struct { sym, fields, typ } => TExpr::Struct { + AExpr::Struct { sym, fields } => ExprValidated::Struct { sym, fields: fields .into_iter() .map(|(sym, atm)| (sym, atm.into())) .collect(), - typ, }, - AExpr::AccessField { strct, field, typ } => TExpr::AccessField { + AExpr::AccessField { strct, field } => ExprValidated::AccessField { strct: Box::new(strct.into()), field, - typ, }, + }; + + Meta { + inner, + meta: value.meta, } } } -// Note that casting to Never here is safe because this `From` is only used by the interpreter which doesn't care about the type information -impl<'p> From> for TExpr<'p, UniqueSym<'p>> { +// Note that casting to Never here is safe because this `From` is only used by the interpreter which doesn't care about the type information. +impl<'p> From> for Typed<'p, ExprValidated<'p>> { fn from(value: Atom<'p>) -> Self { - match value { - Atom::Val { val } => TExpr::Lit { - val, - typ: Type::Never, - }, - Atom::Var { sym } => TExpr::Var { - sym, - typ: Type::Never, + Meta { + meta: Type::Never, + inner: match value { + Atom::Val { val } => ExprValidated::Lit { val }, + Atom::Var { sym } => ExprValidated::Var { sym }, }, } } } - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::passes::uniquify::PrgUniquified; - use crate::utils::split_test::split_test; - use test_each_file::test_each_file; - - fn atomize([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let program: PrgUniquified = program - .validate() - .unwrap() - .uniquify() - .reveal() - .atomize() - .into(); - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as atomize => atomize } -} diff --git a/compiler/src/passes/atomize/tests.rs b/compiler/src/passes/atomize/tests.rs new file mode 100644 index 0000000..53b50d8 --- /dev/null +++ b/compiler/src/passes/atomize/tests.rs @@ -0,0 +1,24 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::passes::validate::PrgValidated; +use crate::utils::split_test::split_test; +use test_each_file::test_each_file; + +fn atomize([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let program: PrgValidated = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .atomize() + .into(); + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as atomize => atomize } diff --git a/compiler/src/passes/conclude/conclude.rs b/compiler/src/passes/conclude/conclude.rs index 00ad8df..90abe9e 100644 --- a/compiler/src/passes/conclude/conclude.rs +++ b/compiler/src/passes/conclude/conclude.rs @@ -1,5 +1,5 @@ use crate::passes::conclude::X86Concluded; -use crate::passes::patch_instructions::X86Patched; +use crate::passes::patch::X86Patched; use crate::utils::gen_sym::gen_sym; use crate::*; @@ -17,7 +17,7 @@ impl<'p> X86Patched<'p> { movq!(reg!(RAX), reg!(RDI)), addq!(imm!(self.stack_space as i64), reg!(RSP)), popq!(reg!(RBP)), - callq_direct!(self.std.exit, 1) + callq_direct!(self.std["exit"], 1) ), ); @@ -28,40 +28,3 @@ impl<'p> X86Patched<'p> { } } } - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::passes::select::X86Selected; - use crate::utils::split_test::split_test; - use test_each_file::test_each_file; - - fn conclude([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let expected_return = expected_return.into(); - - let program: X86Selected = program - .validate() - .unwrap() - .uniquify() - .reveal() - .atomize() - .explicate() - .eliminate() - .select() - .add_liveness() - .compute_interference() - .color_interference() - .assign_homes() - .patch() - .conclude() - .into(); - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return, "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as conclude => conclude } -} diff --git a/compiler/src/passes/conclude/mod.rs b/compiler/src/passes/conclude/mod.rs index 733e2a8..653d1c9 100644 --- a/compiler/src/passes/conclude/mod.rs +++ b/compiler/src/passes/conclude/mod.rs @@ -1,13 +1,14 @@ -use crate::passes::interference::Arg; -use crate::passes::select::io::Std; +use crate::passes::assign::Arg; +use crate::passes::select::std_lib::Std; use crate::passes::select::{Block, X86Selected}; use crate::utils::gen_sym::UniqueSym; use functor_derive::Functor; use std::collections::HashMap; pub mod conclude; +#[cfg(test)] +mod tests; -#[derive(Debug, PartialEq)] pub struct X86Concluded<'p> { pub blocks: HashMap, Block<'p, Arg>>, pub entry: UniqueSym<'p>, diff --git a/compiler/src/passes/conclude/tests.rs b/compiler/src/passes/conclude/tests.rs new file mode 100644 index 0000000..a7d9f70 --- /dev/null +++ b/compiler/src/passes/conclude/tests.rs @@ -0,0 +1,31 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::passes::select::X86Selected; +use crate::utils::split_test::split_test; +use test_each_file::test_each_file; + +fn conclude([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let program: X86Selected = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .atomize() + .explicate() + .eliminate() + .select() + .assign() + .patch() + .conclude() + .into(); + + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as conclude => conclude } diff --git a/compiler/src/passes/eliminate/eliminate.rs b/compiler/src/passes/eliminate/eliminate.rs new file mode 100644 index 0000000..c385e2b --- /dev/null +++ b/compiler/src/passes/eliminate/eliminate.rs @@ -0,0 +1,29 @@ +use crate::passes::eliminate::eliminate_params::eliminate_params; +use crate::passes::eliminate::eliminate_tail::eliminate_tail; +use crate::passes::eliminate::PrgEliminated; +use crate::passes::explicate::PrgExplicated; +use crate::utils::gen_sym::UniqueSym; +use std::collections::HashMap; + +// (Old variable name, field name) -> New variable name +pub type Ctx<'p> = HashMap<(UniqueSym<'p>, &'p str), UniqueSym<'p>>; + +impl<'p> PrgExplicated<'p> { + pub fn eliminate(self) -> PrgEliminated<'p> { + let mut ctx = Ctx::new(); + + let fn_params = eliminate_params(self.fn_params, &mut ctx, &self.defs); + + PrgEliminated { + blocks: self + .blocks + .into_iter() + .map(|(sym, tail)| (sym, eliminate_tail(tail, &mut ctx, &self.defs))) + .collect(), + fn_params, + defs: self.defs, + entry: self.entry, + std: self.std, + } + } +} diff --git a/compiler/src/passes/eliminate/eliminate_expr.rs b/compiler/src/passes/eliminate/eliminate_expr.rs new file mode 100644 index 0000000..4c2b19e --- /dev/null +++ b/compiler/src/passes/eliminate/eliminate_expr.rs @@ -0,0 +1,17 @@ +use crate::passes::eliminate::EExpr; +use crate::passes::explicate::CExpr; +use functor_derive::Functor; + +pub fn eliminate_expr(e: CExpr) -> EExpr { + match e { + CExpr::Atom { atm } => EExpr::Atom { atm }, + CExpr::BinaryOp { op, exprs } => EExpr::BinaryOp { op, exprs }, + CExpr::UnaryOp { op, expr } => EExpr::UnaryOp { op, expr }, + CExpr::Apply { fun, args } => EExpr::Apply { + fun, + args: args.fmap(|(arg, _)| arg), + }, + CExpr::FunRef { sym } => EExpr::FunRef { sym }, + CExpr::Struct { .. } | CExpr::AccessField { .. } => unreachable!(), + } +} diff --git a/compiler/src/passes/eliminate_algebraic/eliminate_params.rs b/compiler/src/passes/eliminate/eliminate_params.rs similarity index 77% rename from compiler/src/passes/eliminate_algebraic/eliminate_params.rs rename to compiler/src/passes/eliminate/eliminate_params.rs index 86a5d3b..d8ca8d6 100644 --- a/compiler/src/passes/eliminate_algebraic/eliminate_params.rs +++ b/compiler/src/passes/eliminate/eliminate_params.rs @@ -1,13 +1,13 @@ -use crate::passes::eliminate_algebraic::eliminate::Ctx; +use crate::passes::eliminate::eliminate::Ctx; use crate::passes::parse::types::Type; use crate::passes::parse::{Param, TypeDef}; -use crate::utils::gen_sym::{gen_sym, UniqueSym}; +use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; pub fn eliminate_params<'p>( fn_params: HashMap, Vec>>>, ctx: &mut Ctx<'p>, - defs: &HashMap, TypeDef<'p, UniqueSym<'p>>>, + defs: &HashMap, TypeDef, &'p str>>, ) -> HashMap, Vec>>> { fn_params .into_iter() @@ -37,19 +37,17 @@ pub fn flatten_type<'p>( sym: UniqueSym<'p>, typ: &Type>, ctx: &mut Ctx<'p>, - defs: &HashMap, TypeDef<'p, UniqueSym<'p>>>, + defs: &HashMap, TypeDef, &'p str>>, ) -> Vec<(UniqueSym<'p>, Type>)> { match typ { - Type::Int | Type::Bool | Type::Unit | Type::Never | Type::Fn { .. } => { + Type::I64 | Type::U64 | Type::Bool | Type::Unit | Type::Never | Type::Fn { .. } => { vec![(sym, typ.clone())] } Type::Var { sym: def_sym } => match &defs[&def_sym] { TypeDef::Struct { fields } => fields .iter() .flat_map(|(field_name, field_type)| { - let new_sym = *ctx - .entry((sym, field_name)) - .or_insert_with(|| gen_sym(sym.sym)); + let new_sym = *ctx.entry((sym, field_name)).or_insert_with(|| sym.fresh()); flatten_type(new_sym, field_type, ctx, defs).into_iter() }) diff --git a/compiler/src/passes/eliminate/eliminate_seq.rs b/compiler/src/passes/eliminate/eliminate_seq.rs new file mode 100644 index 0000000..14b42a9 --- /dev/null +++ b/compiler/src/passes/eliminate/eliminate_seq.rs @@ -0,0 +1,140 @@ +use crate::passes::atomize::Atom; +use crate::passes::eliminate::eliminate::Ctx; +use crate::passes::eliminate::eliminate_expr::eliminate_expr; +use crate::passes::eliminate::eliminate_params::flatten_type; +use crate::passes::eliminate::{EExpr, ETail}; +use crate::passes::explicate::CExpr; +use crate::passes::parse::types::Type; +use crate::passes::parse::{Meta, TypeDef, Typed}; +use crate::utils::gen_sym::UniqueSym; +use std::collections::HashMap; + +pub fn eliminate_seq<'p>( + sym: UniqueSym<'p>, + ctx: &mut Ctx<'p>, + bnd: Typed<'p, CExpr<'p>>, + tail: ETail<'p>, + defs: &HashMap, TypeDef, &'p str>>, +) -> ETail<'p> { + let typ = bnd.meta; + + // Changes based on RHS + let bnd = match bnd.inner { + CExpr::AccessField { strct, field } => { + let strct = strct.var(); + let new_sym = *ctx.entry((strct, field)).or_insert_with(|| sym.fresh()); + + return eliminate_seq( + sym, + ctx, + Meta { + meta: typ, + inner: CExpr::Atom { + atm: Atom::Var { sym: new_sym }, + }, + }, + tail, + defs, + ); + } + CExpr::Apply { fun, args } => { + // Flatten the arguments. This is trivial for `Val` atoms, but for `Var` atoms `flatten_type` is used. + let args = args + .into_iter() + .flat_map(|(atom, typ)| match atom { + Atom::Val { val } => vec![(Atom::Val { val }, typ)], + Atom::Var { sym } => flatten_type(sym, &typ, ctx, defs) + .into_iter() + .map(|(sym, typ)| (Atom::Var { sym }, typ)) + .collect(), + }) + .collect(); + + CExpr::Apply { fun, args } + } + inner => inner, + }; + + // Changes based on LHS + match typ { + // No changes needed + Type::I64 | Type::U64 | Type::Bool | Type::Unit | Type::Never | Type::Fn { .. } => { + ETail::Seq { + syms: vec![sym], + bnd: Meta { + meta: vec![typ], + inner: eliminate_expr(bnd), + }, + tail: Box::new(tail), + } + } + Type::Var { sym: def_sym } => match &defs[&def_sym] { + // Changes needed, since LHS is a struct + TypeDef::Struct { fields: def_fields } => match bnd { + CExpr::Atom { atm, .. } => { + def_fields.iter().fold(tail, |tail, (field, field_type)| { + let sym_lhs = *ctx.entry((sym, field)).or_insert_with(|| sym.fresh()); + let sym_rhs = *ctx + .entry((atm.var(), field)) + .or_insert_with(|| atm.var().fresh()); + + eliminate_seq( + sym_lhs, + ctx, + Meta { + meta: field_type.clone(), + inner: CExpr::Atom { + atm: Atom::Var { sym: sym_rhs }, + }, + }, + tail, + defs, + ) + }) + } + CExpr::Struct { fields, .. } => { + let field_values = fields.into_iter().collect::>(); + + def_fields.iter().fold(tail, |tail, (field, field_type)| { + let sym_lhs = *ctx.entry((sym, field)).or_insert_with(|| sym.fresh()); + let atom_rhs = field_values[field]; + + eliminate_seq( + sym_lhs, + ctx, + Meta { + meta: field_type.clone(), + inner: CExpr::Atom { atm: atom_rhs }, + }, + tail, + defs, + ) + }) + } + CExpr::Apply { fun, args } => { + let (syms, typs): (Vec<_>, Vec<_>) = + flatten_type(sym, &typ, ctx, defs).into_iter().unzip(); + + ETail::Seq { + syms, + bnd: Meta { + meta: typs, + inner: EExpr::Apply { + fun, + args: args.into_iter().map(|(atm, _)| atm).collect(), + }, + }, + tail: Box::new(tail), + } + } + CExpr::BinaryOp { .. } + | CExpr::UnaryOp { .. } + | CExpr::FunRef { .. } + | CExpr::AccessField { .. } => { + unreachable!() + } + }, + TypeDef::Enum { .. } => todo!(), + }, + } +} diff --git a/compiler/src/passes/eliminate/eliminate_tail.rs b/compiler/src/passes/eliminate/eliminate_tail.rs new file mode 100644 index 0000000..5045383 --- /dev/null +++ b/compiler/src/passes/eliminate/eliminate_tail.rs @@ -0,0 +1,39 @@ +use crate::passes::atomize::Atom; +use crate::passes::eliminate::eliminate::Ctx; +use crate::passes::eliminate::eliminate_expr::eliminate_expr; +use crate::passes::eliminate::eliminate_params::flatten_type; +use crate::passes::eliminate::eliminate_seq::eliminate_seq; +use crate::passes::eliminate::ETail; +use crate::passes::explicate::CTail; +use crate::passes::parse::TypeDef; +use crate::utils::gen_sym::UniqueSym; +use functor_derive::Functor; +use std::collections::HashMap; + +pub fn eliminate_tail<'p>( + tail: CTail<'p>, + ctx: &mut Ctx<'p>, + defs: &HashMap, TypeDef, &'p str>>, +) -> ETail<'p> { + match tail { + CTail::Return { expr } => match expr.inner { + Atom::Val { val } => ETail::Return { + exprs: vec![(Atom::Val { val })], + }, + Atom::Var { sym } => ETail::Return { + exprs: flatten_type(sym, &expr.meta, ctx, defs) + .fmap(|(sym, _)| (Atom::Var { sym })), + }, + }, + CTail::Seq { sym, bnd, tail } => { + let tail = eliminate_tail(*tail, ctx, defs); + eliminate_seq(sym, ctx, bnd, tail, defs) + } + CTail::IfStmt { cnd, thn, els } => ETail::IfStmt { + cnd: eliminate_expr(cnd), + thn, + els, + }, + CTail::Goto { lbl } => ETail::Goto { lbl }, + } +} diff --git a/compiler/src/passes/eliminate/interpreter.rs b/compiler/src/passes/eliminate/interpreter.rs new file mode 100644 index 0000000..b4d79e4 --- /dev/null +++ b/compiler/src/passes/eliminate/interpreter.rs @@ -0,0 +1,174 @@ +use crate::interpreter::Val; +use crate::interpreter::IO; +use crate::passes::atomize::Atom; +use crate::passes::eliminate::{EExpr, ETail, PrgEliminated}; +use crate::passes::parse::{BinaryOp, UnaryOp}; +use crate::passes::validate::TLit; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; + +impl<'p> PrgEliminated<'p> { + pub fn interpret(&self, io: &mut impl IO) -> Vec> { + let std_iter = self + .std + .iter() + .map(|(_, &def)| (def, Val::StdlibFunction { sym: def.sym })); + + self.interpret_tail( + &self.blocks[&self.entry], + &mut PushMap::from_iter(std_iter), + io, + ) + } + + fn interpret_tail( + &self, + tail: &ETail<'p>, + scope: &mut PushMap, Val<'p>>, + io: &mut impl IO, + ) -> Vec> { + match tail { + ETail::Return { exprs: expr } => expr + .iter() + .map(|atm| self.interpret_atom(atm, scope)) + .collect(), + ETail::Seq { syms, bnd, tail } => { + let bnds = syms + .iter() + .cloned() + .zip(self.interpret_expr(&bnd.inner, scope, io)); + scope.push_iter(bnds, |scope| self.interpret_tail(tail, scope, io)) + } + ETail::IfStmt { cnd, thn, els } => { + if self.interpret_expr(cnd, scope, io)[0].bool() { + self.interpret_tail(&self.blocks[thn], scope, io) + } else { + self.interpret_tail(&self.blocks[els], scope, io) + } + } + ETail::Goto { lbl } => self.interpret_tail(&self.blocks[lbl], scope, io), + } + } + + pub fn interpret_expr( + &self, + expr: &EExpr<'p>, + scope: &mut PushMap, Val<'p>>, + io: &mut impl IO, + ) -> Vec> { + let val = match expr { + EExpr::BinaryOp { + op, + exprs: [lhs, rhs], + } => { + let lhs = self.interpret_atom(lhs, scope); + let rhs = self.interpret_atom(rhs, scope); + match op { + BinaryOp::Add => Val::Int { + val: lhs.int() + rhs.int(), + }, + BinaryOp::Sub => Val::Int { + val: lhs.int() - rhs.int(), + }, + BinaryOp::Mul => Val::Int { + val: lhs.int() * rhs.int(), + }, + BinaryOp::Div => Val::Int { + val: lhs.int() / rhs.int(), + }, + BinaryOp::Mod => Val::Int { + val: lhs.int() % rhs.int(), + }, + BinaryOp::Xor => Val::Bool { + val: lhs.bool() ^ rhs.bool(), + }, + BinaryOp::GT => Val::Bool { + val: lhs.int() > rhs.int(), + }, + BinaryOp::GE => Val::Bool { + val: lhs.int() >= rhs.int(), + }, + BinaryOp::EQ => Val::Bool { val: lhs == rhs }, + BinaryOp::LE => Val::Bool { + val: lhs.int() <= rhs.int(), + }, + BinaryOp::LT => Val::Bool { + val: lhs.int() < rhs.int(), + }, + BinaryOp::NE => Val::Bool { val: lhs != rhs }, + BinaryOp::LAnd => Val::Bool { + val: lhs.bool() && rhs.bool(), + }, + BinaryOp::LOr => Val::Bool { + val: lhs.bool() || rhs.bool(), + }, + } + } + EExpr::UnaryOp { op, expr } => { + let expr = self.interpret_atom(expr, scope); + match op { + UnaryOp::Neg => Val::Int { val: -expr.int() }, + UnaryOp::Not => Val::Bool { val: !expr.bool() }, + } + } + EExpr::Atom { atm, .. } => self.interpret_atom(atm, scope), + EExpr::FunRef { sym, .. } => { + if self.std.contains_key(sym.sym) { + Val::StdlibFunction { sym: sym.sym } + } else { + Val::Function { sym: *sym } + } + } + EExpr::Apply { fun, args, .. } => { + let fun = self.interpret_atom(fun, scope); + + let mut fn_args = Vec::new(); + for atm in args { + fn_args.push(self.interpret_atom(atm, scope)); + } + + match fun { + Val::StdlibFunction { sym } => { + match sym { + "exit" => { + unreachable!("Validated programs should not have an explicit call to exit yet.") + } + "print" => { + let val = fn_args[0].clone(); + io.print(TLit::I64 { val: val.int() }); + val + } + "read" => io.read().into(), + unknown => unreachable!( + "Encountered an undefined standard library function '{unknown}'" + ), + } + } + Val::Function { sym } => { + let args = self.fn_params[&sym] + .iter() + .zip(fn_args) + .map(|(param, val)| (param.sym, val)); + return scope.push_iter(args, |scope| { + self.interpret_tail(&self.blocks[&sym], scope, io) + }); + } + _ => unreachable!("The symbol did not refer to a function."), + } + } + }; + vec![val] + } + + #[must_use] + pub fn interpret_atom( + &self, + atom: &Atom<'p>, + scope: &PushMap, Val<'p>>, + ) -> Val<'p> { + match atom { + Atom::Val { val } => (*val).into(), + Atom::Var { sym } => scope[sym].clone(), + } + } +} diff --git a/compiler/src/passes/eliminate/mod.rs b/compiler/src/passes/eliminate/mod.rs new file mode 100644 index 0000000..df12a3a --- /dev/null +++ b/compiler/src/passes/eliminate/mod.rs @@ -0,0 +1,50 @@ +pub mod eliminate; +mod eliminate_expr; +mod eliminate_params; +mod eliminate_seq; +mod eliminate_tail; +mod interpreter; +#[cfg(test)] +mod tests; + +use crate::passes::atomize::Atom; +use crate::passes::parse::types::Type; +use crate::passes::parse::{BinaryOp, Meta, Param, TypeDef, UnaryOp}; +use crate::passes::select::std_lib::Std; +use crate::utils::gen_sym::UniqueSym; +use std::collections::HashMap; + +pub struct PrgEliminated<'p> { + pub blocks: HashMap, ETail<'p>>, + pub fn_params: HashMap, Vec>>>, + pub defs: HashMap, TypeDef, &'p str>>, + pub entry: UniqueSym<'p>, + pub std: Std<'p>, +} + +pub enum ETail<'p> { + Return { + exprs: Vec>, + }, + Seq { + syms: Vec>, + bnd: Meta>>, EExpr<'p>>, + tail: Box>, + }, + IfStmt { + cnd: EExpr<'p>, + thn: UniqueSym<'p>, + els: UniqueSym<'p>, + }, + Goto { + lbl: UniqueSym<'p>, + }, +} + +pub enum EExpr<'p> { + Atom { atm: Atom<'p> }, + BinaryOp { op: BinaryOp, exprs: [Atom<'p>; 2] }, + UnaryOp { op: UnaryOp, expr: Atom<'p> }, + Apply { fun: Atom<'p>, args: Vec> }, + FunRef { sym: UniqueSym<'p> }, +} diff --git a/compiler/src/passes/eliminate/tests.rs b/compiler/src/passes/eliminate/tests.rs new file mode 100644 index 0000000..3134e49 --- /dev/null +++ b/compiler/src/passes/eliminate/tests.rs @@ -0,0 +1,25 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::utils::split_test::split_test; +use test_each_file::test_each_file; + +fn eliminate([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let program = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .atomize() + .explicate() + .eliminate(); + + let mut io = TestIO::new(input); + let result = program.interpret(&mut io)[0].clone(); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as eliminate => eliminate } diff --git a/compiler/src/passes/eliminate_algebraic/eliminate.rs b/compiler/src/passes/eliminate_algebraic/eliminate.rs deleted file mode 100644 index a7fc2e5..0000000 --- a/compiler/src/passes/eliminate_algebraic/eliminate.rs +++ /dev/null @@ -1,190 +0,0 @@ -use crate::passes::atomize::Atom; -use crate::passes::eliminate_algebraic::eliminate_params::{eliminate_params, flatten_type}; -use crate::passes::eliminate_algebraic::{EExpr, ETail, PrgEliminated}; -use crate::passes::explicate::{CExpr, CTail, PrgExplicated}; -use crate::passes::parse::types::Type; -use crate::passes::parse::TypeDef; -use crate::utils::gen_sym::{gen_sym, UniqueSym}; -use functor_derive::Functor; -use std::collections::HashMap; - -// (Old variable name, field name) -> New variable name -pub type Ctx<'p> = HashMap<(UniqueSym<'p>, &'p str), UniqueSym<'p>>; - -impl<'p> PrgExplicated<'p> { - pub fn eliminate(self) -> PrgEliminated<'p> { - let mut ctx = Ctx::new(); - - let fn_params = eliminate_params(self.fn_params, &mut ctx, &self.defs); - - PrgEliminated { - blocks: self - .blocks - .into_iter() - .map(|(sym, tail)| (sym, eliminate_tail(tail, &mut ctx, &self.defs))) - .collect(), - fn_params, - defs: self.defs, - entry: self.entry, - } - } -} - -fn eliminate_tail<'p>( - tail: CTail<'p>, - ctx: &mut Ctx<'p>, - defs: &HashMap, TypeDef<'p, UniqueSym<'p>>>, -) -> ETail<'p> { - match tail { - CTail::Return { expr, typ } => match expr { - Atom::Val { val } => ETail::Return { - exprs: vec![(Atom::Val { val }, typ)], - }, - Atom::Var { sym } => { - // dbg!(&sym, &typ); - // dbg!(flatten_type(sym, &typ, ctx, defs)); - - ETail::Return { - // exprs: vec![(Atom::Var { sym }, typ)] - exprs: flatten_type(sym, &typ, ctx, defs) - .fmap(|(sym, typ)| (Atom::Var { sym }, typ)), - } - } - }, - CTail::Seq { sym, bnd, tail } => { - let tail = eliminate_tail(*tail, ctx, defs); - eliminate_seq(sym, ctx, bnd, tail, defs) - } - CTail::IfStmt { cnd, thn, els } => ETail::IfStmt { - cnd: map_expr(cnd), - thn, - els, - }, - CTail::Goto { lbl } => ETail::Goto { lbl }, - } -} - -fn eliminate_seq<'p>( - sym: UniqueSym<'p>, - ctx: &mut Ctx<'p>, - bnd: CExpr<'p>, - tail: ETail<'p>, - defs: &HashMap, TypeDef<'p, UniqueSym<'p>>>, -) -> ETail<'p> { - let bnd = match bnd { - CExpr::AccessField { - strct, - field, - typ: field_typ, - } => { - let strct = strct.var(); - let new_sym = *ctx - .entry((strct, field)) - .or_insert_with(|| gen_sym(sym.sym)); - - return eliminate_seq( - sym, - ctx, - CExpr::Atom { - atm: Atom::Var { sym: new_sym }, - typ: field_typ, - }, - tail, - defs, - ); - } - CExpr::Apply { fun, args, typ } => { - let args = args - .into_iter() - .flat_map(|(atom, typ)| match atom { - Atom::Val { val } => vec![(Atom::Val { val }, typ)], - Atom::Var { sym } => flatten_type(sym, &typ, ctx, defs) - .into_iter() - .map(|(sym, typ)| (Atom::Var { sym }, typ)) - .collect(), - }) - .collect(); - - CExpr::Apply { fun, args, typ } - } - _ => bnd, - }; - - match bnd.typ() { - Type::Int | Type::Bool | Type::Unit | Type::Never | Type::Fn { .. } => ETail::Seq { - syms: vec![sym], - bnd: map_expr(bnd), - tail: Box::new(tail), - }, - Type::Var { sym: def_sym } => match &defs[&def_sym] { - TypeDef::Struct { fields: def_fields } => match bnd { - CExpr::Atom { atm, .. } => { - def_fields.iter().fold(tail, |tail, (field, field_type)| { - let sym_lhs = *ctx.entry((sym, field)).or_insert_with(|| gen_sym(sym.sym)); - let sym_rhs = *ctx - .entry((atm.var(), field)) - .or_insert_with(|| gen_sym(atm.var().sym)); - - eliminate_seq( - sym_lhs, - ctx, - CExpr::Atom { - atm: Atom::Var { sym: sym_rhs }, - typ: field_type.clone(), - }, - tail, - defs, - ) - }) - } - CExpr::Struct { fields, .. } => { - let field_values = fields.into_iter().collect::>(); - - def_fields.iter().fold(tail, |tail, (field, field_type)| { - let sym_lhs = *ctx.entry((sym, field)).or_insert_with(|| gen_sym(sym.sym)); - let sym_rhs = field_values[field]; - - eliminate_seq( - sym_lhs, - ctx, - CExpr::Atom { - atm: sym_rhs, - typ: field_type.clone(), - }, - tail, - defs, - ) - }) - } - CExpr::Apply { fun, args, typ } => { - let (syms, typs): (Vec<_>, Vec<_>) = - flatten_type(sym, &typ, ctx, defs).into_iter().unzip(); - - ETail::Seq { - syms, - bnd: EExpr::Apply { fun, args, typs }, - tail: Box::new(tail), - } - } - CExpr::Prim { .. } | CExpr::FunRef { .. } | CExpr::AccessField { .. } => { - unreachable!() - } - }, - TypeDef::Enum { .. } => todo!(), - }, - } -} - -fn map_expr(e: CExpr) -> EExpr { - match e { - CExpr::Atom { atm, typ } => EExpr::Atom { atm, typ }, - CExpr::Prim { op, args, typ } => EExpr::Prim { op, args, typ }, - CExpr::Apply { fun, args, typ } => EExpr::Apply { - fun, - args, - typs: vec![typ], - }, - CExpr::FunRef { sym, typ } => EExpr::FunRef { sym, typ }, - CExpr::Struct { .. } | CExpr::AccessField { .. } => unreachable!(), - } -} diff --git a/compiler/src/passes/eliminate_algebraic/interpret.rs b/compiler/src/passes/eliminate_algebraic/interpret.rs deleted file mode 100644 index b828e97..0000000 --- a/compiler/src/passes/eliminate_algebraic/interpret.rs +++ /dev/null @@ -1,232 +0,0 @@ -use crate::interpreter::Val; -use crate::interpreter::IO; -use crate::passes::atomize::Atom; -use crate::passes::eliminate_algebraic::{EExpr, ETail, PrgEliminated}; -use crate::passes::parse::Op; -use crate::passes::validate::TLit; -use crate::utils::gen_sym::UniqueSym; -use crate::utils::push_map::PushMap; -use derive_more::Display; - -#[derive(Eq, PartialEq, Copy, Clone, Debug, Display)] -pub enum EVal<'p> { - #[display(fmt = "{val}")] - Int { val: i64 }, - #[display(fmt = "{}", r#"if *val { "true" } else { "false" }"#)] - Bool { val: bool }, - #[display(fmt = "unit")] - Unit, - #[display(fmt = "fn pointer `{sym}`")] - Function { sym: UniqueSym<'p> }, -} - -impl<'p> EVal<'p> { - pub fn int(&self) -> i64 { - match self { - Self::Int { val } => *val, - _ => panic!(), - } - } - - pub fn bool(&self) -> bool { - match self { - Self::Bool { val } => *val, - _ => panic!(), - } - } - - pub fn fun(&self) -> UniqueSym<'p> { - match self { - Self::Function { sym } => *sym, - _ => panic!(), - } - } -} - -impl<'p> From> for Val<'p, UniqueSym<'p>> { - fn from(value: EVal<'p>) -> Self { - match value { - EVal::Int { val } => Val::Int { val }, - EVal::Bool { val } => Val::Bool { val }, - EVal::Unit => Val::Unit, - EVal::Function { sym } => Val::Function { sym }, - } - } -} - -impl<'p> From for EVal<'p> { - fn from(value: TLit) -> Self { - match value { - TLit::Int { val } => Self::Int { val: val as i64 }, - TLit::Bool { val } => Self::Bool { val }, - TLit::Unit => Self::Unit, - } - } -} - -impl<'p> PrgEliminated<'p> { - pub fn interpret(&self, io: &mut impl IO) -> Vec> { - self.interpret_tail(&self.blocks[&self.entry], &mut PushMap::default(), io) - } - - fn interpret_tail( - &self, - tail: &ETail<'p>, - scope: &mut PushMap, EVal<'p>>, - io: &mut impl IO, - ) -> Vec> { - match tail { - ETail::Return { exprs: expr } => expr - .iter() - .map(|(atm, _)| self.interpret_atom(atm, scope)) - .collect(), - ETail::Seq { syms, bnd, tail } => { - let bnds = syms - .iter() - .cloned() - .zip(self.interpret_expr(bnd, scope, io)); - scope.push_iter(bnds, |scope| self.interpret_tail(tail, scope, io)) - } - ETail::IfStmt { cnd, thn, els } => { - if self.interpret_expr(cnd, scope, io)[0].bool() { - self.interpret_tail(&self.blocks[thn], scope, io) - } else { - self.interpret_tail(&self.blocks[els], scope, io) - } - } - ETail::Goto { lbl } => self.interpret_tail(&self.blocks[lbl], scope, io), - } - } - - pub fn interpret_expr( - &self, - expr: &EExpr<'p>, - scope: &mut PushMap, EVal<'p>>, - io: &mut impl IO, - ) -> Vec> { - let val = match expr { - EExpr::Prim { op, args, .. } => match (op, args.as_slice()) { - (Op::Read, []) => io.read().into(), - (Op::Print, [v]) => { - let val = self.interpret_atom(v, scope); - io.print(TLit::Int { - val: val.int() as i32, - }); - val - } - (Op::Plus, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Int { val: e1 + e2 } - } - (Op::Minus, [e1]) => { - let e1 = self.interpret_atom(e1, scope).int(); - EVal::Int { val: -e1 } - } - (Op::Minus, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Int { val: e1 - e2 } - } - (Op::Mul, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Int { val: e1 * e2 } - } - (Op::Div, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Int { val: e1 / e2 } - } - (Op::Mod, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Int { val: e1 % e2 } - } - (Op::GT, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Bool { val: e1 > e2 } - } - (Op::GE, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Bool { val: e1 >= e2 } - } - (Op::LT, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Bool { val: e1 < e2 } - } - (Op::LE, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - EVal::Bool { val: e1 <= e2 } - } - (Op::EQ, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope); - let e2 = self.interpret_atom(e2, scope); - EVal::Bool { val: e1 == e2 } - } - (Op::NE, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope); - let e2 = self.interpret_atom(e2, scope); - EVal::Bool { val: e1 != e2 } - } - (Op::Not, [e1]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - EVal::Bool { val: !e1 } - } - (Op::LAnd, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - if !e1 { - EVal::Bool { val: false } - } else { - self.interpret_atom(e2, scope) - } - } - (Op::LOr, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - if e1 { - EVal::Bool { val: true } - } else { - self.interpret_atom(e2, scope) - } - } - (Op::Xor, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - let e2 = self.interpret_atom(e2, scope).bool(); - EVal::Bool { val: e1 ^ e2 } - } - _ => unreachable!(), - }, - EExpr::Atom { atm, .. } => self.interpret_atom(atm, scope), - EExpr::FunRef { sym, .. } => EVal::Function { sym: *sym }, - EExpr::Apply { fun, args, .. } => { - let fn_sym = self.interpret_atom(fun, scope).fun(); - let args = self.fn_params[&fn_sym] - .iter() - .zip(args.iter()) - .map(|(param, (atm, _))| (param.sym, self.interpret_atom(atm, scope))) - .collect::>(); - - return scope.push_iter(args.into_iter(), |scope| { - self.interpret_tail(&self.blocks[&fn_sym], scope, io) - }); - } - }; - vec![val] - } - - #[must_use] - pub fn interpret_atom( - &self, - atom: &Atom<'p>, - scope: &PushMap, EVal<'p>>, - ) -> EVal<'p> { - match atom { - Atom::Val { val } => (*val).into(), - Atom::Var { sym } => scope[sym], - } - } -} diff --git a/compiler/src/passes/eliminate_algebraic/mod.rs b/compiler/src/passes/eliminate_algebraic/mod.rs deleted file mode 100644 index 1290eed..0000000 --- a/compiler/src/passes/eliminate_algebraic/mod.rs +++ /dev/null @@ -1,87 +0,0 @@ -pub mod eliminate; -mod eliminate_params; -mod interpret; - -use crate::passes::atomize::Atom; -use crate::passes::parse::types::Type; -use crate::passes::parse::{Op, Param, TypeDef}; -use crate::utils::gen_sym::UniqueSym; -use std::collections::HashMap; - -#[derive(Debug, PartialEq)] -pub struct PrgEliminated<'p> { - pub blocks: HashMap, ETail<'p>>, - pub fn_params: HashMap, Vec>>>, - pub defs: HashMap, TypeDef<'p, UniqueSym<'p>>>, - pub entry: UniqueSym<'p>, -} - -#[derive(Debug, PartialEq)] -pub enum ETail<'p> { - Return { - exprs: Vec<(Atom<'p>, Type>)>, - }, - Seq { - syms: Vec>, - bnd: EExpr<'p>, - tail: Box>, - }, - IfStmt { - cnd: EExpr<'p>, - thn: UniqueSym<'p>, - els: UniqueSym<'p>, - }, - Goto { - lbl: UniqueSym<'p>, - }, -} - -#[derive(Clone, Debug, PartialEq)] -pub enum EExpr<'p> { - Atom { - atm: Atom<'p>, - typ: Type>, - }, - Prim { - op: Op, - args: Vec>, - typ: Type>, - }, - Apply { - fun: Atom<'p>, - args: Vec<(Atom<'p>, Type>)>, - typs: Vec>>, - }, - FunRef { - sym: UniqueSym<'p>, - typ: Type>, - }, -} - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::utils::split_test::split_test; - use test_each_file::test_each_file; - - fn eliminated([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let program = program - .validate() - .unwrap() - .uniquify() - .reveal() - .atomize() - .explicate() - .eliminate(); - - let mut io = TestIO::new(input); - - let result = program.interpret(&mut io)[0]; - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as eliminate_algebraic => eliminated } -} diff --git a/compiler/src/passes/emit/binary.rs b/compiler/src/passes/emit/binary.rs index 139dab3..9cdff8f 100644 --- a/compiler/src/passes/emit/binary.rs +++ b/compiler/src/passes/emit/binary.rs @@ -1,5 +1,5 @@ +use crate::passes::assign::Arg; use crate::passes::emit::encode_reg; -use crate::passes::interference::Arg; use crate::passes::select::Reg; pub struct BinaryOpInfo { diff --git a/compiler/src/passes/emit/mod.rs b/compiler/src/passes/emit/mod.rs index 7132312..2e79024 100644 --- a/compiler/src/passes/emit/mod.rs +++ b/compiler/src/passes/emit/mod.rs @@ -6,6 +6,7 @@ mod special; mod unary; use crate::imm; +use crate::passes::assign::Arg; use crate::passes::conclude::X86Concluded; use crate::passes::emit::binary::{ encode_binary_instr, ADDQ_INFO, ANDQ_INFO, CMPQ_INFO, MOVQ_INFO, ORQ_INFO, SUBQ_INFO, XORQ_INFO, @@ -15,7 +16,6 @@ use crate::passes::emit::mul_div::{encode_muldiv_instr, MulDivOpInfo}; use crate::passes::emit::push_pop::{encode_push_pop, POPQ_INFO, PUSHQ_INFO}; use crate::passes::emit::special::encode_setcc; use crate::passes::emit::unary::{encode_unary_instr, CALLQ_INDIRECT_INFO, NEGQ_INFO}; -use crate::passes::interference::Arg; use crate::passes::select::{Block, Cnd, Instr, Reg}; use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; diff --git a/compiler/src/passes/emit/mul_div.rs b/compiler/src/passes/emit/mul_div.rs index 5702cb1..61639bf 100644 --- a/compiler/src/passes/emit/mul_div.rs +++ b/compiler/src/passes/emit/mul_div.rs @@ -1,5 +1,5 @@ +use crate::passes::assign::Arg; use crate::passes::emit::encode_reg; -use crate::passes::interference::Arg; pub struct MulDivOpInfo { pub op: u8, diff --git a/compiler/src/passes/emit/push_pop.rs b/compiler/src/passes/emit/push_pop.rs index 391ed80..3000f1d 100644 --- a/compiler/src/passes/emit/push_pop.rs +++ b/compiler/src/passes/emit/push_pop.rs @@ -1,5 +1,5 @@ +use crate::passes::assign::Arg; use crate::passes::emit; -use crate::passes::interference::Arg; pub struct PushPopInfo { pub op_reg: u8, diff --git a/compiler/src/passes/emit/unary.rs b/compiler/src/passes/emit/unary.rs index 134b5e0..68857d6 100644 --- a/compiler/src/passes/emit/unary.rs +++ b/compiler/src/passes/emit/unary.rs @@ -1,5 +1,5 @@ +use crate::passes::assign::Arg; use crate::passes::emit; -use crate::passes::interference::Arg; use crate::passes::select::Reg; pub struct UnaryOpInfo { diff --git a/compiler/src/passes/explicate/explicate.rs b/compiler/src/passes/explicate/explicate.rs index db97725..d07536d 100644 --- a/compiler/src/passes/explicate/explicate.rs +++ b/compiler/src/passes/explicate/explicate.rs @@ -1,17 +1,16 @@ -use crate::passes::atomize::{AExpr, Atom, PrgAtomized}; -use crate::passes::explicate::{CExpr, CTail, PrgExplicated}; -use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Op, TypeDef}; -use crate::passes::validate::TLit; -use crate::utils::gen_sym::{gen_sym, UniqueSym}; +use crate::passes::atomize::PrgAtomized; +use crate::passes::explicate::explicate_def::explicate_def; +use crate::passes::explicate::{CTail, PrgExplicated}; +use crate::passes::parse::Def; +use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; -struct Env<'a, 'p> { - blocks: &'a mut HashMap, CTail<'p>>, +pub struct Env<'a, 'p> { + pub blocks: &'a mut HashMap, CTail<'p>>, /// (block to jump to, variable to write to) - break_target: Option<(UniqueSym<'p>, UniqueSym<'p>)>, + pub break_target: Option<(UniqueSym<'p>, UniqueSym<'p>)>, /// block to jump to - continue_target: Option>, + pub continue_target: Option>, } impl<'p> PrgAtomized<'p> { @@ -48,350 +47,7 @@ impl<'p> PrgAtomized<'p> { fn_params, defs, entry: self.entry, + std: self.std, } } } - -fn explicate_def<'p>( - def: Def<'p, UniqueSym<'p>, AExpr<'p>>, - env: &mut Env<'_, 'p>, - defs: &mut HashMap, TypeDef<'p, UniqueSym<'p>>>, -) { - match def { - Def::Fn { sym, bdy, .. } => { - let tail = explicate_tail(bdy, env); - env.blocks.insert(sym, tail); - } - Def::TypeDef { sym, def } => { - defs.insert(sym, def); - } - } -} - -fn explicate_tail<'p>(expr: AExpr<'p>, env: &mut Env<'_, 'p>) -> CTail<'p> { - let tmp = gen_sym("return"); - let tail = CTail::Return { - expr: Atom::Var { sym: tmp }, - typ: expr.typ().clone(), - }; - explicate_assign(tmp, expr, tail, env) -} - -fn explicate_assign<'p>( - sym: UniqueSym<'p>, - bnd: AExpr<'p>, - tail: CTail<'p>, - env: &mut Env<'_, 'p>, -) -> CTail<'p> { - let mut create_block = |goto: CTail<'p>| { - let sym = gen_sym("tmp"); - env.blocks.insert(sym, goto); - sym - }; - - match bnd { - AExpr::Apply { fun, args, typ } => CTail::Seq { - sym, - bnd: CExpr::Apply { fun, args, typ }, - tail: Box::new(tail), - }, - AExpr::FunRef { sym: sym_fn, typ } => CTail::Seq { - sym, - bnd: CExpr::FunRef { sym: sym_fn, typ }, - tail: Box::new(tail), - }, - AExpr::Atom { atm, typ } => CTail::Seq { - sym, - bnd: CExpr::Atom { atm, typ }, - tail: Box::new(tail), - }, - AExpr::Prim { op, args, typ } => CTail::Seq { - sym, - bnd: CExpr::Prim { op, args, typ }, - tail: Box::new(tail), - }, - AExpr::Let { - sym: sym_, - bnd: bnd_, - bdy: bdy_, - .. - } => explicate_assign(sym_, *bnd_, explicate_assign(sym, *bdy_, tail, env), env), - AExpr::If { cnd, thn, els, .. } => { - let tb = create_block(tail); - explicate_pred( - *cnd, - explicate_assign(sym, *thn, CTail::Goto { lbl: tb }, env), - explicate_assign(sym, *els, CTail::Goto { lbl: tb }, env), - env, - ) - } - AExpr::Loop { bdy, .. } => { - let loop_block_sym = gen_sym("tmp"); - let tail = create_block(tail); - let mut env = Env { - blocks: env.blocks, - break_target: Some((tail, sym)), - continue_target: Some(loop_block_sym), - }; - - let loop_block = explicate_assign( - gen_sym("ignore"), - *bdy, - CTail::Goto { - lbl: loop_block_sym, - }, - &mut env, - ); - env.blocks.insert(loop_block_sym, loop_block); - CTail::Goto { - lbl: loop_block_sym, - } - } - AExpr::Break { bdy, .. } => { - let (break_sym, break_var) = env.break_target.unwrap(); - let break_goto = CTail::Goto { lbl: break_sym }; - explicate_assign(break_var, *bdy, break_goto, env) - } - AExpr::Seq { stmt, cnt, .. } => explicate_assign( - gen_sym("ignore"), - *stmt, - explicate_assign(sym, *cnt, tail, env), - env, - ), - AExpr::Assign { - sym: sym_, - bnd: bnd_, - .. - } => explicate_assign( - sym_, - *bnd_, - explicate_assign( - sym, - AExpr::Atom { - atm: Atom::Val { val: TLit::Unit }, - typ: Type::Unit, - }, - tail, - env, - ), - env, - ), - AExpr::Continue { .. } => CTail::Goto { - lbl: env.continue_target.unwrap(), - }, - AExpr::Return { bdy, .. } => { - let tmp = gen_sym("return"); - let tail = CTail::Return { - expr: Atom::Var { sym: tmp }, - typ: bdy.typ().clone(), - }; - explicate_assign(tmp, *bdy, tail, env) - } - AExpr::Struct { - sym: sym_, - fields, - typ, - } => CTail::Seq { - sym, - bnd: CExpr::Struct { - sym: sym_, - fields, - typ, - }, - tail: Box::new(tail), - }, - AExpr::AccessField { strct, field, typ } => CTail::Seq { - sym, - bnd: CExpr::AccessField { strct, field, typ }, - tail: Box::new(tail), - }, - } -} - -fn explicate_pred<'p>( - cnd: AExpr<'p>, - thn: CTail<'p>, - els: CTail<'p>, - env: &mut Env<'_, 'p>, -) -> CTail<'p> { - let mut create_block = |goto: CTail<'p>| { - let sym = gen_sym("tmp"); - env.blocks.insert(sym, goto); - sym - }; - - match cnd { - AExpr::Atom { - atm: Atom::Var { sym }, - .. - } => CTail::IfStmt { - cnd: CExpr::Prim { - op: Op::EQ, - args: vec![ - Atom::Var { sym }, - Atom::Val { - val: TLit::Bool { val: true }, - }, - ], - typ: Type::Bool, - }, - thn: create_block(thn), - els: create_block(els), - }, - AExpr::Atom { - atm: Atom::Val { - val: TLit::Bool { val }, - }, - .. - } => { - if val { - thn - } else { - els - } - } - AExpr::Prim { op, args, .. } => match op { - Op::Not => explicate_pred( - AExpr::Atom { - atm: args[0], - typ: Type::Bool, - }, - els, - thn, - env, - ), - Op::EQ | Op::NE | Op::GT | Op::GE | Op::LT | Op::LE => CTail::IfStmt { - cnd: CExpr::Prim { - op, - args, - typ: Type::Bool, - }, - thn: create_block(thn), - els: create_block(els), - }, - Op::LAnd | Op::LOr | Op::Xor => { - let tmp = gen_sym("tmp"); - explicate_assign( - tmp, - AExpr::Prim { - op, - args, - typ: Type::Bool, - }, - explicate_pred( - AExpr::Atom { - atm: Atom::Var { sym: tmp }, - typ: Type::Bool, - }, - thn, - els, - env, - ), - env, - ) - } - Op::Read | Op::Print | Op::Plus | Op::Minus | Op::Mul | Op::Mod | Op::Div => { - unreachable!() - } - }, - AExpr::Let { sym, bnd, bdy, .. } => { - explicate_assign(sym, *bnd, explicate_pred(*bdy, thn, els, env), env) - } - AExpr::If { - cnd: cnd_sub, - thn: thn_sub, - els: els_sub, - .. - } => { - let thn = create_block(thn); - let els = create_block(els); - - explicate_pred( - *cnd_sub, - explicate_pred( - *thn_sub, - CTail::Goto { lbl: thn }, - CTail::Goto { lbl: els }, - env, - ), - explicate_pred( - *els_sub, - CTail::Goto { lbl: thn }, - CTail::Goto { lbl: els }, - env, - ), - env, - ) - } - AExpr::Apply { fun, args, .. } => { - let tmp = gen_sym("tmp"); - explicate_assign( - tmp, - AExpr::Apply { - fun, - args, - typ: Type::Bool, - }, - explicate_pred( - AExpr::Atom { - atm: Atom::Var { sym: tmp }, - typ: Type::Bool, - }, - thn, - els, - env, - ), - env, - ) - } - AExpr::Loop { .. } => { - let tmp = gen_sym("tmp"); - let cnd_ = AExpr::Atom { - atm: Atom::Var { sym: tmp }, - typ: Type::Bool, - }; - explicate_assign(tmp, cnd, explicate_pred(cnd_, thn, els, env), env) - } - AExpr::Seq { stmt, cnt, .. } => explicate_assign( - gen_sym("ignore"), - *stmt, - explicate_pred(*cnt, thn, els, env), - env, - ), - AExpr::AccessField { strct, field, .. } => { - let tmp = gen_sym("tmp"); - explicate_assign( - tmp, - AExpr::AccessField { - strct, - field, - typ: Type::Bool, - }, - explicate_pred( - AExpr::Atom { - atm: Atom::Var { sym: tmp }, - typ: Type::Bool, - }, - thn, - els, - env, - ), - env, - ) - } - - // cargo format should get some help - AExpr::FunRef { .. } - | AExpr::Atom { - atm: Atom::Val { - val: TLit::Int { .. } | TLit::Unit, - }, - .. - } - | AExpr::Assign { .. } - | AExpr::Break { .. } - | AExpr::Continue { .. } - | AExpr::Return { .. } - | AExpr::Struct { .. } => unreachable!(), - } -} diff --git a/compiler/src/passes/explicate/explicate_assign.rs b/compiler/src/passes/explicate/explicate_assign.rs new file mode 100644 index 0000000..495e046 --- /dev/null +++ b/compiler/src/passes/explicate/explicate_assign.rs @@ -0,0 +1,160 @@ +use crate::passes::atomize::{AExpr, Atom}; +use crate::passes::explicate::explicate::Env; +use crate::passes::explicate::{explicate_pred, CExpr, CTail}; + +use crate::passes::parse::{Meta, Typed}; +use crate::passes::validate::TLit; +use crate::utils::gen_sym::{gen_sym, UniqueSym}; + +pub fn explicate_assign<'p>( + sym: UniqueSym<'p>, + bnd: Typed<'p, AExpr<'p>>, + tail: CTail<'p>, + env: &mut Env<'_, 'p>, +) -> CTail<'p> { + let mut create_block = |goto: CTail<'p>| { + let sym = gen_sym("tmp"); + env.blocks.insert(sym, goto); + sym + }; + + match bnd.inner { + AExpr::Apply { fun, args } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::Apply { fun, args }, + }, + tail: Box::new(tail), + }, + AExpr::FunRef { sym: sym_fn } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::FunRef { sym: sym_fn }, + }, + tail: Box::new(tail), + }, + AExpr::Atom { atm } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::Atom { atm }, + }, + tail: Box::new(tail), + }, + AExpr::BinaryOp { op, exprs } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::BinaryOp { op, exprs }, + }, + tail: Box::new(tail), + }, + AExpr::UnaryOp { op, expr } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::UnaryOp { op, expr }, + }, + tail: Box::new(tail), + }, + AExpr::Let { + sym: sym_, + bnd: bnd_, + bdy: bdy_, + .. + } => explicate_assign(sym_, *bnd_, explicate_assign(sym, *bdy_, tail, env), env), + AExpr::If { cnd, thn, els, .. } => { + let tb = create_block(tail); + explicate_pred::explicate_pred( + cnd.inner, + explicate_assign(sym, *thn, CTail::Goto { lbl: tb }, env), + explicate_assign(sym, *els, CTail::Goto { lbl: tb }, env), + env, + ) + } + AExpr::Loop { bdy, .. } => { + let loop_block_sym = gen_sym("tmp"); + let tail = create_block(tail); + let mut env = Env { + blocks: env.blocks, + break_target: Some((tail, sym)), + continue_target: Some(loop_block_sym), + }; + + let loop_block = explicate_assign( + gen_sym("ignore"), + *bdy, + CTail::Goto { + lbl: loop_block_sym, + }, + &mut env, + ); + env.blocks.insert(loop_block_sym, loop_block); + CTail::Goto { + lbl: loop_block_sym, + } + } + AExpr::Break { bdy, .. } => { + let (break_sym, break_var) = env.break_target.unwrap(); + let break_goto = CTail::Goto { lbl: break_sym }; + explicate_assign(break_var, *bdy, break_goto, env) + } + AExpr::Seq { stmt, cnt, .. } => explicate_assign( + gen_sym("ignore"), + *stmt, + explicate_assign(sym, *cnt, tail, env), + env, + ), + AExpr::Assign { + sym: sym_, + bnd: bnd_, + .. + } => explicate_assign( + sym_, + *bnd_, + explicate_assign( + sym, + Meta { + meta: bnd.meta, + inner: AExpr::Atom { + atm: Atom::Val { val: TLit::Unit }, + }, + }, + tail, + env, + ), + env, + ), + AExpr::Continue { .. } => CTail::Goto { + lbl: env.continue_target.unwrap(), + }, + AExpr::Return { bdy, .. } => { + let tmp = gen_sym("return"); + let tail = CTail::Return { + expr: Meta { + meta: bnd.meta, + inner: Atom::Var { sym: tmp }, + }, + }; + explicate_assign(tmp, *bdy, tail, env) + } + AExpr::Struct { sym: sym_, fields } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::Struct { sym: sym_, fields }, + }, + tail: Box::new(tail), + }, + AExpr::AccessField { strct, field } => CTail::Seq { + sym, + bnd: Meta { + meta: bnd.meta, + inner: CExpr::AccessField { strct, field }, + }, + tail: Box::new(tail), + }, + } +} diff --git a/compiler/src/passes/explicate/explicate_def.rs b/compiler/src/passes/explicate/explicate_def.rs new file mode 100644 index 0000000..ac9e0ce --- /dev/null +++ b/compiler/src/passes/explicate/explicate_def.rs @@ -0,0 +1,23 @@ +use crate::passes::atomize::DefAtomized; + +use crate::passes::explicate::explicate::Env; +use crate::passes::explicate::explicate_tail::explicate_tail; +use crate::passes::parse::{Def, TypeDef}; +use crate::utils::gen_sym::UniqueSym; +use std::collections::HashMap; + +pub fn explicate_def<'p>( + def: DefAtomized<'p>, + env: &mut Env<'_, 'p>, + defs: &mut HashMap, TypeDef, &'p str>>, +) { + match def { + Def::Fn { sym, bdy, .. } => { + let tail = explicate_tail(bdy, env); + env.blocks.insert(sym, tail); + } + Def::TypeDef { sym, def } => { + defs.insert(sym, def); + } + } +} diff --git a/compiler/src/passes/explicate/explicate_pred.rs b/compiler/src/passes/explicate/explicate_pred.rs new file mode 100644 index 0000000..36b5cbc --- /dev/null +++ b/compiler/src/passes/explicate/explicate_pred.rs @@ -0,0 +1,193 @@ +use crate::passes::atomize::{AExpr, Atom}; +use crate::passes::explicate::explicate::Env; +use crate::passes::explicate::explicate_assign::explicate_assign; +use crate::passes::explicate::{CExpr, CTail}; +use crate::passes::parse::types::Type; +use crate::passes::parse::{BinaryOp, Meta, UnaryOp}; +use crate::passes::validate::TLit; +use crate::utils::gen_sym::gen_sym; + +pub fn explicate_pred<'p>( + cnd: AExpr<'p>, + thn: CTail<'p>, + els: CTail<'p>, + env: &mut Env<'_, 'p>, +) -> CTail<'p> { + let mut create_block = |goto: CTail<'p>| { + let sym = gen_sym("tmp"); + env.blocks.insert(sym, goto); + sym + }; + + match cnd { + AExpr::Atom { + atm: Atom::Var { sym }, + .. + } => CTail::IfStmt { + cnd: CExpr::BinaryOp { + op: BinaryOp::EQ, + exprs: [ + Atom::Var { sym }, + Atom::Val { + val: TLit::Bool { val: true }, + }, + ], + }, + thn: create_block(thn), + els: create_block(els), + }, + AExpr::Atom { + atm: Atom::Val { + val: TLit::Bool { val }, + }, + .. + } => { + if val { + thn + } else { + els + } + } + AExpr::BinaryOp { op, exprs } => match op { + BinaryOp::LAnd | BinaryOp::LOr | BinaryOp::Xor => { + let tmp = gen_sym("tmp"); + explicate_assign( + tmp, + Meta { + meta: Type::Bool, + inner: AExpr::BinaryOp { op, exprs }, + }, + explicate_pred( + AExpr::Atom { + atm: Atom::Var { sym: tmp }, + }, + thn, + els, + env, + ), + env, + ) + } + BinaryOp::GT + | BinaryOp::GE + | BinaryOp::EQ + | BinaryOp::LE + | BinaryOp::LT + | BinaryOp::NE => CTail::IfStmt { + cnd: CExpr::BinaryOp { op, exprs }, + thn: create_block(thn), + els: create_block(els), + }, + BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => { + unreachable!("Unexpected `BinaryOp` in predicate position.") + } + }, + AExpr::UnaryOp { op, expr } => match op { + UnaryOp::Not => explicate_pred(AExpr::Atom { atm: expr }, els, thn, env), + UnaryOp::Neg => unreachable!("Unexpected `UnaryOp` in predicate position."), + }, + AExpr::Let { sym, bnd, bdy, .. } => { + explicate_assign(sym, *bnd, explicate_pred(bdy.inner, thn, els, env), env) + } + AExpr::If { + cnd: cnd_sub, + thn: thn_sub, + els: els_sub, + .. + } => { + let thn = create_block(thn); + let els = create_block(els); + + explicate_pred( + cnd_sub.inner, + explicate_pred( + thn_sub.inner, + CTail::Goto { lbl: thn }, + CTail::Goto { lbl: els }, + env, + ), + explicate_pred( + els_sub.inner, + CTail::Goto { lbl: thn }, + CTail::Goto { lbl: els }, + env, + ), + env, + ) + } + AExpr::Apply { fun, args, .. } => { + let tmp = gen_sym("tmp"); + explicate_assign( + tmp, + Meta { + meta: Type::Bool, + inner: AExpr::Apply { fun, args }, + }, + explicate_pred( + AExpr::Atom { + atm: Atom::Var { sym: tmp }, + }, + thn, + els, + env, + ), + env, + ) + } + AExpr::Loop { .. } => { + let tmp = gen_sym("tmp"); + let cnd_ = AExpr::Atom { + atm: Atom::Var { sym: tmp }, + }; + explicate_assign( + tmp, + Meta { + meta: Type::Bool, + inner: cnd, + }, + explicate_pred(cnd_, thn, els, env), + env, + ) + } + AExpr::Seq { stmt, cnt, .. } => explicate_assign( + gen_sym("ignore"), + *stmt, + explicate_pred(cnt.inner, thn, els, env), + env, + ), + AExpr::AccessField { strct, field, .. } => { + let tmp = gen_sym("tmp"); + explicate_assign( + tmp, + Meta { + meta: Type::Bool, + inner: AExpr::AccessField { strct, field }, + }, + explicate_pred( + AExpr::Atom { + atm: Atom::Var { sym: tmp }, + }, + thn, + els, + env, + ), + env, + ) + } + + // cargo format should get some help + AExpr::FunRef { .. } + | AExpr::Atom { + atm: + Atom::Val { + val: TLit::U64 { .. } | TLit::I64 { .. } | TLit::Unit, + }, + .. + } + | AExpr::Assign { .. } + | AExpr::Break { .. } + | AExpr::Continue { .. } + | AExpr::Return { .. } + | AExpr::Struct { .. } => unreachable!(), + } +} diff --git a/compiler/src/passes/explicate/explicate_tail.rs b/compiler/src/passes/explicate/explicate_tail.rs new file mode 100644 index 0000000..ee7231e --- /dev/null +++ b/compiler/src/passes/explicate/explicate_tail.rs @@ -0,0 +1,18 @@ +use crate::passes::atomize::{AExpr, Atom}; +use crate::passes::explicate::explicate::Env; +use crate::passes::explicate::explicate_assign::explicate_assign; +use crate::passes::explicate::CTail; + +use crate::passes::parse::{Meta, Typed}; +use crate::utils::gen_sym::gen_sym; + +pub fn explicate_tail<'p>(expr: Typed<'p, AExpr<'p>>, env: &mut Env<'_, 'p>) -> CTail<'p> { + let tmp = gen_sym("return"); + let tail = CTail::Return { + expr: Meta { + meta: expr.meta.clone(), + inner: Atom::Var { sym: tmp }, + }, + }; + explicate_assign(tmp, expr, tail, env) +} diff --git a/compiler/src/passes/explicate/interpret.rs b/compiler/src/passes/explicate/interpret.rs deleted file mode 100644 index abbf53c..0000000 --- a/compiler/src/passes/explicate/interpret.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::interpreter::Val; -use crate::interpreter::IO; -use crate::passes::atomize::Atom; -use crate::passes::explicate::{CExpr, CTail, PrgExplicated}; -use crate::passes::parse::Op; -use crate::passes::validate::TLit; -use crate::utils::gen_sym::UniqueSym; -use crate::utils::push_map::PushMap; -use std::collections::HashMap; - -impl<'p> PrgExplicated<'p> { - pub fn interpret(&self, io: &mut impl IO) -> Val<'p, UniqueSym<'p>> { - self.interpret_tail(&self.blocks[&self.entry], &mut PushMap::default(), io) - } - - fn interpret_tail( - &self, - tail: &CTail<'p>, - scope: &mut PushMap, Val<'p, UniqueSym<'p>>>, - io: &mut impl IO, - ) -> Val<'p, UniqueSym<'p>> { - match tail { - CTail::Return { expr, .. } => self.interpret_atom(expr, scope), - CTail::Seq { sym, bnd, tail } => { - let bnd = self.interpret_expr(bnd, scope, io); - scope.push(*sym, bnd, |scope| self.interpret_tail(tail, scope, io)) - } - CTail::IfStmt { cnd, thn, els } => { - if self.interpret_expr(cnd, scope, io).bool() { - self.interpret_tail(&self.blocks[thn], scope, io) - } else { - self.interpret_tail(&self.blocks[els], scope, io) - } - } - CTail::Goto { lbl } => self.interpret_tail(&self.blocks[lbl], scope, io), - } - } - - pub fn interpret_expr( - &self, - expr: &CExpr<'p>, - scope: &mut PushMap, Val<'p, UniqueSym<'p>>>, - io: &mut impl IO, - ) -> Val<'p, UniqueSym<'p>> { - match expr { - CExpr::Prim { op, args, .. } => match (op, args.as_slice()) { - (Op::Read, []) => io.read().into(), - (Op::Print, [v]) => { - let val = self.interpret_atom(v, scope); - io.print(TLit::Int { - val: val.int() as i32, - }); - val - } - (Op::Plus, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Int { val: e1 + e2 } - } - (Op::Minus, [e1]) => { - let e1 = self.interpret_atom(e1, scope).int(); - Val::Int { val: -e1 } - } - (Op::Minus, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Int { val: e1 - e2 } - } - (Op::Mul, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Int { val: e1 * e2 } - } - (Op::Div, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Int { val: e1 / e2 } - } - (Op::Mod, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Int { val: e1 % e2 } - } - (Op::GT, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Bool { val: e1 > e2 } - } - (Op::GE, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Bool { val: e1 >= e2 } - } - (Op::LT, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Bool { val: e1 < e2 } - } - (Op::LE, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).int(); - let e2 = self.interpret_atom(e2, scope).int(); - Val::Bool { val: e1 <= e2 } - } - (Op::EQ, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope); - let e2 = self.interpret_atom(e2, scope); - Val::Bool { val: e1 == e2 } - } - (Op::NE, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope); - let e2 = self.interpret_atom(e2, scope); - Val::Bool { val: e1 != e2 } - } - (Op::Not, [e1]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - Val::Bool { val: !e1 } - } - (Op::LAnd, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - if !e1 { - return Val::Bool { val: false }; - } - self.interpret_atom(e2, scope) - } - (Op::LOr, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - if e1 { - return Val::Bool { val: true }; - } - self.interpret_atom(e2, scope) - } - (Op::Xor, [e1, e2]) => { - let e1 = self.interpret_atom(e1, scope).bool(); - let e2 = self.interpret_atom(e2, scope).bool(); - Val::Bool { val: e1 ^ e2 } - } - _ => unreachable!(), - }, - CExpr::Atom { atm, .. } => self.interpret_atom(atm, scope), - CExpr::FunRef { sym, .. } => Val::Function { sym: *sym }, - CExpr::Apply { fun, args, .. } => { - let fn_sym = self.interpret_atom(fun, scope).fun(); - let args = self.fn_params[&fn_sym] - .iter() - .zip(args.iter()) - .map(|(param, (atm, _))| (param.sym, self.interpret_atom(atm, scope))) - .collect::>(); - - scope.push_iter(args.into_iter(), |scope| { - self.interpret_tail(&self.blocks[&fn_sym], scope, io) - }) - } - CExpr::Struct { fields, .. } => { - let mut field_values = HashMap::new(); - for (sym, field) in fields { - field_values.insert(*sym, self.interpret_atom(field, scope)); - } - Val::StructInstance { - fields: field_values, - } - } - CExpr::AccessField { strct, field, .. } => { - let s = self.interpret_atom(strct, scope); - s.strct()[field].clone() - } - } - } - - #[must_use] - pub fn interpret_atom( - &self, - atom: &Atom<'p>, - scope: &PushMap, Val<'p, UniqueSym<'p>>>, - ) -> Val<'p, UniqueSym<'p>> { - match atom { - Atom::Val { val } => (*val).into(), - Atom::Var { sym } => scope[sym].clone(), - } - } -} diff --git a/compiler/src/passes/explicate/interpreter.rs b/compiler/src/passes/explicate/interpreter.rs new file mode 100644 index 0000000..c451f4c --- /dev/null +++ b/compiler/src/passes/explicate/interpreter.rs @@ -0,0 +1,182 @@ +use crate::interpreter::Val; +use crate::interpreter::IO; +use crate::passes::atomize::Atom; +use crate::passes::explicate::{CExpr, CTail, PrgExplicated}; + +use crate::passes::parse::{BinaryOp, UnaryOp}; +use crate::passes::validate::TLit; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; +use std::collections::HashMap; + +impl<'p> PrgExplicated<'p> { + pub fn interpret(&self, io: &mut impl IO) -> Val<'p> { + let std_iter = self + .std + .iter() + .map(|(_, &def)| (def, Val::StdlibFunction { sym: def.sym })); + + self.interpret_tail( + &self.blocks[&self.entry], + &mut PushMap::from_iter(std_iter), + io, + ) + } + + fn interpret_tail( + &self, + tail: &CTail<'p>, + scope: &mut PushMap, Val<'p>>, + io: &mut impl IO, + ) -> Val<'p> { + match tail { + CTail::Return { expr, .. } => self.interpret_atom(&expr.inner, scope), + CTail::Seq { sym, bnd, tail } => { + let bnd = self.interpret_expr(&bnd.inner, scope, io); + scope.push(*sym, bnd, |scope| self.interpret_tail(tail, scope, io)) + } + CTail::IfStmt { cnd, thn, els } => { + if self.interpret_expr(cnd, scope, io).bool() { + self.interpret_tail(&self.blocks[thn], scope, io) + } else { + self.interpret_tail(&self.blocks[els], scope, io) + } + } + CTail::Goto { lbl } => self.interpret_tail(&self.blocks[lbl], scope, io), + } + } + + pub fn interpret_expr( + &self, + expr: &CExpr<'p>, + scope: &mut PushMap, Val<'p>>, + io: &mut impl IO, + ) -> Val<'p> { + match &expr { + CExpr::BinaryOp { + op, + exprs: [lhs, rhs], + } => { + let lhs = self.interpret_atom(lhs, scope); + let rhs = self.interpret_atom(rhs, scope); + match op { + BinaryOp::Add => Val::Int { + val: lhs.int() + rhs.int(), + }, + BinaryOp::Sub => Val::Int { + val: lhs.int() - rhs.int(), + }, + BinaryOp::Mul => Val::Int { + val: lhs.int() * rhs.int(), + }, + BinaryOp::Div => Val::Int { + val: lhs.int() / rhs.int(), + }, + BinaryOp::Mod => Val::Int { + val: lhs.int() % rhs.int(), + }, + BinaryOp::Xor => Val::Bool { + val: lhs.bool() ^ rhs.bool(), + }, + BinaryOp::GT => Val::Bool { + val: lhs.int() > rhs.int(), + }, + BinaryOp::GE => Val::Bool { + val: lhs.int() >= rhs.int(), + }, + BinaryOp::EQ => Val::Bool { val: lhs == rhs }, + BinaryOp::LE => Val::Bool { + val: lhs.int() <= rhs.int(), + }, + BinaryOp::LT => Val::Bool { + val: lhs.int() < rhs.int(), + }, + BinaryOp::NE => Val::Bool { val: lhs != rhs }, + BinaryOp::LAnd => Val::Bool { + val: lhs.bool() && rhs.bool(), + }, + BinaryOp::LOr => Val::Bool { + val: lhs.bool() || rhs.bool(), + }, + } + } + CExpr::UnaryOp { op, expr } => { + let expr = self.interpret_atom(expr, scope); + match op { + UnaryOp::Neg => Val::Int { val: -expr.int() }, + UnaryOp::Not => Val::Bool { val: !expr.bool() }, + } + } + CExpr::Atom { atm, .. } => self.interpret_atom(atm, scope), + CExpr::FunRef { sym, .. } => { + if self.std.contains_key(sym.sym) { + Val::StdlibFunction { sym: sym.sym } + } else { + Val::Function { sym: *sym } + } + } + CExpr::Apply { fun, args, .. } => { + let fun = self.interpret_atom(fun, scope); + + let mut fn_args = Vec::new(); + for (atm, _) in args { + fn_args.push(self.interpret_atom(atm, scope)); + } + + match fun { + Val::StdlibFunction { sym } => { + match sym { + "exit" => { + unreachable!("Validated programs should not have an explicit call to exit yet.") + } + "print" => { + let val = fn_args[0].clone(); + io.print(TLit::I64 { val: val.int() }); + val + } + "read" => io.read().into(), + unknown => unreachable!( + "Encountered an undefined standard library function '{unknown}'" + ), + } + } + Val::Function { sym } => { + let args = self.fn_params[&sym] + .iter() + .zip(fn_args) + .map(|(param, val)| (param.sym, val)); + scope.push_iter(args, |scope| { + self.interpret_tail(&self.blocks[&sym], scope, io) + }) + } + _ => unreachable!("The symbol did not refer to a function."), + } + } + CExpr::Struct { fields, .. } => { + let mut field_values = HashMap::new(); + for (sym, field) in fields { + field_values.insert(*sym, self.interpret_atom(field, scope)); + } + Val::StructInstance { + fields: field_values, + } + } + CExpr::AccessField { strct, field, .. } => { + let s = self.interpret_atom(strct, scope); + s.strct()[field].clone() + } + } + } + + #[must_use] + pub fn interpret_atom( + &self, + atom: &Atom<'p>, + scope: &PushMap, Val<'p>>, + ) -> Val<'p> { + match atom { + Atom::Val { val } => (*val).into(), + Atom::Var { sym } => scope[sym].clone(), + } + } +} diff --git a/compiler/src/passes/explicate/mod.rs b/compiler/src/passes/explicate/mod.rs index dd0a792..e0ac0de 100644 --- a/compiler/src/passes/explicate/mod.rs +++ b/compiler/src/passes/explicate/mod.rs @@ -1,29 +1,34 @@ pub mod explicate; -pub mod interpret; +mod explicate_assign; +mod explicate_def; +mod explicate_pred; +mod explicate_tail; +pub mod interpreter; +#[cfg(test)] +mod tests; use crate::passes::atomize::Atom; use crate::passes::parse::types::Type; -use crate::passes::parse::{Op, Param, TypeDef}; +use crate::passes::parse::{BinaryOp, Param, TypeDef, Typed, UnaryOp}; +use crate::passes::select::std_lib::Std; use crate::utils::gen_sym::UniqueSym; use std::collections::HashMap; -#[derive(Debug, PartialEq)] pub struct PrgExplicated<'p> { pub blocks: HashMap, CTail<'p>>, pub fn_params: HashMap, Vec>>>, - pub defs: HashMap, TypeDef<'p, UniqueSym<'p>>>, + pub defs: HashMap, TypeDef, &'p str>>, pub entry: UniqueSym<'p>, + pub std: Std<'p>, } -#[derive(Debug, PartialEq)] pub enum CTail<'p> { Return { - expr: Atom<'p>, - typ: Type>, + expr: Typed<'p, Atom<'p>>, }, Seq { sym: UniqueSym<'p>, - bnd: CExpr<'p>, + bnd: Typed<'p, CExpr<'p>>, tail: Box>, }, IfStmt { @@ -36,74 +41,32 @@ pub enum CTail<'p> { }, } -#[derive(Clone, Debug, PartialEq)] pub enum CExpr<'p> { Atom { atm: Atom<'p>, - typ: Type>, }, - Prim { - op: Op, - args: Vec>, - typ: Type>, + BinaryOp { + op: BinaryOp, + exprs: [Atom<'p>; 2], + }, + UnaryOp { + op: UnaryOp, + expr: Atom<'p>, }, Apply { fun: Atom<'p>, args: Vec<(Atom<'p>, Type>)>, - typ: Type>, }, FunRef { sym: UniqueSym<'p>, - typ: Type>, }, Struct { sym: UniqueSym<'p>, // todo: this does not need to be atom! fields: Vec<(&'p str, Atom<'p>)>, - typ: Type>, }, AccessField { strct: Atom<'p>, field: &'p str, - typ: Type>, }, } - -impl<'p> CExpr<'p> { - pub fn typ(&self) -> &Type> { - match self { - CExpr::Atom { typ, .. } - | CExpr::Prim { typ, .. } - | CExpr::Apply { typ, .. } - | CExpr::FunRef { typ, .. } - | CExpr::Struct { typ, .. } - | CExpr::AccessField { typ, .. } => typ, - } - } -} - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::utils::split_test::split_test; - use test_each_file::test_each_file; - - fn explicated([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let program = program - .validate() - .unwrap() - .uniquify() - .reveal() - .atomize() - .explicate(); - - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as explicate => explicated } -} diff --git a/compiler/src/passes/explicate/tests.rs b/compiler/src/passes/explicate/tests.rs new file mode 100644 index 0000000..0a4b8b7 --- /dev/null +++ b/compiler/src/passes/explicate/tests.rs @@ -0,0 +1,24 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::utils::split_test::split_test; +use test_each_file::test_each_file; + +fn explicated([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let program = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .atomize() + .explicate(); + + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as explicate => explicated } diff --git a/compiler/src/passes/interference/assign_homes.rs b/compiler/src/passes/interference/assign_homes.rs deleted file mode 100644 index ae21286..0000000 --- a/compiler/src/passes/interference/assign_homes.rs +++ /dev/null @@ -1,123 +0,0 @@ -use crate::passes::interference::{Arg, X86Assigned, X86Colored}; -use crate::passes::select::{Block, Instr, VarArg}; -use crate::utils::gen_sym::UniqueSym; -use crate::{ - addq, andq, callq_direct, callq_indirect, cmpq, divq, jcc, jmp, load_lbl, movq, mulq, negq, - notq, orq, popq, pushq, retq, setcc, subq, syscall, xorq, -}; -use std::collections::HashMap; - -impl<'p> X86Colored<'p> { - #[must_use] - pub fn assign_homes(self) -> X86Assigned<'p> { - X86Assigned { - blocks: self - .blocks - .into_iter() - .map(|(name, block)| { - ( - name, - Block { - instrs: block - .instrs - .into_iter() - .map(|instr| assign_instr(instr, &self.color_map)) - .collect(), - }, - ) - }) - .collect(), - entry: self.entry, - stack_space: self.stack_space, - std: self.std, - } - } -} - -fn assign_instr<'p>( - instr: Instr<'p, VarArg>, - color_map: &HashMap, -) -> Instr<'p, Arg> { - let map = |arg: VarArg| -> Arg { - match arg { - VarArg::Imm { val } => Arg::Imm { val }, - VarArg::Reg { reg } => Arg::Reg { reg }, - VarArg::Deref { reg, off } => Arg::Deref { reg, off }, - VarArg::XVar { sym } => color_map[&sym], - } - }; - - match instr { - Instr::Addq { src, dst } => addq!(map(src), map(dst)), - Instr::Subq { src, dst } => subq!(map(src), map(dst)), - Instr::Divq { divisor } => divq!(map(divisor)), - Instr::Mulq { src } => mulq!(map(src)), - Instr::Negq { dst } => negq!(map(dst)), - Instr::Movq { src, dst } => movq!(map(src), map(dst)), - Instr::Pushq { src } => pushq!(map(src)), - Instr::Popq { dst } => popq!(map(dst)), - Instr::CallqDirect { lbl, arity } => callq_direct!(lbl, arity), - Instr::Retq => retq!(), - Instr::Syscall { arity } => syscall!(arity), - Instr::Jmp { lbl } => jmp!(lbl), - Instr::Jcc { lbl, cnd } => jcc!(lbl, cnd), - Instr::Cmpq { src, dst } => cmpq!(map(src), map(dst)), - Instr::Andq { src, dst } => andq!(map(src), map(dst)), - Instr::Orq { src, dst } => orq!(map(src), map(dst)), - Instr::Xorq { src, dst } => xorq!(map(src), map(dst)), - Instr::Notq { dst } => notq!(map(dst)), - Instr::Setcc { cnd } => setcc!(cnd), - Instr::LoadLbl { sym, dst } => load_lbl!(sym, map(dst)), - Instr::CallqIndirect { src, arity } => callq_indirect!(map(src), arity), - } -} - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::passes::select::X86Selected; - use crate::utils::gen_sym::gen_sym; - use crate::utils::split_test::split_test; - use crate::{block, callq_direct, movq, reg}; - use test_each_file::test_each_file; - - fn assign_homes([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let expected_return = expected_return.into(); - - let mut program: X86Selected = program - .validate() - .unwrap() - .uniquify() - .reveal() - .atomize() - .explicate() - .eliminate() - .select() - .add_liveness() - .compute_interference() - .color_interference() - .assign_homes() - .into(); - - // Redirect program to exit - let new_entry = gen_sym("tmp"); - program.blocks.insert( - new_entry, - block!( - callq_direct!(program.entry, 0), - movq!(reg!(RAX), reg!(RDI)), - callq_direct!(program.std.exit, 1) - ), - ); - program.entry = new_entry; - - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return, "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as assign_homes => assign_homes } -} diff --git a/compiler/src/passes/interference/coloring_interference.rs b/compiler/src/passes/interference/coloring_interference.rs deleted file mode 100644 index c53fb05..0000000 --- a/compiler/src/passes/interference/coloring_interference.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::passes::interference::{Arg, InterferenceGraph, LArg, X86Colored, X86WithInterference}; -use crate::passes::select::Reg; -use crate::utils::gen_sym::UniqueSym; -use itertools::Itertools; -use std::collections::{HashMap, HashSet}; - -impl<'p> X86WithInterference<'p> { - #[must_use] - pub fn color_interference(self) -> X86Colored<'p> { - let (color_map, stack_space) = color_graph(self.interference); - - X86Colored { - blocks: self.blocks, - entry: self.entry, - color_map, - stack_space, - std: self.std, - } - } -} - -#[must_use] -fn color_graph(graph: InterferenceGraph) -> (HashMap, usize) { - let mut queue = Vec::new(); - let mut node_map = HashMap::::new(); - - for node in graph.nodes() { - match node { - LArg::Var { .. } => { - queue.push(node); - } - LArg::Reg { reg } => { - let node_weight = match reg { - Reg::RCX => 0, - Reg::RDX => 1, - Reg::RSI => 2, - Reg::RDI => 3, - Reg::R8 => 4, - Reg::R9 => 5, - Reg::R10 => 6, - Reg::RBX => 7, - Reg::R12 => 8, - Reg::R13 => 9, - Reg::R14 => 10, - Reg::RAX => -1, - Reg::RSP => -2, - Reg::RBP => -3, - Reg::R11 => -4, - Reg::R15 => -5, - }; - node_map.insert(node, node_weight); - } - } - } - - while let Some(node) = queue.pop() { - let used_colors = graph - .neighbors(node) - .filter_map(|nb| node_map.get(&nb)) - .collect::>(); - - let chosen_color = (0..) - .find(|i| !used_colors.contains(i)) - .expect("there are infinite numbers, lol"); - - node_map.insert(node, chosen_color); - - queue.sort_by_key(|node| { - graph - .neighbors(*node) - .filter_map(|nb| node_map.get(&nb)) - .unique() - .count() - }); - } - - let used_vars = node_map - .values() - .filter(|&&n| n >= 10) - .map(|&n| n - 10) - .max() - .unwrap_or_default() as usize; - - let stack_space = (8 * used_vars).div_ceil(16) * 16; - - let colors = node_map - .into_iter() - .filter_map(|(node, color)| match node { - LArg::Var { sym } => Some((sym, arg_from_color(color))), - LArg::Reg { .. } => None, - }) - .collect(); - - (colors, stack_space) -} - -fn arg_from_color(i: isize) -> Arg { - match i { - -5 => Arg::Reg { reg: Reg::R15 }, - -4 => Arg::Reg { reg: Reg::R11 }, - -3 => Arg::Reg { reg: Reg::RBP }, - -2 => Arg::Reg { reg: Reg::RSP }, - -1 => Arg::Reg { reg: Reg::RAX }, - 0 => Arg::Reg { reg: Reg::RCX }, - 1 => Arg::Reg { reg: Reg::RDX }, - 2 => Arg::Reg { reg: Reg::RSI }, - 3 => Arg::Reg { reg: Reg::RDI }, - 4 => Arg::Reg { reg: Reg::R8 }, - 5 => Arg::Reg { reg: Reg::R9 }, - 6 => Arg::Reg { reg: Reg::R10 }, - 7 => Arg::Reg { reg: Reg::RBX }, - 8 => Arg::Reg { reg: Reg::R12 }, - 9 => Arg::Reg { reg: Reg::R13 }, - 10 => Arg::Reg { reg: Reg::R14 }, - i => { - assert!( - i > 10, - "Something went wrong while coloring the interference graph." - ); - Arg::Deref { - reg: Reg::RBP, - off: (-8 * (i - 10)) as i64, - } - } - } -} diff --git a/compiler/src/passes/mod.rs b/compiler/src/passes/mod.rs index 43c5622..68c36b5 100644 --- a/compiler/src/passes/mod.rs +++ b/compiler/src/passes/mod.rs @@ -1,12 +1,11 @@ +pub mod assign; pub mod atomize; pub mod conclude; -pub mod eliminate_algebraic; +pub mod eliminate; pub mod emit; pub mod explicate; -pub mod interference; pub mod parse; -pub mod patch_instructions; -pub mod reveal_functions; +pub mod patch; +pub mod reveal; pub mod select; -pub mod uniquify; pub mod validate; diff --git a/compiler/src/passes/parse/display.rs b/compiler/src/passes/parse/display.rs new file mode 100644 index 0000000..940e30d --- /dev/null +++ b/compiler/src/passes/parse/display.rs @@ -0,0 +1,129 @@ +use crate::passes::parse::{Def, Expr, TypeDef}; +use indenter::indented; +use itertools::Itertools; +use std::fmt::Write; +use std::fmt::{Display, Formatter}; + +impl Display + for Def +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Def::Fn { + sym, + params, + typ, + bdy, + } => { + writeln!(f, "fn {sym}({}) -> {typ} {{", params.iter().format(", "))?; + writeln!(indented(f), "{bdy}")?; + writeln!(f, "}}")?; + Ok(()) + } + Def::TypeDef { sym, def } => match def { + TypeDef::Struct { fields } => { + writeln!(f, "struct {sym} {{")?; + writeln!( + indented(f), + "{}", + fields + .iter() + .map(|(sym, bnd)| format!("{sym}: {bnd},")) + .format("\n") + )?; + writeln!(f, "}}") + } + TypeDef::Enum { .. } => todo!(), + }, + } + } +} + +impl Display + for Expr +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Expr::Lit { val } => { + write!(f, "{val}") + } + Expr::Var { sym } => { + write!(f, "{sym}") + } + Expr::UnaryOp { op, expr } => { + write!(f, "({op}{expr})") + } + Expr::BinaryOp { + op, + exprs: [e1, e2], + } => { + write!(f, "({e1} {op} {e2})") + } + Expr::Let { + sym, + mutable, + typ, + bnd, + bdy, + } => { + writeln!( + f, + "let {}{sym}{} = {bnd};", + if *mutable { "mut " } else { "" }, + typ.as_ref() + .map(|typ| format!(": {typ}")) + .unwrap_or("".to_string()) + )?; + write!(f, "{bdy}") + } + Expr::If { cnd, thn, els } => { + writeln!(f, "if {cnd} {{")?; + writeln!(indented(f), "{thn}")?; + writeln!(f, "}} else {{")?; + writeln!(indented(f), "{els}")?; + write!(f, "}}") + } + Expr::Apply { fun, args } => { + write!(f, "{fun}({})", args.iter().format(", ")) + } + Expr::Loop { bdy } => { + writeln!(f, "loop {{")?; + writeln!(indented(f), "{bdy}")?; + write!(f, "}}") + } + Expr::Break { bdy } => { + write!(f, "break {bdy}") + } + Expr::Continue => { + write!(f, "continue") + } + Expr::Return { bdy } => { + write!(f, "return {bdy}") + } + Expr::Seq { stmt, cnt } => { + writeln!(f, "{stmt};")?; + write!(f, "{cnt}") + } + Expr::Assign { sym, bnd } => { + write!(f, "{sym} = {bnd}") + } + Expr::Struct { sym, fields } => { + writeln!(f, "{sym} {{")?; + writeln!( + indented(f), + "{}", + fields + .iter() + .map(|(sym, bnd)| format!("{sym}: {bnd},")) + .format("\n") + )?; + write!(f, "}}") + } + Expr::AccessField { strct, field } => { + write!(f, "{strct}.{field}") + } + Expr::Variant { .. } => todo!(), + Expr::Switch { .. } => todo!(), + } + } +} diff --git a/compiler/src/passes/parse/grammar.lalrpop b/compiler/src/passes/parse/grammar.lalrpop index 6274d0f..94a70ed 100644 --- a/compiler/src/passes/parse/grammar.lalrpop +++ b/compiler/src/passes/parse/grammar.lalrpop @@ -1,8 +1,9 @@ -use crate::passes::parse::{Def, TypeDef, Expr, Lit, Op, Param}; -use crate::passes::parse::PrgParsed; -use crate::passes::parse::Type; -use crate::passes::parse::Spanned; +use crate::passes::parse::{ + BinaryOp, Def, Expr, Lit, Meta, Param, PrgParsed, Span, Type, TypeDef, UnaryOp, +}; +use crate::passes::validate::partial_type::PartialType; use functor_derive::Functor; +use crate::passes::parse::Spanned; grammar; @@ -46,12 +47,9 @@ match { "/", "%", - // Cursed build-in functions - "read", - "print", - // Types - "Int", + "I64", + "U64", "Bool", "Unit", "Never", @@ -61,6 +59,8 @@ match { "false", "unit", r"[0-9]+" => integer, + r"[0-9]+i64" => i64, + r"[0-9]+u64" => u64, // Logical operators "^", @@ -89,11 +89,11 @@ match { pub Program: PrgParsed<'input> = { => PrgParsed { defs, - entry: "main" + entry: "main", } } -Def: Def<'input, &'input str, Spanned>> = { +Def: Def, Spanned<&'input str>, Spanned, Spanned<&'input str>, Lit<'input>, Span>>> = { "struct" "{" ":" )>> "}" => Def::TypeDef { sym, def: TypeDef::Struct { fields }, @@ -110,7 +110,7 @@ Def: Def<'input, &'input str, Spanned>> = { }, } -Param: Param<&'input str> = { +Param: Param> = { ":" => Param { mutable: mutable.is_some(), sym, @@ -118,8 +118,9 @@ Param: Param<&'input str> = { } } -Type: Type<&'input str> = { - "Int" => Type::Int, +Type: Type> = { + "I64" => Type::I64, + "U64" => Type::U64, "Bool" => Type::Bool, "Unit" => Type::Unit, "Never" => Type::Never, @@ -142,10 +143,11 @@ Type: Type<&'input str> = { // Num/Bool/Ident Expr = ExprStmt; -ExprStmt: Expr<'input> = { - "let" "=" >> ";" > => Expr::Let { +ExprStmt: Expr, Spanned<&'input str>, Lit<'input>, Span> = { + "let" )?> "=" >> ";" > => Expr::Let { sym, mutable: mutable.is_some(), + typ, bnd: Box::new(bnd), bdy: Box::new(bdy.fmap(|bdy| bdy.unwrap_or(Expr::Lit { val: Lit::Unit }))), }, @@ -156,38 +158,38 @@ ExprStmt: Expr<'input> = { ExprInStmt, } -ExprInStmt: Expr<'input> = { - > "=" >> => Expr::Assign { +ExprInStmt: Expr, Spanned<&'input str>, Lit<'input>, Span> = { + "=" >> => Expr::Assign { sym, bnd: Box::new(bnd), }, "if" >> "{" > "}" > "}")?> => Expr::If { cnd: Box::new(cnd), thn: Box::new(thn), - els: Box::new(els.unwrap_or(Spanned { span: (l, r), inner: Expr::Lit { val: Lit::Unit }})), + els: Box::new(els.unwrap_or(Meta { meta: (l, r - l), inner: Expr::Lit { val: Lit::Unit }})), }, "loop" "{" > "}" => Expr::Loop { bdy: Box::new(bdy), }, // todo: the spans in this desugaring do not make a lot sense. "while" >> "{" > "}" => Expr::Loop { - bdy: Box::new(Spanned { - span: (l, r), + bdy: Box::new(Meta { + meta: (l, r - l), inner: Expr::If { cnd: Box::new(cnd), thn: Box::new(bdy), - els: Box::new(Spanned { - span: (l, r), + els: Box::new(Meta { + meta: (l, r - l), inner: Expr::Seq { - stmt: Box::new(Spanned { - span: (l, r), - inner: Expr::Break { bdy: Box::new(Spanned { - span: (l, r), + stmt: Box::new(Meta { + meta: (l, r - l), + inner: Expr::Break { bdy: Box::new(Meta { + meta: (l, r - l), inner: Expr::Lit { val: Lit::Unit }, })}, }), - cnt: Box::new(Spanned { - span: (l, r), + cnt: Box::new(Meta { + meta: (l, r - l), inner: Expr::Lit { val: Lit::Unit }, }), }, @@ -209,10 +211,10 @@ ExprInStmt: Expr<'input> = { ExprLogicalOr, } -BinaryOps: Expr<'input> = { - >> > => Expr::Prim { +BinaryOps: Expr, Spanned<&'input str>, Lit<'input>, Span> = { + >> > => Expr::BinaryOp { op, - args: vec![e1, e2], + exprs: [Box::new(e1), Box::new(e2)], }, Next, } @@ -224,40 +226,40 @@ ExprXor = BinaryOps>; ExprAdditive = BinaryOps>; ExprMultiplicative = BinaryOps>; -LogicalOrOp: Op = "||" => Op::LOr; -LogicalAndOp: Op = "&&" => Op::LAnd; -ComparativeOp: Op = { - "==" => Op::EQ, - "!=" => Op::NE, - ">" => Op::GT, - ">=" => Op::GE, - "<" => Op::LT, - "<=" => Op::LE, +LogicalOrOp: BinaryOp = "||" => BinaryOp::LOr; +LogicalAndOp: BinaryOp = "&&" => BinaryOp::LAnd; +ComparativeOp: BinaryOp = { + "==" => BinaryOp::EQ, + "!=" => BinaryOp::NE, + ">" => BinaryOp::GT, + ">=" => BinaryOp::GE, + "<" => BinaryOp::LT, + "<=" => BinaryOp::LE, } -XorOp: Op = "^" => Op::Xor; -AdditiveOp: Op = { - "+" => Op::Plus, - "-" => Op::Minus, +XorOp: BinaryOp = "^" => BinaryOp::Xor; +AdditiveOp: BinaryOp = { + "+" => BinaryOp::Add, + "-" => BinaryOp::Sub, } -MultiplicativeOp: Op = { - "*" => Op::Mul, - "/" => Op::Div, - "%" => Op::Mod, +MultiplicativeOp: BinaryOp = { + "*" => BinaryOp::Mul, + "/" => BinaryOp::Div, + "%" => BinaryOp::Mod, } -UnaryOp: Op = { - "-" => Op::Minus, - "!" => Op::Not, +UnaryOp: UnaryOp = { + "-" => UnaryOp::Neg, + "!" => UnaryOp::Not, } -ExprUnary: Expr<'input> = { - >> => Expr::Prim { +ExprUnary: Expr, Spanned<&'input str>, Lit<'input>, Span> = { + >> => Expr::UnaryOp { op, - args: vec![e], + expr: Box::new(e), }, ExprAccess, } -ExprAccess: Expr<'input> = { +ExprAccess: Expr, Spanned<&'input str>, Lit<'input>, Span> = { >> "." => Expr::AccessField { strct: Box::new(strct), field, @@ -265,15 +267,7 @@ ExprAccess: Expr<'input> = { ExprCall, } -ExprCall: Expr<'input> = { - "read" "(" ")" => Expr::Prim { - op: Op::Read, - args: vec![], - }, - "print" "(" > ")" => Expr::Prim { - op: Op::Print, - args: vec![e], - }, +ExprCall: Expr, Spanned<&'input str>, Lit<'input>, Span> = { >> "(" >> ")" => Expr::Apply { fun: Box::new(fun), args, @@ -281,8 +275,25 @@ ExprCall: Expr<'input> = { ExprAtom, } -ExprAtom: Expr<'input> = { - => Expr::Lit { val: Lit::Int { val } }, +ExprAtom: Expr, Spanned<&'input str>, Lit<'input>, Span> = { + => Expr::Lit { + val: Lit::Int { + val, + typ: None, + }, + }, + => Expr::Lit { + val: Lit::Int { + val: val.trim_end_matches("i64"), + typ: Some(PartialType::I64), + }, + }, + => Expr::Lit { + val: Lit::Int { + val: val.trim_end_matches("u64"), + typ: Some(PartialType::U64), + }, + }, => Expr::Lit { val: Lit::Bool { val } }, "unit" => Expr::Lit { val: Lit::Unit }, => Expr::Var { sym }, @@ -295,23 +306,21 @@ ExprAtom: Expr<'input> = { , } -Struct: Expr<'input> = { +Struct: Expr, Spanned<&'input str>, Lit<'input>, Span> = { "{" > "}" => Expr::Struct { sym, fields, }, } -StructArg : (&'input str, Spanned>) = { +StructArg : (Spanned<&'input str>, Spanned, Spanned<&'input str>, Lit<'input>, Span>>) = { ":" >, - => (sym, Spanned { span: (l, r), inner: Expr::Var { sym } }) + => (sym.clone(), Meta { meta: (l, r - l), inner: Expr::Var { sym } }) } -Never: Expr<'input> = {}; +Never: Expr, Spanned<&'input str>, Lit<'input>, Span> = {}; -Ident: &'input str = identifier; - -Num: &'input str = ; +Ident: Spanned<&'input str> = Spanned; Bool: bool = { "true" => true, @@ -329,5 +338,4 @@ Comma: Vec = { } } -Spanned: Spanned = => Spanned { span: (l, r), inner }; - +Spanned: Spanned = => Meta { meta: (l, r - l), inner }; diff --git a/compiler/src/passes/parse/interpreter.rs b/compiler/src/passes/parse/interpreter.rs deleted file mode 100644 index 3bbfc5d..0000000 --- a/compiler/src/passes/parse/interpreter.rs +++ /dev/null @@ -1,226 +0,0 @@ -use crate::interpreter::Val; -use crate::interpreter::IO; -use crate::passes::parse::{Def, Op}; -use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::{TExpr, TLit}; -use crate::utils::gen_sym::UniqueSym; -use crate::utils::push_map::PushMap; -use std::collections::HashMap; -use std::fmt::Display; -use std::hash::Hash; - -#[derive(Clone)] -pub enum ControlFlow<'p, A: Copy + Hash + Eq + Display> { - Val(Val<'p, A>), - Break(Val<'p, A>), - Return(Val<'p, A>), - Continue, -} - -/// This macro unwraps values and bubbles up continues, breaks and returns. -macro_rules! b { - ($e: expr) => {{ - let e = $e; - match e { - ControlFlow::Val(val) => val, - ControlFlow::Break(_) | ControlFlow::Return(_) | ControlFlow::Continue => return e, - } - }}; -} - -impl<'p> PrgUniquified<'p> { - pub fn interpret(&'p self, io: &mut impl IO) -> Val<'p, UniqueSym<'p>> { - let mut scope = PushMap::from_iter( - self.defs - .iter() - .map(|(&sym, _)| (sym, Val::Function { sym })), - ); - self.interpret_fn(self.entry, Vec::new(), &mut scope, io) - } - - fn interpret_fn( - &'p self, - sym: UniqueSym<'p>, - args: Vec>>, - scope: &mut PushMap, Val<'p, UniqueSym<'p>>>, - io: &mut impl IO, - ) -> Val<'p, UniqueSym<'p>> { - match &self.defs[&sym] { - Def::Fn { params, bdy, .. } => scope.push_iter( - params - .iter() - .zip(args.iter()) - .map(|(param, v)| (param.sym, v.clone())), - |scope| match self.interpret_expr(bdy, scope, io) { - ControlFlow::Return(val) | ControlFlow::Val(val) => val, - ControlFlow::Continue | ControlFlow::Break(_) => unreachable!(), - }, - ), - Def::TypeDef { .. } => unreachable!(), - } - } - - pub fn interpret_expr( - &'p self, - expr: &'p TExpr>, - scope: &mut PushMap, Val<'p, UniqueSym<'p>>>, - io: &mut impl IO, - ) -> ControlFlow<'p, UniqueSym<'p>> { - ControlFlow::Val(match expr { - TExpr::Lit { val, .. } => (*val).into(), - TExpr::Var { sym, .. } => scope[sym].clone(), - TExpr::Prim { op, args, .. } => match (op, args.as_slice()) { - (Op::Read, []) => io.read().into(), - (Op::Print, [v]) => { - let val = b!(self.interpret_expr(v, scope, io)); - io.print(TLit::Int { - val: val.int() as i32, - }); - val - } - (Op::Plus, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Int { val: e1 + e2 } - } - (Op::Minus, [e1]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - Val::Int { val: -e1 } - } - (Op::Minus, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Int { val: e1 - e2 } - } - (Op::Mul, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Int { val: e1 * e2 } - } - (Op::Div, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Int { val: e1 / e2 } - } - (Op::Mod, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Int { val: e1 % e2 } - } - (Op::GT, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Bool { val: e1 > e2 } - } - (Op::GE, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Bool { val: e1 >= e2 } - } - (Op::LT, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Bool { val: e1 < e2 } - } - (Op::LE, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).int(); - let e2 = b!(self.interpret_expr(e2, scope, io)).int(); - Val::Bool { val: e1 <= e2 } - } - (Op::EQ, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)); - let e2 = b!(self.interpret_expr(e2, scope, io)); - Val::Bool { val: e1 == e2 } - } - (Op::NE, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)); - let e2 = b!(self.interpret_expr(e2, scope, io)); - Val::Bool { val: e1 != e2 } - } - (Op::Not, [e1]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).bool(); - Val::Bool { val: !e1 } - } - (Op::LAnd, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).bool(); - if !e1 { - return ControlFlow::Val(Val::Bool { val: false }); - } - b!(self.interpret_expr(e2, scope, io)) - } - (Op::LOr, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).bool(); - if e1 { - return ControlFlow::Val(Val::Bool { val: true }); - } - b!(self.interpret_expr(e2, scope, io)) - } - (Op::Xor, [e1, e2]) => { - let e1 = b!(self.interpret_expr(e1, scope, io)).bool(); - let e2 = b!(self.interpret_expr(e2, scope, io)).bool(); - Val::Bool { val: e1 ^ e2 } - } - _ => unreachable!(), - }, - TExpr::Let { sym, bnd, bdy, .. } => { - let bnd = b!(self.interpret_expr(bnd, scope, io)); - b!(scope.push(*sym, bnd, |scope| self.interpret_expr(bdy, scope, io))) - } - TExpr::If { cnd, thn, els, .. } => { - if b!(self.interpret_expr(cnd, scope, io)).bool() { - b!(self.interpret_expr(thn, scope, io)) - } else { - b!(self.interpret_expr(els, scope, io)) - } - } - TExpr::Apply { fun, args, .. } => { - let sym = b!(self.interpret_expr(fun, scope, io)).fun(); - - let mut fn_args = Vec::new(); - for arg in args { - fn_args.push(b!(self.interpret_expr(arg, scope, io))); - } - - self.interpret_fn(sym, fn_args, scope, io) - } - TExpr::Loop { bdy, .. } => loop { - match self.interpret_expr(bdy, scope, io) { - ControlFlow::Return(val) => return ControlFlow::Return(val), - ControlFlow::Break(val) => return ControlFlow::Val(val), - ControlFlow::Continue | ControlFlow::Val(_) => {} - } - }, - TExpr::Break { bdy, .. } => { - return ControlFlow::Break(b!(self.interpret_expr(bdy, scope, io))) - } - TExpr::Seq { stmt, cnt, .. } => { - b!(self.interpret_expr(stmt, scope, io)); - b!(self.interpret_expr(cnt, scope, io)) - } - TExpr::Assign { sym, bnd, .. } => { - let bnd = b!(self.interpret_expr(bnd, scope, io)); - scope.0.insert(*sym, bnd); - Val::Unit - } - TExpr::Continue { .. } => return ControlFlow::Continue, - TExpr::Return { bdy, .. } => { - return ControlFlow::Return(b!(self.interpret_expr(bdy, scope, io))) - } - TExpr::Struct { fields, .. } => { - let mut field_values = HashMap::new(); - for (sym, field) in fields { - field_values.insert(*sym, b!(self.interpret_expr(field, scope, io))); - } - Val::StructInstance { - fields: field_values, - } - } - TExpr::Variant { .. } => todo!(), - TExpr::AccessField { strct, field, .. } => { - let s = b!(self.interpret_expr(strct, scope, io)); - s.strct()[field].clone() - } - TExpr::Switch { .. } => todo!(), - }) - } -} diff --git a/compiler/src/passes/parse/mod.rs b/compiler/src/passes/parse/mod.rs index 2a0e805..c05fdf4 100644 --- a/compiler/src/passes/parse/mod.rs +++ b/compiler/src/passes/parse/mod.rs @@ -4,62 +4,70 @@ #[rustfmt::skip] #[allow(clippy::all, clippy::pedantic)] mod grammar; -pub mod interpreter; +mod display; pub mod parse; +#[cfg(test)] +mod tests; pub mod types; +use crate::passes::validate::partial_type::PartialType; +use crate::utils::gen_sym::UniqueSym; use derive_more::Display; use functor_derive::Functor; +use itertools::Itertools; use std::fmt::Display; -use std::hash::Hash; use types::Type; /// A parsed program with global definitions and an entry point. -#[derive(Debug, PartialEq)] +#[derive(Display)] +#[display(fmt = "{}", r#"defs.iter().format("\n")"#)] pub struct PrgParsed<'p> { /// The global program definitions. - pub defs: Vec>>>, + pub defs: Vec>, /// The symbol representing the entry point of the program. pub entry: &'p str, } /// A definition. -#[derive(Debug, PartialEq)] -pub enum Def<'p, A: Copy + Hash + Eq + Display, B> { +#[derive(Debug)] +pub enum Def { /// A function definition. Fn { /// Symbol representing the function. - sym: A, + sym: IdentVars, /// Parameters of the function. - params: Vec>, + params: Vec>, /// Return type of the function. - typ: Type, + typ: Type, /// Function body. - bdy: B, + bdy: Expr, }, TypeDef { - sym: A, - def: TypeDef<'p, A>, + sym: IdentVars, + def: TypeDef, }, } -#[derive(Clone, Debug, PartialEq)] -pub enum TypeDef<'p, A: Copy + Hash + Eq + Display> { +pub type DefParsed<'p> = Def, Spanned<&'p str>, Spanned>>; +pub type ExprParsed<'p> = Expr, Spanned<&'p str>, Lit<'p>, Span>; + +#[derive(Clone, Debug)] +pub enum TypeDef { /// A struct definition. Struct { /// Fields of the struct, consisting of field symbols and their types. - fields: Vec<(&'p str, Type)>, + fields: Vec<(IdentFields, Type)>, }, /// An enum definition. Enum { /// Variants of the enum, consisting of variant symbols and their types. - variants: Vec<(&'p str, Type)>, + variants: Vec<(IdentFields, Type)>, }, } -impl<'p, A: Copy + Hash + Eq + Display, B> Def<'p, A, B> { +impl Def { /// Returns the symbol representing the definition. - pub fn sym(&self) -> &A { + pub fn sym(&self) -> &IdentVars { match self { Def::Fn { sym, .. } => sym, Def::TypeDef { sym, .. } => sym, @@ -70,9 +78,11 @@ impl<'p, A: Copy + Hash + Eq + Display, B> Def<'p, A, B> { /// A parameter used in functions. /// /// Parameters are generic and can use symbols that are either `&str` or -/// [`UniqueSym`](crate::utils::gen_sym::UniqueSym) for all passes after uniquify. -#[derive(Debug, PartialEq, Clone)] -pub struct Param { +/// [`UniqueSym`](crate::utils::gen_sym::UniqueSym) for all passes after yeet. +#[derive(Clone, Display, Debug)] +#[display(bound = "A: Display")] +#[display(fmt = "{}{sym}: {typ}", r#"if *mutable { "mut " } else { "" }"#)] +pub struct Param { /// Symbol representing the parameter. pub sym: A, /// The type of the parameter. See [`Type`] @@ -84,25 +94,27 @@ pub struct Param { /// An expression. /// /// Expressions are generic and can use symbols that are either `&str` or -/// [`UniqueSym`](crate::utils::gen_sym::UniqueSym) for all passes after uniquify. -#[derive(Debug, PartialEq)] -pub enum Expr<'p> { +/// [`UniqueSym`](crate::utils::gen_sym::UniqueSym) for all passes after yeet. +#[derive(Debug)] +pub enum Expr { /// A literal value. See [`Lit`]. Lit { /// Value of the literal. See [`Lit`]. - val: Lit<'p>, + val: Lit, }, /// A variable. Var { /// Symbol representing the variable. - sym: &'p str, + sym: IdentVars, + }, + BinaryOp { + op: BinaryOp, + #[allow(clippy::type_complexity)] + exprs: [Box>>; 2], }, - /// A primitive operation with an arbitrary number of arguments. - Prim { - /// Primitive operation (e.g. `Xor`). See [`Op`]. - op: Op, - /// Arguments used by the primitive operation. - args: Vec>>, + UnaryOp { + op: UnaryOp, + expr: Box>>, }, /// A let binding. /// @@ -111,13 +123,14 @@ pub enum Expr<'p> { /// The variable can be immutable or mutable depending on the presence of the `mut` keyword. Let { /// Symbol representing the newly introduced variable. - sym: &'p str, + sym: IdentVars, /// Indicates whether the variable is mutable (true) or immutable (false). mutable: bool, + typ: Option>, /// The expression to which the variable is bound. - bnd: Box>>, + bnd: Box>>, /// The expression that is evaluated using the new variable binding. - bdy: Box>>, + bdy: Box>>, }, /// An if statement. /// @@ -125,11 +138,11 @@ pub enum Expr<'p> { /// the result is true, it executes the `thn` expression; otherwise, it executes the `els` expression. If { /// The conditional expression that determines the execution path. - cnd: Box>>, + cnd: Box>>, /// The expression to execute if the condition is true. - thn: Box>>, + thn: Box>>, /// The expression to execute if the condition is false. - els: Box>>, + els: Box>>, }, /// A function application. /// @@ -137,9 +150,9 @@ pub enum Expr<'p> { /// evaluated to obtain a function symbol, which is invoked with the arguments in `args`. Apply { /// The expression that, when evaluated, represents the function symbol to be invoked. - fun: Box>>, + fun: Box>>, /// The ordered arguments that are passed to the function. - args: Vec>>, + args: Vec>>, }, /// A loop construct. /// @@ -147,7 +160,7 @@ pub enum Expr<'p> { /// expression is evaluated. Loop { /// The expression that defines the body of the loop. - bdy: Box>>, + bdy: Box>>, }, /// A break statement. /// @@ -155,7 +168,7 @@ pub enum Expr<'p> { /// current loop and returns the value of the `bdy` expression from the loop upon termination. Break { /// The expression to be evaluated and returned from the loop. - bdy: Box>>, + bdy: Box>>, }, /// A continue statement. /// @@ -167,7 +180,7 @@ pub enum Expr<'p> { /// The `Return` expression exits the current function and returns the value of the `bdy` expression. Return { /// The expression to be evaluated and returned from the function. - bdy: Box>>, + bdy: Box>>, }, /// A sequence of two expressions. /// @@ -176,9 +189,9 @@ pub enum Expr<'p> { /// the `cnt` expression is evaluated. Seq { /// The first expression to be executed in the sequence. - stmt: Box>>, + stmt: Box>>, /// The second expression to be executed in the sequence. - cnt: Box>>, + cnt: Box>>, }, /// A variable assignment. /// @@ -187,92 +200,139 @@ pub enum Expr<'p> { /// Only mutable or uninitialized immutable variables can be assigned a new value. Assign { /// Symbol representing the variable to which the assignment is made. - sym: Spanned<&'p str>, + sym: IdentVars, /// The expression whose result is assigned to the variable. - bnd: Box>>, + bnd: Box>>, }, /// An instance of a struct. /// /// todo: documentation Struct { - sym: &'p str, - fields: Vec<(&'p str, Spanned>)>, + sym: IdentVars, + #[allow(clippy::type_complexity)] + fields: Vec<(IdentFields, Meta>)>, }, /// A variant of an enum. /// /// todo: documentation Variant { - enum_sym: &'p str, - variant_sym: &'p str, - bdy: Box>>, + enum_sym: IdentVars, + variant_sym: IdentFields, + bdy: Box>>, }, /// A field access. /// /// todo: documentation AccessField { - strct: Box>>, - field: &'p str, + strct: Box>>, + field: IdentFields, }, /// A switch statement. /// /// todo: documentation Switch { - enm: Box>>, - arms: Vec<(&'p str, &'p str, Box>>)>, + enm: Box>>, + arms: Vec>, }, } -#[derive(Debug, PartialEq, Functor)] -pub struct Spanned { - pub span: (usize, usize), - pub inner: T, +pub type SwitchArm = ( + IdentVars, + IdentFields, + Box>>, +); + +#[derive(Clone, Display, Debug)] +#[display(bound = "B: Display")] +#[display(fmt = "{inner}")] +pub struct Meta { + pub meta: M, + pub inner: B, +} + +pub type Spanned = Meta; +pub type Typed<'p, T> = Meta>, T>; + +impl Functor for Meta { + type Target = Meta; + + fn fmap(self, f: impl Fn(B) -> B2) -> Self::Target { + Meta { + meta: self.meta, + inner: f(self.inner), + } + } +} + +pub type Span = (usize, usize); + +/// A unary operation. +#[derive(Display, Debug)] +pub enum UnaryOp { + /// Integer negation. + #[display(fmt = "-")] + Neg, + /// Logical NOT. + #[display(fmt = "!")] + Not, } /// A primitive operation. -#[derive(Copy, Clone, Debug, PartialEq)] -pub enum Op { - /// Read signed integer from stdin. - Read, - /// Print signed integer to stdout. - Print, +#[derive(Display, Debug)] +pub enum BinaryOp { /// Integer addition. - Plus, - /// Integer subtraction or negation. - Minus, + #[display(fmt = "+")] + Add, + /// Integer negation. + #[display(fmt = "-")] + Sub, /// Integer multiplication. + #[display(fmt = "*")] Mul, /// Integer division. + #[display(fmt = "/")] Div, /// Modulo operation. + #[display(fmt = "%")] Mod, /// Logical AND. + #[display(fmt = "&&")] LAnd, /// Logical OR, + #[display(fmt = "||")] LOr, - /// Logical NOT. - Not, /// XOR operation. + #[display(fmt = "^")] Xor, /// Greater Than comparison. + #[display(fmt = ">")] GT, /// Greater Than or Equal To comparison. + #[display(fmt = ">=")] GE, /// Equality comparison. Operates on `Int` and `Bool`. + #[display(fmt = "==")] EQ, /// Less Than or Equal To comparison. + #[display(fmt = "<=")] LE, /// Less Than comparison. + #[display(fmt = "<")] LT, /// Inequality comparison. Operates on `Int` and `Bool`. + #[display(fmt = "!=")] NE, } /// A literal value. -#[derive(Copy, Clone, Debug, PartialEq, Display)] +#[derive(Display)] pub enum Lit<'p> { /// Integer literal, representing a signed 64-bit number. #[display(fmt = "{val}")] - Int { val: &'p str }, + Int { + val: &'p str, + typ: Option>, + }, /// Boolean literal, representing a value of *true* or *false*. #[display(fmt = "{}", r#"if *val { "true" } else { "false" }"#)] Bool { val: bool }, @@ -281,15 +341,8 @@ pub enum Lit<'p> { Unit, } -#[cfg(test)] -mod tests { - use crate::utils::split_test::split_test; - use test_each_file::test_each_file; - - fn parse([test]: [&str; 1]) { - let _ = split_test(test); - } - - test_each_file! { for ["test"] in "./programs/good" as parse => parse } - // todo: add negative tests. +#[derive(Copy, Clone)] +pub enum IntSuffix { + I64, + U64, } diff --git a/compiler/src/passes/parse/parse.rs b/compiler/src/passes/parse/parse.rs index d824426..73f4dae 100644 --- a/compiler/src/passes/parse/parse.rs +++ b/compiler/src/passes/parse/parse.rs @@ -1,11 +1,14 @@ use crate::passes::parse::grammar::ProgramParser; use crate::passes::parse::PrgParsed; +#[cfg(test)] +use derive_name::VariantName; use itertools::Itertools; use lalrpop_util::lexer::Token; use lalrpop_util::ParseError; use miette::Diagnostic; use thiserror::Error; +#[cfg_attr(test, derive(VariantName))] #[derive(Error, Debug, Diagnostic)] pub enum PrettyParseError { #[error("Parse error: Invalid token.")] diff --git a/compiler/src/passes/parse/tests.rs b/compiler/src/passes/parse/tests.rs new file mode 100644 index 0000000..c0e3822 --- /dev/null +++ b/compiler/src/passes/parse/tests.rs @@ -0,0 +1,26 @@ +use crate::passes::parse::parse::parse_program; +use crate::utils::split_test::split_test; +use derive_name::VariantName; +use test_each_file::test_each_file; + +fn parse([test]: [&str; 1]) { + let (_, _, _, expected_error) = split_test(test); + + let result = parse_program(test); + + match (result, expected_error) { + (Ok(_), None) => {} + (Ok(_), Some(expected_error)) => { + panic!("Expected error `{expected_error}`, but succeeded instead.") + } + (Err(error), None) => { + panic!("Should have succeeded, but panicked with `{error}` instead") + } + (Err(error), Some(expected_error)) => { + assert_eq!(error.variant_name(), expected_error); + } + } +} + +test_each_file! { for ["test"] in "./programs/good" as parse_succeed => parse } +test_each_file! { for ["test"] in "./programs/fail/parse" as parse_fail => parse } diff --git a/compiler/src/passes/parse/types.rs b/compiler/src/passes/parse/types.rs index 31f7e22..c698c30 100644 --- a/compiler/src/passes/parse/types.rs +++ b/compiler/src/passes/parse/types.rs @@ -1,13 +1,15 @@ use derive_more::Display; -use functor_derive::Functor; use itertools::Itertools; use std::fmt::Display; -use std::hash::Hash; -#[derive(Debug, Clone, PartialEq, Display)] -pub enum Type { - #[display(fmt = "Int")] - Int, +//TODO generic not needed +#[derive(Debug, Clone, Display)] +#[display(bound = "A: Display")] +pub enum Type { + #[display(fmt = "I64")] + I64, + #[display(fmt = "U64")] + U64, #[display(fmt = "Bool")] Bool, #[display(fmt = "Unit")] @@ -22,26 +24,3 @@ pub enum Type { #[display(fmt = "{sym}")] Var { sym: A }, } - -impl Type { - pub fn fmap<__B: Hash + Eq + Display>(self, __f: impl Fn(A) -> __B) -> Type<__B> { - fn fmap_ref( - s: Type, - __f: &impl Fn(A) -> __B, - ) -> Type<__B> { - match s { - Type::Int => Type::Int, - Type::Bool => Type::Bool, - Type::Unit => Type::Unit, - Type::Never => Type::Never, - Type::Fn { typ, params: args } => Type::Fn { - typ: typ.fmap(|v| fmap_ref(v, __f)), - params: args.fmap(|v| fmap_ref(v, __f)), - }, - Type::Var { sym } => Type::Var { sym: __f(sym) }, - } - } - - fmap_ref(self, &__f) - } -} diff --git a/compiler/src/passes/patch_instructions/mod.rs b/compiler/src/passes/patch/mod.rs similarity index 81% rename from compiler/src/passes/patch_instructions/mod.rs rename to compiler/src/passes/patch/mod.rs index 684cb3c..d8a5988 100644 --- a/compiler/src/passes/patch_instructions/mod.rs +++ b/compiler/src/passes/patch/mod.rs @@ -1,13 +1,14 @@ -use crate::passes::interference::Arg; -use crate::passes::select::io::Std; +pub mod patch; +#[cfg(test)] +mod tests; + +use crate::passes::assign::Arg; +use crate::passes::select::std_lib::Std; use crate::passes::select::{Block, X86Selected}; use crate::utils::gen_sym::UniqueSym; use functor_derive::Functor; use std::collections::HashMap; -pub mod patch_instructions; - -#[derive(Debug, PartialEq)] pub struct X86Patched<'p> { pub blocks: HashMap, Block<'p, Arg>>, pub entry: UniqueSym<'p>, diff --git a/compiler/src/passes/patch/patch.rs b/compiler/src/passes/patch/patch.rs new file mode 100644 index 0000000..6095149 --- /dev/null +++ b/compiler/src/passes/patch/patch.rs @@ -0,0 +1,46 @@ +use crate::passes::assign::{Arg, X86Assigned}; +use crate::passes::patch::X86Patched; +use crate::passes::select::{Block, Instr}; +use crate::{addq, movq, reg, subq}; + +impl<'p> X86Assigned<'p> { + #[must_use] + pub fn patch(self) -> X86Patched<'p> { + X86Patched { + blocks: self + .blocks + .into_iter() + .map(|(lbl, block)| (lbl, patch_block(block))) + .collect(), + entry: self.entry, + stack_space: self.stack_space, + std: self.std, + } + } +} + +fn patch_block(block: Block<'_, Arg>) -> Block<'_, Arg> { + Block { + instrs: block + .instrs + .into_iter() + .flat_map(patch_instr) + .collect::>(), + } +} + +fn patch_instr(instr: Instr<'_, Arg>) -> Vec> { + match instr { + Instr::Addq { src, dst } => patch_args(src, dst, |src, dst| addq!(src, dst)), + Instr::Subq { src, dst } => patch_args(src, dst, |src, dst| subq!(src, dst)), + Instr::Movq { src, dst } => patch_args(src, dst, |src, dst| movq!(src, dst)), + _ => vec![instr], + } +} + +fn patch_args<'p>(src: Arg, dst: Arg, op: fn(Arg, Arg) -> Instr<'p, Arg>) -> Vec> { + match (&src, &dst) { + (Arg::Deref { .. }, Arg::Deref { .. }) => vec![movq!(src, reg!(RAX)), op(reg!(RAX), dst)], + _ => vec![op(src, dst)], + } +} diff --git a/compiler/src/passes/patch/tests.rs b/compiler/src/passes/patch/tests.rs new file mode 100644 index 0000000..1e85261 --- /dev/null +++ b/compiler/src/passes/patch/tests.rs @@ -0,0 +1,44 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::passes::select::X86Selected; +use crate::utils::gen_sym::gen_sym; +use crate::utils::split_test::split_test; +use crate::{block, callq_direct, movq, reg}; +use test_each_file::test_each_file; + +fn patch([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let mut program: X86Selected = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .atomize() + .explicate() + .eliminate() + .select() + .assign() + .patch() + .into(); + + // Redirect program to exit + let new_entry = gen_sym("tmp"); + program.blocks.insert( + new_entry, + block!( + callq_direct!(program.entry, 0), + movq!(reg!(RAX), reg!(RDI)), + callq_direct!(program.std["exit"], 1) + ), + ); + program.entry = new_entry; + + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as patch => patch } diff --git a/compiler/src/passes/patch_instructions/patch_instructions.rs b/compiler/src/passes/patch_instructions/patch_instructions.rs deleted file mode 100644 index fa34fc3..0000000 --- a/compiler/src/passes/patch_instructions/patch_instructions.rs +++ /dev/null @@ -1,97 +0,0 @@ -use crate::passes::interference::{Arg, X86Assigned}; -use crate::passes::patch_instructions::X86Patched; -use crate::passes::select::{Block, Instr}; -use crate::{addq, movq, reg, subq}; - -impl<'p> X86Assigned<'p> { - #[must_use] - pub fn patch(self) -> X86Patched<'p> { - X86Patched { - blocks: self - .blocks - .into_iter() - .map(|(lbl, block)| (lbl, patch_block(block))) - .collect(), - entry: self.entry, - stack_space: self.stack_space, - std: self.std, - } - } -} - -fn patch_block(block: Block<'_, Arg>) -> Block<'_, Arg> { - Block { - instrs: block - .instrs - .into_iter() - .flat_map(patch_instr) - .collect::>(), - } -} - -fn patch_instr(instr: Instr<'_, Arg>) -> Vec> { - match instr { - Instr::Addq { src, dst } => patch_args(src, dst, |src, dst| addq!(src, dst)), - Instr::Subq { src, dst } => patch_args(src, dst, |src, dst| subq!(src, dst)), - Instr::Movq { src, dst } => patch_args(src, dst, |src, dst| movq!(src, dst)), - _ => vec![instr], - } -} - -fn patch_args<'p>(src: Arg, dst: Arg, op: fn(Arg, Arg) -> Instr<'p, Arg>) -> Vec> { - match (&src, &dst) { - (Arg::Deref { .. }, Arg::Deref { .. }) => vec![movq!(src, reg!(RAX)), op(reg!(RAX), dst)], - _ => vec![op(src, dst)], - } -} - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::passes::select::X86Selected; - use crate::utils::gen_sym::gen_sym; - use crate::utils::split_test::split_test; - use crate::{block, callq_direct, movq, reg}; - use test_each_file::test_each_file; - - fn patch_instructions([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let expected_return = expected_return.into(); - - let mut program: X86Selected = program - .validate() - .unwrap() - .uniquify() - .reveal() - .atomize() - .explicate() - .eliminate() - .select() - .add_liveness() - .compute_interference() - .color_interference() - .assign_homes() - .patch() - .into(); - - // Redirect program to exit - let new_entry = gen_sym("tmp"); - program.blocks.insert( - new_entry, - block!( - callq_direct!(program.entry, 0), - movq!(reg!(RAX), reg!(RDI)), - callq_direct!(program.std.exit, 1) - ), - ); - program.entry = new_entry; - - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return, "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as patch_instructions => patch_instructions } -} diff --git a/compiler/src/passes/reveal/mod.rs b/compiler/src/passes/reveal/mod.rs new file mode 100644 index 0000000..639870e --- /dev/null +++ b/compiler/src/passes/reveal/mod.rs @@ -0,0 +1,178 @@ +pub mod reveal; +#[cfg(test)] +mod tests; + +use crate::passes::parse::{BinaryOp, Def, Meta, Typed, UnaryOp}; +use crate::passes::select::std_lib::Std; +use crate::passes::validate::{DefValidated, ExprValidated, PrgValidated, TLit}; +use crate::utils::gen_sym::UniqueSym; +use functor_derive::Functor; +use std::collections::HashMap; + +pub struct PrgRevealed<'p> { + pub defs: HashMap, DefRevealed<'p>>, + pub entry: UniqueSym<'p>, + pub std: Std<'p>, +} + +pub type DefRevealed<'p> = Def, &'p str, Typed<'p, RExpr<'p>>>; + +pub enum RExpr<'p> { + Lit { + val: TLit, + }, + Var { + sym: UniqueSym<'p>, + }, + FunRef { + sym: UniqueSym<'p>, + }, + BinaryOp { + op: BinaryOp, + exprs: [Box>>; 2], + }, + UnaryOp { + op: UnaryOp, + expr: Box>>, + }, + Let { + sym: UniqueSym<'p>, + bnd: Box>>, + bdy: Box>>, + }, + If { + cnd: Box>>, + thn: Box>>, + els: Box>>, + }, + Apply { + fun: Box>>, + args: Vec>>, + }, + Loop { + bdy: Box>>, + }, + Break { + bdy: Box>>, + }, + Return { + bdy: Box>>, + }, + Continue, + Seq { + stmt: Box>>, + cnt: Box>>, + }, + Assign { + sym: UniqueSym<'p>, + bnd: Box>>, + }, + Struct { + sym: UniqueSym<'p>, + fields: Vec<(&'p str, Typed<'p, RExpr<'p>>)>, + }, + AccessField { + strct: Box>>, + field: &'p str, + }, +} + +impl<'p> From> for PrgValidated<'p> { + fn from(value: PrgRevealed<'p>) -> Self { + PrgValidated { + defs: value + .defs + .into_iter() + .map(|(sym, def)| (sym, def.into())) + .collect(), + entry: value.entry, + std: value.std, + } + } +} + +impl<'p> From> for DefValidated<'p> { + fn from(value: DefRevealed<'p>) -> Self { + match value { + DefRevealed::Fn { + sym, + params, + typ, + bdy, + } => DefValidated::Fn { + sym, + params, + typ, + bdy: bdy.into(), + }, + DefRevealed::TypeDef { sym, def } => DefValidated::TypeDef { sym, def }, + } + } +} + +impl<'p> From>> for Typed<'p, ExprValidated<'p>> { + fn from(value: Typed<'p, RExpr<'p>>) -> Self { + let inner = match value.inner { + RExpr::Lit { val } => ExprValidated::Lit { val }, + RExpr::BinaryOp { op, exprs } => ExprValidated::BinaryOp { + op, + exprs: exprs.map(|expr| expr.fmap(Into::into)), + }, + RExpr::UnaryOp { op, expr } => ExprValidated::UnaryOp { + op, + expr: expr.fmap(Into::into), + }, + RExpr::Let { sym, bnd, bdy } => ExprValidated::Let { + sym, + mutable: true, + typ: None, + bnd: Box::new((*bnd).into()), + bdy: Box::new((*bdy).into()), + }, + RExpr::If { cnd, thn, els } => ExprValidated::If { + cnd: Box::new((*cnd).into()), + thn: Box::new((*thn).into()), + els: Box::new((*els).into()), + }, + RExpr::Apply { fun, args } => ExprValidated::Apply { + fun: Box::new((*fun).into()), + args: args.into_iter().map(Into::into).collect(), + }, + RExpr::Var { sym } | RExpr::FunRef { sym } => ExprValidated::Var { sym }, + RExpr::Loop { bdy } => ExprValidated::Loop { + bdy: Box::new((*bdy).into()), + }, + RExpr::Break { bdy } => ExprValidated::Break { + bdy: Box::new((*bdy).into()), + }, + RExpr::Seq { stmt, cnt } => ExprValidated::Seq { + stmt: Box::new((*stmt).into()), + cnt: Box::new((*cnt).into()), + }, + RExpr::Assign { sym, bnd } => ExprValidated::Assign { + sym, + bnd: Box::new((*bnd).into()), + }, + RExpr::Continue => ExprValidated::Continue, + RExpr::Return { bdy } => ExprValidated::Return { + bdy: Box::new((*bdy).into()), + }, + RExpr::Struct { sym, fields } => ExprValidated::Struct { + sym, + fields: fields + .into_iter() + .map(|(sym, expr)| (sym, expr.into())) + .collect(), + }, + RExpr::AccessField { strct, field } => ExprValidated::AccessField { + strct: Box::new((*strct).into()), + field, + }, + }; + + Meta { + inner, + meta: value.meta, + } + } +} diff --git a/compiler/src/passes/reveal_functions/reveal_functions.rs b/compiler/src/passes/reveal/reveal.rs similarity index 50% rename from compiler/src/passes/reveal_functions/reveal_functions.rs rename to compiler/src/passes/reveal/reveal.rs index 87ed233..1db1bfe 100644 --- a/compiler/src/passes/reveal_functions/reveal_functions.rs +++ b/compiler/src/passes/reveal/reveal.rs @@ -1,14 +1,15 @@ -use crate::passes::parse::Def; -use crate::passes::reveal_functions::{PrgRevealed, RExpr}; -use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::TExpr; +use crate::passes::parse::{Meta, Typed}; +use crate::passes::reveal::{DefRevealed, PrgRevealed, RExpr}; +use crate::passes::validate::{DefValidated, ExprValidated, PrgValidated}; use crate::utils::gen_sym::UniqueSym; use crate::utils::push_map::PushMap; -impl<'p> PrgUniquified<'p> { +impl<'p> PrgValidated<'p> { #[must_use] pub fn reveal(self) -> PrgRevealed<'p> { - let mut scope = PushMap::from_iter(self.defs.keys().map(|s| (*s, ()))); + let mut scope = + PushMap::from_iter(self.defs.keys().chain(self.std.values()).map(|s| (*s, ()))); + PrgRevealed { defs: self .defs @@ -16,113 +17,113 @@ impl<'p> PrgUniquified<'p> { .map(|(sym, def)| (sym, reveal_def(def, &mut scope))) .collect(), entry: self.entry, + std: self.std, } } } fn reveal_def<'p>( - def: Def<'p, UniqueSym<'p>, TExpr<'p, UniqueSym<'p>>>, + def: DefValidated<'p>, scope: &mut PushMap, ()>, -) -> Def<'p, UniqueSym<'p>, RExpr<'p>> { +) -> DefRevealed<'p> { match def { - Def::Fn { + DefValidated::Fn { sym, params, typ, bdy, - } => Def::Fn { + } => DefRevealed::Fn { sym, params, typ, bdy: reveal_expr(bdy, scope), }, - Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, + DefValidated::TypeDef { sym, def } => DefRevealed::TypeDef { sym, def }, } } fn reveal_expr<'p>( - expr: TExpr<'p, UniqueSym<'p>>, + expr: Typed<'p, ExprValidated<'p>>, scope: &mut PushMap, ()>, -) -> RExpr<'p> { - match expr { - TExpr::Lit { val, typ } => RExpr::Lit { val, typ }, - TExpr::Var { sym, typ } => { +) -> Typed<'p, RExpr<'p>> { + let inner = match expr.inner { + ExprValidated::Lit { val } => RExpr::Lit { val }, + ExprValidated::Var { sym } => { if scope.contains(&sym) { - RExpr::FunRef { sym, typ } + RExpr::FunRef { sym } } else { - RExpr::Var { sym, typ } + RExpr::Var { sym } } } - TExpr::Prim { op, args, typ } => RExpr::Prim { + ExprValidated::UnaryOp { op, expr } => RExpr::UnaryOp { op, - args: args - .into_iter() - .map(|arg| reveal_expr(arg, scope)) - .collect(), - typ, + expr: Box::new(reveal_expr(*expr, scope)), }, - TExpr::Let { - sym, bnd, bdy, typ, .. - } => { + ExprValidated::BinaryOp { + op, + exprs: [lhs, rhs], + } => RExpr::BinaryOp { + op, + exprs: [ + Box::new(reveal_expr(*lhs, scope)), + Box::new(reveal_expr(*rhs, scope)), + ], + }, + ExprValidated::Let { sym, bnd, bdy, .. } => { let bnd = Box::new(reveal_expr(*bnd, scope)); scope.remove(sym, |scope| RExpr::Let { sym, bnd, bdy: Box::new(reveal_expr(*bdy, scope)), - typ, }) } - TExpr::If { cnd, thn, els, typ } => RExpr::If { + ExprValidated::If { cnd, thn, els } => RExpr::If { cnd: Box::new(reveal_expr(*cnd, scope)), thn: Box::new(reveal_expr(*thn, scope)), els: Box::new(reveal_expr(*els, scope)), - typ, }, - TExpr::Apply { fun, args, typ } => RExpr::Apply { + ExprValidated::Apply { fun, args } => RExpr::Apply { fun: Box::new(reveal_expr(*fun, scope)), args: args .into_iter() .map(|arg| reveal_expr(arg, scope)) .collect(), - typ, }, - TExpr::Loop { bdy, typ } => RExpr::Loop { + ExprValidated::Loop { bdy } => RExpr::Loop { bdy: Box::new(reveal_expr(*bdy, scope)), - typ, }, - TExpr::Break { bdy, typ } => RExpr::Break { + ExprValidated::Break { bdy } => RExpr::Break { bdy: Box::new(reveal_expr(*bdy, scope)), - typ, }, - TExpr::Seq { stmt, cnt, typ } => RExpr::Seq { + ExprValidated::Seq { stmt, cnt } => RExpr::Seq { stmt: Box::new(reveal_expr(*stmt, scope)), cnt: Box::new(reveal_expr(*cnt, scope)), - typ, }, - TExpr::Assign { sym, bnd, typ } => RExpr::Assign { + ExprValidated::Assign { sym, bnd } => RExpr::Assign { sym, bnd: Box::new(reveal_expr(*bnd, scope)), - typ, }, - TExpr::Continue { typ } => RExpr::Continue { typ }, - TExpr::Return { bdy, typ } => RExpr::Return { + ExprValidated::Continue => RExpr::Continue, + ExprValidated::Return { bdy } => RExpr::Return { bdy: Box::new(reveal_expr(*bdy, scope)), - typ, }, - TExpr::Struct { sym, fields, typ } => RExpr::Struct { + ExprValidated::Struct { sym, fields } => RExpr::Struct { sym, fields: fields .into_iter() .map(|(sym, expr)| (sym, reveal_expr(expr, scope))) .collect(), - typ, }, - TExpr::AccessField { strct, field, typ } => RExpr::AccessField { + ExprValidated::AccessField { strct, field } => RExpr::AccessField { strct: Box::new(reveal_expr(*strct, scope)), field, - typ, }, - TExpr::Variant { .. } => todo!(), - TExpr::Switch { .. } => todo!(), + ExprValidated::Variant { .. } => todo!(), + ExprValidated::Switch { .. } => todo!(), + }; + + Meta { + meta: expr.meta, + inner, } } diff --git a/compiler/src/passes/reveal/tests.rs b/compiler/src/passes/reveal/tests.rs new file mode 100644 index 0000000..9bdf7f8 --- /dev/null +++ b/compiler/src/passes/reveal/tests.rs @@ -0,0 +1,24 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::passes::validate::PrgValidated; +use crate::utils::split_test::split_test; +use test_each_file::test_each_file; + +fn reveal([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let program: PrgValidated = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .into(); + + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as reveal => reveal } diff --git a/compiler/src/passes/reveal_functions/mod.rs b/compiler/src/passes/reveal_functions/mod.rs deleted file mode 100644 index b6dc993..0000000 --- a/compiler/src/passes/reveal_functions/mod.rs +++ /dev/null @@ -1,231 +0,0 @@ -pub mod reveal_functions; - -use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Op}; -use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::{TExpr, TLit}; -use crate::utils::gen_sym::UniqueSym; -use std::collections::HashMap; - -#[derive(Debug, PartialEq)] -pub struct PrgRevealed<'p> { - pub defs: HashMap, Def<'p, UniqueSym<'p>, RExpr<'p>>>, - pub entry: UniqueSym<'p>, -} - -#[derive(Debug, PartialEq)] -pub enum RExpr<'p> { - Lit { - val: TLit, - typ: Type>, - }, - Var { - sym: UniqueSym<'p>, - typ: Type>, - }, - FunRef { - sym: UniqueSym<'p>, - typ: Type>, - }, - Prim { - op: Op, - args: Vec>, - typ: Type>, - }, - Let { - sym: UniqueSym<'p>, - bnd: Box>, - bdy: Box>, - typ: Type>, - }, - If { - cnd: Box>, - thn: Box>, - els: Box>, - typ: Type>, - }, - Apply { - fun: Box>, - args: Vec>, - typ: Type>, - }, - Loop { - bdy: Box>, - typ: Type>, - }, - Break { - bdy: Box>, - typ: Type>, - }, - Return { - bdy: Box>, - typ: Type>, - }, - Continue { - typ: Type>, - }, - Seq { - stmt: Box>, - cnt: Box>, - typ: Type>, - }, - Assign { - sym: UniqueSym<'p>, - bnd: Box>, - typ: Type>, - }, - Struct { - sym: UniqueSym<'p>, - fields: Vec<(&'p str, RExpr<'p>)>, - typ: Type>, - }, - AccessField { - strct: Box>, - field: &'p str, - typ: Type>, - }, -} - -impl<'p> RExpr<'p> { - pub fn typ(&self) -> &Type> { - match self { - RExpr::Var { typ, .. } - | RExpr::Lit { typ, .. } - | RExpr::Prim { typ, .. } - | RExpr::Let { typ, .. } - | RExpr::If { typ, .. } - | RExpr::Apply { typ, .. } - | RExpr::Loop { typ, .. } - | RExpr::Break { typ, .. } - | RExpr::Continue { typ, .. } - | RExpr::Return { typ, .. } - | RExpr::Seq { typ, .. } - | RExpr::Assign { typ, .. } - | RExpr::Struct { typ, .. } - | RExpr::AccessField { typ, .. } - | RExpr::FunRef { typ, .. } => typ, - } - } -} - -impl<'p> From> for PrgUniquified<'p> { - fn from(value: PrgRevealed<'p>) -> Self { - PrgUniquified { - defs: value - .defs - .into_iter() - .map(|(sym, def)| (sym, def.into())) - .collect(), - entry: value.entry, - } - } -} - -impl<'p> From, RExpr<'p>>> - for Def<'p, UniqueSym<'p>, TExpr<'p, UniqueSym<'p>>> -{ - fn from(value: Def<'p, UniqueSym<'p>, RExpr<'p>>) -> Self { - match value { - Def::Fn { - sym, - params, - typ, - bdy, - } => Def::Fn { - sym, - params, - typ, - bdy: bdy.into(), - }, - Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, - } - } -} - -impl<'p> From> for TExpr<'p, UniqueSym<'p>> { - fn from(value: RExpr<'p>) -> Self { - match value { - RExpr::Lit { val, typ } => TExpr::Lit { val, typ }, - RExpr::Prim { op, args, typ } => TExpr::Prim { - op, - args: args.into_iter().map(Into::into).collect(), - typ, - }, - RExpr::Let { sym, bnd, bdy, typ } => TExpr::Let { - sym, - bnd: Box::new((*bnd).into()), - bdy: Box::new((*bdy).into()), - typ, - }, - RExpr::If { cnd, thn, els, typ } => TExpr::If { - cnd: Box::new((*cnd).into()), - thn: Box::new((*thn).into()), - els: Box::new((*els).into()), - typ, - }, - RExpr::Apply { fun, args, typ } => TExpr::Apply { - fun: Box::new((*fun).into()), - args: args.into_iter().map(Into::into).collect(), - typ, - }, - RExpr::Var { sym, typ } | RExpr::FunRef { sym, typ } => TExpr::Var { sym, typ }, - RExpr::Loop { bdy, typ } => TExpr::Loop { - bdy: Box::new((*bdy).into()), - typ, - }, - RExpr::Break { bdy, typ } => TExpr::Break { - bdy: Box::new((*bdy).into()), - typ, - }, - RExpr::Seq { stmt, cnt, typ } => TExpr::Seq { - stmt: Box::new((*stmt).into()), - cnt: Box::new((*cnt).into()), - typ, - }, - RExpr::Assign { sym, bnd, typ } => TExpr::Assign { - sym, - bnd: Box::new((*bnd).into()), - typ, - }, - RExpr::Continue { typ } => TExpr::Continue { typ }, - RExpr::Return { bdy, typ } => TExpr::Return { - bdy: Box::new((*bdy).into()), - typ, - }, - RExpr::Struct { sym, fields, typ } => TExpr::Struct { - sym, - fields: fields - .into_iter() - .map(|(sym, expr)| (sym, expr.into())) - .collect(), - typ, - }, - RExpr::AccessField { strct, field, typ } => TExpr::AccessField { - strct: Box::new((*strct).into()), - field, - typ, - }, - } - } -} - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::passes::uniquify::PrgUniquified; - use crate::utils::split_test::split_test; - use test_each_file::test_each_file; - - fn reveal([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let uniquified_program: PrgUniquified = - program.validate().unwrap().uniquify().reveal().into(); - let mut io = TestIO::new(input); - let result = uniquified_program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as reveal => reveal } -} diff --git a/compiler/src/passes/select/interpreter.rs b/compiler/src/passes/select/interpreter.rs index 137aa03..ecaf990 100644 --- a/compiler/src/passes/select/interpreter.rs +++ b/compiler/src/passes/select/interpreter.rs @@ -358,7 +358,7 @@ impl<'p, I: IO> X86Interpreter<'p, I> { .unwrap() .parse() .unwrap(); - self.io.print(TLit::Int { val }); + self.io.print(TLit::I64 { val }); self.write_buffer.clear(); } val => { diff --git a/compiler/src/passes/select/macros.rs b/compiler/src/passes/select/macros.rs index bab6e64..cdc3717 100644 --- a/compiler/src/passes/select/macros.rs +++ b/compiler/src/passes/select/macros.rs @@ -187,14 +187,14 @@ macro_rules! syscall { #[macro_export] macro_rules! imm { ($val:expr) => { - $crate::passes::interference::Arg::Imm { val: $val.into() }.into() + $crate::passes::assign::Arg::Imm { val: $val.into() }.into() }; } #[macro_export] macro_rules! reg { ($reg:ident) => { - $crate::passes::interference::Arg::Reg { + $crate::passes::assign::Arg::Reg { reg: $crate::passes::select::Reg::$reg, } .into() @@ -211,7 +211,7 @@ macro_rules! var { #[macro_export] macro_rules! deref { ($reg:ident, $off:expr) => { - $crate::passes::interference::Arg::Deref { + $crate::passes::assign::Arg::Deref { reg: $crate::passes::select::Reg::$reg, off: $off, } diff --git a/compiler/src/passes/select/mod.rs b/compiler/src/passes/select/mod.rs index 63c12ae..b9b7e7b 100644 --- a/compiler/src/passes/select/mod.rs +++ b/compiler/src/passes/select/mod.rs @@ -1,4 +1,11 @@ -use crate::passes::select::io::Std; +pub mod interpreter; +pub mod macros; +pub mod select; +pub mod std_lib; +#[cfg(test)] +mod tests; + +use crate::passes::select::std_lib::Std; use crate::utils::gen_sym::UniqueSym; use derive_more::Display; use functor_derive::Functor; @@ -6,12 +13,7 @@ use itertools::Itertools; use std::collections::HashMap; use std::fmt::Display; -pub mod interpreter; -pub mod io; -pub mod macros; -pub mod select; - -#[derive(Debug, PartialEq, Display)] +#[derive(Display)] #[display( fmt = "{}", r#"blocks.iter().map(|(sym, block)| format!("{sym}:\n{block}")).format("\n")"# @@ -22,13 +24,13 @@ pub struct X86Selected<'p> { pub std: Std<'p>, } -#[derive(Debug, PartialEq, Clone, Display, Functor)] +#[derive(Clone, Display, Functor)] #[display(fmt = "\t{}", r#"instrs.iter().format("\n\t")"#)] pub struct Block<'p, A: Display> { pub instrs: Vec>, } -#[derive(Copy, Clone, Debug, PartialEq, Display)] +#[derive(Copy, Clone, PartialEq, Display)] pub enum Cnd { Above, AboveOrEqual, @@ -50,7 +52,7 @@ pub enum Cnd { Sign, } -#[derive(Clone, Debug, PartialEq, Display, Functor)] +#[derive(Clone, PartialEq, Display, Functor)] pub enum Instr<'p, A: Display> { #[display(fmt = "addq\t{src}\t{dst}")] Addq { src: A, dst: A }, @@ -96,7 +98,7 @@ pub enum Instr<'p, A: Display> { CallqIndirect { src: A, arity: usize }, } -#[derive(Debug, PartialEq, Clone, Copy, Hash, Eq, Display)] +#[derive(PartialEq, Clone, Display)] pub enum VarArg<'p> { #[display(fmt = "${val}")] Imm { val: i64 }, @@ -140,7 +142,7 @@ pub const SYSCALL_REGS: [Reg; 7] = [ Reg::R9, ]; -#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash, Ord, PartialOrd, Display)] +#[derive(Hash, Clone, Copy, Eq, Ord, PartialEq, PartialOrd, Display)] #[allow(clippy::upper_case_acronyms)] pub enum Reg { RSP, @@ -160,47 +162,3 @@ pub enum Reg { R14, R15, } - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::utils::gen_sym::gen_sym; - use crate::utils::split_test::split_test; - use crate::{block, callq_direct, movq, reg}; - use test_each_file::test_each_file; - - fn select([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let expected_return = expected_return.into(); - - let mut program = program - .validate() - .unwrap() - .uniquify() - .reveal() - .atomize() - .explicate() - .eliminate() - .select(); - - // Redirect program to exit - let new_entry = gen_sym("tmp"); - program.blocks.insert( - new_entry, - block!( - callq_direct!(program.entry, 0), - movq!(reg!(RAX), reg!(RDI)), - callq_direct!(program.std.exit, 1) - ), - ); - program.entry = new_entry; - - let mut io = TestIO::new(input); - let result = program.interpret(&mut io); - - assert_eq!(result, expected_return, "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as select_instructions => select } -} diff --git a/compiler/src/passes/select/select.rs b/compiler/src/passes/select/select.rs index 954b1de..13de1f6 100644 --- a/compiler/src/passes/select/select.rs +++ b/compiler/src/passes/select/select.rs @@ -1,7 +1,8 @@ use crate::passes::atomize::Atom; -use crate::passes::eliminate_algebraic::{EExpr, ETail, PrgEliminated}; -use crate::passes::parse::{Op, Param}; -use crate::passes::select::io::Std; +use crate::passes::eliminate::{EExpr, ETail, PrgEliminated}; +use crate::passes::parse::types::Type; +use crate::passes::parse::{BinaryOp, Meta, Param, UnaryOp}; +use crate::passes::select::std_lib::add_std_library; use crate::passes::select::{ Block, Cnd, Instr, VarArg, X86Selected, CALLEE_SAVED_NO_STACK, CALLER_SAVED, }; @@ -13,18 +14,20 @@ impl<'p> PrgEliminated<'p> { #[must_use] pub fn select(self) -> X86Selected<'p> { let mut blocks = HashMap::new(); - let std = Std::new(&mut blocks); blocks.extend( self.blocks .into_iter() - .map(|(sym, block)| (sym, select_block(sym, block, &std, &self.fn_params))), + .map(|(sym, block)| (sym, select_block(sym, block, &self.fn_params))), ); + add_std_library(&self.std, &mut blocks); + X86Selected { blocks, entry: self.entry, - std, + // todo: technically we only need this for testing + std: self.std, } } } @@ -32,7 +35,6 @@ impl<'p> PrgEliminated<'p> { fn select_block<'p>( sym: UniqueSym<'p>, tail: ETail<'p>, - std: &Std<'p>, fn_params: &HashMap, Vec>>>, ) -> Block<'p, VarArg<'p>> { let mut instrs = Vec::new(); @@ -53,12 +55,12 @@ fn select_block<'p>( ); } - select_tail(tail, &mut instrs, std); + select_tail(tail, &mut instrs); Block { instrs } } -fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>>, std: &Std<'p>) { +fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>>) { match tail { ETail::Return { exprs } => { assert!( @@ -66,8 +68,8 @@ fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>>, std "Argument passing to stack is not yet implemented." ); - for (reg, (arg, _)) in CALLER_SAVED.into_iter().zip(exprs) { - instrs.push(movq!(select_atom(&arg), VarArg::Reg { reg })); + for (reg, arg) in CALLER_SAVED.into_iter().zip(exprs) { + instrs.push(movq!(select_atom(arg), VarArg::Reg { reg })); } for reg in CALLEE_SAVED_NO_STACK.into_iter().rev() { @@ -82,15 +84,19 @@ fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>>, std bnd, tail, } => { - instrs.extend(select_assign(&sym, bnd, std)); - select_tail(*tail, instrs, std); + instrs.extend(select_assign(&sym, bnd)); + select_tail(*tail, instrs); } ETail::IfStmt { cnd, thn, els } => match cnd { - EExpr::Prim { op, args, .. } => { + EExpr::BinaryOp { + op, + exprs: [expr_lhs, expr_rhs], + .. + } => { let tmp = gen_sym("tmp"); instrs.extend(vec![ - movq!(select_atom(&args[0]), var!(tmp)), - cmpq!(select_atom(&args[1]), var!(tmp)), + movq!(select_atom(expr_lhs), var!(tmp)), + cmpq!(select_atom(expr_rhs), var!(tmp)), jcc!(thn, select_cmp(op)), jmp!(els), ]); @@ -105,11 +111,10 @@ fn select_tail<'p>(tail: ETail<'p>, instrs: &mut Vec>>, std fn select_assign<'p>( dsts: &[UniqueSym<'p>], - expr: EExpr<'p>, - std: &Std<'p>, + expr: Meta>>, EExpr<'p>>, ) -> Vec>> { let dst = var!(dsts[0]); - match expr { + match expr.inner { EExpr::Atom { atm: Atom::Val { val }, .. @@ -118,43 +123,56 @@ fn select_assign<'p>( atm: Atom::Var { sym }, .. } => vec![movq!(var!(sym), dst)], - EExpr::Prim { op, args, .. } => match (op, args.as_slice()) { - (Op::Plus, [a0, a1]) => vec![movq!(select_atom(a0), dst), addq!(select_atom(a1), dst)], - (Op::Minus, [a0, a1]) => vec![movq!(select_atom(a0), dst), subq!(select_atom(a1), dst)], - (Op::Minus, [a0]) => vec![movq!(select_atom(a0), dst), negq!(dst)], - (Op::Mul, [a0, a1]) => vec![ + EExpr::BinaryOp { + op, + exprs: [a0, a1], + } => match op { + BinaryOp::Add => vec![ + movq!(select_atom(a0), dst.clone()), + addq!(select_atom(a1), dst), + ], + BinaryOp::Sub => vec![ + movq!(select_atom(a0), dst.clone()), + subq!(select_atom(a1), dst), + ], + BinaryOp::Mul => vec![ movq!(select_atom(a1), reg!(RAX)), movq!(select_atom(a0), reg!(RBX)), mulq!(reg!(RBX)), movq!(reg!(RAX), dst), ], - (Op::Div, [a0, a1]) => vec![ + BinaryOp::Div => vec![ movq!(imm!(0), reg!(RDX)), movq!(select_atom(a0), reg!(RAX)), movq!(select_atom(a1), reg!(RBX)), divq!(reg!(RBX)), movq!(reg!(RAX), dst), ], - (Op::Mod, [a0, a1]) => vec![ + BinaryOp::Mod => vec![ movq!(imm!(0), reg!(RDX)), movq!(select_atom(a0), reg!(RAX)), movq!(select_atom(a1), reg!(RBX)), divq!(reg!(RBX)), movq!(reg!(RDX), dst), ], - (Op::Read, []) => { - vec![callq_direct!(std.read_int, 0), movq!(reg!(RAX), dst)] - } - (Op::Print, [a0]) => vec![ - movq!(select_atom(a0), reg!(RDI)), - callq_direct!(std.print_int, 1), - movq!(select_atom(a0), dst), + BinaryOp::LAnd => vec![ + movq!(select_atom(a0), dst.clone()), + andq!(select_atom(a1), dst), + ], + BinaryOp::LOr => vec![ + movq!(select_atom(a0), dst.clone()), + orq!(select_atom(a1), dst), + ], + BinaryOp::Xor => vec![ + movq!(select_atom(a0), dst.clone()), + xorq!(select_atom(a1), dst), ], - (Op::LAnd, [a0, a1]) => vec![movq!(select_atom(a0), dst), andq!(select_atom(a1), dst)], - (Op::LOr, [a0, a1]) => vec![movq!(select_atom(a0), dst), orq!(select_atom(a1), dst)], - (Op::Not, [a0]) => vec![movq!(select_atom(a0), dst), xorq!(imm!(1), dst)], - (Op::Xor, [a0, a1]) => vec![movq!(select_atom(a0), dst), xorq!(select_atom(a1), dst)], - (op @ (Op::GT | Op::GE | Op::EQ | Op::LE | Op::LT | Op::NE), [a0, a1]) => { + op @ (BinaryOp::GT + | BinaryOp::GE + | BinaryOp::EQ + | BinaryOp::LE + | BinaryOp::LT + | BinaryOp::NE) => { let tmp = gen_sym("tmp"); vec![ movq!(select_atom(a0), var!(tmp)), @@ -164,21 +182,24 @@ fn select_assign<'p>( movq!(reg!(RAX), dst), ] } - _ => panic!("Encountered Prim with incorrect arity during select instructions pass."), + }, + EExpr::UnaryOp { op, expr: a0 } => match op { + UnaryOp::Neg => vec![movq!(select_atom(a0), dst.clone()), negq!(dst)], + UnaryOp::Not => vec![movq!(select_atom(a0), dst.clone()), xorq!(imm!(1), dst)], }, EExpr::FunRef { sym, .. } => vec![load_lbl!(sym, dst)], EExpr::Apply { fun, args, .. } => { let mut instrs = vec![]; - for ((arg, _), reg) in args.iter().zip(CALLER_SAVED.into_iter()) { - instrs.push(movq!(select_atom(arg), VarArg::Reg { reg })); + for (arg, reg) in args.iter().zip(CALLER_SAVED.into_iter()) { + instrs.push(movq!(select_atom(*arg), VarArg::Reg { reg })); } assert!( args.len() <= 9, "Argument passing to stack is not yet implemented." ); - instrs.push(callq_indirect!(select_atom(&fun), args.len())); + instrs.push(callq_indirect!(select_atom(fun), args.len())); for (reg, dst) in CALLER_SAVED.into_iter().zip(dsts) { instrs.push(movq!(VarArg::Reg { reg }, var!(*dst))); @@ -189,21 +210,21 @@ fn select_assign<'p>( } } -fn select_atom<'p>(expr: &Atom<'p>) -> VarArg<'p> { +fn select_atom(expr: Atom) -> VarArg { match expr { - Atom::Val { val } => imm!(*val), - Atom::Var { sym } => var!(*sym), + Atom::Val { val } => imm!(val), + Atom::Var { sym } => var!(sym), } } -fn select_cmp(op: Op) -> Cnd { +fn select_cmp(op: BinaryOp) -> Cnd { match op { - Op::GT => Cnd::GT, - Op::GE => Cnd::GE, - Op::EQ => Cnd::EQ, - Op::LE => Cnd::LE, - Op::LT => Cnd::LT, - Op::NE => Cnd::NE, + BinaryOp::GT => Cnd::GT, + BinaryOp::GE => Cnd::GE, + BinaryOp::EQ => Cnd::EQ, + BinaryOp::LE => Cnd::LE, + BinaryOp::LT => Cnd::LT, + BinaryOp::NE => Cnd::NE, _ => unreachable!(), } } diff --git a/compiler/src/passes/select/io.rs b/compiler/src/passes/select/std_lib.rs similarity index 61% rename from compiler/src/passes/select/io.rs rename to compiler/src/passes/select/std_lib.rs index c153219..3369af9 100644 --- a/compiler/src/passes/select/io.rs +++ b/compiler/src/passes/select/std_lib.rs @@ -7,75 +7,74 @@ use crate::{ }; use std::collections::HashMap; -#[derive(Debug, PartialEq, Eq)] -pub struct Std<'p> { - pub exit: UniqueSym<'p>, - pub print_int: UniqueSym<'p>, - pub read_int: UniqueSym<'p>, -} - -impl<'p> Std<'p> { - pub fn new(blocks: &mut HashMap, Block<'p, VarArg>>) -> Self { - let exit = add_exit_block(blocks); +pub type Std<'p> = HashMap<&'p str, UniqueSym<'p>>; - Std { - exit, - print_int: add_print_block(blocks), - read_int: add_read_block(blocks, exit), - } - } +pub fn add_std_library<'p>(std: &Std<'p>, blocks: &mut HashMap, Block<'p, VarArg>>) { + add_exit_block(std["exit"], blocks); + add_print_block(std["print"], blocks); + add_read_block(std["read"], blocks, std["exit"]); } -fn add_exit_block<'p>(blocks: &mut HashMap, Block<'p, VarArg>>) -> UniqueSym<'p> { - let entry = gen_sym("exit"); - blocks.insert(entry, block!(movq!(imm!(0x3C), reg!(RAX)), syscall!(2))); - entry +fn add_exit_block<'p>( + entry: UniqueSym<'p>, + blocks: &mut HashMap, Block<'p, VarArg>>, +) { + blocks.insert( + entry, + block!( + movq!(reg!(RAX), reg!(RDI)), + movq!(imm!(0x3C), reg!(RAX)), + syscall!(2) + ), + ); } -fn add_print_block<'p>(blocks: &mut HashMap, Block<'p, VarArg>>) -> UniqueSym<'p> { - let entry = gen_sym("print_int"); - let print_int_neg = gen_sym("print_int_neg"); - let print_int_push_loop = gen_sym("print_int_push_loop"); - let print_int_print_loop = gen_sym("print_int_print_loop"); - let print_int_exit = gen_sym("print_int_exit"); +fn add_print_block<'p>( + entry: UniqueSym<'p>, + blocks: &mut HashMap, Block<'p, VarArg>>, +) { + let print_neg = gen_sym("print_neg"); + let print_push_loop = gen_sym("print_push_loop"); + let print_print_loop = gen_sym("print_print_loop"); + let print_exit = gen_sym("print_exit"); blocks.insert( entry, block!( + pushq!(reg!(RAX)), movq!(imm!(10), reg!(RCX)), pushq!(imm!(i64::from(b'\n'))), - movq!(reg!(RDI), reg!(RAX)), movq!(imm!(0), reg!(RSI)), cmpq!(imm!(0), reg!(RAX)), - jcc!(print_int_neg, Cnd::Sign), - jmp!(print_int_push_loop) + jcc!(print_neg, Cnd::Sign), + jmp!(print_push_loop) ), ); blocks.insert( - print_int_neg, + print_neg, block!( movq!(imm!(1), reg!(RSI)), negq!(reg!(RAX)), - jmp!(print_int_push_loop) + jmp!(print_push_loop) ), ); blocks.insert( - print_int_push_loop, + print_push_loop, block!( movq!(imm!(0), reg!(RDX)), divq!(reg!(RCX)), addq!(imm!(i64::from(b'0')), reg!(RDX)), pushq!(reg!(RDX)), cmpq!(imm!(0), reg!(RAX)), - jcc!(print_int_push_loop, Cnd::NE), + jcc!(print_push_loop, Cnd::NE), cmpq!(imm!(0), reg!(RSI)), - jcc!(print_int_print_loop, Cnd::EQ), + jcc!(print_print_loop, Cnd::EQ), pushq!(imm!(i64::from(b'-'))), - jmp!(print_int_print_loop) + jmp!(print_print_loop) ), ); blocks.insert( - print_int_print_loop, + print_print_loop, block!( // Print top of stack movq!(imm!(1), reg!(RAX)), // syscall 1: Write @@ -86,26 +85,24 @@ fn add_print_block<'p>(blocks: &mut HashMap, Block<'p, VarArg>>) - // Check if we continue popq!(reg!(RAX)), cmpq!(imm!(i64::from(b'\n')), reg!(RAX)), - jcc!(print_int_print_loop, Cnd::NE), - jmp!(print_int_exit) + jcc!(print_print_loop, Cnd::NE), + jmp!(print_exit) ), ); - blocks.insert(print_int_exit, block!(retq!())); - - entry + blocks.insert(print_exit, block!(popq!(reg!(RAX)), retq!())); } fn add_read_block<'p>( + entry: UniqueSym<'p>, blocks: &mut HashMap, Block<'p, VarArg>>, exit: UniqueSym<'p>, -) -> UniqueSym<'p> { - let entry = gen_sym("read_int"); - let read_int_is_neg = gen_sym("read_int_is_neg"); - let read_int_loop = gen_sym("read_int_loop"); - let read_int_first = gen_sym("read_int_first"); - let read_int_exit = gen_sym("read_int_exit"); - let read_int_neg = gen_sym("read_int_neg"); - let read_int_actual_exit = gen_sym("read_int_actual_exit"); +) { + let read_is_neg = gen_sym("read_is_neg"); + let read_loop = gen_sym("read_loop"); + let read_first = gen_sym("read_first"); + let read_exit = gen_sym("read_exit"); + let read_neg = gen_sym("read_neg"); + let read_actual_exit = gen_sym("read_actual_exit"); blocks.insert( entry, @@ -125,35 +122,35 @@ fn add_read_block<'p>( movq!(deref!(RSP, 0), reg!(RAX)), movq!(reg!(RAX), reg!(RCX)), cmpq!(imm!(i64::from(b'-')), reg!(RCX)), - jcc!(read_int_is_neg, Cnd::EQ), - jmp!(read_int_first) + jcc!(read_is_neg, Cnd::EQ), + jmp!(read_first) ), ); blocks.insert( - read_int_is_neg, - block!(movq!(imm!(1), reg!(R13)), jmp!(read_int_loop)), + read_is_neg, + block!(movq!(imm!(1), reg!(R13)), jmp!(read_loop)), ); blocks.insert( - read_int_loop, + read_loop, block!( movq!(imm!(0), reg!(RAX)), // READ = 0 movq!(imm!(0), reg!(RDI)), // STDIN = 0 movq!(reg!(RSP), reg!(RSI)), // RSI is pointer to allocated byte movq!(imm!(1), reg!(RDX)), // bytes to read = 1 syscall!(4), - jmp!(read_int_first) + jmp!(read_first) ), ); blocks.insert( - read_int_first, + read_first, block!( movq!(deref!(RSP, 0), reg!(RAX)), // check if newline movq!(reg!(RAX), reg!(RCX)), cmpq!(imm!(i64::from(b'\n')), reg!(RCX)), - jcc!(read_int_exit, Cnd::EQ), + jcc!(read_exit, Cnd::EQ), movq!(imm!(66), reg!(RDI)), // check if >b'9' movq!(reg!(RAX), reg!(RCX)), @@ -169,25 +166,22 @@ fn add_read_block<'p>( movq!(deref!(RSP, 0), reg!(RAX)), subq!(imm!(i64::from(b'0')), reg!(RAX)), addq!(reg!(RAX), reg!(RBX)), - jmp!(read_int_loop) + jmp!(read_loop) ), ); blocks.insert( - read_int_exit, + read_exit, block!( cmpq!(imm!(0), reg!(R13)), - jcc!(read_int_neg, Cnd::NE), - jmp!(read_int_actual_exit) + jcc!(read_neg, Cnd::NE), + jmp!(read_actual_exit) ), ); - blocks.insert( - read_int_neg, - block!(negq!(reg!(RBX)), jmp!(read_int_actual_exit)), - ); + blocks.insert(read_neg, block!(negq!(reg!(RBX)), jmp!(read_actual_exit))); blocks.insert( - read_int_actual_exit, + read_actual_exit, block!( movq!(reg!(RBX), reg!(RAX)), addq!(imm!(8), reg!(RSP)), @@ -196,6 +190,4 @@ fn add_read_block<'p>( retq!() ), ); - - entry } diff --git a/compiler/src/passes/select/tests.rs b/compiler/src/passes/select/tests.rs new file mode 100644 index 0000000..0d011ec --- /dev/null +++ b/compiler/src/passes/select/tests.rs @@ -0,0 +1,40 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::utils::gen_sym::gen_sym; +use crate::utils::split_test::split_test; +use crate::{block, callq_direct, movq, reg}; +use test_each_file::test_each_file; + +fn select([test]: [&str; 1]) { + let (input, expected_output, expected_return, _) = split_test(test); + + let mut program = parse_program(test) + .unwrap() + .validate() + .unwrap() + .reveal() + .atomize() + .explicate() + .eliminate() + .select(); + + // Redirect program to exit + let new_entry = gen_sym("tmp"); + program.blocks.insert( + new_entry, + block!( + callq_direct!(program.entry, 0), + movq!(reg!(RAX), reg!(RDI)), + callq_direct!(program.std["exit"], 1) + ), + ); + program.entry = new_entry; + + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); +} + +test_each_file! { for ["test"] in "./programs/good" as select => select } diff --git a/compiler/src/passes/uniquify/mod.rs b/compiler/src/passes/uniquify/mod.rs deleted file mode 100644 index 7f9e33f..0000000 --- a/compiler/src/passes/uniquify/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -pub mod uniquify; - -use crate::passes::parse::Def; -use crate::passes::validate::TExpr; -use crate::utils::gen_sym::UniqueSym; -use std::collections::HashMap; - -#[derive(Debug, PartialEq)] -pub struct PrgUniquified<'p> { - pub defs: HashMap, Def<'p, UniqueSym<'p>, TExpr<'p, UniqueSym<'p>>>>, - pub entry: UniqueSym<'p>, -} - -#[cfg(test)] -mod tests { - use crate::interpreter::TestIO; - use crate::utils::split_test::split_test; - use test_each_file::test_each_file; - - fn unique([test]: [&str; 1]) { - let (input, expected_output, expected_return, program) = split_test(test); - let uniquified_program = program.validate().unwrap().uniquify(); - let mut io = TestIO::new(input); - let result = uniquified_program.interpret(&mut io); - - assert_eq!(result, expected_return.into(), "Incorrect program result."); - assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); - } - - test_each_file! { for ["test"] in "./programs/good" as uniquify => unique } -} diff --git a/compiler/src/passes/uniquify/uniquify.rs b/compiler/src/passes/uniquify/uniquify.rs deleted file mode 100644 index 43b4770..0000000 --- a/compiler/src/passes/uniquify/uniquify.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::passes::parse::{Def, Param, TypeDef}; -use crate::passes::uniquify::PrgUniquified; -use crate::passes::validate::{PrgTypeChecked, TExpr}; -use crate::utils::gen_sym::{gen_sym, UniqueSym}; -use crate::utils::push_map::PushMap; - -impl<'p> PrgTypeChecked<'p> { - #[must_use] - pub fn uniquify(self) -> PrgUniquified<'p> { - let mut scope = PushMap::from_iter(self.defs.iter().map(|(&sym, _)| (sym, gen_sym(sym)))); - - PrgUniquified { - defs: self - .defs - .into_iter() - .map(|(sym, def)| (scope[&sym], uniquify_def(def, &mut scope))) - .collect(), - entry: scope[&self.entry], - } - } -} - -fn uniquify_def<'p>( - def: Def<'p, &'p str, TExpr<'p, &'p str>>, - scope: &mut PushMap<&'p str, UniqueSym<'p>>, -) -> Def<'p, UniqueSym<'p>, TExpr<'p, UniqueSym<'p>>> { - match def { - Def::Fn { - sym, - params, - typ, - bdy, - } => scope.push_iter( - params.iter().map(|param| (param.sym, gen_sym(param.sym))), - |scope| { - let params = params - .iter() - .map(|param| Param { - sym: scope[¶m.sym], - mutable: param.mutable, - typ: param.typ.clone().fmap(|v| scope[v]), - }) - .collect(); - let bdy = uniquify_expression(bdy, scope); - Def::Fn { - sym: scope[&sym], - params, - typ: typ.fmap(|v| scope[v]), - bdy, - } - }, - ), - Def::TypeDef { sym, def } => { - let def = match def { - TypeDef::Struct { fields } => TypeDef::Struct { - fields: fields - .into_iter() - .map(|(sym, typ)| (sym, typ.fmap(|sym| scope[sym]))) - .collect(), - }, - TypeDef::Enum { .. } => todo!(), - }; - Def::TypeDef { - sym: scope[&sym], - def, - } - } - } -} - -fn uniquify_expression<'p>( - expr: TExpr<'p, &'p str>, - scope: &mut PushMap<&'p str, UniqueSym<'p>>, -) -> TExpr<'p, UniqueSym<'p>> { - match expr { - TExpr::Let { sym, bnd, bdy, typ } => { - let unique_bnd = uniquify_expression(*bnd, scope); - let unique_sym = gen_sym(sym); - let unique_bdy = scope.push(sym, unique_sym, |scope| uniquify_expression(*bdy, scope)); - - TExpr::Let { - sym: unique_sym, - bnd: Box::new(unique_bnd), - bdy: Box::new(unique_bdy), - typ: typ.fmap(|s| scope[s]), - } - } - TExpr::Var { sym, typ } => TExpr::Var { - sym: scope[&sym], - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Assign { sym, bnd, typ } => TExpr::Assign { - sym: scope[sym], - bnd: Box::new(uniquify_expression(*bnd, scope)), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Struct { sym, fields, typ } => TExpr::Struct { - sym: scope[sym], - fields: fields - .into_iter() - .map(|(sym, expr)| (sym, uniquify_expression(expr, scope))) - .collect(), - typ: typ.fmap(|s| scope[s]), - }, - - TExpr::Lit { val, typ } => TExpr::Lit { - val, - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Prim { op, args, typ } => TExpr::Prim { - op, - args: args - .into_iter() - .map(|arg| uniquify_expression(arg, scope)) - .collect(), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::If { cnd, thn, els, typ } => TExpr::If { - cnd: Box::new(uniquify_expression(*cnd, scope)), - thn: Box::new(uniquify_expression(*thn, scope)), - els: Box::new(uniquify_expression(*els, scope)), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Apply { fun, args, typ } => TExpr::Apply { - fun: Box::new(uniquify_expression(*fun, scope)), - args: args - .into_iter() - .map(|arg| uniquify_expression(arg, scope)) - .collect(), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Loop { bdy, typ } => TExpr::Loop { - bdy: Box::new(uniquify_expression(*bdy, scope)), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Break { bdy, typ } => TExpr::Break { - bdy: Box::new(uniquify_expression(*bdy, scope)), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Seq { stmt, cnt, typ } => TExpr::Seq { - stmt: Box::new(uniquify_expression(*stmt, scope)), - cnt: Box::new(uniquify_expression(*cnt, scope)), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Continue { typ } => TExpr::Continue { - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Return { bdy, typ } => TExpr::Return { - bdy: Box::new(uniquify_expression(*bdy, scope)), - typ: typ.fmap(|s| scope[s]), - }, - TExpr::AccessField { strct, field, typ } => TExpr::AccessField { - strct: Box::new(uniquify_expression(*strct, scope)), - field, - typ: typ.fmap(|s| scope[s]), - }, - TExpr::Variant { .. } => todo!(), - TExpr::Switch { .. } => todo!(), - } -} diff --git a/compiler/src/passes/validate/check_sized.rs b/compiler/src/passes/validate/check_sized.rs index 5750140..e5d94a7 100644 --- a/compiler/src/passes/validate/check_sized.rs +++ b/compiler/src/passes/validate/check_sized.rs @@ -1,13 +1,15 @@ use crate::passes::parse::types::Type; use crate::passes::parse::{Def, TypeDef}; -use crate::passes::validate::{PrgTypeChecked, ValidateError}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::PrgConstrained; +use crate::utils::gen_sym::UniqueSym; use petgraph::algo::toposort; use petgraph::prelude::GraphMap; use petgraph::Directed; -impl<'p> PrgTypeChecked<'p> { - pub fn check_sized(&self) -> Result<(), ValidateError> { - let mut size_graph: GraphMap<&str, (), Directed> = GraphMap::new(); +impl<'p> PrgConstrained<'p> { + pub fn check_sized(&self) -> Result<(), TypeError> { + let mut size_graph: GraphMap, (), Directed> = GraphMap::new(); for def in self.defs.values() { #[allow(clippy::single_match)] match def { @@ -15,13 +17,14 @@ impl<'p> PrgTypeChecked<'p> { TypeDef::Struct { fields } => { for (_, field) in fields { match field { - Type::Int + Type::I64 + | Type::U64 | Type::Bool | Type::Unit | Type::Never | Type::Fn { .. } => {} Type::Var { sym: field_sym } => { - size_graph.add_edge(sym, field_sym, ()); + size_graph.add_edge(sym.inner, field_sym.inner, ()); } } } @@ -34,8 +37,9 @@ impl<'p> PrgTypeChecked<'p> { match toposort(&size_graph, None) { Ok(_) => Ok(()), - Err(cycle) => Err(ValidateError::UnsizedType { + Err(cycle) => Err(TypeError::UnsizedType { sym: cycle.node_id().to_string(), + span: self.defs[&cycle.node_id()].sym().meta, }), } } diff --git a/compiler/src/passes/validate/constrain/access_field.rs b/compiler/src/passes/validate/constrain/access_field.rs new file mode 100644 index 0000000..e9d9499 --- /dev/null +++ b/compiler/src/passes/validate/constrain/access_field.rs @@ -0,0 +1,48 @@ +use crate::passes::parse::{Meta, Span, Spanned, TypeDef}; +use crate::passes::validate::constrain::expr::constrain_expr; +use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_access_field<'p>( + env: &mut Env<'_, 'p>, + span: Span, + strct: Spanned>, + field: Spanned<&'p str>, +) -> Result>, TypeError> { + let strct = constrain_expr(strct, env)?; + + let PartialType::Var { sym } = env.uf.get(strct.meta.index) else { + return Err(TypeError::SymbolShouldBeStruct { + span: strct.meta.span, + }); + }; + + let EnvEntry::Def { + def: TypeDef::Struct { + fields: def_fields, .. + }, + } = &env.scope[sym] + else { + return Err(TypeError::SymbolShouldBeStruct { + span: strct.meta.span, + }); + }; + + let Some((_, typ)) = def_fields.iter().find(|(sym, _)| sym.inner == field.inner) else { + return Err(TypeError::UnknownStructField { + sym: field.inner.to_string(), + span: field.meta, + }); + }; + + let index = env.uf.type_to_index(typ.clone()); + Ok(Meta { + meta: CMeta { span, index }, + inner: ExprConstrained::AccessField { + strct: Box::new(strct), + field, + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/apply.rs b/compiler/src/passes/validate/constrain/apply.rs new file mode 100644 index 0000000..61b9e16 --- /dev/null +++ b/compiler/src/passes/validate/constrain/apply.rs @@ -0,0 +1,56 @@ +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::utils::expect::expect; + +pub fn constrain_apply<'p>( + env: &mut Env<'_, 'p>, + span: Span, + fun: Spanned>, + args: Vec>>, +) -> Result>, TypeError> { + let fun = expr::constrain_expr(fun, env)?; + let args: Vec<_> = args + .into_iter() + .map(|arg| expr::constrain_expr(arg, env)) + .collect::>()?; + + let p_typ = env.uf.get(fun.meta.index).clone(); + let PartialType::Fn { params, typ } = p_typ else { + return Err(TypeError::MismatchedExpectFn { + got: p_typ.to_string(env.uf), + span_got: fun.meta.span, + }); + }; + + expect( + params.len() == args.len(), + TypeError::ArgCountMismatch { + got: args.len(), + expected: params.len(), + span, // todo: maybe highlight only the args and params? + }, + )?; + + for (arg, param_type) in args.iter().zip(params.iter()) { + env.uf + .expect_equal(arg.meta.index, *param_type, |arg_type, param_type| { + TypeError::FnArgExpect { + arg: arg_type, + param: param_type, + span_arg: arg.meta.span, + } + })?; + } + + Ok(Meta { + meta: CMeta { span, index: typ }, + inner: ExprConstrained::Apply { + fun: Box::new(fun), + args, + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/assign.rs b/compiler/src/passes/validate/constrain/assign.rs new file mode 100644 index 0000000..fc824d2 --- /dev/null +++ b/compiler/src/passes/validate/constrain/assign.rs @@ -0,0 +1,44 @@ +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::error::TypeError::MismatchedAssignBinding; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::utils::expect::expect; +use crate::utils::gen_sym::UniqueSym; + +pub fn constrain_assign<'p>( + env: &mut Env<'_, 'p>, + span: Span, + sym: Spanned>, + bnd: Spanned>, +) -> Result>, TypeError> { + let bnd = expr::constrain_expr(bnd, env)?; + + let EnvEntry::Type { mutable, typ } = env.scope[&sym.inner] else { + return Err(TypeError::SymbolShouldBeVariable { span: sym.meta }); + }; + + expect(mutable, TypeError::ModifyImmutable { span: sym.meta })?; + + env.uf + .expect_equal(typ, bnd.meta.index, |sym_typ, bnd_type| { + MismatchedAssignBinding { + expect: sym_typ, + got: bnd_type, + span_expected: sym.meta, + span_got: bnd.meta.span, + } + })?; + + let typ = env.uf.add(PartialType::Unit); + + Ok(Meta { + meta: CMeta { span, index: typ }, + inner: ExprConstrained::Assign { + sym, + bnd: Box::new(bnd), + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/binary_op.rs b/compiler/src/passes/validate/constrain/binary_op.rs new file mode 100644 index 0000000..1fd251d --- /dev/null +++ b/compiler/src/passes/validate/constrain/binary_op.rs @@ -0,0 +1,80 @@ +use crate::passes::parse::{BinaryOp, Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_binary_op<'p>( + env: &mut Env<'_, 'p>, + span: Span, + op: BinaryOp, + lhs: Spanned>, + rhs: Spanned>, +) -> Result>, TypeError> { + // input: None = Any but equal, Some = expect this + // output: None = Same as input, Some = this + let (input, output) = match op { + BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => { + (Some(PartialType::Int), None) + } + BinaryOp::LAnd | BinaryOp::LOr | BinaryOp::Xor => (Some(PartialType::Bool), None), + BinaryOp::GT | BinaryOp::GE | BinaryOp::LE | BinaryOp::LT => { + (Some(PartialType::Int), Some(PartialType::Bool)) + } + BinaryOp::EQ | BinaryOp::NE => (None, Some(PartialType::Bool)), + }; + + let e1 = expr::constrain_expr(lhs, env)?; + let e2 = expr::constrain_expr(rhs, env)?; + + // Check inputs satisfy constraints + if let Some(input) = input { + let mut check = |expr: &Meta>| { + env.uf + .expect_partial_type(expr.meta.index, input.clone(), |got, expect| { + TypeError::OperandExpect { + expect, + got, + op: op.to_string(), + span_op: span, + span_arg: expr.meta.span, + } + }) + }; + + check(&e1)?; + check(&e2)?; + } + + // Check inputs equal + let input_index = env + .uf + .expect_equal(e1.meta.index, e2.meta.index, |lhs, rhs| { + TypeError::OperandEqual { + lhs, + rhs, + op: op.to_string(), + span_op: span, + span_lhs: e1.meta.span, + span_rhs: e2.meta.span, + } + })?; + + // Generate output index + let output_index = match output { + None => input_index, + Some(e) => env.uf.add(e), + }; + + Ok(Meta { + meta: CMeta { + span, + index: output_index, + }, + inner: ExprConstrained::BinaryOp { + op, + exprs: [e1, e2].map(Box::new), + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/break.rs b/compiler/src/passes/validate/constrain/break.rs new file mode 100644 index 0000000..5584a77 --- /dev/null +++ b/compiler/src/passes/validate/constrain/break.rs @@ -0,0 +1,34 @@ +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_break<'p>( + env: &mut Env<'_, 'p>, + span: Span, + bdy: Spanned>, +) -> Result>, TypeError> { + let Some(loop_type) = env.loop_type else { + return Err(TypeError::BreakOutsideLoop { span }); + }; + + let bdy = expr::constrain_expr(bdy, env)?; + env.uf + .expect_equal(bdy.meta.index, loop_type, |got, expect| { + TypeError::MismatchedLoop { + expect, + got, + span_break: bdy.meta.span, + } + })?; + + Ok(Meta { + meta: CMeta { + span, + index: env.uf.add(PartialType::Never), + }, + inner: ExprConstrained::Break { bdy: Box::new(bdy) }, + }) +} diff --git a/compiler/src/passes/validate/constrain/continue.rs b/compiler/src/passes/validate/constrain/continue.rs new file mode 100644 index 0000000..f6bbbdf --- /dev/null +++ b/compiler/src/passes/validate/constrain/continue.rs @@ -0,0 +1,24 @@ +use crate::passes::parse::{Meta, Span}; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained}; +use crate::utils::expect::expect; + +pub fn constrain_continue<'p>( + env: &mut Env<'_, 'p>, + span: Span, +) -> Result>, TypeError> { + expect( + env.loop_type.is_some(), + TypeError::ContinueOutsideLoop { span }, + )?; + + Ok(Meta { + meta: CMeta { + span, + index: env.uf.add(PartialType::Never), + }, + inner: ExprConstrained::Continue, + }) +} diff --git a/compiler/src/passes/validate/constrain/def.rs b/compiler/src/passes/validate/constrain/def.rs new file mode 100644 index 0000000..4dfa03c --- /dev/null +++ b/compiler/src/passes/validate/constrain/def.rs @@ -0,0 +1,70 @@ +use crate::passes::parse::{Def, Meta}; +use crate::passes::validate::constrain::expr::constrain_expr; +use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{DefConstrained, DefUniquified}; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::union_find::UnionFind; +use std::collections::HashMap; + +pub fn constrain_def<'p>( + def: DefUniquified<'p>, + scope: &mut HashMap, EnvEntry<'p>>, + uf: &mut UnionFind>, +) -> Result, TypeError> { + let def = match def { + Def::Fn { + sym, + params, + typ, + bdy, + } => { + // Put function parameters in scope. + scope.extend(params.iter().map(|p| { + ( + p.sym.inner, + EnvEntry::Type { + mutable: p.mutable, + typ: uf.type_to_index(p.typ.clone()), + }, + ) + })); + + // Add return type to env and keep it for error handling. + let return_index = uf.type_to_index(typ.clone()); + let mut env = Env { + uf, + scope, + loop_type: None, + return_type: &Meta { + inner: return_index, + meta: sym.meta, + }, // TODO replace sym.meta with return type index + }; + + // Constrain body of function. + let bdy = constrain_expr(bdy, &mut env)?; + + // Return error if function body a type differs from its return type. + uf.expect_equal(return_index, bdy.meta.index, |r, b| { + TypeError::MismatchedFnReturn { + expect: r, + got: b, + span_expected: sym.meta, + span_got: bdy.meta.span, + } + })?; + + Def::Fn { + sym, + params, + bdy, + typ, + } + } + Def::TypeDef { sym, def } => Def::TypeDef { sym, def }, + }; + + Ok(def) +} diff --git a/compiler/src/passes/validate/constrain/expr.rs b/compiler/src/passes/validate/constrain/expr.rs new file mode 100644 index 0000000..faa82a2 --- /dev/null +++ b/compiler/src/passes/validate/constrain/expr.rs @@ -0,0 +1,55 @@ +use crate::passes::parse::{Expr, Meta, Spanned}; +use crate::passes::validate::constrain::access_field::constrain_access_field; +use crate::passes::validate::constrain::apply::constrain_apply; +use crate::passes::validate::constrain::assign::constrain_assign; +use crate::passes::validate::constrain::binary_op::constrain_binary_op; +use crate::passes::validate::constrain::lit::constrain_lit; +use crate::passes::validate::constrain::r#break::constrain_break; +use crate::passes::validate::constrain::r#continue::constrain_continue; +use crate::passes::validate::constrain::r#if::constrain_if; +use crate::passes::validate::constrain::r#let::constrain_let; +use crate::passes::validate::constrain::r#loop::constrain_loop; +use crate::passes::validate::constrain::r#return::constrain_return; +use crate::passes::validate::constrain::r#struct::constrain_struct; +use crate::passes::validate::constrain::seq::constrain_seq; +use crate::passes::validate::constrain::unary_op::constrain_unary_op; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::constrain::var::constrain_var; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_expr<'p>( + expr: Spanned>, + env: &mut Env<'_, 'p>, +) -> Result>, TypeError> { + let span = expr.meta; + + match expr.inner { + Expr::Lit { val } => constrain_lit(env, span, val), + Expr::Var { sym } => constrain_var(env, span, sym), + Expr::UnaryOp { op, expr } => constrain_unary_op(env, span, op, *expr), + Expr::BinaryOp { + op, + exprs: [lhs, rhs], + } => constrain_binary_op(env, span, op, *lhs, *rhs), + Expr::Let { + sym, + mutable, + typ, + bnd, + bdy, + } => constrain_let(env, span, sym, mutable, typ, *bnd, *bdy), + Expr::If { cnd, thn, els } => constrain_if(env, span, *cnd, *thn, *els), + Expr::Apply { fun, args } => constrain_apply(env, span, *fun, args), + Expr::Loop { bdy } => constrain_loop(env, span, *bdy), + Expr::Break { bdy } => constrain_break(env, span, *bdy), + Expr::Continue => constrain_continue(env, span), + Expr::Return { bdy } => constrain_return(env, span, *bdy), + Expr::Seq { stmt, cnt } => constrain_seq(env, span, *stmt, *cnt), + Expr::Assign { sym, bnd } => constrain_assign(env, span, sym, *bnd), + Expr::Struct { sym, fields } => constrain_struct(env, span, sym, fields), + Expr::AccessField { strct, field } => constrain_access_field(env, span, *strct, field), + Expr::Variant { .. } => todo!(), + Expr::Switch { .. } => todo!(), + } +} diff --git a/compiler/src/passes/validate/constrain/if.rs b/compiler/src/passes/validate/constrain/if.rs new file mode 100644 index 0000000..1a912b6 --- /dev/null +++ b/compiler/src/passes/validate/constrain/if.rs @@ -0,0 +1,49 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_if<'p>( + env: &mut Env<'_, 'p>, + span: Span, + cnd: Spanned>, + thn: Spanned>, + els: Spanned>, +) -> Result>, TypeError> { + let cnd = expr::constrain_expr(cnd, env)?; + + env.uf.expect_type(cnd.meta.index, Type::Bool, |got, _| { + TypeError::IfExpectBool { + got, + span_got: cnd.meta.span, + } + })?; + + let thn = expr::constrain_expr(thn, env)?; + let els = expr::constrain_expr(els, env)?; + + let out_index = env + .uf + .expect_equal(thn.meta.index, els.meta.index, |thn_type, els_type| { + TypeError::IfExpectEqual { + thn: thn_type, + els: els_type, + span_thn: thn.meta.span, + span_els: els.meta.span, + } + })?; + + Ok(Meta { + meta: CMeta { + span, + index: out_index, + }, + inner: ExprConstrained::If { + cnd: Box::new(cnd), + thn: Box::new(thn), + els: Box::new(els), + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/let.rs b/compiler/src/passes/validate/constrain/let.rs new file mode 100644 index 0000000..c87fa30 --- /dev/null +++ b/compiler/src/passes/validate/constrain/let.rs @@ -0,0 +1,52 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::utils::gen_sym::UniqueSym; + +pub fn constrain_let<'p>( + env: &mut Env<'_, 'p>, + span: Span, + sym: Spanned>, + mutable: bool, + typ: Option>>>, + bnd: Spanned>, + bdy: Spanned>, +) -> Result>, TypeError> { + let bnd = expr::constrain_expr(bnd, env)?; + + if let Some(typ) = &typ { + env.uf.expect_type(bnd.meta.index, typ.clone(), |got, _| { + TypeError::MismatchedLetBinding { + got, + span_expected: (0, 0), //TODO span of typ + span_got: bnd.meta.span, + } + })?; + } + + env.scope.insert( + sym.inner, + EnvEntry::Type { + mutable, + typ: bnd.meta.index, + }, + ); + let bdy = expr::constrain_expr(bdy, env)?; + + Ok(Meta { + meta: CMeta { + span, + index: bdy.meta.index, + }, + inner: ExprConstrained::Let { + sym, + mutable, + typ, + bnd: Box::new(bnd), + bdy: Box::new(bdy), + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/lit.rs b/compiler/src/passes/validate/constrain/lit.rs new file mode 100644 index 0000000..94ff485 --- /dev/null +++ b/compiler/src/passes/validate/constrain/lit.rs @@ -0,0 +1,29 @@ +use crate::passes::parse::{Lit, Meta, Span}; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained}; + +pub fn constrain_lit<'p>( + env: &mut Env<'_, 'p>, + span: Span, + val: Lit<'p>, +) -> Result>, TypeError> { + // Get the type of the literal. + let typ = match &val { + Lit::Int { typ, .. } => { + // If no type hint is given, use the generic `Int`. + typ.clone().unwrap_or(PartialType::Int) + } + Lit::Bool { .. } => PartialType::Bool, + Lit::Unit => PartialType::Unit, + }; + + // Add the type to the constraints. + let index = env.uf.add(typ); + + Ok(Meta { + meta: CMeta { span, index }, + inner: ExprConstrained::Lit { val }, + }) +} diff --git a/compiler/src/passes/validate/constrain/loop.rs b/compiler/src/passes/validate/constrain/loop.rs new file mode 100644 index 0000000..7ca0d54 --- /dev/null +++ b/compiler/src/passes/validate/constrain/loop.rs @@ -0,0 +1,31 @@ +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_loop<'p>( + env: &mut Env<'_, 'p>, + span: Span, + bdy: Spanned>, +) -> Result>, TypeError> { + let loop_type = env.uf.add(PartialType::Never); + + let mut env = Env { + uf: env.uf, + scope: env.scope, + loop_type: Some(loop_type), + return_type: env.return_type, + }; + + let bdy = expr::constrain_expr(bdy, &mut env)?; + + Ok(Meta { + meta: CMeta { + span, + index: loop_type, + }, + inner: ExprConstrained::Loop { bdy: Box::new(bdy) }, + }) +} diff --git a/compiler/src/passes/validate/constrain/mod.rs b/compiler/src/passes/validate/constrain/mod.rs new file mode 100644 index 0000000..45fb39e --- /dev/null +++ b/compiler/src/passes/validate/constrain/mod.rs @@ -0,0 +1,107 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::Spanned; +use crate::passes::validate::constrain::def::constrain_def; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::uniquify::PrgUniquified; +use crate::passes::validate::{partial_type, PrgConstrained}; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::union_find::{UnionFind, UnionIndex}; +use uncover_globals::uncover_globals; + +mod access_field; +mod apply; +mod assign; +mod binary_op; +mod r#break; +mod r#continue; +pub mod def; +pub mod expr; +mod r#if; +mod r#let; +mod lit; +mod r#loop; +mod r#return; +mod seq; +mod r#struct; +mod unary_op; +mod uncover_globals; +mod var; + +impl<'p> PrgUniquified<'p> { + pub fn constrain(self) -> Result, TypeError> { + let mut uf = UnionFind::new(); + let mut scope = uncover_globals(&self, &mut uf)?; + + Ok(PrgConstrained { + defs: self + .defs + .into_iter() + .map(|def| { + constrain_def(def, &mut scope, &mut uf).map(|def| (def.sym().inner, def)) + }) + .collect::>()?, + entry: self.entry, + uf, + std: self.std, + }) + } +} + +impl<'p> UnionFind> { + pub fn expect_equal( + &mut self, + a: UnionIndex, + b: UnionIndex, + map_err: impl FnOnce(String, String) -> TypeError, + ) -> Result { + self.try_union_by(a, b, partial_type::combine_partial_types) + .map_err(|_| { + let typ_a = self.get(a).clone(); + let str_a = typ_a.to_string(self); + let typ_b = self.get(b).clone(); + let str_b = typ_b.to_string(self); + map_err(str_a, str_b) + }) + } + + pub fn expect_type( + &mut self, + a: UnionIndex, + t: Type>>, + map_err: impl FnOnce(String, String) -> TypeError, + ) -> Result { + let t_index = self.type_to_index(t); + self.expect_equal(a, t_index, map_err) + } + + pub fn expect_partial_type( + &mut self, + a: UnionIndex, + t: PartialType<'p>, + map_err: impl FnOnce(String, String) -> TypeError, + ) -> Result { + let t_index = self.add(t); + self.expect_equal(a, t_index, map_err) + } + + pub fn type_to_index(&mut self, t: Type>>) -> UnionIndex { + let pt = match t { + Type::I64 => PartialType::I64, + Type::U64 => PartialType::U64, + Type::Bool => PartialType::Bool, + Type::Unit => PartialType::Unit, + Type::Never => PartialType::Never, + Type::Fn { params, typ } => PartialType::Fn { + params: params + .into_iter() + .map(|param| self.type_to_index(param)) + .collect(), + typ: self.type_to_index(*typ), + }, + Type::Var { sym } => PartialType::Var { sym: sym.inner }, + }; + + self.add(pt) + } +} diff --git a/compiler/src/passes/validate/constrain/return.rs b/compiler/src/passes/validate/constrain/return.rs new file mode 100644 index 0000000..63ee2b4 --- /dev/null +++ b/compiler/src/passes/validate/constrain/return.rs @@ -0,0 +1,32 @@ +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_return<'p>( + env: &mut Env<'_, 'p>, + span: Span, + bdy: Spanned>, +) -> Result>, TypeError> { + let bdy = expr::constrain_expr(bdy, env)?; + + env.uf + .expect_equal(bdy.meta.index, env.return_type.inner, |bdy_typ, rtrn| { + TypeError::MismatchedFnReturn { + got: bdy_typ, + expect: rtrn, + span_got: bdy.meta.span, + span_expected: env.return_type.meta, //TODO span of return type, should be passed via env + } + })?; + + Ok(Meta { + meta: CMeta { + span, + index: env.uf.add(PartialType::Never), + }, + inner: ExprConstrained::Return { bdy: Box::new(bdy) }, + }) +} diff --git a/compiler/src/passes/validate/constrain/seq.rs b/compiler/src/passes/validate/constrain/seq.rs new file mode 100644 index 0000000..4f9d85e --- /dev/null +++ b/compiler/src/passes/validate/constrain/seq.rs @@ -0,0 +1,26 @@ +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_seq<'p>( + env: &mut Env<'_, 'p>, + span: Span, + stmt: Spanned>, + cnt: Spanned>, +) -> Result>, TypeError> { + let stmt = expr::constrain_expr(stmt, env)?; + let cnt = expr::constrain_expr(cnt, env)?; + + Ok(Meta { + meta: CMeta { + span, + index: cnt.meta.index, + }, + inner: ExprConstrained::Seq { + stmt: Box::new(stmt), + cnt: Box::new(cnt), + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/struct.rs b/compiler/src/passes/validate/constrain/struct.rs new file mode 100644 index 0000000..69679a5 --- /dev/null +++ b/compiler/src/passes/validate/constrain/struct.rs @@ -0,0 +1,87 @@ +use crate::passes::parse::{Meta, Span, Spanned, TypeDef}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; +use crate::utils::expect::expect; +use crate::utils::gen_sym::UniqueSym; +use std::collections::{HashMap, HashSet}; + +pub fn constrain_struct<'p>( + env: &mut Env<'_, 'p>, + span: Span, + sym: Spanned>, + fields: Vec<(Spanned<&'p str>, Spanned>)>, +) -> Result>, TypeError> { + // Get the `EnvEntry` from the scope. + // This should exist after yeet, but could potentially not be a struct definition. + let EnvEntry::Def { + def: TypeDef::Struct { fields: def_fields }, + } = &env.scope[&sym.inner] + else { + return Err(TypeError::SymbolShouldBeStruct { span }); + }; + + let def_fields = def_fields + .iter() + .map(|(field_sym, field_typ)| (field_sym.inner, (field_sym.meta, field_typ.clone()))) + .collect::>(); + + // Set to keep track of fields in the struct constructor. Used to make sure no duplicates occur. + let mut seen_fields = HashSet::new(); + + let fields = fields + .into_iter() + .map(|(field_sym, field_bnd)| { + let field_bnd = expr::constrain_expr(field_bnd, env)?; + + expect( + seen_fields.insert(field_sym.inner), + TypeError::ConstructDuplicateField { + sym: field_sym.to_string(), + span: field_sym.meta, + }, + )?; + + let Some((def_span, def_typ)) = def_fields.get(field_sym.inner) else { + return Err(TypeError::UnknownStructField { + sym: field_sym.to_string(), + span: field_sym.meta, + }); + }; + + env.uf.expect_type( + field_bnd.meta.index, + def_typ.clone(), + |field_typ, def_typ| TypeError::MismatchedStructField { + expect: def_typ, + got: field_typ, + span_expected: *def_span, + span_got: field_sym.meta, + }, + )?; + + Ok((field_sym, field_bnd)) + }) + .collect::, _>>()?; + + // Verify that all fields from the struct definition are present. + for (def_sym, (def_span, _)) in def_fields { + expect( + seen_fields.contains(def_sym), + TypeError::ConstructMissingField { + sym: def_sym.to_string(), + struct_span: sym.meta, + def_span, + }, + )?; + } + + let index = env.uf.add(PartialType::Var { sym: sym.inner }); + + Ok(Meta { + meta: CMeta { span, index }, + inner: ExprConstrained::Struct { sym, fields }, + }) +} diff --git a/compiler/src/passes/validate/constrain/unary_op.rs b/compiler/src/passes/validate/constrain/unary_op.rs new file mode 100644 index 0000000..55e8e36 --- /dev/null +++ b/compiler/src/passes/validate/constrain/unary_op.rs @@ -0,0 +1,40 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Meta, Span, Spanned, UnaryOp}; +use crate::passes::validate::constrain::expr; +use crate::passes::validate::constrain::uncover_globals::Env; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::{CMeta, ExprConstrained, ExprUniquified}; + +pub fn constrain_unary_op<'p>( + env: &mut Env<'_, 'p>, + span: Span, + op: UnaryOp, + expr: Spanned>, +) -> Result>, TypeError> { + let typ = match op { + UnaryOp::Neg => Type::I64, + UnaryOp::Not => Type::Bool, + }; + let expr = expr::constrain_expr(expr, env)?; + + env.uf.expect_type(expr.meta.index, typ, |got, expect| { + TypeError::OperandExpect { + expect, + got, + op: op.to_string(), + span_op: span, + span_arg: expr.meta.span, + } + })?; + + Ok(Meta { + meta: CMeta { + span, + index: expr.meta.index, + }, + inner: ExprConstrained::UnaryOp { + op, + expr: Box::new(expr), + }, + }) +} diff --git a/compiler/src/passes/validate/constrain/uncover_globals.rs b/compiler/src/passes/validate/constrain/uncover_globals.rs new file mode 100644 index 0000000..d8264fa --- /dev/null +++ b/compiler/src/passes/validate/constrain/uncover_globals.rs @@ -0,0 +1,99 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Def, Spanned, TypeDef}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::uniquify::{PrgUniquified, BUILT_INS}; +use crate::passes::validate::DefUniquified; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::union_find::{UnionFind, UnionIndex}; + +use std::collections::HashMap; + +pub struct Env<'a, 'p> { + pub uf: &'a mut UnionFind>, + pub scope: &'a mut HashMap, EnvEntry<'p>>, + pub loop_type: Option, + pub return_type: &'a Spanned, +} + +pub enum EnvEntry<'p> { + Type { + mutable: bool, + typ: UnionIndex, + }, + Def { + def: TypeDef>, Spanned<&'p str>>, + }, +} + +/// Returns a `PushMap` with all the definitions (functions, structs, etc.) in scope. +pub fn uncover_globals<'p>( + program: &PrgUniquified<'p>, + uf: &mut UnionFind>, +) -> Result, EnvEntry<'p>>, TypeError> { + let builtins = program + .std + .iter() + .map(|(k, v)| { + let typ = uf.type_to_index(BUILT_INS[k].clone()); + ( + *v, + EnvEntry::Type { + mutable: false, + typ, + }, + ) + }) + .collect::>(); + + // Check for duplicate global names. Names from the standard library are inserted with `None` for its span. + // todo: Later on this should be replaced by a module system, so that we can span the `use` statement. + let mut seen = builtins + .iter() + .map(|(sym, _)| (sym.sym, None)) + .collect::>(); + + for def in &program.defs { + let sym = def.sym(); + + if let Some(prev_span) = seen.insert(sym.inner.sym, Some(sym.meta)) { + let error = match prev_span { + Some(prev_span) => TypeError::DuplicateGlobal { + span1: prev_span, + span2: sym.meta, + sym: sym.inner.sym.to_string(), + }, + None => TypeError::DuplicateGlobalBuiltin { + span: sym.meta, + sym: sym.inner.sym.to_string(), + }, + }; + + return Err(error); + } + } + + Ok(program + .defs + .iter() + .map(|def| (def.sym().inner, uncover_def(def, uf))) + .chain(builtins) + .collect()) +} + +fn uncover_def<'p>(def: &DefUniquified<'p>, uf: &mut UnionFind>) -> EnvEntry<'p> { + match def { + Def::Fn { + params: args, typ, .. + } => EnvEntry::Type { + mutable: false, + typ: uf.type_to_index(Type::Fn { + typ: Box::new(typ.clone()), + params: args.iter().map(|param| param.typ.clone()).collect(), + }), + }, + Def::TypeDef { def, .. } => EnvEntry::Def { + def: (*def).clone(), + }, + } +} diff --git a/compiler/src/passes/validate/constrain/var.rs b/compiler/src/passes/validate/constrain/var.rs new file mode 100644 index 0000000..ec01ce1 --- /dev/null +++ b/compiler/src/passes/validate/constrain/var.rs @@ -0,0 +1,19 @@ +use crate::passes::parse::{Meta, Span, Spanned}; +use crate::passes::validate::constrain::uncover_globals::{Env, EnvEntry}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::{CMeta, ExprConstrained}; +use crate::utils::gen_sym::UniqueSym; + +pub fn constrain_var<'p>( + env: &mut Env<'_, 'p>, + span: Span, + sym: Spanned>, +) -> Result>, TypeError> { + let EnvEntry::Type { typ, .. } = env.scope[&sym.inner] else { + return Err(TypeError::SymbolShouldBeVariable { span }); + }; + Ok(Meta { + meta: CMeta { span, index: typ }, + inner: ExprConstrained::Var { sym }, + }) +} diff --git a/compiler/src/passes/validate/error.rs b/compiler/src/passes/validate/error.rs new file mode 100644 index 0000000..f9b997f --- /dev/null +++ b/compiler/src/passes/validate/error.rs @@ -0,0 +1,222 @@ +#[cfg(test)] +use derive_name::VariantName; +use miette::Diagnostic; +use thiserror::Error; + +#[cfg_attr(test, derive(VariantName))] +#[derive(Debug, Error, Diagnostic)] +pub enum TypeError { + #[error("Encountered an undeclared variable.")] + UndeclaredVar { + sym: String, + #[label = "This variable `{sym}` was not declared yet"] + span: (usize, usize), + }, + #[error("Duplicate global definition.")] + DuplicateGlobal { + #[label = "Global `{sym}` was first declared here"] + span1: (usize, usize), + #[label = "And was redeclared here"] + span2: (usize, usize), + sym: String, + }, + #[error("Duplicate global definition.")] + DuplicateGlobalBuiltin { + #[label = "Global `{sym}` conflicts with a builtin definition."] + span: (usize, usize), + sym: String, + }, + #[error("Duplicate argument name.")] + DuplicateArg { + #[label = "Argument `{sym}` was first declared here"] + span1: (usize, usize), + #[label = "And was redeclared here"] + span2: (usize, usize), + sym: String, + }, + #[error("Break outside loop.")] + BreakOutsideLoop { + #[label = "Found a break outside of a loop"] + span: (usize, usize), + }, + #[error("Continue outside loop.")] + ContinueOutsideLoop { + #[label = "Found a continue outside of a loop"] + span: (usize, usize), + }, + #[error("Tried to modify immutable variable.")] + ModifyImmutable { + #[label = "This variable was declared as immutable."] + span: (usize, usize), + }, + #[error("Tried to put type in variable.'")] + SymbolShouldBeVariable { + #[label = "This should be a variable."] + span: (usize, usize), + }, + #[error("Tried to use variable as type.'")] + SymbolShouldBeStruct { + #[label = "This should be a struct."] + span: (usize, usize), + }, + #[error("Unknown struct field.")] + UnknownStructField { + sym: String, + #[label = "The field `{sym}` is not present in the struct definition."] + span: (usize, usize), + }, + #[error("Missing struct field.")] + ConstructMissingField { + sym: String, + #[label = "The field `{sym}` is missing in the struct."] + struct_span: (usize, usize), + #[label = "It was defined here."] + def_span: (usize, usize), + }, + #[error("Duplicate struct field.")] + ConstructDuplicateField { + sym: String, + #[label = "The field `{sym}` was already provided earlier."] + span: (usize, usize), + }, + #[error("Unsized type.")] + UnsizedType { + sym: String, + #[label = "The type definition `{sym}` is not sized."] + span: (usize, usize), + }, + #[error("Integer out of bounds.")] + IntegerOutOfBounds { + #[label = "This number does not fit in type `{typ}`"] + span: (usize, usize), + typ: &'static str, + }, + #[error("Integer ambiguous.")] + IntegerAmbiguous { + #[label = "Could not determine the exact type of this integer"] + span: (usize, usize), + }, + #[error("The program doesn't have a main function.")] + NoMain, + #[error("Types did not match.")] + MismatchedFnReturn { + expect: String, + got: String, + + //TODO would like this span to be return type if present + #[label = "Expected this function to return: `{expect}`"] + span_expected: (usize, usize), + #[label = "But got this type: `{got}`"] + span_got: (usize, usize), + }, + #[error("Types did not match.")] + OperandExpect { + expect: String, + got: String, + op: String, + + //TODO would like this span to be operator + #[label = "Arguments of {op} are of type: `{expect}`"] + span_op: (usize, usize), + #[label = "But got this type: `{got}`"] + span_arg: (usize, usize), + }, + #[error("Types did not match.")] + OperandEqual { + lhs: String, + rhs: String, + op: String, + + //TODO would like this span to be operator + #[label = "Arguments of {op} should be of equal types"] + span_op: (usize, usize), + #[label = "Type: `{lhs}`"] + span_lhs: (usize, usize), + #[label = "Type: `{rhs}`"] + span_rhs: (usize, usize), + }, + #[error("Types did not match.")] + MismatchedLetBinding { + got: String, + + //TODO would like this span of type of let binding + #[label = "Expected binding of let to have this type"] + span_expected: (usize, usize), + #[label = "But got this type: `{got}`"] + span_got: (usize, usize), + }, + #[error("Types did not match.")] + MismatchedAssignBinding { + expect: String, + got: String, + + #[label = "Expected binding of assign to have type: `{expect}`"] + span_expected: (usize, usize), + #[label = "But got this type: `{got}`"] + span_got: (usize, usize), + }, + #[error("Types did not match.")] + MismatchedStructField { + expect: String, + got: String, + + #[label = "Expected struct field to have type: `{expect}`"] + span_expected: (usize, usize), + #[label = "But got this type: `{got}`"] + span_got: (usize, usize), + }, + #[error("Types did not match.")] + IfExpectBool { + got: String, + + #[label = "Expected this condition to be `Bool`, but got: `{got}`"] + span_got: (usize, usize), + }, + + #[error("Types did not match.")] + IfExpectEqual { + thn: String, + els: String, + + #[label = "Type: `{thn}`"] + span_thn: (usize, usize), + #[label = "Type: `{els}`"] + span_els: (usize, usize), + }, + + #[error("Types did not match.")] + MismatchedExpectFn { + got: String, + + #[label = "Expected function, but found '{got}'"] + span_got: (usize, usize), + }, + + #[error("Types did not match.")] + MismatchedLoop { + expect: String, + got: String, + + #[label = "Expected loop to return `{expect}`, but got: `{got}``"] + span_break: (usize, usize), + }, + + #[error("Types did not match.")] + ArgCountMismatch { + expected: usize, + got: usize, + + //TODO span of args + #[label = "Function {expected} arguments, but found {got} arguments"] + span: (usize, usize), + }, + + #[error("Types did not match.")] + FnArgExpect { + param: String, + arg: String, + + #[label = "Expected this function argument to be of type `{param}`, but got `{arg}`"] + span_arg: (usize, usize), + }, +} diff --git a/compiler/src/passes/validate/interpreter.rs b/compiler/src/passes/validate/interpreter.rs new file mode 100644 index 0000000..542c919 --- /dev/null +++ b/compiler/src/passes/validate/interpreter.rs @@ -0,0 +1,225 @@ +use crate::interpreter::{Val, IO}; +use crate::passes::parse::types::Type; +use crate::passes::parse::{BinaryOp, Def, Meta, UnaryOp}; +use crate::passes::validate::{ExprValidated, PrgValidated, TLit}; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; +use std::collections::HashMap; + +#[derive(Clone)] +pub enum ControlFlow<'p> { + Val(Val<'p>), + Break(Val<'p>), + Return(Val<'p>), + Continue, +} + +/// This macro unwraps values and bubbles up continues, breaks and returns. +macro_rules! b { + ($e: expr) => {{ + let e = $e; + match e { + ControlFlow::Val(val) => val, + ControlFlow::Break(_) | ControlFlow::Return(_) | ControlFlow::Continue => return e, + } + }}; +} + +impl<'p> PrgValidated<'p> { + pub fn interpret(&'p self, io: &mut impl IO) -> Val<'p> { + let std_iter = self + .std + .iter() + .map(|(_, &def)| (def, Val::StdlibFunction { sym: def.sym })); + + // Create a scope with all global definitions. + let mut scope = PushMap::from_iter( + self.defs + .iter() + .map(|(&sym, _)| (sym, Val::Function { sym })) + // Include the standard library in the scope. + .chain(std_iter), + ); + + // Interpret the program starting from the entry point. + self.interpret_fn(self.entry, Vec::new(), &mut scope, io) + } + + fn interpret_fn( + &'p self, + sym: UniqueSym<'p>, + args: Vec>, + scope: &mut PushMap, Val<'p>>, + io: &mut impl IO, + ) -> Val<'p> { + match &self.defs[&sym] { + Def::Fn { params, bdy, .. } => scope.push_iter( + params + .iter() + .zip(args.iter()) + .map(|(param, v)| (param.sym, v.clone())), + |scope| match self.interpret_expr(bdy, scope, io) { + ControlFlow::Return(val) | ControlFlow::Val(val) => val, + ControlFlow::Continue | ControlFlow::Break(_) => unreachable!(), + }, + ), + Def::TypeDef { .. } => unreachable!(), + } + } + + pub fn interpret_expr( + &'p self, + expr: &'p Meta, ExprValidated<'p>>, + scope: &mut PushMap, Val<'p>>, + io: &mut impl IO, + ) -> ControlFlow<'p> { + ControlFlow::Val(match &expr.inner { + ExprValidated::Lit { val, .. } => (*val).into(), + ExprValidated::Var { sym, .. } => scope[sym].clone(), + ExprValidated::BinaryOp { + op, + exprs: [lhs, rhs], + } => { + let lhs = b!(self.interpret_expr(lhs, scope, io)); + let mut rhs = || self.interpret_expr(rhs, scope, io); + match op { + BinaryOp::Add => Val::Int { + val: lhs.int() + b!(rhs()).int(), + }, + BinaryOp::Sub => Val::Int { + val: lhs.int() - b!(rhs()).int(), + }, + BinaryOp::Mul => Val::Int { + val: lhs.int() * b!(rhs()).int(), + }, + BinaryOp::Div => Val::Int { + val: lhs.int() / b!(rhs()).int(), + }, + BinaryOp::Mod => Val::Int { + val: lhs.int() % b!(rhs()).int(), + }, + BinaryOp::Xor => Val::Bool { + val: lhs.bool() ^ b!(rhs()).bool(), + }, + BinaryOp::GT => Val::Bool { + val: lhs.int() > b!(rhs()).int(), + }, + BinaryOp::GE => Val::Bool { + val: lhs.int() >= b!(rhs()).int(), + }, + BinaryOp::EQ => Val::Bool { + val: lhs == b!(rhs()), + }, + BinaryOp::LE => Val::Bool { + val: lhs.int() <= b!(rhs()).int(), + }, + BinaryOp::LT => Val::Bool { + val: lhs.int() < b!(rhs()).int(), + }, + BinaryOp::NE => Val::Bool { + val: lhs != b!(rhs()), + }, + BinaryOp::LAnd => { + // Short-circuit logical AND. + if !lhs.bool() { + return ControlFlow::Val(lhs); + } + b!(rhs()) + } + BinaryOp::LOr => { + // Short-circuit logical OR. + if lhs.bool() { + return ControlFlow::Val(lhs); + } + b!(rhs()) + } + } + } + ExprValidated::UnaryOp { op, expr } => { + let expr = b!(self.interpret_expr(expr, scope, io)); + match op { + UnaryOp::Neg => Val::Int { val: -expr.int() }, + UnaryOp::Not => Val::Bool { val: !expr.bool() }, + } + } + ExprValidated::Let { sym, bnd, bdy, .. } => { + let bnd = b!(self.interpret_expr(bnd, scope, io)); + b!(scope.push(*sym, bnd, |scope| self.interpret_expr(bdy, scope, io))) + } + ExprValidated::If { cnd, thn, els, .. } => { + if b!(self.interpret_expr(cnd, scope, io)).bool() { + b!(self.interpret_expr(thn, scope, io)) + } else { + b!(self.interpret_expr(els, scope, io)) + } + } + ExprValidated::Apply { fun, args, .. } => { + let fun = b!(self.interpret_expr(fun, scope, io)); + + let mut fn_args = Vec::new(); + for arg in args { + fn_args.push(b!(self.interpret_expr(arg, scope, io))); + } + + match fun { + Val::StdlibFunction { sym } => { + match sym { + "exit" => { + unreachable!("Validated programs should not have an explicit call to exit yet.") + } + "print" => { + let val = fn_args[0].clone(); + io.print(TLit::I64 { val: val.int() }); + val + } + "read" => io.read().into(), + unknown => unreachable!( + "Encountered an undefined standard library function '{unknown}'" + ), + } + } + Val::Function { sym } => self.interpret_fn(sym, fn_args, scope, io), + _ => unreachable!("The symbol did not refer to a function."), + } + } + ExprValidated::Loop { bdy, .. } => loop { + match self.interpret_expr(bdy, scope, io) { + ControlFlow::Return(val) => return ControlFlow::Return(val), + ControlFlow::Break(val) => return ControlFlow::Val(val), + ControlFlow::Continue | ControlFlow::Val(_) => {} + } + }, + ExprValidated::Break { bdy, .. } => { + return ControlFlow::Break(b!(self.interpret_expr(bdy, scope, io))) + } + ExprValidated::Seq { stmt, cnt, .. } => { + b!(self.interpret_expr(stmt, scope, io)); + b!(self.interpret_expr(cnt, scope, io)) + } + ExprValidated::Assign { sym, bnd, .. } => { + let bnd = b!(self.interpret_expr(bnd, scope, io)); + scope.0.insert(*sym, bnd); + Val::Unit + } + ExprValidated::Continue { .. } => return ControlFlow::Continue, + ExprValidated::Return { bdy, .. } => { + return ControlFlow::Return(b!(self.interpret_expr(bdy, scope, io))) + } + ExprValidated::Struct { fields, .. } => { + let mut field_values = HashMap::new(); + for (sym, field) in fields { + field_values.insert(*sym, b!(self.interpret_expr(field, scope, io))); + } + Val::StructInstance { + fields: field_values, + } + } + ExprValidated::AccessField { strct, field, .. } => { + let s = b!(self.interpret_expr(strct, scope, io)); + s.strct()[field].clone() + } + ExprValidated::Variant { .. } => todo!(), + ExprValidated::Switch { .. } => todo!(), + }) + } +} diff --git a/compiler/src/passes/validate/mod.rs b/compiler/src/passes/validate/mod.rs index dd3c132..8d19a5c 100644 --- a/compiler/src/passes/validate/mod.rs +++ b/compiler/src/passes/validate/mod.rs @@ -1,108 +1,60 @@ mod check_sized; -mod type_check; +mod constrain; +pub mod error; +pub mod interpreter; +pub mod partial_type; +mod resolve; +#[cfg(test)] +mod tests; +mod uniquify; pub mod validate; use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, Op}; -use crate::passes::validate::type_check::error::TypeError; +use crate::passes::parse::{Def, Expr, Lit, Meta, Span, Spanned, Typed}; +use crate::passes::select::std_lib::Std; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::union_find::{UnionFind, UnionIndex}; use derive_more::Display; -use miette::Diagnostic; +use partial_type::PartialType; use std::collections::HashMap; -use std::fmt::Display; -use std::hash::Hash; use std::str::FromStr; -use thiserror::Error; -#[derive(Debug, PartialEq)] -pub struct PrgTypeChecked<'p> { - pub defs: HashMap<&'p str, Def<'p, &'p str, TExpr<'p, &'p str>>>, - pub entry: &'p str, +#[derive(Debug)] +pub struct PrgValidated<'p> { + pub defs: HashMap, DefValidated<'p>>, + pub entry: UniqueSym<'p>, + pub std: Std<'p>, } -#[derive(Debug, PartialEq)] -pub enum TExpr<'p, A: Copy + Hash + Eq + Display> { - Lit { - val: TLit, - typ: Type, - }, - Var { - sym: A, - typ: Type, - }, - Prim { - op: Op, - args: Vec>, - typ: Type, - }, - Let { - sym: A, - bnd: Box>, - bdy: Box>, - typ: Type, - }, - If { - cnd: Box>, - thn: Box>, - els: Box>, - typ: Type, - }, - Apply { - fun: Box>, - args: Vec>, - typ: Type, - }, - Loop { - bdy: Box>, - typ: Type, - }, - Break { - bdy: Box>, - typ: Type, - }, - Continue { - typ: Type, - }, - Return { - bdy: Box>, - typ: Type, - }, - Seq { - stmt: Box>, - cnt: Box>, - typ: Type, - }, - Assign { - sym: A, - bnd: Box>, - typ: Type, - }, - Struct { - sym: A, - fields: Vec<(&'p str, TExpr<'p, A>)>, - typ: Type, - }, - Variant { - enum_sym: A, - variant_sym: A, - bdy: Box>, - typ: Type, - }, - AccessField { - strct: Box>, - field: &'p str, - typ: Type, - }, - Switch { - enm: Box>, - arms: Vec<(A, A, Box>)>, - typ: Type, - }, +pub struct PrgConstrained<'p> { + pub defs: HashMap, DefConstrained<'p>>, + pub entry: UniqueSym<'p>, + pub uf: UnionFind>, + pub std: Std<'p>, +} + +pub type DefValidated<'p> = Def, &'p str, Typed<'p, ExprValidated<'p>>>; +pub type ExprValidated<'p> = Expr, &'p str, TLit, Type>>; + +pub type DefConstrained<'p> = + Def>, Spanned<&'p str>, Meta>>; +pub type ExprConstrained<'p> = Expr>, Spanned<&'p str>, Lit<'p>, CMeta>; + +pub type DefUniquified<'p> = + Def>, Spanned<&'p str>, Spanned>>; +pub type ExprUniquified<'p> = Expr>, Spanned<&'p str>, Lit<'p>, Span>; + +pub struct CMeta { + pub span: Span, + pub index: UnionIndex, } #[derive(Copy, Clone, Debug, PartialEq, Display)] pub enum TLit { #[display(fmt = "{val}")] - Int { val: i32 }, + I64 { val: i64 }, + #[display(fmt = "{val}")] + U64 { val: u64 }, #[display(fmt = "{}", r#"if *val { "true" } else { "false" }"#)] Bool { val: bool }, #[display(fmt = "unit")] @@ -115,10 +67,10 @@ impl TLit { /// Panics if `TLit` is not `Int`. #[must_use] pub fn int(self) -> i64 { - if let TLit::Int { val } = self { - val as i64 - } else { - panic!() + match self { + TLit::I64 { val, .. } => val, + TLit::U64 { val, .. } => val as i64, + _ => panic!(), } } @@ -138,14 +90,14 @@ impl TLit { impl From for i64 { fn from(value: TLit) -> Self { match value { - TLit::Int { val } => val as i64, + TLit::I64 { val } => val, + TLit::U64 { val } => val as i64, TLit::Bool { val } => val as i64, TLit::Unit => 0, } } } -// This implementation is used by the parser. impl FromStr for TLit { type Err = (); @@ -154,43 +106,9 @@ impl FromStr for TLit { "false" => TLit::Bool { val: false }, "true" => TLit::Bool { val: true }, "unit" => TLit::Unit, - s => TLit::Int { + s => TLit::I64 { val: s.parse().map_err(|_| ())?, }, }) } } - -impl<'p, A: Copy + Hash + Eq + Display> TExpr<'p, A> { - pub fn typ(&self) -> &Type { - match self { - TExpr::Lit { typ, .. } - | TExpr::Var { typ, .. } - | TExpr::Prim { typ, .. } - | TExpr::Let { typ, .. } - | TExpr::If { typ, .. } - | TExpr::Apply { typ, .. } - | TExpr::Loop { typ, .. } - | TExpr::Break { typ, .. } - | TExpr::Continue { typ, .. } - | TExpr::Return { typ, .. } - | TExpr::Seq { typ, .. } - | TExpr::Assign { typ, .. } - | TExpr::Struct { typ, .. } - | TExpr::Variant { typ, .. } - | TExpr::AccessField { typ, .. } - | TExpr::Switch { typ, .. } => typ, - } - } -} - -#[derive(Debug, Error, Diagnostic)] -pub enum ValidateError { - #[error(transparent)] - #[diagnostic(transparent)] - TypeError(#[from] TypeError), - #[error("The program doesn't have a main function.")] - NoMain, - #[error("The type '{sym}' is not sized.")] - UnsizedType { sym: String }, -} diff --git a/compiler/src/passes/validate/partial_type.rs b/compiler/src/passes/validate/partial_type.rs new file mode 100644 index 0000000..ff9265b --- /dev/null +++ b/compiler/src/passes/validate/partial_type.rs @@ -0,0 +1,96 @@ +use crate::utils::gen_sym::UniqueSym; +use crate::utils::union_find::{UnionFind, UnionIndex}; +use itertools::Itertools; + +#[derive(Debug, Clone)] +pub enum PartialType<'p> { + I64, + U64, + Int, + Bool, + Unit, + Never, + Var { + sym: UniqueSym<'p>, + }, + Fn { + params: Vec, + typ: UnionIndex, + }, +} + +impl<'p> PartialType<'p> { + pub fn to_string(&self, uf: &mut UnionFind) -> String { + match self { + PartialType::I64 => "I64".to_string(), + PartialType::U64 => "U64".to_string(), + PartialType::Int => "{int}".to_string(), + PartialType::Bool => "Bool".to_string(), + PartialType::Unit => "Unit".to_string(), + PartialType::Never => "Never".to_string(), + PartialType::Var { sym } => sym.sym.to_string(), + PartialType::Fn { params, typ } => { + let params_string = params + .iter() + .map(|index| { + let pt = uf.get(*index).clone(); + pt.to_string(uf) + }) + .format(", ") + .to_string(); + let pt = uf.get(*typ).clone(); + let typ_string = pt.to_string(uf); + format!("fn({params_string}) -> {typ_string}") + } + } + } +} + +#[allow(clippy::result_unit_err)] +pub fn combine_partial_types<'p>( + a: PartialType<'p>, + b: PartialType<'p>, + uf: &mut UnionFind>, +) -> Result, ()> { + let typ = match (a, b) { + (PartialType::I64, PartialType::I64 | PartialType::Int) => PartialType::I64, + (PartialType::Int, PartialType::I64) => PartialType::I64, + (PartialType::U64, PartialType::U64 | PartialType::Int) => PartialType::U64, + (PartialType::Int, PartialType::U64) => PartialType::U64, + (PartialType::Int, PartialType::Int) => PartialType::Int, + (PartialType::Bool, PartialType::Bool) => PartialType::Bool, + (PartialType::Unit, PartialType::Unit) => PartialType::Unit, + (PartialType::Never, t) => t.clone(), + (t, PartialType::Never) => t.clone(), + (PartialType::Var { sym: sym_a }, PartialType::Var { sym: sym_b }) if sym_a == sym_b => { + PartialType::Var { sym: sym_a } + } + ( + PartialType::Fn { + params: params_a, + typ: typ_a, + }, + PartialType::Fn { + params: params_b, + typ: typ_b, + }, + ) => { + if params_a.len() != params_b.len() { + return Err(()); + } + + let params = params_a + .into_iter() + .zip(params_b) + .map(|(param_a, param_b)| uf.try_union_by(param_a, param_b, combine_partial_types)) + .collect::>()?; + + let typ = uf.try_union_by(typ_a, typ_b, combine_partial_types)?; + + PartialType::Fn { params, typ } + } + _ => return Err(()), + }; + + Ok(typ) +} diff --git a/compiler/src/passes/validate/resolve.rs b/compiler/src/passes/validate/resolve.rs new file mode 100644 index 0000000..148c933 --- /dev/null +++ b/compiler/src/passes/validate/resolve.rs @@ -0,0 +1,222 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Expr, Lit, Meta, Param, Spanned, TypeDef, Typed}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::partial_type::PartialType; +use crate::passes::validate::{ + CMeta, DefConstrained, DefValidated, ExprConstrained, ExprValidated, PrgConstrained, + PrgValidated, TLit, +}; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::union_find::{UnionFind, UnionIndex}; +use functor_derive::Functor; + +impl<'p> PrgConstrained<'p> { + pub fn resolve(mut self) -> Result, TypeError> { + Ok(PrgValidated { + defs: self + .defs + .into_iter() + .map(|(sym, def)| resolve_def(def, &mut self.uf).map(|def| (sym, def))) + .collect::>()?, + entry: self.entry, + std: self.std, + }) + } +} + +fn resolve_def<'p>( + def: DefConstrained<'p>, + uf: &mut UnionFind>, +) -> Result, TypeError> { + let def = match def { + DefConstrained::Fn { + sym, + params, + typ, + bdy, + } => DefValidated::Fn { + sym: sym.inner, + params: params.fmap(|p| Param { + sym: p.sym.inner, + typ: resolve_type(p.typ), + mutable: p.mutable, + }), + typ: resolve_type(typ), + bdy: resolve_expr(bdy, uf)?, + }, + DefConstrained::TypeDef { sym, def } => DefValidated::TypeDef { + sym: sym.inner, + def: resolve_typedef(def), + }, + }; + + Ok(def) +} + +fn resolve_typedef<'p>( + typedef: TypeDef>, Spanned<&'p str>>, +) -> TypeDef, &'p str> { + match typedef { + TypeDef::Struct { fields } => TypeDef::Struct { + fields: fields + .fmap(|(field_sym, field_typ)| (field_sym.inner, resolve_type(field_typ))), + }, + TypeDef::Enum { .. } => todo!(), + } +} + +fn resolve_type(typ: Type>) -> Type { + match typ { + Type::I64 => Type::I64, + Type::U64 => Type::U64, + Type::Bool => Type::Bool, + Type::Unit => Type::Unit, + Type::Never => Type::Never, + Type::Fn { params, typ } => Type::Fn { + params: params.fmap(resolve_type), + typ: typ.fmap(resolve_type), + }, + Type::Var { sym } => Type::Var { sym: sym.inner }, + } +} + +/// Panic if not possible +fn partial_type_to_type<'p>( + value: UnionIndex, + uf: &mut UnionFind>, +) -> Option>> { + Some(match uf.get(value).clone() { + PartialType::I64 => Type::I64, + PartialType::U64 => Type::U64, + PartialType::Int => return None, + PartialType::Bool => Type::Bool, + PartialType::Unit => Type::Unit, + PartialType::Never => Type::Never, + PartialType::Var { sym } => Type::Var { sym }, + PartialType::Fn { params, typ } => Type::Fn { + params: params + .into_iter() + .map(|param| partial_type_to_type(uf.find(param), uf)) + .collect::>()?, + typ: Box::new(partial_type_to_type(typ, uf)?), + }, + }) +} + +fn resolve_expr<'p>( + expr: Meta>, + uf: &mut UnionFind>, +) -> Result>, TypeError> { + // Type of the expression, if `None` then type is still ambiguous. + let typ = partial_type_to_type(expr.meta.index, uf); + + let expr = match expr.inner { + Expr::Lit { val } => { + let val = match val { + Lit::Int { val, .. } => match &typ { + None => { + return Err(TypeError::IntegerAmbiguous { + span: expr.meta.span, + }) + } + Some(typ) => match typ { + Type::I64 => TLit::I64 { + val: val.parse().map_err(|_| TypeError::IntegerOutOfBounds { + span: expr.meta.span, + typ: "I64", + })?, + }, + Type::U64 => TLit::U64 { + val: val.parse().map_err(|_| TypeError::IntegerOutOfBounds { + span: expr.meta.span, + typ: "U64", + })?, + }, + _ => unreachable!(), + }, + }, + Lit::Bool { val } => TLit::Bool { val }, + Lit::Unit => TLit::Unit, + }; + Expr::Lit { val } + } + Expr::Var { sym } => Expr::Var { sym: sym.inner }, + Expr::UnaryOp { + op, + expr: expr_inner, + } => Expr::UnaryOp { + op, + expr: Box::new(resolve_expr(*expr_inner, uf)?), + }, + Expr::BinaryOp { + op, + exprs: [e1, e2], + } => Expr::BinaryOp { + op, + exprs: [resolve_expr(*e1, uf)?, resolve_expr(*e2, uf)?].map(Box::new), + }, + Expr::Let { + sym, + mutable, + typ, + bnd, + bdy, + } => Expr::Let { + sym: sym.inner, + mutable, + typ: typ.map(resolve_type), + bnd: Box::new(resolve_expr(*bnd, uf)?), + bdy: Box::new(resolve_expr(*bdy, uf)?), + }, + Expr::If { cnd, thn, els } => Expr::If { + cnd: Box::new(resolve_expr(*cnd, uf)?), + thn: Box::new(resolve_expr(*thn, uf)?), + els: Box::new(resolve_expr(*els, uf)?), + }, + Expr::Apply { fun, args } => Expr::Apply { + fun: Box::new(resolve_expr(*fun, uf)?), + args: args + .into_iter() + .map(|arg| resolve_expr(arg, uf)) + .collect::>()?, + }, + Expr::Loop { bdy } => Expr::Loop { + bdy: Box::new(resolve_expr(*bdy, uf)?), + }, + Expr::Break { bdy } => Expr::Break { + bdy: Box::new(resolve_expr(*bdy, uf)?), + }, + Expr::Continue => Expr::Continue, + Expr::Return { bdy } => Expr::Return { + bdy: Box::new(resolve_expr(*bdy, uf)?), + }, + Expr::Seq { stmt, cnt } => Expr::Seq { + stmt: Box::new(resolve_expr(*stmt, uf)?), + cnt: Box::new(resolve_expr(*cnt, uf)?), + }, + Expr::Assign { sym, bnd } => Expr::Assign { + sym: sym.inner, + bnd: Box::new(resolve_expr(*bnd, uf)?), + }, + Expr::Struct { sym, fields } => Expr::Struct { + sym: sym.inner, + fields: fields + .into_iter() + .map(|(field_sym, field_bnd)| { + resolve_expr(field_bnd, uf).map(|bnd| (field_sym.inner, bnd)) + }) + .collect::>()?, + }, + Expr::AccessField { strct, field } => Expr::AccessField { + strct: Box::new(resolve_expr(*strct, uf)?), + field: field.inner, + }, + Expr::Variant { .. } => todo!(), + Expr::Switch { .. } => todo!(), + }; + + Ok(Meta { + meta: typ.unwrap(), + inner: expr, + }) +} diff --git a/compiler/src/passes/validate/tests.rs b/compiler/src/passes/validate/tests.rs new file mode 100644 index 0000000..5091f2c --- /dev/null +++ b/compiler/src/passes/validate/tests.rs @@ -0,0 +1,40 @@ +use crate::interpreter::TestIO; +use crate::passes::parse::parse::parse_program; +use crate::utils::split_test::split_test; +use derive_name::VariantName; +use miette::{NamedSource, Report}; +use test_each_file::test_each_file; + +fn validate([test]: [&str; 1], good: bool) { + let (input, expected_output, expected_return, expected_error) = split_test(test); + assert_eq!(good, expected_error.is_none()); + + let result = parse_program(test).unwrap().validate(); + + match (result, expected_error) { + (Ok(program), None) => { + let mut io = TestIO::new(input); + let result = program.interpret(&mut io); + + assert_eq!(result, expected_return.into(), "Incorrect program result."); + assert_eq!(io.outputs(), &expected_output, "Incorrect program output."); + } + (Ok(_), Some(expected_error)) => { + panic!("Expected validation to fail with: {expected_error}.") + } + (Err(error), None) => { + let report = Report::with_source_code( + error.into(), + NamedSource::new("", test.to_string()), + ); + println!("{report}"); + panic!("Expected validation to succeed.") + } + (Err(error), Some(expected_error)) => { + assert_eq!(error.variant_name(), expected_error); + } + } +} + +test_each_file! { for ["test"] in "./programs/good" as validate_succeed => |i| validate(i, true) } +test_each_file! { for ["test"] in "./programs/fail/validate" as validate_fail => |i| validate(i, false) } diff --git a/compiler/src/passes/validate/type_check/error.rs b/compiler/src/passes/validate/type_check/error.rs deleted file mode 100644 index 48afb0f..0000000 --- a/compiler/src/passes/validate/type_check/error.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::passes::parse::types::Type; -use miette::Diagnostic; -use thiserror::Error; - -#[derive(Debug, Error, Diagnostic)] -pub enum TypeError { - #[error("Encountered an undeclared variable.")] - UndeclaredVar { - sym: String, - #[label = "This variable `{sym}` was not declared yet"] - span: (usize, usize), - }, - #[error("Type was mismatched.")] - MismatchedType { - expect: Type, - got: Type, - #[label = "Expected this to be of type `{expect}`, but got `{got}`"] - span: (usize, usize), - }, - #[error("Types were mismatched. Expected function, but found '{got}'.")] - TypeMismatchExpectFn { got: Type }, - #[error("Types were mismatched. Expected '{t1}' and '{t2}' to be equal.")] - MismatchedTypes { - t1: Type, - t2: Type, - #[label = "This has type `{t1}`"] - span_t1: (usize, usize), - #[label = "but this has type `{t2}`"] - span_t2: (usize, usize), - }, - #[error("There are multiple functions named `{sym}`.")] - DuplicateFunction { sym: String }, - #[error("Function `{sym}` has duplicate argument names.")] - DuplicateArg { sym: String }, - #[error("Function `{expected}` has {expected} arguments, but found {got} arguments.")] - ArgCountMismatch { expected: usize, got: usize }, - #[error("Found a break outside of a loop.")] - BreakOutsideLoop, - #[error("Tried to modify immutable variable '{sym}'")] - ModifyImmutable { sym: String }, - #[error("The name {sym} should refer to a variable binding.'")] - VariableShouldBeExpr { sym: String }, - #[error("The name `{sym}` should refer to a struct type.'")] - VariableShouldBeStruct { sym: String }, - #[error("The field `{sym}` is not present in the struct definition.'")] - UnknownStructField { sym: String }, - #[error("The field `{sym}` is missing in the struct.'")] - VariableConstructMissingField { sym: String }, - #[error("The field `{sym}` was already provided earlier.'")] - VariableConstructDuplicateField { sym: String }, - #[error("The type `{typ}` should be a struct type.'")] - TypeShouldBeStruct { typ: Type }, - #[error("The type definition `{sym}` is not sized.'")] - UnsizedType { sym: String }, - - #[error("Integer out of bounds.")] - IntegerOutOfBounds { - #[label = "This number does not fit in an i32: `-2147483648..=2147483647`"] - span: (usize, usize), - }, -} diff --git a/compiler/src/passes/validate/type_check/mod.rs b/compiler/src/passes/validate/type_check/mod.rs deleted file mode 100644 index ecafe6b..0000000 --- a/compiler/src/passes/validate/type_check/mod.rs +++ /dev/null @@ -1,162 +0,0 @@ -pub mod error; -mod uncover_globals; -mod validate_access_field; -mod validate_apply; -mod validate_assign; -mod validate_break; -mod validate_continue; -mod validate_expr; -mod validate_if; -mod validate_let; -mod validate_lit; -mod validate_loop; -mod validate_prim; -mod validate_return; -mod validate_seq; -mod validate_struct; -mod validate_switch; -mod validate_type; -mod validate_typedef; -mod validate_var; -mod validate_variant; - -use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, PrgParsed, Spanned, TypeDef}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::uncover_globals::uncover_globals; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::validate_typedef::validate_typedef; -use crate::passes::validate::{PrgTypeChecked, TExpr}; -use crate::utils::expect::expect; -use crate::utils::push_map::PushMap; -use std::collections::HashMap; - -pub struct Env<'a, 'p> { - pub scope: &'a mut PushMap<&'p str, EnvEntry<'p>>, - pub loop_type: &'a mut Option>, - pub in_loop: bool, - pub return_type: &'a Type<&'p str>, -} - -pub enum EnvEntry<'p> { - Type { mutable: bool, typ: Type<&'p str> }, - Def { def: TypeDef<'p, &'p str> }, -} - -impl<'a, 'p> Env<'a, 'p> { - pub fn push( - &mut self, - k: &'p str, - v: EnvEntry<'p>, - sub: impl FnOnce(&mut Env<'_, 'p>) -> O, - ) -> O { - self.scope.push(k, v, |scope| { - sub(&mut Env { - scope, - loop_type: self.loop_type, - in_loop: self.in_loop, - return_type: self.return_type, - }) - }) - } -} - -impl<'p> PrgParsed<'p> { - pub(super) fn type_check(self) -> Result, TypeError> { - let mut scope = uncover_globals(&self)?; - - let defs = self - .defs - .into_iter() - .map(|def| match def { - Def::Fn { - sym, - params, - bdy, - typ, - } => scope.push_iter( - params.clone().iter().map(|p| { - ( - p.sym, - EnvEntry::Type { - mutable: p.mutable, - typ: p.typ.clone(), - }, - ) - }), - |scope| { - let mut env = Env { - scope, - loop_type: &mut None, - in_loop: false, - return_type: &typ, - }; - let bdy = validate_expr(bdy, &mut env)?; - expect_type(&bdy, &typ)?; - Ok(( - sym, - Def::Fn { - sym, - params, - bdy: bdy.inner, - typ, - }, - )) - }, - ), - Def::TypeDef { sym, def } => Ok(( - sym, - Def::TypeDef { - sym, - def: validate_typedef(sym, def, &scope)?, - }, - )), - }) - .collect::, _>>()?; - - Ok(PrgTypeChecked { - defs, - entry: self.entry, - }) - } -} - -pub fn expect_type_eq<'p>( - e1: &Spanned>, - e2: &Spanned>, -) -> Result, TypeError> { - let t1 = e1.inner.typ(); - let t2 = e2.inner.typ(); - - expect( - t1 == t2, - MismatchedTypes { - t1: t1.clone().fmap(str::to_string), - t2: t2.clone().fmap(str::to_string), - span_t1: s(e1.span), - span_t2: s(e2.span), - }, - )?; - - Ok(t1.clone()) -} - -pub fn expect_type<'p>( - expr: &Spanned>, - expected: &Type<&'p str>, -) -> Result<(), TypeError> { - let typ = expr.inner.typ(); - expect( - typ == expected, - MismatchedType { - got: typ.clone().fmap(str::to_string), - expect: expected.clone().fmap(str::to_string), - span: s(expr.span), - }, - ) -} - -pub fn s(span: (usize, usize)) -> (usize, usize) { - (span.0, span.1 - span.0) -} diff --git a/compiler/src/passes/validate/type_check/uncover_globals.rs b/compiler/src/passes/validate/type_check/uncover_globals.rs deleted file mode 100644 index 27660ac..0000000 --- a/compiler/src/passes/validate/type_check/uncover_globals.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Def, PrgParsed}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::EnvEntry; -use crate::utils::expect::expect; -use crate::utils::push_map::PushMap; -use std::collections::{HashMap, HashSet}; - -/// Returns a `PushMap` with all the functions in scope. -pub fn uncover_globals<'p>( - program: &PrgParsed<'p>, -) -> Result>, TypeError> { - let mut globals = HashMap::new(); - - for def in &program.defs { - match def { - Def::Fn { - sym, - params: args, - typ, - .. - } => { - let signature = Type::Fn { - typ: Box::new(typ.clone()), - params: args.iter().map(|param| param.typ.clone()).collect(), - }; - expect( - globals - .insert( - *sym, - EnvEntry::Type { - mutable: false, - typ: signature, - }, - ) - .is_none(), - DuplicateFunction { - sym: (*sym).to_string(), - }, - )?; - - let mut arg_syms = HashSet::new(); - expect( - args.iter().all(|param| arg_syms.insert(param.sym)), - DuplicateArg { - sym: (*sym).to_string(), - }, - )?; - } - Def::TypeDef { sym, def } => { - globals.insert( - sym, - EnvEntry::Def { - def: (*def).clone(), - }, - ); - } - } - } - - Ok(PushMap::from(globals)) -} diff --git a/compiler/src/passes/validate/type_check/validate_access_field.rs b/compiler/src/passes/validate/type_check/validate_access_field.rs deleted file mode 100644 index 61df5b9..0000000 --- a/compiler/src/passes/validate/type_check/validate_access_field.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned, TypeDef}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{Env, EnvEntry}; -use crate::passes::validate::TExpr; - -pub fn validate_access_field<'p>( - strct: Spanned>, - field: &'p str, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let strct = validate_expr(strct, env)?; - - let Type::Var { sym } = strct.inner.typ() else { - return Err(TypeShouldBeStruct { - typ: strct.inner.typ().clone().fmap(str::to_string), - }); - }; - - let EnvEntry::Def { - def: TypeDef::Struct { - fields: def_fields, .. - }, - } = &env.scope[sym] - else { - return Err(VariableShouldBeStruct { - sym: sym.to_string(), - }); - }; - - let Some((_, typ)) = def_fields.iter().find(|&(sym, _)| *sym == field) else { - return Err(UnknownStructField { - sym: sym.to_string(), - }); - }; - - Ok(Spanned { - span, - inner: TExpr::AccessField { - strct: Box::new(strct.inner), - field, - typ: typ.clone(), - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_apply.rs b/compiler/src/passes/validate/type_check/validate_apply.rs deleted file mode 100644 index 2642199..0000000 --- a/compiler/src/passes/validate/type_check/validate_apply.rs +++ /dev/null @@ -1,49 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{expect_type, Env}; -use crate::passes::validate::TExpr; -use crate::utils::expect::expect; -use functor_derive::Functor; - -pub fn validate_apply<'p>( - fun: Spanned>, - args: Vec>>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let fun = validate_expr(fun, env)?; - let args = args - .into_iter() - .map(|arg| validate_expr(arg, env)) - .collect::, _>>()?; - - let Type::Fn { params, typ } = fun.inner.typ() else { - return Err(TypeMismatchExpectFn { - got: fun.inner.typ().clone().fmap(str::to_string), - }); - }; - - expect( - params.len() == args.len(), - ArgCountMismatch { - expected: params.len(), - got: args.len(), - }, - )?; - - for (arg, param_type) in args.iter().zip(params.iter()) { - expect_type(arg, param_type)?; - } - - Ok(Spanned { - span, - inner: TExpr::Apply { - typ: (**typ).clone(), - fun: Box::new(fun.inner), - args: args.fmap(|arg| arg.inner), - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_assign.rs b/compiler/src/passes/validate/type_check/validate_assign.rs deleted file mode 100644 index 60afeaf..0000000 --- a/compiler/src/passes/validate/type_check/validate_assign.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{s, Env, EnvEntry}; -use crate::passes::validate::TExpr; -use crate::utils::expect::expect; - -pub fn validate_assign<'p>( - sym: Spanned<&'p str>, - bnd: Spanned>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let entry = env.scope.get(&sym.inner).ok_or(UndeclaredVar { - sym: (*sym.inner).to_string(), - span: s(sym.span), - })?; - - let EnvEntry::Type { mutable, .. } = entry else { - return Err(VariableShouldBeExpr { - sym: (*sym.inner).to_string(), - }); - }; - - expect( - *mutable, - ModifyImmutable { - sym: (*sym.inner).to_string(), - }, - )?; - - let bnd = validate_expr(bnd, env)?; - - Ok(Spanned { - span, - inner: TExpr::Assign { - sym: sym.inner, - bnd: Box::new(bnd.inner), - typ: Type::Unit, - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_break.rs b/compiler/src/passes/validate/type_check/validate_break.rs deleted file mode 100644 index b1a79bb..0000000 --- a/compiler/src/passes/validate/type_check/validate_break.rs +++ /dev/null @@ -1,32 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{expect_type, Env}; -use crate::passes::validate::TExpr; -use crate::utils::expect::expect; - -pub fn validate_break<'p>( - bdy: Spanned>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - expect(env.in_loop, BreakOutsideLoop)?; - - let bdy = validate_expr(bdy, env)?; - - if let Some(loop_type) = env.loop_type { - expect_type(&bdy, loop_type)?; - } else { - *env.loop_type = Some(bdy.inner.typ().clone()); - } - - Ok(Spanned { - span, - inner: TExpr::Break { - bdy: Box::new(bdy.inner), - typ: Type::Never, - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_continue.rs b/compiler/src/passes/validate/type_check/validate_continue.rs deleted file mode 100644 index 596fb18..0000000 --- a/compiler/src/passes/validate/type_check/validate_continue.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::Spanned; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::TExpr; - -pub fn validate_continue<'p>( - span: (usize, usize), -) -> Result>, TypeError> { - Ok(Spanned { - span, - inner: TExpr::Continue { typ: Type::Never }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_expr.rs b/compiler/src/passes/validate/type_check/validate_expr.rs deleted file mode 100644 index 20954e5..0000000 --- a/compiler/src/passes/validate/type_check/validate_expr.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_access_field::validate_access_field; -use crate::passes::validate::type_check::validate_apply::validate_apply; -use crate::passes::validate::type_check::validate_assign::validate_assign; -use crate::passes::validate::type_check::validate_break::validate_break; -use crate::passes::validate::type_check::validate_continue::validate_continue; -use crate::passes::validate::type_check::validate_if::validate_if; -use crate::passes::validate::type_check::validate_let::validate_let; -use crate::passes::validate::type_check::validate_lit::validate_lit; -use crate::passes::validate::type_check::validate_loop::validate_loop; -use crate::passes::validate::type_check::validate_prim::validate_prim; -use crate::passes::validate::type_check::validate_return::validate_return; -use crate::passes::validate::type_check::validate_seq::validate_seq; -use crate::passes::validate::type_check::validate_struct::validate_struct; -use crate::passes::validate::type_check::validate_switch::validate_switch; -use crate::passes::validate::type_check::validate_var::validate_var; -use crate::passes::validate::type_check::validate_variant::validate_variant; -use crate::passes::validate::type_check::Env; -use crate::passes::validate::TExpr; - -#[rustfmt::skip] -pub fn validate_expr<'p>( - expr: Spanned>, - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - match expr.inner { - Expr::Lit { val } => validate_lit(val, expr.span), - Expr::Var { sym } => validate_var(sym, expr.span, env), - Expr::Prim { op, args } => validate_prim(op, args, expr.span, env), - Expr::Let { sym, mutable, bnd, bdy, } => validate_let(sym, mutable, *bnd, *bdy, expr.span, env), - Expr::If { cnd, thn, els } => validate_if(*cnd, *thn, *els, expr.span, env), - Expr::Apply { fun, args } => validate_apply(*fun, args, expr.span, env), - Expr::Loop { bdy } => validate_loop(*bdy, expr.span, env), - Expr::Break { bdy } => validate_break(*bdy, expr.span, env), - Expr::Continue => validate_continue(expr.span), - Expr::Return { bdy } => validate_return(*bdy, expr.span, env), - Expr::Seq { stmt, cnt } => validate_seq(*stmt, *cnt, expr.span, env), - Expr::Assign { sym, bnd } => validate_assign(sym, *bnd, expr.span, env), - Expr::Struct { sym, fields } => validate_struct(sym, fields, expr.span, env), - Expr::Variant { enum_sym, variant_sym, bdy } => validate_variant(enum_sym, variant_sym, *bdy, expr.span), - Expr::AccessField { strct, field } => validate_access_field(*strct, field, expr.span, env), - Expr::Switch { enm, arms } => validate_switch(*enm, arms, expr.span), - } -} diff --git a/compiler/src/passes/validate/type_check/validate_if.rs b/compiler/src/passes/validate/type_check/validate_if.rs deleted file mode 100644 index 2447f52..0000000 --- a/compiler/src/passes/validate/type_check/validate_if.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{expect_type, expect_type_eq, Env}; -use crate::passes::validate::TExpr; - -pub fn validate_if<'p>( - cnd: Spanned>, - thn: Spanned>, - els: Spanned>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let cnd = validate_expr(cnd, env)?; - let thn = validate_expr(thn, env)?; - let els = validate_expr(els, env)?; - - expect_type(&cnd, &Type::Bool)?; - expect_type_eq(&thn, &els)?; - - Ok(Spanned { - span, - inner: TExpr::If { - typ: thn.inner.typ().clone(), - cnd: Box::new(cnd.inner), - thn: Box::new(thn.inner), - els: Box::new(els.inner), - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_let.rs b/compiler/src/passes/validate/type_check/validate_let.rs deleted file mode 100644 index 2d92e61..0000000 --- a/compiler/src/passes/validate/type_check/validate_let.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{Env, EnvEntry}; -use crate::passes::validate::TExpr; - -pub fn validate_let<'p>( - sym: &'p str, - mutable: bool, - bnd: Spanned>, - bdy: Spanned>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let bnd = validate_expr(bnd, env)?; - let bdy = env.push( - sym, - EnvEntry::Type { - mutable, - typ: bnd.inner.typ().clone(), - }, - |env| validate_expr(bdy, env), - )?; - - Ok(Spanned { - span, - inner: TExpr::Let { - typ: bdy.inner.typ().clone(), - sym, - bnd: Box::new(bnd.inner), - bdy: Box::new(bdy.inner), - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_lit.rs b/compiler/src/passes/validate/type_check/validate_lit.rs deleted file mode 100644 index a670a8b..0000000 --- a/compiler/src/passes/validate/type_check/validate_lit.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Lit, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::s; -use crate::passes::validate::{TExpr, TLit}; - -pub fn validate_lit<'p>( - val: Lit, - span: (usize, usize), -) -> Result>, TypeError> { - let inner = match val { - Lit::Int { val } => { - let val = val - .parse() - .map_err(|_| IntegerOutOfBounds { span: s(span) })?; - - TExpr::Lit { - val: TLit::Int { val }, - typ: Type::Int, - } - } - Lit::Bool { val } => TExpr::Lit { - val: TLit::Bool { val }, - typ: Type::Bool, - }, - Lit::Unit => TExpr::Lit { - val: TLit::Unit, - typ: Type::Unit, - }, - }; - - Ok(Spanned { span, inner }) -} diff --git a/compiler/src/passes/validate/type_check/validate_loop.rs b/compiler/src/passes/validate/type_check/validate_loop.rs deleted file mode 100644 index 8b7878c..0000000 --- a/compiler/src/passes/validate/type_check/validate_loop.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::Env; -use crate::passes::validate::TExpr; - -pub fn validate_loop<'p>( - bdy: Spanned>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let mut loop_type = None; - let mut env = Env { - scope: env.scope, - loop_type: &mut loop_type, - in_loop: true, - return_type: env.return_type, - }; - let bdy = validate_expr(bdy, &mut env)?; - - Ok(Spanned { - span, - inner: TExpr::Loop { - bdy: Box::new(bdy.inner), - typ: loop_type.unwrap_or(Type::Never), - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_prim.rs b/compiler/src/passes/validate/type_check/validate_prim.rs deleted file mode 100644 index ca2f3aa..0000000 --- a/compiler/src/passes/validate/type_check/validate_prim.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Op, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{expect_type, expect_type_eq, Env}; -use crate::passes::validate::TExpr; -use functor_derive::Functor; - -pub fn validate_prim<'p>( - op: Op, - args: Vec>>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let args = args - .into_iter() - .map(|arg| validate_expr(arg, env)) - .collect::, _>>()?; - - let typ = match &(op, args.as_slice()) { - (Op::Plus | Op::Minus | Op::Mul | Op::Mod | Op::Div, [e1, e2]) => { - expect_type(e1, &Type::Int)?; - expect_type(e2, &Type::Int)?; - Type::Int - } - (Op::Minus, [e1]) => { - expect_type(e1, &Type::Int)?; - Type::Int - } - (Op::Read, []) => Type::Int, - (Op::Print, [e1]) => { - // todo: Eventually `Print` should become a function call, not an `Expr`. - expect_type(e1, &Type::Int)?; - Type::Int - } - (Op::GT | Op::GE | Op::LT | Op::LE, [e1, e2]) => { - expect_type(e1, &Type::Int)?; - expect_type(e2, &Type::Int)?; - Type::Bool - } - (Op::EQ | Op::NE, [e1, e2]) => { - expect_type_eq(e1, e2)?; - Type::Bool - } - (Op::Not, [e1]) => { - expect_type(e1, &Type::Bool)?; - Type::Bool - } - (Op::LAnd | Op::LOr | Op::Xor, [e1, e2]) => { - expect_type(e1, &Type::Bool)?; - expect_type(e2, &Type::Bool)?; - Type::Bool - } - _ => panic!("Found incorrect operator during type checking"), - }; - - Ok(Spanned { - span, - inner: TExpr::Prim { - op, - args: args.fmap(|arg| arg.inner), - typ, - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_return.rs b/compiler/src/passes/validate/type_check/validate_return.rs deleted file mode 100644 index 1f29f99..0000000 --- a/compiler/src/passes/validate/type_check/validate_return.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{expect_type, Env}; -use crate::passes::validate::TExpr; - -pub fn validate_return<'p>( - bdy: Spanned>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let bdy = validate_expr(bdy, env)?; - - expect_type(&bdy, env.return_type)?; - - Ok(Spanned { - span, - inner: TExpr::Return { - bdy: Box::new(bdy.inner), - typ: Type::Never, - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_seq.rs b/compiler/src/passes/validate/type_check/validate_seq.rs deleted file mode 100644 index 9f4f51c..0000000 --- a/compiler/src/passes/validate/type_check/validate_seq.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::Env; -use crate::passes::validate::TExpr; - -pub fn validate_seq<'p>( - stmt: Spanned>, - cnt: Spanned>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let stmt = validate_expr(stmt, env)?; - let cnt = validate_expr(cnt, env)?; - - Ok(Spanned { - span, - inner: TExpr::Seq { - typ: cnt.inner.typ().clone(), - stmt: Box::new(stmt.inner), - cnt: Box::new(cnt.inner), - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_struct.rs b/compiler/src/passes/validate/type_check/validate_struct.rs deleted file mode 100644 index bba7c44..0000000 --- a/compiler/src/passes/validate/type_check/validate_struct.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::parse::{Expr, Spanned, TypeDef}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::validate_expr::validate_expr; -use crate::passes::validate::type_check::{expect_type, Env, EnvEntry}; -use crate::passes::validate::TExpr; -use crate::utils::expect::expect; -use functor_derive::Functor; -use std::collections::{HashMap, HashSet}; - -pub fn validate_struct<'p>( - sym: &'p str, - fields: Vec<(&'p str, Spanned>)>, - span: (usize, usize), - env: &mut Env<'_, 'p>, -) -> Result>, TypeError> { - let entry = env.scope.get(&sym).ok_or(UndeclaredVar { - sym: sym.to_string(), - span: (0, 0), // todo: not correct - })?; - - let EnvEntry::Def { - def: TypeDef::Struct { - fields: def_fields, .. - }, - } = &entry - else { - return Err(VariableShouldBeStruct { - sym: sym.to_string(), - }); - }; - - let mut new_provided_fields = HashSet::new(); - let def_fields = def_fields - .iter() - .map(|(k, v)| (*k, v.clone())) - .collect::>(); - - let fields = fields - .into_iter() - .map(|(field, expr)| { - let expr = validate_expr(expr, env)?; - - expect( - new_provided_fields.insert(field), - VariableConstructDuplicateField { - sym: field.to_string(), - }, - )?; - - if let Some(typ) = def_fields.get(field) { - expect_type(&expr, typ)?; - } else { - return Err(UnknownStructField { - sym: field.to_string(), - }); - } - - Ok((field, expr)) - }) - .collect::, _>>()?; - - for field in def_fields.keys() { - expect( - new_provided_fields.contains(field), - VariableConstructMissingField { - sym: field.to_string(), - }, - )?; - } - - Ok(Spanned { - span, - inner: TExpr::Struct { - sym, - fields: fields.fmap(|(sym, field)| (sym, field.inner)), - typ: Type::Var { sym }, - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_switch.rs b/compiler/src/passes/validate/type_check/validate_switch.rs deleted file mode 100644 index 3ac0af0..0000000 --- a/compiler/src/passes/validate/type_check/validate_switch.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::TExpr; - -pub fn validate_switch<'p>( - _enm: Spanned, - _arms: Vec<(&str, &str, Box>)>, - _span: (usize, usize), -) -> Result>, TypeError> { - todo!() -} diff --git a/compiler/src/passes/validate/type_check/validate_type.rs b/compiler/src/passes/validate/type_check/validate_type.rs deleted file mode 100644 index 1d9c987..0000000 --- a/compiler/src/passes/validate/type_check/validate_type.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::passes::parse::types::Type; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::EnvEntry; -use crate::utils::expect::expect; -use crate::utils::push_map::PushMap; - -/// Verifies that the given type exists in the current scope. -pub fn validate_type<'p>( - typ: &'p Type<&'p str>, - scope: &PushMap<&str, EnvEntry<'p>>, -) -> Result<(), TypeError> { - match typ { - Type::Int | Type::Bool | Type::Unit | Type::Never => {} - Type::Fn { typ, params } => { - validate_type(typ, scope)?; - - for typ in params { - validate_type(typ, scope)?; - } - } - Type::Var { sym } => { - expect( - scope.contains(sym), - UndeclaredVar { - sym: sym.to_string(), - span: (0, 0), // todo: not correct - }, - )?; - } - } - - Ok(()) -} diff --git a/compiler/src/passes/validate/type_check/validate_typedef.rs b/compiler/src/passes/validate/type_check/validate_typedef.rs deleted file mode 100644 index ae15b17..0000000 --- a/compiler/src/passes/validate/type_check/validate_typedef.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::passes::parse::TypeDef; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::validate_type::validate_type; -use crate::passes::validate::type_check::EnvEntry; -use crate::utils::push_map::PushMap; - -pub fn validate_typedef<'p>( - _sym: &'p str, - def: TypeDef<'p, &'p str>, - scope: &PushMap<&str, EnvEntry<'p>>, -) -> Result, TypeError> { - Ok(match def { - TypeDef::Struct { fields } => { - fields - .iter() - .try_for_each(|(_, typ)| validate_type(typ, scope))?; - TypeDef::Struct { fields } - } - TypeDef::Enum { .. } => todo!(), - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_var.rs b/compiler/src/passes/validate/type_check/validate_var.rs deleted file mode 100644 index 4955ecc..0000000 --- a/compiler/src/passes/validate/type_check/validate_var.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::passes::parse::Spanned; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::type_check::error::TypeError::*; -use crate::passes::validate::type_check::{s, Env, EnvEntry}; -use crate::passes::validate::TExpr; - -pub fn validate_var<'p>( - sym: &'p str, - span: (usize, usize), - env: &Env<'_, 'p>, -) -> Result>, TypeError> { - let entry = env.scope.get(&sym).ok_or(UndeclaredVar { - sym: (*sym).to_string(), - span: s(span), - })?; - - let EnvEntry::Type { typ, .. } = entry else { - return Err(VariableShouldBeExpr { - sym: (*sym).to_string(), - }); - }; - - Ok(Spanned { - span, - inner: TExpr::Var { - sym, - typ: typ.clone(), - }, - }) -} diff --git a/compiler/src/passes/validate/type_check/validate_variant.rs b/compiler/src/passes/validate/type_check/validate_variant.rs deleted file mode 100644 index aa3eff5..0000000 --- a/compiler/src/passes/validate/type_check/validate_variant.rs +++ /dev/null @@ -1,12 +0,0 @@ -use crate::passes::parse::{Expr, Spanned}; -use crate::passes::validate::type_check::error::TypeError; -use crate::passes::validate::TExpr; - -pub fn validate_variant<'p>( - _enum_sym: &str, - _variant_sym: &str, - _bdy: Spanned, - _span: (usize, usize), -) -> Result>, TypeError> { - todo!() -} diff --git a/compiler/src/passes/validate/uniquify/def.rs b/compiler/src/passes/validate/uniquify/def.rs new file mode 100644 index 0000000..3be29dc --- /dev/null +++ b/compiler/src/passes/validate/uniquify/def.rs @@ -0,0 +1,22 @@ +use crate::passes::parse::{Def, DefParsed}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::uniquify::r#fn::uniquify_fn; +use crate::passes::validate::uniquify::typedef::uniquify_typedef; +use crate::passes::validate::DefUniquified; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; + +pub fn uniquify_def<'p>( + def: DefParsed<'p>, + scope: &mut PushMap<&'p str, UniqueSym<'p>>, +) -> Result, TypeError> { + match def { + Def::Fn { + sym, + params, + typ, + bdy, + } => uniquify_fn(scope, sym, params, typ, bdy), + Def::TypeDef { sym, def } => uniquify_typedef(scope, sym, def), + } +} diff --git a/compiler/src/passes/validate/uniquify/expr.rs b/compiler/src/passes/validate/uniquify/expr.rs new file mode 100644 index 0000000..a509735 --- /dev/null +++ b/compiler/src/passes/validate/uniquify/expr.rs @@ -0,0 +1,100 @@ +use crate::passes::parse::{Expr, ExprParsed, Meta, Spanned}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::uniquify::gen_spanned_sym; +use crate::passes::validate::uniquify::r#type::uniquify_type; +use crate::passes::validate::{uniquify, ExprUniquified}; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; + +pub fn uniquify_expr<'p>( + expr: Spanned>, + scope: &mut PushMap<&'p str, UniqueSym<'p>>, +) -> Result>, TypeError> { + let inner = match expr.inner { + Expr::Let { + sym, + typ, + bnd, + bdy, + mutable, + } => { + let unique_bnd = uniquify_expr(*bnd, scope)?; + let unique_sym = gen_spanned_sym(sym.clone()); + let unique_bdy = scope.push(sym.inner, unique_sym.inner, |scope| { + uniquify_expr(*bdy, scope) + })?; + + Expr::Let { + sym: unique_sym, + mutable, + typ: typ.map(|typ| uniquify_type(typ, scope)).transpose()?, + bnd: Box::new(unique_bnd), + bdy: Box::new(unique_bdy), + } + } + Expr::Var { sym } => Expr::Var { + sym: uniquify::try_get(sym, scope)?, + }, + Expr::Assign { sym, bnd } => Expr::Assign { + sym: uniquify::try_get(sym, scope)?, + bnd: Box::new(uniquify_expr(*bnd, scope)?), + }, + Expr::Struct { sym, fields } => Expr::Struct { + sym: uniquify::try_get(sym, scope)?, + fields: fields + .into_iter() + .map(|(sym, expr)| uniquify_expr(expr, scope).map(|expr| (sym, expr))) + .collect::>()?, + }, + + Expr::Lit { val } => Expr::Lit { val }, + Expr::UnaryOp { op, expr } => Expr::UnaryOp { + op, + expr: Box::new(uniquify_expr(*expr, scope)?), + }, + Expr::BinaryOp { + op, + exprs: [e1, e2], + } => Expr::BinaryOp { + op, + exprs: [uniquify_expr(*e1, scope)?, uniquify_expr(*e2, scope)?].map(Box::new), + }, + Expr::If { cnd, thn, els } => Expr::If { + cnd: Box::new(uniquify_expr(*cnd, scope)?), + thn: Box::new(uniquify_expr(*thn, scope)?), + els: Box::new(uniquify_expr(*els, scope)?), + }, + Expr::Apply { fun, args } => Expr::Apply { + fun: Box::new(uniquify_expr(*fun, scope)?), + args: args + .into_iter() + .map(|arg| uniquify_expr(arg, scope)) + .collect::>()?, + }, + Expr::Loop { bdy } => Expr::Loop { + bdy: Box::new(uniquify_expr(*bdy, scope)?), + }, + Expr::Break { bdy } => Expr::Break { + bdy: Box::new(uniquify_expr(*bdy, scope)?), + }, + Expr::Seq { stmt, cnt } => Expr::Seq { + stmt: Box::new(uniquify_expr(*stmt, scope)?), + cnt: Box::new(uniquify_expr(*cnt, scope)?), + }, + Expr::Continue => Expr::Continue, + Expr::Return { bdy } => Expr::Return { + bdy: Box::new(uniquify_expr(*bdy, scope)?), + }, + Expr::AccessField { strct, field } => Expr::AccessField { + strct: Box::new(uniquify_expr(*strct, scope)?), + field, + }, + Expr::Variant { .. } => todo!(), + Expr::Switch { .. } => todo!(), + }; + + Ok(Meta { + inner, + meta: expr.meta, + }) +} diff --git a/compiler/src/passes/validate/uniquify/fn.rs b/compiler/src/passes/validate/uniquify/fn.rs new file mode 100644 index 0000000..6bc4219 --- /dev/null +++ b/compiler/src/passes/validate/uniquify/fn.rs @@ -0,0 +1,66 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Def, ExprParsed, Param, Spanned}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::uniquify::expr::uniquify_expr; +use crate::passes::validate::uniquify::gen_spanned_sym; +use crate::passes::validate::uniquify::r#type::uniquify_type; +use crate::passes::validate::{uniquify, DefUniquified}; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; +use std::collections::HashMap; + +pub fn uniquify_fn<'p>( + scope: &mut PushMap<&'p str, UniqueSym<'p>>, + sym: Spanned<&'p str>, + params: Vec>>, + typ: Type>, + bdy: Spanned>, +) -> Result, TypeError> { + // Generate unique names for the parameters. + let iterator = params + .iter() + .map(|param| (param.sym.inner, gen_spanned_sym(param.sym.clone()).inner)); + + // Push the parameters into scope and yeet the function. + scope.push_iter(iterator, |scope| { + // Collect uniquified parameters. + let params = params + .iter() + .map(|param| uniquify_param(param, scope)) + .collect::, _>>()?; + + // Uniquify body of the function. + let bdy = uniquify_expr(bdy, scope)?; + + // Check that there are no duplicate parameter names. + let mut param_syms = HashMap::new(); + for param in ¶ms { + // Span of the previously defined duplicate. + if let Some(prev_span) = param_syms.insert(param.sym.inner, param.sym.meta) { + return Err(TypeError::DuplicateArg { + span1: prev_span, + span2: param.sym.meta, + sym: param.sym.inner.sym.to_string(), + }); + } + } + + Ok(Def::Fn { + sym: uniquify::try_get(sym, scope)?, + params, + typ: uniquify_type(typ, scope)?, + bdy, + }) + }) +} + +fn uniquify_param<'p>( + param: &Param>, + scope: &mut PushMap<&'p str, UniqueSym<'p>>, +) -> Result>>, TypeError> { + Ok(Param { + sym: uniquify::try_get(param.sym.clone(), scope)?, + mutable: param.mutable, + typ: uniquify_type(param.typ.clone(), scope)?, + }) +} diff --git a/compiler/src/passes/validate/uniquify/mod.rs b/compiler/src/passes/validate/uniquify/mod.rs new file mode 100644 index 0000000..7dabfff --- /dev/null +++ b/compiler/src/passes/validate/uniquify/mod.rs @@ -0,0 +1,103 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::{Meta, PrgParsed, Spanned}; +use crate::passes::select::std_lib::Std; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::error::TypeError::{NoMain, UndeclaredVar}; +use crate::passes::validate::DefUniquified; +use crate::utils::gen_sym::{gen_sym, UniqueSym}; +use crate::utils::push_map::PushMap; +use once_cell::sync::Lazy; +use std::collections::HashMap; + +mod def; +mod expr; +mod r#fn; +mod r#type; +mod typedef; + +pub struct PrgUniquified<'p> { + /// The global program definitions. + pub defs: Vec>, + /// The symbol representing the entry point of the program. + pub entry: UniqueSym<'p>, + /// Entry points to functions from the standard library. + pub std: Std<'p>, +} + +pub static BUILT_INS: Lazy>>>> = + Lazy::new(|| { + HashMap::from([ + ( + "exit", + Type::Fn { + params: vec![Type::I64], + typ: Box::new(Type::Never), + }, + ), + ( + "print", + Type::Fn { + params: vec![Type::I64], + typ: Box::new(Type::I64), + }, + ), + ( + "read", + Type::Fn { + params: vec![], + typ: Box::new(Type::I64), + }, + ), + ]) + }); + +impl<'p> PrgParsed<'p> { + pub fn uniquify(self) -> Result, TypeError> { + let std: Std<'p> = BUILT_INS + .iter() + .map(|(sym, _)| (*sym, gen_sym(sym))) + .collect(); + + let mut scope = PushMap::from_iter( + self.defs + .iter() + .map(|def| (def.sym().inner, gen_sym(def.sym().inner))) + .chain(std.iter().map(|(&k, &v)| (k, v))), + ); + + let entry = *scope.get(&"main").ok_or(NoMain)?; + + Ok(PrgUniquified { + defs: self + .defs + .into_iter() + .map(|def| def::uniquify_def(def, &mut scope)) + .collect::>()?, + entry, + std, + }) + } +} + +fn try_get<'p>( + sym: Spanned<&'p str>, + scope: &PushMap<&'p str, UniqueSym<'p>>, +) -> Result>, TypeError> { + scope + .get(&sym.inner) + .ok_or(UndeclaredVar { + sym: sym.inner.to_string(), + span: sym.meta, + }) + .map(|&inner| Meta { + meta: sym.meta, + inner, + }) +} + +fn gen_spanned_sym(sym: Spanned<&str>) -> Spanned { + Meta { + inner: gen_sym(sym.inner), + meta: sym.meta, + } +} diff --git a/compiler/src/passes/validate/uniquify/type.rs b/compiler/src/passes/validate/uniquify/type.rs new file mode 100644 index 0000000..e80832d --- /dev/null +++ b/compiler/src/passes/validate/uniquify/type.rs @@ -0,0 +1,31 @@ +use crate::passes::parse::types::Type; +use crate::passes::parse::Spanned; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::uniquify::try_get; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; + +pub fn uniquify_type<'p>( + typ: Type>, + scope: &mut PushMap<&'p str, UniqueSym<'p>>, +) -> Result>>, TypeError> { + let typ = match typ { + Type::I64 => Type::I64, + Type::U64 => Type::U64, + Type::Bool => Type::Bool, + Type::Unit => Type::Unit, + Type::Never => Type::Never, + Type::Fn { params, typ } => Type::Fn { + params: params + .into_iter() + .map(|param| uniquify_type(param, scope)) + .collect::>()?, + typ: Box::new(uniquify_type(*typ, scope)?), + }, + Type::Var { sym } => Type::Var { + sym: try_get(sym, scope)?, + }, + }; + + Ok(typ) +} diff --git a/compiler/src/passes/validate/uniquify/typedef.rs b/compiler/src/passes/validate/uniquify/typedef.rs new file mode 100644 index 0000000..ed72763 --- /dev/null +++ b/compiler/src/passes/validate/uniquify/typedef.rs @@ -0,0 +1,28 @@ +use crate::passes::parse::{Def, Spanned, TypeDef}; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::uniquify::r#type::uniquify_type; +use crate::passes::validate::uniquify::try_get; +use crate::passes::validate::DefUniquified; +use crate::utils::gen_sym::UniqueSym; +use crate::utils::push_map::PushMap; + +pub fn uniquify_typedef<'p>( + scope: &mut PushMap<&'p str, UniqueSym<'p>>, + sym: Spanned<&'p str>, + def: TypeDef, Spanned<&'p str>>, +) -> Result, TypeError> { + let def = match def { + TypeDef::Struct { fields } => TypeDef::Struct { + fields: fields + .into_iter() + .map(|(sym, typ)| Ok((sym, uniquify_type(typ, scope)?))) + .collect::>()?, + }, + TypeDef::Enum { .. } => todo!(), + }; + + Ok(Def::TypeDef { + sym: try_get(sym, scope)?, + def, + }) +} diff --git a/compiler/src/passes/validate/validate.rs b/compiler/src/passes/validate/validate.rs index eb05338..a9d7786 100644 --- a/compiler/src/passes/validate/validate.rs +++ b/compiler/src/passes/validate/validate.rs @@ -1,38 +1,11 @@ use crate::passes::parse::PrgParsed; -use crate::passes::validate::PrgTypeChecked; -use crate::passes::validate::ValidateError; -use crate::passes::validate::ValidateError::NoMain; -use crate::utils::expect::expect; +use crate::passes::validate::error::TypeError; +use crate::passes::validate::PrgValidated; impl<'p> PrgParsed<'p> { - pub fn validate(self) -> Result, ValidateError> { - let program = self.type_check()?; - + pub fn validate(self) -> Result, TypeError> { + let program = self.uniquify()?.constrain()?; program.check_sized()?; - - expect(program.defs.contains_key("main"), NoMain)?; - - Ok(program) + program.resolve() } } - -#[cfg(test)] -mod tests { - use crate::passes::parse::parse::parse_program; - use test_each_file::test_each_file; - - fn validate([test]: [&str; 1], should_fail: bool) { - let res = parse_program(test).unwrap().validate(); - - match (res, should_fail) { - (Ok(_), true) => panic!("Program should not pass type-checking."), - (Err(e), false) => { - panic!("Program should have passed type-checking, but returned error: '{e}'.") - } - _ => {} - } - } - - test_each_file! { for ["test"] in "./programs/good" as validate_succeed => |p| validate(p, false) } - test_each_file! { for ["test"] in "./programs/fail/type_check" as validate_fail => |p| validate(p, true) } -} diff --git a/compiler/src/utils/gen_sym.rs b/compiler/src/utils/gen_sym.rs index bc8a792..7f5a43c 100644 --- a/compiler/src/utils/gen_sym.rs +++ b/compiler/src/utils/gen_sym.rs @@ -1,4 +1,6 @@ use derive_more::Display; +use std::fmt::{Display, Formatter}; +use std::marker::PhantomData; use std::sync::atomic::{AtomicUsize, Ordering}; static COUNT: AtomicUsize = AtomicUsize::new(0); @@ -10,6 +12,15 @@ pub struct UniqueSym<'p> { pub id: usize, } +impl<'p> UniqueSym<'p> { + pub fn fresh(self) -> Self { + Self { + sym: self.sym, + id: COUNT.fetch_add(1, Ordering::SeqCst), + } + } +} + pub fn gen_sym(sym: &str) -> UniqueSym<'_> { UniqueSym { sym, diff --git a/compiler/src/utils/mod.rs b/compiler/src/utils/mod.rs index 0bb1351..e614137 100644 --- a/compiler/src/utils/mod.rs +++ b/compiler/src/utils/mod.rs @@ -3,3 +3,4 @@ pub mod expect; pub mod gen_sym; pub mod push_map; pub mod split_test; +pub mod union_find; diff --git a/compiler/src/utils/push_map.rs b/compiler/src/utils/push_map.rs index fed575a..45e574f 100644 --- a/compiler/src/utils/push_map.rs +++ b/compiler/src/utils/push_map.rs @@ -2,7 +2,6 @@ use derive_more::Index; use std::collections::HashMap; use std::fmt::Debug; use std::hash::Hash; -use std::ops::Index; #[derive(Index)] pub struct PushMap(pub HashMap); diff --git a/compiler/src/utils/split_test.rs b/compiler/src/utils/split_test.rs index 582d6af..4cc35ba 100644 --- a/compiler/src/utils/split_test.rs +++ b/compiler/src/utils/split_test.rs @@ -1,18 +1,14 @@ -use crate::interpreter::Val; -use crate::passes::parse::parse::parse_program; -use crate::passes::parse::{Lit, PrgParsed}; use crate::passes::validate::TLit; use std::cell::OnceCell; -use std::hash::Hash; -use std::str::SplitWhitespace; /// Splits the inputs, expected outputs and expected return from the test. /// The values must be preceded by `//*` and `inp:`, `out:` or `ret:`. #[must_use] -pub fn split_test(test: &str) -> (Vec, Vec, TLit, PrgParsed) { +pub fn split_test(test: &str) -> (Vec, Vec, TLit, Option<&str>) { let mut input = OnceCell::new(); let mut output = OnceCell::new(); let mut expected_return = OnceCell::new(); + let mut expected_error = OnceCell::new(); for line in test.lines() { let mut parts = line.split_whitespace(); @@ -27,6 +23,7 @@ pub fn split_test(test: &str) -> (Vec, Vec, TLit, PrgParsed) { (Some("//*"), Some("ret:")) => expected_return .set(parts.next().unwrap().parse().unwrap()) .unwrap(), + (Some("//*"), Some("err:")) => expected_error.set(parts.next().unwrap()).unwrap(), _ => {} } } @@ -35,6 +32,6 @@ pub fn split_test(test: &str) -> (Vec, Vec, TLit, PrgParsed) { input.take().unwrap_or_default(), output.take().unwrap_or_default(), expected_return.take().unwrap_or(TLit::Unit), - parse_program(test).unwrap(), // todo: pass test file name + expected_error.take(), ) } diff --git a/compiler/src/utils/union_find.rs b/compiler/src/utils/union_find.rs new file mode 100644 index 0000000..9edd773 --- /dev/null +++ b/compiler/src/utils/union_find.rs @@ -0,0 +1,151 @@ +use std::fmt::Debug; +use std::ops::{Index, IndexMut}; + +pub struct UnionFind { + parents: Vec, + data: Vec, +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct UnionIndex(usize); + +impl Default for UnionFind { + fn default() -> Self { + Self::new() + } +} + +impl UnionFind { + pub fn new() -> Self { + Self { + parents: Vec::new(), + data: Vec::new(), + } + } + + pub fn add(&mut self, value: T) -> UnionIndex { + let index = self.data.len(); + self.parents.push(index); + self.data.push(value); + UnionIndex(index) + } + + pub fn add_iter( + &mut self, + values: impl Iterator, + ) -> impl Iterator { + let i = self.parents.len(); + self.data.extend(values); + self.parents.extend(i..self.data.len()); + (i..self.data.len()).map(UnionIndex) + } + + pub fn find(&mut self, index: UnionIndex) -> UnionIndex { + let mut child = index.0; + let mut parent = self.parents[child]; + + // early exit if root + if parent == child { + return UnionIndex(parent); + } + + let parent_parent = self.parents[parent]; + + // early exit if one away from root + if parent_parent == parent { + return UnionIndex(parent_parent); + } + + let mut child_indexes = vec![child, parent]; + child = parent_parent; + + // loop until root is found + loop { + parent = self.parents[child]; + if parent == child { + break; + } + child_indexes.push(child); + child = parent; + } + + // set parent of each child to root + for child_index in child_indexes { + self.parents[child_index] = child + } + + UnionIndex(parent) + } + + pub fn union(&mut self, a: UnionIndex, b: UnionIndex) -> UnionIndex { + let a_root = self.find(a); + let b_root = self.find(b); + self.parents[b_root.0] = a_root.0; + self.parents[b.0] = a_root.0; + a_root + } + + pub fn try_union_by( + &mut self, + a: UnionIndex, + b: UnionIndex, + f: impl FnOnce(T, T, &mut Self) -> Result, + ) -> Result + where + T: Clone + Debug, + { + let at = self.get(a).clone(); + let bt = self.get(b).clone(); + let v = f(at, bt, self)?; + let root = self.union(a, b); + self.data[root.0] = v; + Ok(root) + } + + pub fn get(&mut self, index: UnionIndex) -> &T { + let index = self.find(index).0; + &self.data[index] + } +} + +#[cfg(test)] +mod tests { + use super::*; + use itertools::Itertools; + + #[test] + fn test_simple() { + let mut uf = UnionFind::new(); + let x = uf.add(0); + let y = uf.add(1); + + assert_eq!(uf.find(x), x); + assert_eq!(uf.find(y), y); + + uf.union(x, y); + + assert_eq!(uf.find(x), uf.find(y)); + } + + #[test] + fn test_iter() { + let mut uf = UnionFind::new(); + let ids = uf.add_iter(0..20); + + for (left, _, right) in ids.tuple_windows() { + uf.union(left, right); + } + + assert_ne!(uf.find(UnionIndex(0)), uf.find(UnionIndex(1))); + + for i in (0..20).step_by(2) { + assert_eq!(uf.find(UnionIndex(0)), uf.find(UnionIndex(i))) + } + + for i in (0..20).skip(1).step_by(2) { + assert_eq!(uf.find(UnionIndex(1)), uf.find(UnionIndex(i))) + } + + assert_ne!(uf.find(UnionIndex(0)), uf.find(UnionIndex(1))); + } +} diff --git a/compiler/tests/integration.rs b/compiler/tests/integration.rs index b833daf..e4649d2 100644 --- a/compiler/tests/integration.rs +++ b/compiler/tests/integration.rs @@ -1,5 +1,6 @@ #![cfg(unix)] +use compiler::passes::parse::parse::parse_program; use compiler::passes::validate::TLit; use compiler::utils::split_test::split_test; use std::fs::OpenOptions; @@ -12,7 +13,7 @@ use test_each_file::test_each_file; fn integration([test]: [&str; 1]) { let tempdir = TempDir::with_prefix("rust-compiler-construction-integration").unwrap(); - let (input, expected_output, expected_return, program) = split_test(test); + let (input, expected_output, expected_return, _) = split_test(test); let expected_return: i64 = expected_return.into(); let input_path = tempdir.path().join("output"); @@ -23,19 +24,16 @@ fn integration([test]: [&str; 1]) { .open(input_path) .unwrap(); - program + parse_program(test) + .unwrap() .validate() .unwrap() - .uniquify() .reveal() .atomize() .explicate() .eliminate() .select() - .add_liveness() - .compute_interference() - .color_interference() - .assign_homes() + .assign() .patch() .conclude() .emit() diff --git a/integer_oob b/integer_oob deleted file mode 100644 index e69de29..0000000 diff --git a/invalid_token b/invalid_token deleted file mode 100644 index e69de29..0000000 diff --git a/programs/fail/parse/invalid_token.test b/programs/fail/parse/invalid_token.test index 3420e70..1d1f6e2 100644 --- a/programs/fail/parse/invalid_token.test +++ b/programs/fail/parse/invalid_token.test @@ -1,3 +1,4 @@ -fn main() -> Int { +//* err: InvalidToken +fn main() -> I64 { @ } diff --git a/programs/fail/parse/min_arity.test b/programs/fail/parse/min_arity.test deleted file mode 100644 index 14a2125..0000000 --- a/programs/fail/parse/min_arity.test +++ /dev/null @@ -1,5 +0,0 @@ -//* err: ParseToken -fn main() -> Int { - - -// ^ -} diff --git a/programs/fail/parse/min_arity2.test b/programs/fail/parse/min_arity2.test deleted file mode 100644 index 93fa7eb..0000000 --- a/programs/fail/parse/min_arity2.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - (- 1 2 3) -} diff --git a/programs/fail/parse/plus_arity.test b/programs/fail/parse/plus_arity.test deleted file mode 100644 index ba639a1..0000000 --- a/programs/fail/parse/plus_arity.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - (+ 1 2 3) -} diff --git a/programs/fail/parse/plus_arity2.test b/programs/fail/parse/plus_arity2.test deleted file mode 100644 index 46d1d18..0000000 --- a/programs/fail/parse/plus_arity2.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - (+ 1) -} diff --git a/programs/fail/parse/plus_arity3.test b/programs/fail/parse/plus_arity3.test deleted file mode 100644 index 15deca0..0000000 --- a/programs/fail/parse/plus_arity3.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - (+) -} diff --git a/programs/fail/parse/unexpected_eof.test b/programs/fail/parse/unexpected_eof.test index 4a3ed75..f55bb41 100644 --- a/programs/fail/parse/unexpected_eof.test +++ b/programs/fail/parse/unexpected_eof.test @@ -1 +1,2 @@ -fn main() -> Int { +//* err: UnexpectedEOF +fn main() -> I64 { diff --git a/programs/fail/parse/unexpected_token.rs b/programs/fail/parse/unexpected_token.rs deleted file mode 100644 index 16d8da8..0000000 --- a/programs/fail/parse/unexpected_token.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - - -} diff --git a/programs/fail/parse/unexpected_token.test b/programs/fail/parse/unexpected_token.test new file mode 100644 index 0000000..7d67eeb --- /dev/null +++ b/programs/fail/parse/unexpected_token.test @@ -0,0 +1,4 @@ +//* err: UnexpectedToken +fn main() -> I64 { + - +} diff --git a/programs/fail/type_check/add_bool.test b/programs/fail/type_check/add_bool.test deleted file mode 100644 index b546ddc..0000000 --- a/programs/fail/type_check/add_bool.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - true + 1 -} diff --git a/programs/fail/type_check/and_num.test b/programs/fail/type_check/and_num.test deleted file mode 100644 index 1f10e00..0000000 --- a/programs/fail/type_check/and_num.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - 1 && true -} diff --git a/programs/fail/type_check/bad_return.test b/programs/fail/type_check/bad_return.test deleted file mode 100644 index ffc9400..0000000 --- a/programs/fail/type_check/bad_return.test +++ /dev/null @@ -1,9 +0,0 @@ -//* ret: 42 -fn add(x: Int, y: Int) -> Int { - return true; - 8 -} - -fn main() -> Int { - add(40, 2) -} diff --git a/programs/fail/type_check/break_outside_loop.test b/programs/fail/type_check/break_outside_loop.test deleted file mode 100644 index 7302c17..0000000 --- a/programs/fail/type_check/break_outside_loop.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Unit { - break -} diff --git a/programs/fail/type_check/eq_unmatched.test b/programs/fail/type_check/eq_unmatched.test deleted file mode 100644 index b0db94a..0000000 --- a/programs/fail/type_check/eq_unmatched.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - true == 1 -} diff --git a/programs/fail/type_check/fn_rtrn_1.test b/programs/fail/type_check/fn_rtrn_1.test deleted file mode 100644 index b4c5801..0000000 --- a/programs/fail/type_check/fn_rtrn_1.test +++ /dev/null @@ -1,4 +0,0 @@ -fn main() -> Bool { - 42 -} - diff --git a/programs/fail/type_check/fn_rtrn_2.test b/programs/fail/type_check/fn_rtrn_2.test deleted file mode 100644 index b3c171f..0000000 --- a/programs/fail/type_check/fn_rtrn_2.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - true -} diff --git a/programs/fail/type_check/if_num.test b/programs/fail/type_check/if_num.test deleted file mode 100644 index f4b7aeb..0000000 --- a/programs/fail/type_check/if_num.test +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> Int { - if 1 { - true - } else { - false - } -} diff --git a/programs/fail/type_check/if_unmatched.test b/programs/fail/type_check/if_unmatched.test deleted file mode 100644 index 6a4efad..0000000 --- a/programs/fail/type_check/if_unmatched.test +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> Int { - if true { - 1 - } else { - true - } -} diff --git a/programs/fail/type_check/integer_oob.test b/programs/fail/type_check/integer_oob.test deleted file mode 100644 index cce1c1d..0000000 --- a/programs/fail/type_check/integer_oob.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - 2147483648 -} diff --git a/programs/fail/type_check/let_wrong_type.test b/programs/fail/type_check/let_wrong_type.test deleted file mode 100644 index 508b6e0..0000000 --- a/programs/fail/type_check/let_wrong_type.test +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - let x = 2; - x -} diff --git a/programs/fail/type_check/lt_bool.test b/programs/fail/type_check/lt_bool.test deleted file mode 100644 index b87ef90..0000000 --- a/programs/fail/type_check/lt_bool.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Bool { - 1 < true -} diff --git a/programs/fail/type_check/mut_var.test b/programs/fail/type_check/mut_var.test deleted file mode 100644 index 2904f97..0000000 --- a/programs/fail/type_check/mut_var.test +++ /dev/null @@ -1,9 +0,0 @@ -//* ret: 6 -fn test(x: Int) -> Int { - x = 42; - x -} - -fn main() -> Int { - test(8) -} diff --git a/programs/fail/type_check/name_1.test b/programs/fail/type_check/name_1.test deleted file mode 100644 index 7b428eb..0000000 --- a/programs/fail/type_check/name_1.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - x -} diff --git a/programs/fail/type_check/name_after_let.test b/programs/fail/type_check/name_after_let.test deleted file mode 100644 index 852e5a5..0000000 --- a/programs/fail/type_check/name_after_let.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - (let x = 3; x) + x -} diff --git a/programs/fail/type_check/name_in_let_bind.test b/programs/fail/type_check/name_in_let_bind.test deleted file mode 100644 index 2fc980b..0000000 --- a/programs/fail/type_check/name_in_let_bind.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - let y = x; 1 -} diff --git a/programs/fail/type_check/name_in_let_body.test b/programs/fail/type_check/name_in_let_body.test deleted file mode 100644 index 4eedac1..0000000 --- a/programs/fail/type_check/name_in_let_body.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - let y = 3; x -} diff --git a/programs/fail/type_check/name_plus_1.test b/programs/fail/type_check/name_plus_1.test deleted file mode 100644 index ddc94b9..0000000 --- a/programs/fail/type_check/name_plus_1.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - a + 3 -} diff --git a/programs/fail/type_check/name_plus_2.test b/programs/fail/type_check/name_plus_2.test deleted file mode 100644 index 833a1fe..0000000 --- a/programs/fail/type_check/name_plus_2.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - 3 + a -} diff --git a/programs/fail/type_check/no_main.test b/programs/fail/type_check/no_main.test deleted file mode 100644 index b4173ae..0000000 --- a/programs/fail/type_check/no_main.test +++ /dev/null @@ -1,3 +0,0 @@ -fn unmain() -> Int { - 1 -} diff --git a/programs/fail/type_check/not_num.test b/programs/fail/type_check/not_num.test deleted file mode 100644 index 8a28dad..0000000 --- a/programs/fail/type_check/not_num.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Bool { - !18 -} diff --git a/programs/fail/type_check/read_bool.test b/programs/fail/type_check/read_bool.test deleted file mode 100644 index d267efe..0000000 --- a/programs/fail/type_check/read_bool.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - read() + true -} diff --git a/programs/fail/type_check/struct_2.test b/programs/fail/type_check/struct_2.test deleted file mode 100644 index 0cb5dfd..0000000 --- a/programs/fail/type_check/struct_2.test +++ /dev/null @@ -1,11 +0,0 @@ -struct MyStruct { - field_1: Int, -} - -fn main() { - let x = MyStruct { - field_1: 42, - field_2: 15, - }; - unit -} diff --git a/programs/fail/type_check/struct_3.test b/programs/fail/type_check/struct_3.test deleted file mode 100644 index 05f1e2d..0000000 --- a/programs/fail/type_check/struct_3.test +++ /dev/null @@ -1,8 +0,0 @@ -struct MyStruct { - field_1: Int, -} - -fn main() { - let x = MyStruct { }; - unit -} diff --git a/programs/fail/type_check/struct_4.test b/programs/fail/type_check/struct_4.test deleted file mode 100644 index 9a00c1f..0000000 --- a/programs/fail/type_check/struct_4.test +++ /dev/null @@ -1,11 +0,0 @@ -struct MyStruct { - field_1: Int, -} - -fn main() { - let x = MyStruct { - field_1: 42, - field_1: 15, - }; - unit -} diff --git a/programs/fail/type_check/undeclared_var.test b/programs/fail/type_check/undeclared_var.test deleted file mode 100644 index 9fec7b4..0000000 --- a/programs/fail/type_check/undeclared_var.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - x -} diff --git a/programs/fail/type_check/undeclared_var_assign.test b/programs/fail/type_check/undeclared_var_assign.test deleted file mode 100644 index 75e0c32..0000000 --- a/programs/fail/type_check/undeclared_var_assign.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - x = 2 -} diff --git a/programs/fail/type_check/unit_from_int_1.test b/programs/fail/type_check/unit_from_int_1.test deleted file mode 100644 index afc5440..0000000 --- a/programs/fail/type_check/unit_from_int_1.test +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - unit -} diff --git a/programs/fail/type_check/unit_from_int_2.test b/programs/fail/type_check/unit_from_int_2.test deleted file mode 100644 index 7507649..0000000 --- a/programs/fail/type_check/unit_from_int_2.test +++ /dev/null @@ -1,5 +0,0 @@ -fn main() -> Int { - loop { - break - } -} diff --git a/programs/fail/type_check/unit_from_int_3.test b/programs/fail/type_check/unit_from_int_3.test deleted file mode 100644 index 0402828..0000000 --- a/programs/fail/type_check/unit_from_int_3.test +++ /dev/null @@ -1,4 +0,0 @@ -fn main() -> Int { - let x = unit; - x -} diff --git a/programs/fail/validate/arg_count_mismatch.test b/programs/fail/validate/arg_count_mismatch.test new file mode 100644 index 0000000..bd583c1 --- /dev/null +++ b/programs/fail/validate/arg_count_mismatch.test @@ -0,0 +1,6 @@ +//* err: ArgCountMismatch +fn test() {} + +fn main() { + test(true); +} diff --git a/programs/fail/validate/break_outside_loop.test b/programs/fail/validate/break_outside_loop.test new file mode 100644 index 0000000..103ade5 --- /dev/null +++ b/programs/fail/validate/break_outside_loop.test @@ -0,0 +1,4 @@ +//* err: BreakOutsideLoop +fn main() { + break; +} diff --git a/programs/fail/validate/construct_duplicate_field.test b/programs/fail/validate/construct_duplicate_field.test new file mode 100644 index 0000000..1056986 --- /dev/null +++ b/programs/fail/validate/construct_duplicate_field.test @@ -0,0 +1,11 @@ +//* err: ConstructDuplicateField +struct TestStruct { + field_1: I64, +} + +fn main() { + let x = TestStruct { + field_1: 13, + field_1: 8, + }; +} diff --git a/programs/fail/validate/construct_missing_field.test b/programs/fail/validate/construct_missing_field.test new file mode 100644 index 0000000..c79d2f5 --- /dev/null +++ b/programs/fail/validate/construct_missing_field.test @@ -0,0 +1,11 @@ +//* err: ConstructMissingField +struct TestStruct { + field_1: I64, + field_2: I64, +} + +fn main() { + let x = TestStruct { + field_1: 13, + }; +} diff --git a/programs/fail/validate/continue_outside_loop.test b/programs/fail/validate/continue_outside_loop.test new file mode 100644 index 0000000..69aa400 --- /dev/null +++ b/programs/fail/validate/continue_outside_loop.test @@ -0,0 +1,4 @@ +//* err: ContinueOutsideLoop +fn main() { + continue; +} diff --git a/programs/fail/validate/duplicate_arg.test b/programs/fail/validate/duplicate_arg.test new file mode 100644 index 0000000..192eb34 --- /dev/null +++ b/programs/fail/validate/duplicate_arg.test @@ -0,0 +1,4 @@ +//* err: DuplicateArg +fn test(x: Bool, x: Bool) {} + +fn main() {} diff --git a/programs/fail/validate/duplicate_global_1.test b/programs/fail/validate/duplicate_global_1.test new file mode 100644 index 0000000..8d590dc --- /dev/null +++ b/programs/fail/validate/duplicate_global_1.test @@ -0,0 +1,5 @@ +//* err: DuplicateGlobal +fn test() {} +struct test {} + +fn main() {} diff --git a/programs/fail/validate/duplicate_global_2.test b/programs/fail/validate/duplicate_global_2.test new file mode 100644 index 0000000..ea914ff --- /dev/null +++ b/programs/fail/validate/duplicate_global_2.test @@ -0,0 +1,5 @@ +//* err: DuplicateGlobal +fn test() {} +fn test() {} + +fn main() {} diff --git a/programs/fail/validate/duplicate_global_builtin.test b/programs/fail/validate/duplicate_global_builtin.test new file mode 100644 index 0000000..b1b77b6 --- /dev/null +++ b/programs/fail/validate/duplicate_global_builtin.test @@ -0,0 +1,4 @@ +//* err: DuplicateGlobalBuiltin +fn exit() {} + +fn main() {} diff --git a/programs/fail/validate/fn_arg_expect.test b/programs/fail/validate/fn_arg_expect.test new file mode 100644 index 0000000..f9ba3e1 --- /dev/null +++ b/programs/fail/validate/fn_arg_expect.test @@ -0,0 +1,6 @@ +//* err: FnArgExpect +fn test(x: Bool) {} + +fn main() { + test(42i64); +} diff --git a/programs/fail/validate/if_expect_bool.test b/programs/fail/validate/if_expect_bool.test new file mode 100644 index 0000000..9ce5d8f --- /dev/null +++ b/programs/fail/validate/if_expect_bool.test @@ -0,0 +1,4 @@ +//* err: IfExpectBool +fn main() { + if 5i64 { unit }; +} diff --git a/programs/fail/validate/if_expect_equal_1.test b/programs/fail/validate/if_expect_equal_1.test new file mode 100644 index 0000000..5b558c0 --- /dev/null +++ b/programs/fail/validate/if_expect_equal_1.test @@ -0,0 +1,8 @@ +//* err: IfExpectEqual +fn main() { + if true { + 5i64 + } else { + true + }; +} \ No newline at end of file diff --git a/programs/fail/validate/if_expect_equal_2.test b/programs/fail/validate/if_expect_equal_2.test new file mode 100644 index 0000000..4aba11d --- /dev/null +++ b/programs/fail/validate/if_expect_equal_2.test @@ -0,0 +1,6 @@ +//* err: IfExpectEqual +fn main() { + if true { + 5i64 + }; +} \ No newline at end of file diff --git a/programs/fail/validate/integer_ambiguous.test b/programs/fail/validate/integer_ambiguous.test new file mode 100644 index 0000000..80ea20d --- /dev/null +++ b/programs/fail/validate/integer_ambiguous.test @@ -0,0 +1,4 @@ +//* err: IntegerAmbiguous +fn main() { + 42; +} diff --git a/programs/fail/validate/integer_out_of_bounds.test b/programs/fail/validate/integer_out_of_bounds.test new file mode 100644 index 0000000..053ba31 --- /dev/null +++ b/programs/fail/validate/integer_out_of_bounds.test @@ -0,0 +1,5 @@ +//* err: IntegerOutOfBounds +fn main() { + // 9_223_372_036_854_775_807 is the max for an I64. + 9223372036854775808i64; +} diff --git a/programs/fail/validate/mismatched_assign_binding.test b/programs/fail/validate/mismatched_assign_binding.test new file mode 100644 index 0000000..595f4f8 --- /dev/null +++ b/programs/fail/validate/mismatched_assign_binding.test @@ -0,0 +1,5 @@ +//* err: MismatchedAssignBinding +fn main() { + let mut x = true; + x = 5i64; +} diff --git a/programs/fail/validate/mismatched_expect_fn.test b/programs/fail/validate/mismatched_expect_fn.test new file mode 100644 index 0000000..c9ac403 --- /dev/null +++ b/programs/fail/validate/mismatched_expect_fn.test @@ -0,0 +1,5 @@ +//* err: MismatchedExpectFn +fn main() { + let x = true; + x(); +} diff --git a/programs/fail/validate/mismatched_fn_return.test b/programs/fail/validate/mismatched_fn_return.test new file mode 100644 index 0000000..463c80c --- /dev/null +++ b/programs/fail/validate/mismatched_fn_return.test @@ -0,0 +1,4 @@ +//* err: MismatchedFnReturn +fn main() -> I64 { + true +} \ No newline at end of file diff --git a/programs/fail/validate/mismatched_let_binding.test b/programs/fail/validate/mismatched_let_binding.test new file mode 100644 index 0000000..ef35c5d --- /dev/null +++ b/programs/fail/validate/mismatched_let_binding.test @@ -0,0 +1,4 @@ +//* err: MismatchedLetBinding +fn main() { + let x : Bool = 42i64; +} diff --git a/programs/fail/validate/mismatched_loop.test b/programs/fail/validate/mismatched_loop.test new file mode 100644 index 0000000..6546e97 --- /dev/null +++ b/programs/fail/validate/mismatched_loop.test @@ -0,0 +1,7 @@ +//* err: MismatchedLoop +fn main() { + loop { + break 5i64; + break true; + }; +} diff --git a/programs/fail/validate/mismatched_struct_field.test b/programs/fail/validate/mismatched_struct_field.test new file mode 100644 index 0000000..5972d8b --- /dev/null +++ b/programs/fail/validate/mismatched_struct_field.test @@ -0,0 +1,10 @@ +//* err: MismatchedStructField +struct TestStruct { + field_1: I64, +} + +fn main() { + TestStruct { + field_1: true, + }; +} diff --git a/programs/fail/validate/modify_immutable.test b/programs/fail/validate/modify_immutable.test new file mode 100644 index 0000000..4564265 --- /dev/null +++ b/programs/fail/validate/modify_immutable.test @@ -0,0 +1,5 @@ +//* err: ModifyImmutable +fn main() { + let x = true; + x = false; +} diff --git a/programs/fail/validate/no_main.test b/programs/fail/validate/no_main.test new file mode 100644 index 0000000..7caa89d --- /dev/null +++ b/programs/fail/validate/no_main.test @@ -0,0 +1 @@ +//* err: NoMain diff --git a/programs/fail/validate/operand_equal.test b/programs/fail/validate/operand_equal.test new file mode 100644 index 0000000..f26d092 --- /dev/null +++ b/programs/fail/validate/operand_equal.test @@ -0,0 +1,4 @@ +//* err: OperandEqual +fn main() { + 5i64 == true; +} diff --git a/programs/fail/validate/operand_expect_1.test b/programs/fail/validate/operand_expect_1.test new file mode 100644 index 0000000..b480039 --- /dev/null +++ b/programs/fail/validate/operand_expect_1.test @@ -0,0 +1,4 @@ +//* err: OperandExpect +fn main() { + -true; +} diff --git a/programs/fail/validate/operand_expect_2.test b/programs/fail/validate/operand_expect_2.test new file mode 100644 index 0000000..b87a329 --- /dev/null +++ b/programs/fail/validate/operand_expect_2.test @@ -0,0 +1,4 @@ +//* err: OperandExpect +fn main() { + 5 + true; +} diff --git a/programs/fail/validate/symbol_should_be_struct.test b/programs/fail/validate/symbol_should_be_struct.test new file mode 100644 index 0000000..c7c8df0 --- /dev/null +++ b/programs/fail/validate/symbol_should_be_struct.test @@ -0,0 +1,5 @@ +//* err: SymbolShouldBeStruct +fn main() { + let x = true; + x { }; +} \ No newline at end of file diff --git a/programs/fail/validate/symbol_should_be_variable.test b/programs/fail/validate/symbol_should_be_variable.test new file mode 100644 index 0000000..60d549f --- /dev/null +++ b/programs/fail/validate/symbol_should_be_variable.test @@ -0,0 +1,6 @@ +//* err: SymbolShouldBeVariable +struct TestStruct {} + +fn main() { + TestStruct; +} \ No newline at end of file diff --git a/programs/fail/validate/undeclared_var.test b/programs/fail/validate/undeclared_var.test new file mode 100644 index 0000000..a5d3f43 --- /dev/null +++ b/programs/fail/validate/undeclared_var.test @@ -0,0 +1,4 @@ +//* err: UndeclaredVar +fn main() { + x; +} diff --git a/programs/fail/validate/unknown_struct_field_1.test b/programs/fail/validate/unknown_struct_field_1.test new file mode 100644 index 0000000..4f028ca --- /dev/null +++ b/programs/fail/validate/unknown_struct_field_1.test @@ -0,0 +1,11 @@ +//* err: UnknownStructField +struct TestStruct { + field_1: I64, +} + +fn main() { + let x = TestStruct { + field_1: 13, + field_2: 42, + }; +} diff --git a/programs/fail/validate/unknown_struct_field_2.test b/programs/fail/validate/unknown_struct_field_2.test new file mode 100644 index 0000000..c822c7e --- /dev/null +++ b/programs/fail/validate/unknown_struct_field_2.test @@ -0,0 +1,11 @@ +//* err: UnknownStructField +struct TestStruct { + field_1: I64, +} + +fn main() { + let x = TestStruct { + field_1: 13 + }; + x.field_2; +} diff --git a/programs/fail/type_check/recursive_struct.test b/programs/fail/validate/unsized_type_1.test similarity index 53% rename from programs/fail/type_check/recursive_struct.test rename to programs/fail/validate/unsized_type_1.test index 0f8c64d..24f106c 100644 --- a/programs/fail/type_check/recursive_struct.test +++ b/programs/fail/validate/unsized_type_1.test @@ -1,8 +1,6 @@ +//* err: UnsizedType struct TestStruct { field_1: TestStruct, - field_2: Bool, } -fn main() { - unit -} +fn main() {} diff --git a/programs/fail/type_check/mutually_recursive_struct.test b/programs/fail/validate/unsized_type_2.test similarity index 62% rename from programs/fail/type_check/mutually_recursive_struct.test rename to programs/fail/validate/unsized_type_2.test index dbf184c..3ecb45b 100644 --- a/programs/fail/type_check/mutually_recursive_struct.test +++ b/programs/fail/validate/unsized_type_2.test @@ -1,13 +1,10 @@ +//* err: UnsizedType struct TestStruct1 { field_1: TestStruct2, - field_2: Bool, } struct TestStruct2 { field_1: TestStruct1, - field_2: Bool, } -fn main() { - unit -} +fn main() {} diff --git a/programs/good/algebraic/fn_struct_access.test b/programs/good/algebraic/fn_struct_access.test index 3ed8356..3967595 100644 --- a/programs/good/algebraic/fn_struct_access.test +++ b/programs/good/algebraic/fn_struct_access.test @@ -1,14 +1,14 @@ //* ret: 5 struct TestStruct { - field_1: Int, + field_1: I64, field_2: Bool, } -fn test(v: TestStruct) -> Int { +fn test(v: TestStruct) -> I64 { v.field_1 } -fn main() -> Int { +fn main() -> I64 { let x = TestStruct { field_1: 5, field_2: true, diff --git a/programs/good/algebraic/fn_struct_pass.test b/programs/good/algebraic/fn_struct_pass.test index 3c012ef..db27ee1 100644 --- a/programs/good/algebraic/fn_struct_pass.test +++ b/programs/good/algebraic/fn_struct_pass.test @@ -1,14 +1,14 @@ //* ret: 5 struct TestStruct { - field_1: Int, + field_1: I64, field_2: Bool, } -fn test(v: TestStruct) -> Int { +fn test(v: TestStruct) -> I64 { 5 } -fn main() -> Int { +fn main() -> I64 { let x = TestStruct { field_1: 5, field_2: true, diff --git a/programs/good/algebraic/fn_struct_pass_nested.test b/programs/good/algebraic/fn_struct_pass_nested.test index 876d8fc..7535eff 100644 --- a/programs/good/algebraic/fn_struct_pass_nested.test +++ b/programs/good/algebraic/fn_struct_pass_nested.test @@ -1,19 +1,19 @@ //* ret: 5 struct Foo { - a : Int, + a : I64, b : Bool } struct Bar { c: Foo, - d: Int + d: I64 } -fn test(x: Bar, y: Bool) -> Int { +fn test(x: Bar, y: Bool) -> I64 { 5 } -fn main() -> Int { +fn main() -> I64 { let z = Bar { c: Foo { a : 5, b: true }, d: 7 }; test(z, false) } diff --git a/programs/good/algebraic/simple_struct.test b/programs/good/algebraic/simple_struct.test index f855287..22eed37 100644 --- a/programs/good/algebraic/simple_struct.test +++ b/programs/good/algebraic/simple_struct.test @@ -1,5 +1,5 @@ struct TestStruct { - field_1: Int, + field_1: I64, field_2: Bool, } diff --git a/programs/good/algebraic/simple_struct_access.test b/programs/good/algebraic/simple_struct_access.test index e7ded8e..044962e 100644 --- a/programs/good/algebraic/simple_struct_access.test +++ b/programs/good/algebraic/simple_struct_access.test @@ -1,10 +1,10 @@ //* ret: 5 struct TestStruct { - field_1: Int, + field_1: I64, field_2: Bool, } -fn main() -> Int { +fn main() -> I64 { let x = TestStruct { field_1: 5, field_2: true, diff --git a/programs/good/algebraic/simple_struct_access_nested.test b/programs/good/algebraic/simple_struct_access_nested.test index d5eb8ac..b01d862 100644 --- a/programs/good/algebraic/simple_struct_access_nested.test +++ b/programs/good/algebraic/simple_struct_access_nested.test @@ -1,13 +1,13 @@ //* ret: 5 struct Foo { - x : Int, + x : I64, } struct Bar { y: Foo, } -fn main() -> Int { +fn main() -> I64 { let z = Bar { y: Foo { x : 5 } }; z.y.x } diff --git a/programs/good/algebraic/simple_struct_construct.test b/programs/good/algebraic/simple_struct_construct.test index 16aec0c..239c907 100644 --- a/programs/good/algebraic/simple_struct_construct.test +++ b/programs/good/algebraic/simple_struct_construct.test @@ -1,5 +1,5 @@ struct TestStruct { - field_1: Int, + field_1: I64, field_2: Bool, } diff --git a/programs/good/algebraic/simple_struct_construct_nested.test b/programs/good/algebraic/simple_struct_construct_nested.test index 20b8010..d6268ce 100644 --- a/programs/good/algebraic/simple_struct_construct_nested.test +++ b/programs/good/algebraic/simple_struct_construct_nested.test @@ -1,5 +1,5 @@ struct Foo { - x : Int, + x : I64, } struct Bar { diff --git a/programs/good/algebraic/simple_struct_construct_shorthand.test b/programs/good/algebraic/simple_struct_construct_shorthand.test index c649da8..4840e38 100644 --- a/programs/good/algebraic/simple_struct_construct_shorthand.test +++ b/programs/good/algebraic/simple_struct_construct_shorthand.test @@ -1,5 +1,5 @@ struct TestStruct { - field_1: Int, + field_1: I64, field_2: Bool, } diff --git a/programs/good/algebraic/struct_return.test b/programs/good/algebraic/struct_return.test index 96a9b9c..3b990af 100644 --- a/programs/good/algebraic/struct_return.test +++ b/programs/good/algebraic/struct_return.test @@ -1,6 +1,6 @@ //* ret: true struct TestStruct { - field_1: Int, + field_1: I64, field_2: Bool, } diff --git a/programs/good/booleans/equal_1.test b/programs/good/booleans/equal_1.test index a382f34..d83003c 100644 --- a/programs/good/booleans/equal_1.test +++ b/programs/good/booleans/equal_1.test @@ -1,4 +1,4 @@ //* ret: false fn main() -> Bool { - 42 == 13 + 42i64 == 13 } diff --git a/programs/good/booleans/equal_2.test b/programs/good/booleans/equal_2.test index 3a3d20d..ffbdab0 100644 --- a/programs/good/booleans/equal_2.test +++ b/programs/good/booleans/equal_2.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 42 == 42 + 42i64 == 42 } diff --git a/programs/good/booleans/greater_1.test b/programs/good/booleans/greater_1.test index f56f370..36fd769 100644 --- a/programs/good/booleans/greater_1.test +++ b/programs/good/booleans/greater_1.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 42 > 13 + 42i64 > 13 } diff --git a/programs/good/booleans/greater_2.test b/programs/good/booleans/greater_2.test index 107dc5f..f0f6364 100644 --- a/programs/good/booleans/greater_2.test +++ b/programs/good/booleans/greater_2.test @@ -1,4 +1,4 @@ //* ret: false fn main() -> Bool { - 42 > 42 + 42i64 > 42 } diff --git a/programs/good/booleans/greater_3.test b/programs/good/booleans/greater_3.test index 9a43468..da93d49 100644 --- a/programs/good/booleans/greater_3.test +++ b/programs/good/booleans/greater_3.test @@ -1,4 +1,4 @@ //* ret: false fn main() -> Bool { - 41 > 42 + 41i64 > 42 } diff --git a/programs/good/booleans/greater_or_equal_1.test b/programs/good/booleans/greater_or_equal_1.test index 2bc743e..66f69d5 100644 --- a/programs/good/booleans/greater_or_equal_1.test +++ b/programs/good/booleans/greater_or_equal_1.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 42 >= 13 + 42i64 >= 13 } diff --git a/programs/good/booleans/greater_or_equal_2.test b/programs/good/booleans/greater_or_equal_2.test index 3c03a6c..9dc75ab 100644 --- a/programs/good/booleans/greater_or_equal_2.test +++ b/programs/good/booleans/greater_or_equal_2.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 42 >= 42 + 42i64 >= 42 } diff --git a/programs/good/booleans/if_and.test b/programs/good/booleans/if_and.test index 9f48f22..ce0bc84 100644 --- a/programs/good/booleans/if_and.test +++ b/programs/good/booleans/if_and.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { if true && true { 42 } else { diff --git a/programs/good/booleans/if_els.test b/programs/good/booleans/if_els.test index 04324af..6f54136 100644 --- a/programs/good/booleans/if_els.test +++ b/programs/good/booleans/if_els.test @@ -1,5 +1,5 @@ //* ret: 13 -fn main() -> Int { +fn main() -> I64 { if false { 42 } else { diff --git a/programs/good/booleans/if_false.test b/programs/good/booleans/if_false.test index 63a7d7a..16ef82e 100644 --- a/programs/good/booleans/if_false.test +++ b/programs/good/booleans/if_false.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { +fn main() -> I64 { if false { 1} else {2} } \ No newline at end of file diff --git a/programs/good/booleans/if_gt.test b/programs/good/booleans/if_gt.test index 090bdf4..dff7719 100644 --- a/programs/good/booleans/if_gt.test +++ b/programs/good/booleans/if_gt.test @@ -1,4 +1,4 @@ //* ret: 1 -fn main() -> Int { - if 5 > 3 {1} else {2} +fn main() -> I64 { + if 5i64 > 3 {1} else {2} } \ No newline at end of file diff --git a/programs/good/booleans/if_lazy1.test b/programs/good/booleans/if_lazy1.test index ad6d452..895c22d 100644 --- a/programs/good/booleans/if_lazy1.test +++ b/programs/good/booleans/if_lazy1.test @@ -1,5 +1,5 @@ //* ret: 1 //* out: 1 -fn main() -> Int { - if 5 > 3 { print(1) } else { print(2) } +fn main() -> I64 { + if 5i64 > 3 { print(1) } else { print(2) } } \ No newline at end of file diff --git a/programs/good/booleans/if_lazy2.test b/programs/good/booleans/if_lazy2.test index f23594c..c1dd7b4 100644 --- a/programs/good/booleans/if_lazy2.test +++ b/programs/good/booleans/if_lazy2.test @@ -1,5 +1,5 @@ //* ret: 2 //* out: 2 -fn main() -> Int { - if 5 < 3 { print(1) } else { print(2) } +fn main() -> I64 { + if 5i64 < 3 { print(1) } else { print(2) } } \ No newline at end of file diff --git a/programs/good/booleans/if_let.test b/programs/good/booleans/if_let.test index 46ddd28..d3634c0 100644 --- a/programs/good/booleans/if_let.test +++ b/programs/good/booleans/if_let.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { if (let x = true; x) { 42 } else { diff --git a/programs/good/booleans/if_lt.test b/programs/good/booleans/if_lt.test index 700702c..b09114a 100644 --- a/programs/good/booleans/if_lt.test +++ b/programs/good/booleans/if_lt.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { - if 5 < 3 { 1 } else { 2} +fn main() -> I64 { + if 5i64 < 3 { 1 } else { 2} } \ No newline at end of file diff --git a/programs/good/booleans/if_nested.test b/programs/good/booleans/if_nested.test index 2e8c0d7..1aa20d2 100644 --- a/programs/good/booleans/if_nested.test +++ b/programs/good/booleans/if_nested.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { - (if 5 > 3 { 1 } else { 2 }) + 1 +fn main() -> I64 { + (if 5i64 > 3 { 1 } else { 2 }) + 1 } diff --git a/programs/good/booleans/if_nested1.test b/programs/good/booleans/if_nested1.test index 91b685f..6de6c7a 100644 --- a/programs/good/booleans/if_nested1.test +++ b/programs/good/booleans/if_nested1.test @@ -1,4 +1,4 @@ //* ret: 1 -fn main() -> Int { +fn main() -> I64 { if (if true { true } else {false}) {1 } else {2} } \ No newline at end of file diff --git a/programs/good/booleans/if_nested2.test b/programs/good/booleans/if_nested2.test index 19d0794..b3e0cd8 100644 --- a/programs/good/booleans/if_nested2.test +++ b/programs/good/booleans/if_nested2.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { +fn main() -> I64 { if (if false { true } else { false }) { 1 } else { 2 } } \ No newline at end of file diff --git a/programs/good/booleans/if_nested3.test b/programs/good/booleans/if_nested3.test index bc4c682..d59a800 100644 --- a/programs/good/booleans/if_nested3.test +++ b/programs/good/booleans/if_nested3.test @@ -1,4 +1,4 @@ //* ret: 1 -fn main() -> Int { - if (if 5 > 3 { true } else { false}) {1} else {2} +fn main() -> I64 { + if (if 5i64 > 3 { true } else { false}) {1} else {2} } \ No newline at end of file diff --git a/programs/good/booleans/if_nested4.test b/programs/good/booleans/if_nested4.test index b7d4cd3..5cee6f5 100644 --- a/programs/good/booleans/if_nested4.test +++ b/programs/good/booleans/if_nested4.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { - if (if 5 < 3 { true} else {false}) {1} else {2} +fn main() -> I64 { + if (if 5i64 < 3 { true} else {false}) {1} else {2} } \ No newline at end of file diff --git a/programs/good/booleans/if_not_eq.test b/programs/good/booleans/if_not_eq.test index b5e88ac..5eb16c4 100644 --- a/programs/good/booleans/if_not_eq.test +++ b/programs/good/booleans/if_not_eq.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { - if !(5 >= 3) {1} else {2} +fn main() -> I64 { + if !(5i64 >= 3) {1} else {2} } \ No newline at end of file diff --git a/programs/good/booleans/if_not_false.test b/programs/good/booleans/if_not_false.test index 0573eb9..62d9017 100644 --- a/programs/good/booleans/if_not_false.test +++ b/programs/good/booleans/if_not_false.test @@ -1,4 +1,4 @@ //* ret: 1 -fn main() -> Int { +fn main() -> I64 { if !false {1} else {2} } \ No newline at end of file diff --git a/programs/good/booleans/if_not_lte.test b/programs/good/booleans/if_not_lte.test index 2b1eaa5..a224e9a 100644 --- a/programs/good/booleans/if_not_lte.test +++ b/programs/good/booleans/if_not_lte.test @@ -1,4 +1,4 @@ //* ret: 1 -fn main() -> Int { - if !(5 <= 3) {1} else {2} +fn main() -> I64 { + if !(5i64 <= 3) {1} else {2} } diff --git a/programs/good/booleans/if_not_true.test b/programs/good/booleans/if_not_true.test index 2031f68..38d8225 100644 --- a/programs/good/booleans/if_not_true.test +++ b/programs/good/booleans/if_not_true.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { +fn main() -> I64 { if !true {1} else {2} } diff --git a/programs/good/booleans/if_thn.test b/programs/good/booleans/if_thn.test index 14ba4ea..68d9f87 100644 --- a/programs/good/booleans/if_thn.test +++ b/programs/good/booleans/if_thn.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { if true { 42 } else { diff --git a/programs/good/booleans/if_true.test b/programs/good/booleans/if_true.test index 4daa8ad..8056d90 100644 --- a/programs/good/booleans/if_true.test +++ b/programs/good/booleans/if_true.test @@ -1,4 +1,4 @@ //* ret: 1 -fn main() -> Int { +fn main() -> I64 { if true {1} else {2} } diff --git a/programs/good/booleans/if_with_let_inside.test b/programs/good/booleans/if_with_let_inside.test index 3a99d45..d6b092a 100644 --- a/programs/good/booleans/if_with_let_inside.test +++ b/programs/good/booleans/if_with_let_inside.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { - if (let x = 5; x > 5) {1} else {2} +fn main() -> I64 { + if (let x: I64 = 5; x > 5) {1} else {2} } diff --git a/programs/good/booleans/if_with_let_outside_false.test b/programs/good/booleans/if_with_let_outside_false.test index bee4991..0130440 100644 --- a/programs/good/booleans/if_with_let_outside_false.test +++ b/programs/good/booleans/if_with_let_outside_false.test @@ -1,5 +1,5 @@ //* ret: 1 -fn main() -> Int { +fn main() -> I64 { let x = true; if x { 1 } else {2} } diff --git a/programs/good/booleans/if_with_let_outside_true.test b/programs/good/booleans/if_with_let_outside_true.test index 58d4a64..0b2822c 100644 --- a/programs/good/booleans/if_with_let_outside_true.test +++ b/programs/good/booleans/if_with_let_outside_true.test @@ -1,5 +1,5 @@ //* ret: 2 -fn main() -> Int { +fn main() -> I64 { let x = false; if x { 1 } else { 2 } } diff --git a/programs/good/booleans/less_1.test b/programs/good/booleans/less_1.test index 6e9f2d9..0c804ae 100644 --- a/programs/good/booleans/less_1.test +++ b/programs/good/booleans/less_1.test @@ -1,4 +1,4 @@ //* ret: false fn main() -> Bool { - 42 < 13 + 42i64 < 13 } diff --git a/programs/good/booleans/less_2.test b/programs/good/booleans/less_2.test index 4b9bf40..b81f029 100644 --- a/programs/good/booleans/less_2.test +++ b/programs/good/booleans/less_2.test @@ -1,4 +1,4 @@ //* ret: false fn main() -> Bool { - 42 < 42 + 42i64 < 42 } diff --git a/programs/good/booleans/less_3.test b/programs/good/booleans/less_3.test index 43797ac..8ceaa05 100644 --- a/programs/good/booleans/less_3.test +++ b/programs/good/booleans/less_3.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 42 < 43 + 42i64 < 43 } diff --git a/programs/good/booleans/less_or_equal_1.test b/programs/good/booleans/less_or_equal_1.test index c0f7a56..a521790 100644 --- a/programs/good/booleans/less_or_equal_1.test +++ b/programs/good/booleans/less_or_equal_1.test @@ -1,4 +1,4 @@ //* ret: false fn main() -> Bool { - 42 <= 13 + 42i64 <= 13 } diff --git a/programs/good/booleans/less_or_equal_2.test b/programs/good/booleans/less_or_equal_2.test index 8e3fa09..2c9484d 100644 --- a/programs/good/booleans/less_or_equal_2.test +++ b/programs/good/booleans/less_or_equal_2.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 42 <= 42 + 42i64 <= 42 } diff --git a/programs/good/booleans/let_bdy_if.test b/programs/good/booleans/let_bdy_if.test index 75f975e..2afb576 100644 --- a/programs/good/booleans/let_bdy_if.test +++ b/programs/good/booleans/let_bdy_if.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = true; if x { 42 diff --git a/programs/good/booleans/let_bnd_if.test b/programs/good/booleans/let_bnd_if.test index 6f5e083..5cce5ba 100644 --- a/programs/good/booleans/let_bnd_if.test +++ b/programs/good/booleans/let_bnd_if.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = (if true { 42 } else { 13 }); x } diff --git a/programs/good/booleans/not_equal_1.test b/programs/good/booleans/not_equal_1.test index 79f82ee..347b0ff 100644 --- a/programs/good/booleans/not_equal_1.test +++ b/programs/good/booleans/not_equal_1.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 42 != 13 + 42i64 != 13 } diff --git a/programs/good/booleans/not_equal_2.test b/programs/good/booleans/not_equal_2.test index 21ca106..a2d59d8 100644 --- a/programs/good/booleans/not_equal_2.test +++ b/programs/good/booleans/not_equal_2.test @@ -1,4 +1,4 @@ //* ret: false fn main() -> Bool { - 42 != 42 + 42i64 != 42 } diff --git a/programs/good/booleans/or_gt.test b/programs/good/booleans/or_gt.test index 4dda657..a4f0ec9 100644 --- a/programs/good/booleans/or_gt.test +++ b/programs/good/booleans/or_gt.test @@ -1,4 +1,4 @@ //* ret: true fn main() -> Bool { - 5 > 3 || 3 > 5 + 5i64 > 3 || 3i64 > 5 } diff --git a/programs/good/comments/block_comment.test b/programs/good/comments/block_comment.test index 59af021..2ec282b 100644 --- a/programs/good/comments/block_comment.test +++ b/programs/good/comments/block_comment.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { /* Return the meaning of life */ 20 + 22 } diff --git a/programs/good/comments/block_comment_same_line.test b/programs/good/comments/block_comment_same_line.test index 46308bb..298190a 100644 --- a/programs/good/comments/block_comment_same_line.test +++ b/programs/good/comments/block_comment_same_line.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 20 + /* Return the meaning of life */ 22 } diff --git a/programs/good/comments/line_comment.test b/programs/good/comments/line_comment.test index 413966e..822d327 100644 --- a/programs/good/comments/line_comment.test +++ b/programs/good/comments/line_comment.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { // Return the meaning of life 20 + 22 } diff --git a/programs/good/comments/line_comment_same_line.test b/programs/good/comments/line_comment_same_line.test index f712256..1109c56 100644 --- a/programs/good/comments/line_comment_same_line.test +++ b/programs/good/comments/line_comment_same_line.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 20 + 22 // Return the meaning of life } diff --git a/programs/good/functions/apply.test b/programs/good/functions/apply.test index c7aa15f..3038d24 100644 --- a/programs/good/functions/apply.test +++ b/programs/good/functions/apply.test @@ -1,8 +1,8 @@ //* ret: 42 -fn add(x: Int, y: Int) -> Int { +fn add(x: I64, y: I64) -> I64 { x + y } -fn main() -> Int { +fn main() -> I64 { add(40, 2) } diff --git a/programs/good/functions/bind_to_let.test b/programs/good/functions/bind_to_let.test index bf04d3c..234dc62 100644 --- a/programs/good/functions/bind_to_let.test +++ b/programs/good/functions/bind_to_let.test @@ -1,9 +1,9 @@ //* ret: 42 -fn add(x: Int, y: Int) -> Int { +fn add(x: I64, y: I64) -> I64 { x + y } -fn main() -> Int { +fn main() -> I64 { let add2 = add; add2(40, 2) } diff --git a/programs/good/functions/col.test b/programs/good/functions/col.test index cfc19cb..6272dcb 100644 --- a/programs/good/functions/col.test +++ b/programs/good/functions/col.test @@ -1,14 +1,14 @@ //* out: 76 38 19 58 29 88 44 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 //* ret: 0 -fn div(n: Int) -> Int { +fn div(n: I64) -> I64 { n / 2 } -fn mul(n: Int) -> Int { +fn mul(n: I64) -> I64 { 1 + 3 * n } -fn col(n: Int) -> Bool { +fn col(n: I64) -> Bool { if n == 1 { true } else { @@ -17,7 +17,7 @@ fn col(n: Int) -> Bool { } } -fn main() -> Int { +fn main() -> I64 { if col(25) { 0 } else { diff --git a/programs/good/functions/double_apply.test b/programs/good/functions/double_apply.test index 46ad4a4..4ce5d18 100644 --- a/programs/good/functions/double_apply.test +++ b/programs/good/functions/double_apply.test @@ -1,12 +1,12 @@ //* ret: 42 -fn add(x: Int, y: Int) -> Int { +fn add(x: I64, y: I64) -> I64 { x + y } -fn mul(x: Int, y: Int) -> Int { +fn mul(x: I64, y: I64) -> I64 { x * y } -fn main() -> Int { +fn main() -> I64 { mul(add(2, 4), 7) } diff --git a/programs/good/functions/fib.test b/programs/good/functions/fib.test index bb8e9e6..8c5ed57 100644 --- a/programs/good/functions/fib.test +++ b/programs/good/functions/fib.test @@ -1,11 +1,11 @@ //* ret: 34 -fn fib(n: Int) -> Int { +fn fib(n: I64) -> I64 { if n <= 1 { if n == 0 { 0 } else { 1 } } else { fib(n - 1) + fib(n - 2) } } -fn main() -> Int { +fn main() -> I64 { fib(9) } diff --git a/programs/good/functions/maybe_return.test b/programs/good/functions/maybe_return.test index 1dbc6c7..c921957 100644 --- a/programs/good/functions/maybe_return.test +++ b/programs/good/functions/maybe_return.test @@ -1,5 +1,5 @@ //* out: 42 13 -fn add(x: Int, y: Int) -> Int { +fn add(x: I64, y: I64) -> I64 { if x + y != 13 { return 42; }; diff --git a/programs/good/functions/simple_fn.test b/programs/good/functions/simple_fn.test index 5be036c..5280903 100644 --- a/programs/good/functions/simple_fn.test +++ b/programs/good/functions/simple_fn.test @@ -1,8 +1,8 @@ //* ret: 42 -fn add(x: Int, y: Int) -> Int { +fn add(x: I64, y: I64) -> I64 { x + y } -fn main() -> Int { +fn main() -> I64 { 40 + 2 } diff --git a/programs/good/functions/simple_return.test b/programs/good/functions/simple_return.test index 686e74f..c808829 100644 --- a/programs/good/functions/simple_return.test +++ b/programs/good/functions/simple_return.test @@ -1,9 +1,9 @@ //* ret: 42 -fn add(x: Int, y: Int) -> Int { +fn add(x: I64, y: I64) -> I64 { return x + y; 8 } -fn main() -> Int { +fn main() -> I64 { add(40, 2) } diff --git a/programs/good/integration/is_prime.test b/programs/good/integration/is_prime.test index ed798fb..0559158 100644 --- a/programs/good/integration/is_prime.test +++ b/programs/good/integration/is_prime.test @@ -1,6 +1,6 @@ //* inp: 2 3 4 5 6 7 8 9 10 11 12 13 0 //* out: 1 1 0 1 0 1 0 0 0 1 0 1 -fn is_prime(n: Int) -> Bool { +fn is_prime(n: I64) -> Bool { let mut i = 2; loop { if i == n { diff --git a/programs/good/integration/is_pythagoreon_triple.test b/programs/good/integration/is_pythagoreon_triple.test index f58d28a..518edb0 100644 --- a/programs/good/integration/is_pythagoreon_triple.test +++ b/programs/good/integration/is_pythagoreon_triple.test @@ -1,9 +1,9 @@ //* inp: 3 4 5 5 12 13 1 2 3 0 //* out: 1 1 0 struct Triple { - a: Int, - b: Int, - c: Int, + a: I64, + b: I64, + c: I64, } fn is_triple(triple: Triple) -> Bool { diff --git a/programs/good/integration/mixed1.test b/programs/good/integration/mixed1.test index 6fc94c6..313ebf7 100644 --- a/programs/good/integration/mixed1.test +++ b/programs/good/integration/mixed1.test @@ -1,6 +1,6 @@ //* inp: 45 3 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = read(); let y = read(); x +- y diff --git a/programs/good/integration/mixed10.test b/programs/good/integration/mixed10.test index 023b935..57ff364 100644 --- a/programs/good/integration/mixed10.test +++ b/programs/good/integration/mixed10.test @@ -1,6 +1,6 @@ //* inp: 40 2 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let b = read(); let c = read(); b + c diff --git a/programs/good/integration/mixed11.test b/programs/good/integration/mixed11.test index 74d4d7c..c17d70f 100644 --- a/programs/good/integration/mixed11.test +++ b/programs/good/integration/mixed11.test @@ -1,6 +1,6 @@ //* inp: 1 1 2 2 3 3 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x1 = read(); let x2 = read(); let x3 = read(); diff --git a/programs/good/integration/mixed2.test b/programs/good/integration/mixed2.test index 0cdb31c..294a5de 100644 --- a/programs/good/integration/mixed2.test +++ b/programs/good/integration/mixed2.test @@ -1,6 +1,6 @@ //* inp: 0 0 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = read(); let y = read(); x + y + 42 diff --git a/programs/good/integration/mixed3.test b/programs/good/integration/mixed3.test index 09fe788..fde86e2 100644 --- a/programs/good/integration/mixed3.test +++ b/programs/good/integration/mixed3.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = 30; let z = x + 14; let y = 2; diff --git a/programs/good/integration/mixed4.test b/programs/good/integration/mixed4.test index e94f1ae..145d227 100644 --- a/programs/good/integration/mixed4.test +++ b/programs/good/integration/mixed4.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let v = 1; let w = 46; let x = v + 7; diff --git a/programs/good/integration/mixed5.test b/programs/good/integration/mixed5.test index 08fd60a..1bb762e 100644 --- a/programs/good/integration/mixed5.test +++ b/programs/good/integration/mixed5.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let a = 1; let b = 42; let g = b; diff --git a/programs/good/integration/mixed6.test b/programs/good/integration/mixed6.test index fefcd66..29875a0 100644 --- a/programs/good/integration/mixed6.test +++ b/programs/good/integration/mixed6.test @@ -1,6 +1,6 @@ //* inp: 4 7 //* ret: 33 -fn main() -> Int { +fn main() -> I64 { let x = 30; let y = read(); let z = read(); diff --git a/programs/good/integration/mixed8.test b/programs/good/integration/mixed8.test index b2aa4ab..320b82d 100644 --- a/programs/good/integration/mixed8.test +++ b/programs/good/integration/mixed8.test @@ -1,6 +1,6 @@ //* inp: 82 //* ret: 82 -fn main() -> Int { +fn main() -> I64 { let a = read(); let b = a; let c = b; diff --git a/programs/good/integration/mixed9.test b/programs/good/integration/mixed9.test index 0b839fa..71255cf 100644 --- a/programs/good/integration/mixed9.test +++ b/programs/good/integration/mixed9.test @@ -1,6 +1,6 @@ //* inp: 40 5 7 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = read(); let y = read(); x + (let z = read(); z + -y) diff --git a/programs/good/loops/loop_bool.test b/programs/good/loops/loop_bool.test index f1411e3..62af306 100644 --- a/programs/good/loops/loop_bool.test +++ b/programs/good/loops/loop_bool.test @@ -1,5 +1,5 @@ //* ret: 5 -fn main() -> Int { +fn main() -> I64 { loop { break 5 } diff --git a/programs/good/loops/loop_break_dead.test b/programs/good/loops/loop_break_dead.test index 755c8eb..a6f707d 100644 --- a/programs/good/loops/loop_break_dead.test +++ b/programs/good/loops/loop_break_dead.test @@ -1,5 +1,5 @@ //* ret: 5 -fn main() -> Int { +fn main() -> I64 { loop { break 5; print(3); diff --git a/programs/good/loops/loop_in_if.test b/programs/good/loops/loop_in_if.test index f14a206..8555b72 100644 --- a/programs/good/loops/loop_in_if.test +++ b/programs/good/loops/loop_in_if.test @@ -1,5 +1,5 @@ //* ret: 5 -fn main() -> Int { +fn main() -> I64 { if (loop { break true; }) { 5 } else { diff --git a/programs/good/loops/loop_in_loop.test b/programs/good/loops/loop_in_loop.test index 129c466..5bdf5b3 100644 --- a/programs/good/loops/loop_in_loop.test +++ b/programs/good/loops/loop_in_loop.test @@ -1,5 +1,5 @@ //* ret: 5 -fn main() -> Int { +fn main() -> I64 { loop { let x = (loop { break true diff --git a/programs/good/loops/loop_let.test b/programs/good/loops/loop_let.test index 3c12198..aee20ca 100644 --- a/programs/good/loops/loop_let.test +++ b/programs/good/loops/loop_let.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { loop { let x = 42; break x diff --git a/programs/good/loops/loop_never.test b/programs/good/loops/loop_never.test index 54380fc..ab551f1 100644 --- a/programs/good/loops/loop_never.test +++ b/programs/good/loops/loop_never.test @@ -1,5 +1,5 @@ fn test() -> Never { - loop { 1 } + loop { 1i64 } } fn main() -> Unit { diff --git a/programs/good/loops/loop_never_if.test b/programs/good/loops/loop_never_if.test index 67a5bd0..be06af2 100644 --- a/programs/good/loops/loop_never_if.test +++ b/programs/good/loops/loop_never_if.test @@ -4,7 +4,7 @@ fn main() -> Unit { let x = (break); unit } else { - let x = (loop { 5 }); + let x = (loop { 5i64 }); unit } } diff --git a/programs/good/loops/loop_num.test b/programs/good/loops/loop_num.test index f1411e3..62af306 100644 --- a/programs/good/loops/loop_num.test +++ b/programs/good/loops/loop_num.test @@ -1,5 +1,5 @@ //* ret: 5 -fn main() -> Int { +fn main() -> I64 { loop { break 5 } diff --git a/programs/good/loops/loop_num_shortcircuit.test b/programs/good/loops/loop_num_shortcircuit.test index b0e2a37..5fe64a7 100644 --- a/programs/good/loops/loop_num_shortcircuit.test +++ b/programs/good/loops/loop_num_shortcircuit.test @@ -1,5 +1,5 @@ //* ret: 5 -fn main() -> Int { +fn main() -> I64 { loop { (let x = (break 5); 2) + print(3) } diff --git a/programs/good/loops/loop_to_42.test b/programs/good/loops/loop_to_42.test index 02d5c41..0ab8e3f 100644 --- a/programs/good/loops/loop_to_42.test +++ b/programs/good/loops/loop_to_42.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let mut x = 1; loop { if x < 42 { diff --git a/programs/good/loops/while__count_down.test b/programs/good/loops/while__count_down.test index d0de7e6..f281b85 100644 --- a/programs/good/loops/while__count_down.test +++ b/programs/good/loops/while__count_down.test @@ -1,5 +1,5 @@ //* ret: 5 -fn main() -> Int { +fn main() -> I64 { let mut i = 10; while i > 5 { i = i - 1; diff --git a/programs/good/loops/while_continue.test b/programs/good/loops/while_continue.test index b67747c..f847cc5 100644 --- a/programs/good/loops/while_continue.test +++ b/programs/good/loops/while_continue.test @@ -1,6 +1,6 @@ //* inp: 1 2 3 4 5 6 7 8 9 10 0 //* ret: 30 -fn main() -> Int { +fn main() -> I64 { let mut x = 0; let mut sum = 0; while (x = read(); x != 0) { diff --git a/programs/good/loops/while_sum.test b/programs/good/loops/while_sum.test index 622b094..63f6ec3 100644 --- a/programs/good/loops/while_sum.test +++ b/programs/good/loops/while_sum.test @@ -1,6 +1,6 @@ //* inp: 1 2 3 4 5 6 7 8 9 10 0 //* ret: 55 -fn main() -> Int { +fn main() -> I64 { let mut x = 0; let mut sum = 0; while (x = read(); x != 0) { diff --git a/programs/good/mutability/let_semicolon.test b/programs/good/mutability/let_semicolon.test index f32f947..ddfd9d0 100644 --- a/programs/good/mutability/let_semicolon.test +++ b/programs/good/mutability/let_semicolon.test @@ -1,3 +1,3 @@ fn main() -> Unit { - let x = 5; + let x = 5i64; } diff --git a/programs/good/mutability/let_set.test b/programs/good/mutability/let_set.test index 5a8438c..3858f9b 100644 --- a/programs/good/mutability/let_set.test +++ b/programs/good/mutability/let_set.test @@ -1,5 +1,5 @@ //* ret: 6 -fn main() -> Int { +fn main() -> I64 { let mut x = 5; x = 6; x diff --git a/programs/good/mutability/mut_var.test b/programs/good/mutability/mut_var.test index c9d6afa..cf527af 100644 --- a/programs/good/mutability/mut_var.test +++ b/programs/good/mutability/mut_var.test @@ -1,9 +1,9 @@ //* ret: 42 -fn test(mut x: Int) -> Int { +fn test(mut x: I64) -> I64 { x = 42; x } -fn main() -> Int { +fn main() -> I64 { test(8) } diff --git a/programs/good/mutability/possibly_set.test b/programs/good/mutability/possibly_set.test index 3ccf4ec..c6b07a1 100644 --- a/programs/good/mutability/possibly_set.test +++ b/programs/good/mutability/possibly_set.test @@ -1,6 +1,6 @@ //* inp: 45 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let mut x = read(); if x > 42 { x = 42; diff --git a/programs/good/mutability/semicolon.test b/programs/good/mutability/semicolon.test index e7259a2..520a89d 100644 --- a/programs/good/mutability/semicolon.test +++ b/programs/good/mutability/semicolon.test @@ -1,3 +1,3 @@ fn main() -> Unit { - 5; + 5i64; } diff --git a/programs/good/mutability/semicolons.test b/programs/good/mutability/semicolons.test index 173d28d..2b0237a 100644 --- a/programs/good/mutability/semicolons.test +++ b/programs/good/mutability/semicolons.test @@ -1,5 +1,5 @@ fn main() -> Unit { - 5; - 6; - 7; + 5i64; + 6u64; + 7i64; } diff --git a/programs/good/mutability/uncover_get.test b/programs/good/mutability/uncover_get.test index c134faa..9c927e7 100644 --- a/programs/good/mutability/uncover_get.test +++ b/programs/good/mutability/uncover_get.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let mut x = 2; x + (x = 40; x) } diff --git a/programs/good/lvar/add.test b/programs/good/simple/add.test similarity index 57% rename from programs/good/lvar/add.test rename to programs/good/simple/add.test index 4ac8cd5..3a50830 100644 --- a/programs/good/lvar/add.test +++ b/programs/good/simple/add.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 20 + 22 } diff --git a/programs/good/lvar/arith1.test b/programs/good/simple/arith1.test similarity index 62% rename from programs/good/lvar/arith1.test rename to programs/good/simple/arith1.test index a017981..112e213 100644 --- a/programs/good/lvar/arith1.test +++ b/programs/good/simple/arith1.test @@ -1,4 +1,4 @@ //* ret: 22 -fn main() -> Int { +fn main() -> I64 { (32 + (-10)) } diff --git a/programs/good/lvar/arith2.test b/programs/good/simple/arith2.test similarity index 68% rename from programs/good/lvar/arith2.test rename to programs/good/simple/arith2.test index 58018d6..8837786 100644 --- a/programs/good/lvar/arith2.test +++ b/programs/good/simple/arith2.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { ((10 + 11) + (25 + -4)) } diff --git a/programs/good/lvar/arith3.test b/programs/good/simple/arith3.test similarity index 61% rename from programs/good/lvar/arith3.test rename to programs/good/simple/arith3.test index 74e036c..9d60f96 100644 --- a/programs/good/lvar/arith3.test +++ b/programs/good/simple/arith3.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 30 + 10 + 2 } diff --git a/programs/good/lvar/arith4.test b/programs/good/simple/arith4.test similarity index 62% rename from programs/good/lvar/arith4.test rename to programs/good/simple/arith4.test index a44c00d..1bba9df 100644 --- a/programs/good/lvar/arith4.test +++ b/programs/good/simple/arith4.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 52 + -(3 + 7) } diff --git a/programs/good/lvar/arith5.test b/programs/good/simple/arith5.test similarity index 88% rename from programs/good/lvar/arith5.test rename to programs/good/simple/arith5.test index 396b8c7..d9a3c37 100644 --- a/programs/good/lvar/arith5.test +++ b/programs/good/simple/arith5.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 10 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 } diff --git a/programs/good/lvar/arith6.test b/programs/good/simple/arith6.test similarity index 56% rename from programs/good/lvar/arith6.test rename to programs/good/simple/arith6.test index d8e23e0..f6ee20f 100644 --- a/programs/good/lvar/arith6.test +++ b/programs/good/simple/arith6.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { -(-42) } diff --git a/programs/good/lvar/arith7.test b/programs/good/simple/arith7.test similarity index 68% rename from programs/good/lvar/arith7.test rename to programs/good/simple/arith7.test index 4c7224c..d94a04c 100644 --- a/programs/good/lvar/arith7.test +++ b/programs/good/simple/arith7.test @@ -1,4 +1,4 @@ //* ret: -40 -fn main() -> Int { +fn main() -> I64 { (-10 - 20) + (20 - 30) } diff --git a/programs/good/lvar/div.test b/programs/good/simple/div.test similarity index 55% rename from programs/good/lvar/div.test rename to programs/good/simple/div.test index 5e83224..29b13c0 100644 --- a/programs/good/lvar/div.test +++ b/programs/good/simple/div.test @@ -1,4 +1,4 @@ //* ret: 2 -fn main() -> Int { +fn main() -> I64 { 15 / 6 } diff --git a/programs/good/simple/int_hint.test b/programs/good/simple/int_hint.test new file mode 100644 index 0000000..ffbdab0 --- /dev/null +++ b/programs/good/simple/int_hint.test @@ -0,0 +1,4 @@ +//* ret: true +fn main() -> Bool { + 42i64 == 42 +} diff --git a/programs/good/lvar/io2.test b/programs/good/simple/io2.test similarity index 78% rename from programs/good/lvar/io2.test rename to programs/good/simple/io2.test index 7b12221..dc136d4 100644 --- a/programs/good/lvar/io2.test +++ b/programs/good/simple/io2.test @@ -1,6 +1,6 @@ //* inp: 12 13 //* out: -1 //* ret: -1 -fn main() -> Int { +fn main() -> I64 { print(read() - read()) } diff --git a/programs/good/lvar/io3.test b/programs/good/simple/io3.test similarity index 80% rename from programs/good/lvar/io3.test rename to programs/good/simple/io3.test index d2bad4c..47a0cd8 100644 --- a/programs/good/lvar/io3.test +++ b/programs/good/simple/io3.test @@ -1,6 +1,6 @@ //* inp: 12 13 //* out: 12 13 //* ret: 25 -fn main() -> Int { +fn main() -> I64 { print(read()) + print(read()) } diff --git a/programs/good/lvar/let2.test b/programs/good/simple/let2.test similarity index 80% rename from programs/good/lvar/let2.test rename to programs/good/simple/let2.test index b277c44..1ab335b 100644 --- a/programs/good/lvar/let2.test +++ b/programs/good/simple/let2.test @@ -1,6 +1,6 @@ //* inp: 3 5 //* ret: -2 -fn main() -> Int { +fn main() -> I64 { let x = read(); let y = read(); x - y diff --git a/programs/good/lvar/let3.test b/programs/good/simple/let3.test similarity index 84% rename from programs/good/lvar/let3.test rename to programs/good/simple/let3.test index a88f38c..230cbff 100644 --- a/programs/good/lvar/let3.test +++ b/programs/good/simple/let3.test @@ -1,6 +1,6 @@ //* inp: 3 5 11 //* ret: -13 -fn main() -> Int { +fn main() -> I64 { let x = read(); let y = read(); let z = read(); diff --git a/programs/good/lvar/let4.test b/programs/good/simple/let4.test similarity index 76% rename from programs/good/lvar/let4.test rename to programs/good/simple/let4.test index e8ea154..d50db5d 100644 --- a/programs/good/lvar/let4.test +++ b/programs/good/simple/let4.test @@ -1,6 +1,6 @@ //* inp: 8 //* ret: 8 -fn main() -> Int { +fn main() -> I64 { let x = (let y = read(); y); x } diff --git a/programs/good/lvar/let5.test b/programs/good/simple/let5.test similarity index 81% rename from programs/good/lvar/let5.test rename to programs/good/simple/let5.test index 4625f25..25658de 100644 --- a/programs/good/lvar/let5.test +++ b/programs/good/simple/let5.test @@ -1,6 +1,6 @@ //* inp: 8 5 //* ret: 3 -fn main() -> Int { +fn main() -> I64 { let x = (let y = read(); let z = read(); y - z); x } diff --git a/programs/good/lvar/let6.test b/programs/good/simple/let6.test similarity index 85% rename from programs/good/lvar/let6.test rename to programs/good/simple/let6.test index 69e78a7..9db3322 100644 --- a/programs/good/lvar/let6.test +++ b/programs/good/simple/let6.test @@ -1,6 +1,6 @@ //* inp: 7 9 211 //* ret: 227 -fn main() -> Int { +fn main() -> I64 { let x = (let y = read(); let z = read(); y + z); let w = read(); x + w diff --git a/programs/good/lvar/let7.test b/programs/good/simple/let7.test similarity index 79% rename from programs/good/lvar/let7.test rename to programs/good/simple/let7.test index 9087ff2..05fc7df 100644 --- a/programs/good/lvar/let7.test +++ b/programs/good/simple/let7.test @@ -1,5 +1,5 @@ //* inp: 7 9 //* ret: -2 -fn main() -> Int { +fn main() -> I64 { (let x = read(); x) - (let y = read(); y) } diff --git a/programs/good/lvar/let8.test b/programs/good/simple/let8.test similarity index 83% rename from programs/good/lvar/let8.test rename to programs/good/simple/let8.test index 26ef0e9..59d8ed6 100644 --- a/programs/good/lvar/let8.test +++ b/programs/good/simple/let8.test @@ -1,6 +1,6 @@ //* inp: 7 9 //* ret: -30 -fn main() -> Int { +fn main() -> I64 { let y = (read() - read()); (let z = y; z - 8) - (18 - y) } diff --git a/programs/good/lvar/let9.test b/programs/good/simple/let9.test similarity index 79% rename from programs/good/lvar/let9.test rename to programs/good/simple/let9.test index 852fd28..9094f98 100644 --- a/programs/good/lvar/let9.test +++ b/programs/good/simple/let9.test @@ -1,6 +1,6 @@ //* inp: 7 9 //* ret: 16 -fn main() -> Int { +fn main() -> I64 { let x = read(); x + (let x = read(); x) } diff --git a/programs/good/lvar/let_chain_1.test b/programs/good/simple/let_chain_1.test similarity index 72% rename from programs/good/lvar/let_chain_1.test rename to programs/good/simple/let_chain_1.test index 2a69943..a59ec58 100644 --- a/programs/good/lvar/let_chain_1.test +++ b/programs/good/simple/let_chain_1.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = 42; let y = x; y diff --git a/programs/good/lvar/let_chain_2.test b/programs/good/simple/let_chain_2.test similarity index 74% rename from programs/good/lvar/let_chain_2.test rename to programs/good/simple/let_chain_2.test index 4e56004..a6d52d4 100644 --- a/programs/good/lvar/let_chain_2.test +++ b/programs/good/simple/let_chain_2.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = 20; let y = 22; x + y diff --git a/programs/good/lvar/let_chain_bind.test b/programs/good/simple/let_chain_bind.test similarity index 72% rename from programs/good/lvar/let_chain_bind.test rename to programs/good/simple/let_chain_bind.test index 865620e..48abb1d 100644 --- a/programs/good/lvar/let_chain_bind.test +++ b/programs/good/simple/let_chain_bind.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = (let x = 42; x); x } diff --git a/programs/good/simple/let_hint.test b/programs/good/simple/let_hint.test new file mode 100644 index 0000000..b7b4a92 --- /dev/null +++ b/programs/good/simple/let_hint.test @@ -0,0 +1,5 @@ +//* ret: true +fn main() -> Bool { + let x : I64 = 42; + x == 42 +} diff --git a/programs/good/lvar/let_shadow.test b/programs/good/simple/let_shadow.test similarity index 72% rename from programs/good/lvar/let_shadow.test rename to programs/good/simple/let_shadow.test index 4960889..8192696 100644 --- a/programs/good/lvar/let_shadow.test +++ b/programs/good/simple/let_shadow.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = 42; let x = x; x diff --git a/programs/good/lvar/let_simple.test b/programs/good/simple/let_simple.test similarity index 65% rename from programs/good/lvar/let_simple.test rename to programs/good/simple/let_simple.test index 0d186b1..502b8ca 100644 --- a/programs/good/lvar/let_simple.test +++ b/programs/good/simple/let_simple.test @@ -1,5 +1,5 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { let x = 42; x } diff --git a/programs/good/lvar/modulo.test b/programs/good/simple/modulo.test similarity index 55% rename from programs/good/lvar/modulo.test rename to programs/good/simple/modulo.test index 11f9df1..cb449e9 100644 --- a/programs/good/lvar/modulo.test +++ b/programs/good/simple/modulo.test @@ -1,4 +1,4 @@ //* ret: 3 -fn main() -> Int { +fn main() -> I64 { 15 % 6 } diff --git a/programs/good/lvar/mul.test b/programs/good/simple/mul.test similarity index 55% rename from programs/good/lvar/mul.test rename to programs/good/simple/mul.test index 7975072..54d1577 100644 --- a/programs/good/lvar/mul.test +++ b/programs/good/simple/mul.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 6 * 7 } diff --git a/programs/good/lvar/multi_read.test b/programs/good/simple/multi_read.test similarity index 72% rename from programs/good/lvar/multi_read.test rename to programs/good/simple/multi_read.test index bc931a5..f3b83af 100644 --- a/programs/good/lvar/multi_read.test +++ b/programs/good/simple/multi_read.test @@ -1,5 +1,5 @@ //* inp: 42 42 //* ret: 84 -fn main() -> Int { +fn main() -> I64 { read() + read() } diff --git a/programs/good/lvar/multiple_adds.test b/programs/good/simple/multiple_adds.test similarity index 67% rename from programs/good/lvar/multiple_adds.test rename to programs/good/simple/multiple_adds.test index 77dca99..faf2984 100644 --- a/programs/good/lvar/multiple_adds.test +++ b/programs/good/simple/multiple_adds.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { ((32 + 8) + (2 + 0)) } diff --git a/programs/good/lvar/neg.test b/programs/good/simple/neg.test similarity index 52% rename from programs/good/lvar/neg.test rename to programs/good/simple/neg.test index 63fc77d..21c1e3d 100644 --- a/programs/good/lvar/neg.test +++ b/programs/good/simple/neg.test @@ -1,4 +1,4 @@ //* ret: -8 -fn main() -> Int { +fn main() -> I64 { -8 } diff --git a/programs/good/lvar/num.test b/programs/good/simple/num.test similarity index 52% rename from programs/good/lvar/num.test rename to programs/good/simple/num.test index 85e69ed..55acb2b 100644 --- a/programs/good/lvar/num.test +++ b/programs/good/simple/num.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 42 } diff --git a/programs/good/lvar/print.test b/programs/good/simple/print.test similarity index 67% rename from programs/good/lvar/print.test rename to programs/good/simple/print.test index fb91f50..fbfd05e 100644 --- a/programs/good/lvar/print.test +++ b/programs/good/simple/print.test @@ -1,5 +1,5 @@ //* out: 42 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { print(42) } diff --git a/programs/good/lvar/read.test b/programs/good/simple/read.test similarity index 66% rename from programs/good/lvar/read.test rename to programs/good/simple/read.test index a9345a4..f9e5686 100644 --- a/programs/good/lvar/read.test +++ b/programs/good/simple/read.test @@ -1,5 +1,5 @@ //* inp: 42 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { read() } diff --git a/programs/good/lvar/read_print.test b/programs/good/simple/read_print.test similarity index 74% rename from programs/good/lvar/read_print.test rename to programs/good/simple/read_print.test index 9188780..e06089a 100644 --- a/programs/good/lvar/read_print.test +++ b/programs/good/simple/read_print.test @@ -1,6 +1,6 @@ //* inp: 42 //* out: 42 //* ret: 42 -fn main() -> Int { +fn main() -> I64 { print(read()) } diff --git a/programs/good/lvar/sub.test b/programs/good/simple/sub.test similarity index 56% rename from programs/good/lvar/sub.test rename to programs/good/simple/sub.test index c169afd..27f72ef 100644 --- a/programs/good/lvar/sub.test +++ b/programs/good/simple/sub.test @@ -1,4 +1,4 @@ //* ret: 42 -fn main() -> Int { +fn main() -> I64 { 48 - 6 } diff --git a/unexpected_eof b/unexpected_eof deleted file mode 100644 index e69de29..0000000