├── .gitignore ├── .rustfmt.toml ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── README.md ├── build.rs ├── docker-compose.yml ├── examples ├── generics.bl ├── main.bl └── strings.bl └── src ├── ast.rs ├── builtins.rs ├── compiler.rs ├── errors.rs ├── grammar.lalrpop ├── interpreter.rs ├── main.rs ├── trait_checking.rs ├── type_alias_resolution.rs ├── type_checking.rs └── types.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | 107 | #Added by cargo 108 | # 109 | #already existing elements are commented out 110 | 111 | /target 112 | **/*.rs.bk 113 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width=140 # Not ideal 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "0.7.15" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "ansi_term" 16 | version = "0.11.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 19 | dependencies = [ 20 | "winapi", 21 | ] 22 | 23 | [[package]] 24 | name = "ascii-canvas" 25 | version = "3.0.0" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" 28 | dependencies = [ 29 | "term", 30 | ] 31 | 32 | [[package]] 33 | name = "atty" 34 | version = "0.2.14" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 37 | dependencies = [ 38 | "hermit-abi", 39 | "libc", 40 | "winapi", 41 | ] 42 | 43 | [[package]] 44 | name = "autocfg" 45 | version = "1.0.1" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 48 | 49 | [[package]] 50 | name = "bit-set" 51 | version = "0.5.2" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" 54 | dependencies = [ 55 | "bit-vec", 56 | ] 57 | 58 | [[package]] 59 | name = "bit-vec" 60 | version = "0.6.3" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" 63 | 64 | [[package]] 65 | name = "bitflags" 66 | version = "1.2.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 69 | 70 | [[package]] 71 | name = "boring-lang" 72 | version = "0.0.1" 73 | dependencies = [ 74 | "clap", 75 | "lalrpop", 76 | "lalrpop-util", 77 | "regex", 78 | "thiserror", 79 | ] 80 | 81 | [[package]] 82 | name = "cfg-if" 83 | version = "1.0.0" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 86 | 87 | [[package]] 88 | name = "clap" 89 | version = "2.33.3" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" 92 | dependencies = [ 93 | "ansi_term", 94 | "atty", 95 | "bitflags", 96 | "strsim", 97 | "textwrap", 98 | "unicode-width", 99 | "vec_map", 100 | ] 101 | 102 | [[package]] 103 | name = "crunchy" 104 | version = "0.2.2" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 107 | 108 | [[package]] 109 | name = "diff" 110 | version = "0.1.12" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" 113 | 114 | [[package]] 115 | name = "dirs-next" 116 | version = "2.0.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 119 | dependencies = [ 120 | "cfg-if", 121 | "dirs-sys-next", 122 | ] 123 | 124 | [[package]] 125 | name = "dirs-sys-next" 126 | version = "0.1.2" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 129 | dependencies = [ 130 | "libc", 131 | "redox_users", 132 | "winapi", 133 | ] 134 | 135 | [[package]] 136 | name = "either" 137 | version = "1.6.1" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 140 | 141 | [[package]] 142 | name = "ena" 143 | version = "0.14.0" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3" 146 | dependencies = [ 147 | "log", 148 | ] 149 | 150 | [[package]] 151 | name = "fixedbitset" 152 | version = "0.2.0" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" 155 | 156 | [[package]] 157 | name = "getrandom" 158 | version = "0.2.3" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 161 | dependencies = [ 162 | "cfg-if", 163 | "libc", 164 | "wasi", 165 | ] 166 | 167 | [[package]] 168 | name = "hashbrown" 169 | version = "0.11.2" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 172 | 173 | [[package]] 174 | name = "hermit-abi" 175 | version = "0.1.18" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" 178 | dependencies = [ 179 | "libc", 180 | ] 181 | 182 | [[package]] 183 | name = "indexmap" 184 | version = "1.7.0" 185 | source = "registry+https://github.com/rust-lang/crates.io-index" 186 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" 187 | dependencies = [ 188 | "autocfg", 189 | "hashbrown", 190 | ] 191 | 192 | [[package]] 193 | name = "itertools" 194 | version = "0.10.1" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" 197 | dependencies = [ 198 | "either", 199 | ] 200 | 201 | [[package]] 202 | name = "lalrpop" 203 | version = "0.19.6" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "b15174f1c529af5bf1283c3bc0058266b483a67156f79589fab2a25e23cf8988" 206 | dependencies = [ 207 | "ascii-canvas", 208 | "atty", 209 | "bit-set", 210 | "diff", 211 | "ena", 212 | "itertools", 213 | "lalrpop-util", 214 | "petgraph", 215 | "pico-args", 216 | "regex", 217 | "regex-syntax", 218 | "string_cache", 219 | "term", 220 | "tiny-keccak", 221 | "unicode-xid", 222 | ] 223 | 224 | [[package]] 225 | name = "lalrpop-util" 226 | version = "0.19.6" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "d3e58cce361efcc90ba8a0a5f982c741ff86b603495bb15a998412e957dcd278" 229 | dependencies = [ 230 | "regex", 231 | ] 232 | 233 | [[package]] 234 | name = "lazy_static" 235 | version = "1.4.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 238 | 239 | [[package]] 240 | name = "libc" 241 | version = "0.2.88" 242 | source = "registry+https://github.com/rust-lang/crates.io-index" 243 | checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" 244 | 245 | [[package]] 246 | name = "log" 247 | version = "0.4.14" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 250 | dependencies = [ 251 | "cfg-if", 252 | ] 253 | 254 | [[package]] 255 | name = "memchr" 256 | version = "2.3.4" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" 259 | 260 | [[package]] 261 | name = "new_debug_unreachable" 262 | version = "1.0.4" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" 265 | 266 | [[package]] 267 | name = "once_cell" 268 | version = "1.7.2" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" 271 | 272 | [[package]] 273 | name = "petgraph" 274 | version = "0.5.1" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" 277 | dependencies = [ 278 | "fixedbitset", 279 | "indexmap", 280 | ] 281 | 282 | [[package]] 283 | name = "phf_shared" 284 | version = "0.8.0" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" 287 | dependencies = [ 288 | "siphasher", 289 | ] 290 | 291 | [[package]] 292 | name = "pico-args" 293 | version = "0.4.2" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "db8bcd96cb740d03149cbad5518db9fd87126a10ab519c011893b1754134c468" 296 | 297 | [[package]] 298 | name = "precomputed-hash" 299 | version = "0.1.1" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" 302 | 303 | [[package]] 304 | name = "proc-macro2" 305 | version = "1.0.24" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 308 | dependencies = [ 309 | "unicode-xid", 310 | ] 311 | 312 | [[package]] 313 | name = "quote" 314 | version = "1.0.9" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" 317 | dependencies = [ 318 | "proc-macro2", 319 | ] 320 | 321 | [[package]] 322 | name = "redox_syscall" 323 | version = "0.2.5" 324 | source = "registry+https://github.com/rust-lang/crates.io-index" 325 | checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" 326 | dependencies = [ 327 | "bitflags", 328 | ] 329 | 330 | [[package]] 331 | name = "redox_users" 332 | version = "0.4.0" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" 335 | dependencies = [ 336 | "getrandom", 337 | "redox_syscall", 338 | ] 339 | 340 | [[package]] 341 | name = "regex" 342 | version = "1.4.3" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" 345 | dependencies = [ 346 | "aho-corasick", 347 | "memchr", 348 | "regex-syntax", 349 | "thread_local", 350 | ] 351 | 352 | [[package]] 353 | name = "regex-syntax" 354 | version = "0.6.22" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" 357 | 358 | [[package]] 359 | name = "rustversion" 360 | version = "1.0.5" 361 | source = "registry+https://github.com/rust-lang/crates.io-index" 362 | checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" 363 | 364 | [[package]] 365 | name = "siphasher" 366 | version = "0.3.6" 367 | source = "registry+https://github.com/rust-lang/crates.io-index" 368 | checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1" 369 | 370 | [[package]] 371 | name = "string_cache" 372 | version = "0.8.1" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "8ddb1139b5353f96e429e1a5e19fbaf663bddedaa06d1dbd49f82e352601209a" 375 | dependencies = [ 376 | "lazy_static", 377 | "new_debug_unreachable", 378 | "phf_shared", 379 | "precomputed-hash", 380 | ] 381 | 382 | [[package]] 383 | name = "strsim" 384 | version = "0.8.0" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 387 | 388 | [[package]] 389 | name = "syn" 390 | version = "1.0.62" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" 393 | dependencies = [ 394 | "proc-macro2", 395 | "quote", 396 | "unicode-xid", 397 | ] 398 | 399 | [[package]] 400 | name = "term" 401 | version = "0.7.0" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" 404 | dependencies = [ 405 | "dirs-next", 406 | "rustversion", 407 | "winapi", 408 | ] 409 | 410 | [[package]] 411 | name = "textwrap" 412 | version = "0.11.0" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 415 | dependencies = [ 416 | "unicode-width", 417 | ] 418 | 419 | [[package]] 420 | name = "thiserror" 421 | version = "1.0.29" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" 424 | dependencies = [ 425 | "thiserror-impl", 426 | ] 427 | 428 | [[package]] 429 | name = "thiserror-impl" 430 | version = "1.0.29" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" 433 | dependencies = [ 434 | "proc-macro2", 435 | "quote", 436 | "syn", 437 | ] 438 | 439 | [[package]] 440 | name = "thread_local" 441 | version = "1.1.3" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" 444 | dependencies = [ 445 | "once_cell", 446 | ] 447 | 448 | [[package]] 449 | name = "tiny-keccak" 450 | version = "2.0.2" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 453 | dependencies = [ 454 | "crunchy", 455 | ] 456 | 457 | [[package]] 458 | name = "unicode-width" 459 | version = "0.1.8" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 462 | 463 | [[package]] 464 | name = "unicode-xid" 465 | version = "0.2.1" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 468 | 469 | [[package]] 470 | name = "vec_map" 471 | version = "0.8.2" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 474 | 475 | [[package]] 476 | name = "wasi" 477 | version = "0.10.2+wasi-snapshot-preview1" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 480 | 481 | [[package]] 482 | name = "winapi" 483 | version = "0.3.9" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 486 | dependencies = [ 487 | "winapi-i686-pc-windows-gnu", 488 | "winapi-x86_64-pc-windows-gnu", 489 | ] 490 | 491 | [[package]] 492 | name = "winapi-i686-pc-windows-gnu" 493 | version = "0.4.0" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 496 | 497 | [[package]] 498 | name = "winapi-x86_64-pc-windows-gnu" 499 | version = "0.4.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 502 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "boring-lang" 3 | version = "0.0.1" 4 | authors = ["asegavac"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [build-dependencies.lalrpop] # <-- We added this and everything after! 10 | version = "0.19.6" 11 | features = ["lexer"] 12 | 13 | [dependencies] 14 | lalrpop-util = "0.19.6" 15 | regex = "1" 16 | # inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" } 17 | clap = "2.33.0" 18 | thiserror = "1" 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.54 2 | 3 | RUN apt update && apt-get install -y llvm clang 4 | RUN rustup component add rustfmt 5 | WORKDIR /code 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Andrew Segavac 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Boring Lang 2 | 3 | The Boring Programming Language (Boring-Lang) is an attempt to create an easy, productive, general purpose programming language that makes as few interesting choices as possible while still being in line with modern concepts in programming languages. 4 | 5 | The language (goals): 6 | * is compiled with a run-time (llvm for convenience + c/rust compatibility) 7 | * has managed memory (via strong/weak pointers and automatic reference counting) 8 | * uses async-await for all IO, with a built-in multi-core scheduler (tokio-based) 9 | * supports algebraic data types (Result type for errors, Maybe/Optional type for nullables) 10 | * supports parametric polymorphism (generics) with higher kinded types 11 | * uses struct+traits, rather than classes or stuct+interfaces 12 | * has a rich standard library (similar scale to python or go) 13 | * is immutable by default 14 | * is sandboxed by default 15 | 16 | It's a middle-ground of Rust, Golang, Swift, Typescript, and Python. The goal is not to break any new ground in PL theory, or even create a language anyone likes, but rather to create a language with as few deal-breakers as possible for maximum day-to-day industrial programming ergonomics. 17 | 18 | This language is under active development, progress will be marked here as the language is developed. 19 | 20 | - [x] Functions 21 | - [x] Int Literals 22 | - [x] Float Literals 23 | - [x] String Literals 24 | - [x] Block expression 25 | - [x] Return keyword 26 | - [x] Normal assignment 27 | - [x] Structs 28 | - [x] Define 29 | - [x] Literal 30 | - [x] Getter 31 | - [x] Setter 32 | - [x] Type Aliases 33 | - [x] Methods 34 | - [x] Declaration 35 | - [x] Use 36 | - [ ] Traits 37 | - [x] Basic 38 | - [ ] Default Functions 39 | - [ ] Generics 40 | - [x] Basic 41 | - [ ] Inferred 42 | - [ ] Higher kinded types 43 | - [ ] Variadic generic types 44 | - [ ] Control Flow 45 | - [x] If 46 | - [ ] While 47 | - [ ] For 48 | - [ ] Async-Await / Futures 49 | - [ ] Enums 50 | - [ ] Lambdas 51 | - [ ] Imports 52 | - [ ] Visibility 53 | - [ ] Const / Mut 54 | - [ ] Macros 55 | - [ ] Standard Library 56 | 57 | This project is actively looking for contributors, so if you're interested in programming language design or have experience working with LLVM, don't hesitate to contact. The current plan is to have this language transpile to a C-like subset of Rust, which can then be compiled into executable code. 58 | 59 | ## Philosophy 60 | 61 | Boring-lang is meant to be an industrial usage programming language optimized for quickly writing maintainable code above all else. To accomplish this, boring-lang has a simple rule: 62 | 63 | 1. Referential transparency is preferred, but anywhere it is broken must me encoded into the type system. 64 | 2. You cannot fundamentally change the behavior of a function (effects) without changing the type signature. 65 | 66 | We accomplish this in a few ways: 67 | 68 | ### Sandboxing 69 | 70 | Unlike many other programming languages, boringlang's `main` function takes in two arguments: a vector of command line arguments, and a reference to the OS which is the program's only link to the outside world. To open a file in boringlang, you cannot just call `open` anywhere, you *must* call `os.fs().open("path")`. All `os.whatever()` methods return an interface for interacting with that part of the OS, such as `fs`, `net`, `datetime`, and `syscall`. Because this is the only way to interact with the world outside of the program, this means that any IO the program does can be trivially mocked for testing, and that all operations the program can perform are sandboxed. If a function doesn't require a reference to the `FS` trait, you can be sure it doesn't interact with the file system. 71 | 72 | ### "Effects" System 73 | 74 | Boring-lang doesn't have a formal effects system, but rather the "effects" are simply traits that get tacked onto a functions type. For an example, let's use a GUI program where clicking on a button can have an effect, in this case writing to a file. 75 | 76 | ```rust 77 | type ClickHandler trait { 78 | async fn on_click(self): ClickError; 79 | } 80 | 81 | type MyButton[T: FS] struct { // T is a generic type implementing fs 82 | fs: T, 83 | } 84 | 85 | impl MyButton[T] { 86 | fn new(fs: T): MyButton { 87 | return MyButton{fs: fs}; 88 | } 89 | } 90 | 91 | impl ClickHandler for MyButton[T] { 92 | async fn on_click(self): ClickError { 93 | let file = await self.fs.open("my_file")?; 94 | await file.write("foo")?; 95 | } 96 | } 97 | ``` 98 | 99 | ## Http Server Example 100 | 101 | ```rust 102 | import net.http as http; 103 | import logging as logging; 104 | import json as json; 105 | 106 | type ExampleResponse struct { 107 | id: i32, 108 | name: Str, 109 | email: Str, 110 | } 111 | 112 | async fn handle(req: http.Request, resp: mut http.Response): { 113 | let response_data = ExampleResponse{ 114 | id: 4, 115 | name: "Andrew", 116 | email: "andrew@boringlang.com", 117 | }; 118 | await resp.set_status(200); 119 | await resp.write(json.encode[ExampleResponse](response_data)); 120 | } 121 | 122 | async fn main(args: Vec[String], os: OS): i32 { 123 | let log = logging.new_logger(os.console.stdout()); 124 | let router = http.Router("").add_route("/myroute", handle); 125 | let http_server = http.Server(os.net(), "localhost", 8080, router); 126 | let err = await http_server.serve_forever(); 127 | await log.info("error serving: ", err); 128 | return 1; 129 | } 130 | ``` 131 | 132 | ## Mutability 133 | 134 | All variables are immutable by default, to make them mutable use the `mut` keyword. Once a variable becomes immutable it cannot become mutable again. If you need it to become mutable, either implement the `clone` trait, or simply create a new one with the same data. 135 | 136 | ```rust 137 | let mut foo = Dict[String, i32].new(); // constructor returns a mutable reference 138 | foo.insert("eggs", 12); 139 | foo.insert("bananas", 2); 140 | foo.insert("grapes", 2); 141 | 142 | let bar = foo; // bar is not mutable 143 | 144 | bar.insert("apples", 4); // fails with compiler error 145 | 146 | let mut baz = bar.clone(); 147 | baz.insert("apples", 4); // fine 148 | ``` 149 | 150 | Methods on a struct must specify if they mutate the struct. 151 | 152 | ```rust 153 | impl Dict[Key: Hashable, Value] { 154 | fn insert(self: mut Self, key: Key, value: Value) { 155 | // mutate self here 156 | } 157 | 158 | fn get(self: Self, key: Key) Optional[Value] { 159 | // no need for `mut` 160 | } 161 | } 162 | ``` 163 | 164 | ## Context 165 | 166 | Context is an exceptionally useful feature in golang, but a common complaint is that: 167 | 168 | 1. Because it works as an arbitrary map, it can be used to pass arguments into a function that aren't explicitly stated. 169 | 2. It is used for both passing context parameters and cancellation, two fundamentally different tasks that have no reason to be in the same object. 170 | 171 | The boring standard library solves this by using parametric polymorphism. Context is by default an empty object passed through the chain, and each function/set of context parameters is an additional trait condition applied at compile time. 172 | 173 | ```rust 174 | type HTTPRequest = async fn[Ctx: Context](Ctx, http.Request, mut http.Response); 175 | 176 | pub fn tracing_middleware[Ctx: Tracing](handler: HTTPRequest[Ctx]): HTTPRequest { 177 | return async fn(ctx: Ctx, req: http.Request, resp: mut http.Response) { 178 | with tracing.Span(ctx, "request_duration") { 179 | await handler(ctx, req, resp); 180 | } 181 | }; 182 | } 183 | 184 | pub fn auth_middleware[Ctx: Auth](handler: HTTPRequest[Ctx], scope: Str): HTTPRequest { 185 | return async fn(ctx: Ctx, req: http.Request, resp: mut http.Response) { 186 | if (ctx.has_scope(scope)) { 187 | await handler(ctx, req, resp); 188 | } 189 | await resp.set_status(403); 190 | await resp.write("missing scope"); 191 | }; 192 | } 193 | 194 | pub fn cancel_middleware[Ctx: Cancel](handler: HTTPRequest[Ctx]): HTTPRequest { 195 | return async fn(ctx: Ctx, req: http.Request, resp: mut http.Response) { 196 | if (!(await ctx.is_cancelled())) { // check cancel token 197 | await handler(ctx, req, resp); 198 | } 199 | await resp.set_status(400); 200 | await resp.write("cancelled"); 201 | }; 202 | } 203 | ``` 204 | 205 | for the above examples, you would pass a context type that implements all three traits. 206 | 207 | ## Import System 208 | 209 | Similar to python, folders/files represent the `.` seperated import path, but relative imports are *not* supported. Exported values must be marked with `pub`. All imports take the form: 210 | 211 | ```rust 212 | import package.path as local_name; 213 | 214 | pub type MyStruct struct { 215 | pub id: i32, 216 | } 217 | ``` 218 | 219 | 220 | ## Basic Statements 221 | ### `if` 222 | 223 | `if` is an expression in boring-lang, with the last expression in a block being the return value. 224 | 225 | ```rust 226 | let a = if (true) { 227 | 4 228 | } else { 229 | 2 230 | } 231 | 232 | // a == 4 233 | 234 | ``` 235 | 236 | Conditions do not require parenthesis and *must* evaluate to the Boolean type. 237 | 238 | ### Loops 239 | 240 | Boring-lang supports `for` and `while` loops, with `for` having an `async` variant. `while` loops require an expression of Boolean type, while `for` loops require an expression that implements the `Iter` or `AIter` traits. 241 | 242 | ```rust 243 | let mut i = 0; 244 | while i < 100 { 245 | i = i + 1; 246 | // do something here 247 | } 248 | 249 | 250 | for i in range(100) { 251 | // do something here 252 | } 253 | 254 | async for result in paginated_list { 255 | // do something with result 256 | } 257 | ``` 258 | 259 | `continue` and `break` work similar to other languages. 260 | 261 | ```rust 262 | while true { 263 | break; // do nothing 264 | } 265 | 266 | for i in range(100) { 267 | continue; // do nothing 268 | } 269 | 270 | ``` 271 | 272 | ### `with` 273 | 274 | `with` and `async with` blocks are similar to the python statement with the same name. But unlike the python version, `with` blocks are expressions. `with` blocks take in an expression that implements the `With` or `AWith` trait, and execute a block that *may* return a result (non-result returns are assumed success). 275 | 276 | ```rust 277 | // commits on success, aborts on error. 278 | // transation.aexit may just return an error as a pass-through after aborting, 279 | // but it may also transform it into another error adding context. 280 | 281 | return async with db.transation(ctx) as t { 282 | await t.insert(ctx, record); // returns result type 283 | }; 284 | ``` 285 | 286 | ### `return` 287 | 288 | `return` statements exit a function early, returning the given value. They are purely optional as the last expression in a function will automatically return its value. 289 | 290 | ### `match` 291 | 292 | `match` expressions provide pattern matching, similar to a `C` switch statement. 293 | 294 | ```rust 295 | let number = 3; 296 | let result = match number { 297 | 1 => 'foo', 298 | 3 => 'bar', 299 | _ => 'baz', 300 | }; 301 | 302 | // result = 'bar' 303 | ``` 304 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate lalrpop; 2 | 3 | fn main() { 4 | lalrpop::process_root().unwrap(); 5 | } 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | boring: 4 | build: . 5 | volumes: 6 | - .:/code/ 7 | -------------------------------------------------------------------------------- /examples/generics.bl: -------------------------------------------------------------------------------- 1 | type MyTrait trait {} 2 | 3 | type Pair[K, V: MyTrait] struct { 4 | k: K, 5 | v: V, 6 | } 7 | 8 | type Value struct { 9 | value: i64, 10 | } 11 | 12 | impl MyTrait for Value {} 13 | 14 | 15 | impl [K, V: MyTrait] Pair[K, V] { 16 | fn get_value[T](self: Self, a: T): V { 17 | return self.v; 18 | } 19 | } 20 | 21 | fn main(): i64 { 22 | let a = Pair[i64, Value]{ 23 | k: 4, 24 | v: Value{value: 6}, 25 | }; 26 | return a.get_value[i64](999).value; 27 | } 28 | -------------------------------------------------------------------------------- /examples/main.bl: -------------------------------------------------------------------------------- 1 | // adds a and b, but also 4 for some reason 2 | fn add(a: i64, b: i64): i64 { 3 | let foo = 4; // because I feel like it 4 | let test_float: f64 = { 5 | 10.2 6 | }; 7 | test_float = 5.0; 8 | a + b + foo 9 | } 10 | 11 | fn subtract(a: i64, b: i64): i64 { 12 | a - b 13 | } 14 | 15 | fn return_type_test(a: f64): f64 { 16 | return a * 2.0; 17 | } 18 | 19 | fn i_hate_this(a: f64): f64 { 20 | return { 21 | return { 22 | return a; 23 | }; 24 | }; 25 | } 26 | 27 | fn unit_function() { 28 | let a: i64 = 4; 29 | } 30 | 31 | fn returns_user(): User { 32 | return User{ 33 | id: 4, 34 | }; 35 | } 36 | 37 | fn get_user_id(): i64 { 38 | let user = returns_user(); 39 | user.id = 5; 40 | return user.id; 41 | } 42 | 43 | fn use_method(user: User): i64 { 44 | return user.get_id(); 45 | } 46 | 47 | type User struct { 48 | id: i64, 49 | } 50 | 51 | type Generic[T] struct { 52 | value: T, 53 | } 54 | 55 | impl User { 56 | fn new(id: i64): Self { 57 | return Self{ 58 | id: id, 59 | }; 60 | } 61 | 62 | fn get_id(self: Self): i64 { 63 | return self.id; 64 | } 65 | } 66 | 67 | fn if_expression(): i64 { 68 | if (true) { 69 | return 6; 70 | } else { 71 | return 9; 72 | } 73 | } 74 | 75 | fn main(): i64 { 76 | let a = User{id: 4}; 77 | let b = a.instance_method(); 78 | b 79 | } 80 | 81 | type TestTrait trait { 82 | fn class_method(id: i64): Self; 83 | fn instance_method(self: Self): i64; 84 | fn default_impl(self: Self): i64 { 85 | return self.instance_method(); 86 | } 87 | } 88 | 89 | impl TestTrait for User { 90 | fn class_method(id: i64): Self { 91 | return User{id: id,}; 92 | } 93 | fn instance_method(self: Self): i64 { 94 | return self.get_id(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /examples/strings.bl: -------------------------------------------------------------------------------- 1 | 2 | 3 | fn main(): String { 4 | return "foo"; 5 | } 6 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq)] 4 | pub struct IdGenerator { 5 | id_key: String, 6 | counter: RefCell, 7 | } 8 | 9 | impl IdGenerator { 10 | pub fn new(key: &str) -> Self { 11 | IdGenerator { id_key: key.to_string(), counter: RefCell::new(0) } 12 | } 13 | 14 | pub fn next(&self) -> String { 15 | *self.counter.borrow_mut() += 1; 16 | (self.id_key.to_owned() + &self.counter.borrow().to_string()).to_string() 17 | } 18 | } 19 | 20 | pub fn new_unit() -> TypeUsage { 21 | TypeUsage::Named(NamedTypeUsage { 22 | type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }), 23 | name: Identifier { 24 | name: Spanned { 25 | span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these 26 | value: "unit".to_string(), 27 | }, 28 | }, 29 | }) 30 | } 31 | 32 | pub fn new_never() -> TypeUsage { 33 | TypeUsage::Named(NamedTypeUsage { 34 | type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }), 35 | name: Identifier { 36 | name: Spanned { 37 | span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these 38 | value: "!".to_string(), 39 | }, 40 | }, 41 | }) 42 | } 43 | 44 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 45 | pub struct Span { 46 | pub left: usize, 47 | pub right: usize, 48 | } 49 | 50 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 51 | pub struct Spanned { 52 | pub span: Span, 53 | pub value: T, 54 | } 55 | 56 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 57 | pub struct FunctionTypeUsage { 58 | pub arguments: Vec, 59 | pub return_type: Box, 60 | } 61 | 62 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 63 | pub struct NamedTypeUsage { 64 | pub type_parameters: GenericUsage, 65 | pub name: Identifier, 66 | } 67 | 68 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 69 | pub struct UnknownTypeUsage { 70 | pub name: String, 71 | } 72 | 73 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 74 | pub enum NamespaceTypeUsage { 75 | Type(NamedTypeUsage) 76 | // Module 77 | } 78 | 79 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 80 | pub enum TypeUsage { 81 | Function(FunctionTypeUsage), 82 | Named(NamedTypeUsage), 83 | Unknown(UnknownTypeUsage), 84 | Namespace(NamespaceTypeUsage) 85 | } 86 | 87 | impl TypeUsage { 88 | pub fn new_unknown(id_gen: &IdGenerator) -> TypeUsage { 89 | return TypeUsage::Unknown(UnknownTypeUsage { name: id_gen.next() }); 90 | } 91 | 92 | pub fn new_named(identifier: &Identifier, generic_usage: &GenericUsage) -> TypeUsage { 93 | return TypeUsage::Named(NamedTypeUsage { 94 | type_parameters: generic_usage.clone(), 95 | name: identifier.clone(), 96 | }); 97 | } 98 | 99 | pub fn new_builtin(name: String) -> TypeUsage { 100 | TypeUsage::Named(NamedTypeUsage { 101 | type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }), 102 | name: Identifier { 103 | name: Spanned { 104 | span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these 105 | value: name, 106 | }, 107 | }, 108 | }) 109 | } 110 | 111 | pub fn new_function(arg_count: usize, id_gen: &IdGenerator) -> TypeUsage { 112 | return TypeUsage::Function(FunctionTypeUsage { 113 | arguments: (0..arg_count).map(|_| TypeUsage::new_unknown(&id_gen)).collect(), 114 | return_type: Box::new(TypeUsage::new_unknown(&id_gen)), 115 | }); 116 | } 117 | } 118 | 119 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 120 | pub struct GenericParameter { 121 | pub name: Identifier, 122 | pub bounds: Vec, 123 | } 124 | 125 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 126 | pub struct Generic { 127 | pub parameters: Vec, 128 | } 129 | 130 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 131 | pub struct GenericInstantiation { 132 | pub parameters: Vec, 133 | } 134 | 135 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 136 | pub enum GenericUsage { 137 | Known(GenericInstantiation), 138 | Unknown, 139 | } 140 | 141 | impl GenericUsage { 142 | pub fn new(type_parameters: &[TypeUsage]) -> Self { 143 | GenericUsage::Known(GenericInstantiation { 144 | parameters: type_parameters.iter().map(|tp| tp.clone()).collect(), 145 | }) 146 | } 147 | } 148 | 149 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 150 | pub enum Operator { 151 | Mul, 152 | Div, 153 | Plus, 154 | Minus, 155 | } 156 | 157 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 158 | pub struct LiteralInt { 159 | pub value: Spanned, 160 | pub type_: TypeUsage, 161 | } 162 | 163 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 164 | pub struct LiteralFloat { 165 | pub value: Spanned, 166 | pub type_: TypeUsage, 167 | } 168 | 169 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 170 | pub struct LiteralBool { 171 | pub value: Spanned, 172 | pub type_: TypeUsage, 173 | } 174 | 175 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 176 | pub struct LiteralString { 177 | pub value: Spanned, 178 | pub type_: TypeUsage, 179 | } 180 | 181 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 182 | pub struct LiteralStruct { 183 | pub type_parameters: GenericUsage, 184 | pub name: Identifier, 185 | pub fields: Vec<(Identifier, Expression)>, 186 | pub type_: TypeUsage, 187 | } 188 | 189 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 190 | pub struct Identifier { 191 | pub name: Spanned, 192 | } 193 | 194 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 195 | pub struct FunctionCall { 196 | pub source: Expression, 197 | pub arguments: Vec, 198 | pub type_: TypeUsage, 199 | } 200 | 201 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 202 | pub struct StructGetter { 203 | pub type_parameters: GenericUsage, 204 | pub source: Expression, 205 | pub attribute: Identifier, 206 | pub type_: TypeUsage, 207 | } 208 | 209 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 210 | pub struct Operation { 211 | pub left: Expression, 212 | pub op: Operator, 213 | pub right: Expression, 214 | } 215 | 216 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 217 | pub struct VariableUsage { 218 | pub name: Identifier, 219 | pub type_parameters: GenericUsage, 220 | pub type_: TypeUsage, 221 | } 222 | 223 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 224 | pub struct IfExpression { 225 | pub condition: Expression, 226 | pub block: Block, 227 | pub else_: Option, 228 | pub type_: TypeUsage, 229 | } 230 | 231 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 232 | pub enum Subexpression { 233 | LiteralInt(LiteralInt), 234 | LiteralFloat(LiteralFloat), 235 | LiteralBool(LiteralBool), 236 | LiteralString(LiteralString), 237 | LiteralStruct(LiteralStruct), 238 | FunctionCall(FunctionCall), 239 | VariableUsage(VariableUsage), 240 | If(IfExpression), 241 | StructGetter(StructGetter), 242 | Block(Block), 243 | Op(Operation), 244 | } 245 | 246 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 247 | pub struct Expression { 248 | pub subexpression: Box, 249 | pub type_: TypeUsage, 250 | } 251 | 252 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 253 | pub struct ReturnStatement { 254 | pub source: Expression, 255 | } 256 | 257 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 258 | pub struct LetStatement { 259 | pub variable_name: Identifier, 260 | pub expression: Expression, 261 | pub type_: TypeUsage, 262 | } 263 | 264 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 265 | pub enum AssignmentTarget { 266 | Variable(VariableUsage), 267 | StructAttr(StructGetter), 268 | } 269 | 270 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 271 | pub struct AssignmentStatement { 272 | pub source: AssignmentTarget, 273 | pub expression: Expression, 274 | } 275 | 276 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 277 | pub enum Statement { 278 | Return(ReturnStatement), 279 | Let(LetStatement), 280 | Assignment(AssignmentStatement), 281 | Expression(Expression), 282 | } 283 | 284 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 285 | pub struct Block { 286 | pub statements: Vec, 287 | pub type_: TypeUsage, 288 | } 289 | 290 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 291 | pub struct VariableDeclaration { 292 | pub name: Identifier, 293 | pub type_: TypeUsage, 294 | } 295 | 296 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 297 | pub struct FunctionDeclaration { 298 | pub generic: Generic, 299 | pub name: Identifier, 300 | pub arguments: Vec, 301 | pub return_type: TypeUsage, 302 | } 303 | 304 | impl FunctionDeclaration { 305 | pub fn to_type(&self) -> TypeUsage { 306 | TypeUsage::Function(FunctionTypeUsage { 307 | arguments: self.arguments.iter().map(|arg| arg.type_.clone()).collect(), 308 | return_type: Box::new(self.return_type.clone()), 309 | }) 310 | } 311 | 312 | pub fn to_method_type(&self) -> TypeUsage { 313 | TypeUsage::Function(FunctionTypeUsage { 314 | arguments: self.arguments[1..self.arguments.len()] 315 | .iter() 316 | .map(|arg| arg.type_.clone()) 317 | .collect(), 318 | return_type: Box::new(self.return_type.clone()), 319 | }) 320 | } 321 | } 322 | 323 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 324 | pub struct Function { 325 | pub declaration: FunctionDeclaration, 326 | pub block: Block, 327 | } 328 | 329 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 330 | pub struct PrimitiveTypeDeclaration { 331 | pub name: String, // cannot be identifier as it's not declared anywhere specific, it's builtins 332 | } 333 | 334 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 335 | pub struct StructField { 336 | pub name: Identifier, 337 | pub type_: TypeUsage, 338 | } 339 | 340 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 341 | pub struct StructTypeDeclaration { 342 | pub generic: Generic, 343 | pub name: Identifier, 344 | pub fields: Vec, 345 | } 346 | 347 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 348 | pub enum TraitItem { 349 | FunctionDeclaration(FunctionDeclaration), 350 | Function(Function), 351 | } 352 | 353 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 354 | pub struct TraitTypeDeclaration { 355 | pub generic: Generic, 356 | pub name: Identifier, 357 | pub functions: Vec, 358 | } 359 | 360 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 361 | pub struct AliasTypeDeclaration { 362 | pub name: Identifier, 363 | pub replaces: TypeUsage, 364 | } 365 | 366 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 367 | pub enum TypeDeclaration { 368 | Struct(StructTypeDeclaration), 369 | Primitive(PrimitiveTypeDeclaration), 370 | Alias(AliasTypeDeclaration), 371 | Trait(TraitTypeDeclaration), 372 | } 373 | 374 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 375 | pub struct Impl { 376 | pub generic: Generic, 377 | pub struct_: NamedTypeUsage, 378 | pub trait_: Option, 379 | pub functions: Vec, 380 | } 381 | 382 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 383 | pub enum ModuleItem { 384 | Function(Function), 385 | TypeDeclaration(TypeDeclaration), 386 | Impl(Impl), 387 | } 388 | 389 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 390 | pub struct Module { 391 | pub items: Vec, 392 | } 393 | -------------------------------------------------------------------------------- /src/builtins.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::ast; 4 | -------------------------------------------------------------------------------- /src/compiler.rs: -------------------------------------------------------------------------------- 1 | use inkwell::builder::Builder; 2 | use inkwell::context::Context; 3 | use inkwell::module::Module; 4 | use inkwell::values::{BasicValueEnum, FunctionValue, IntValue}; 5 | use std::collections::HashMap; 6 | use std::convert::TryInto; 7 | use std::mem; 8 | 9 | use crate::ast; 10 | 11 | type Scope<'ctx> = HashMap>; 12 | 13 | pub struct ModuleCodeGen<'ctx> { 14 | context: &'ctx Context, 15 | module: Module<'ctx>, 16 | builder: Builder<'ctx>, 17 | scope: Scope<'ctx>, 18 | } 19 | 20 | impl<'ctx> ModuleCodeGen<'ctx> { 21 | pub fn new(context: &'ctx Context, name: String) -> Self { 22 | return ModuleCodeGen { 23 | context: context, 24 | module: context.create_module(&name), 25 | builder: context.create_builder(), 26 | scope: Scope::new(), 27 | }; 28 | } 29 | 30 | pub fn gen_literal_int(&mut self, literal_int: &ast::LiteralInt) -> IntValue<'ctx> { 31 | self.context 32 | .i64_type() 33 | .const_int(unsafe { mem::transmute::(literal_int.value) }, true) 34 | } 35 | 36 | pub fn gen_op_expression(&mut self, scope: &Scope<'ctx>, operation: &ast::Operation) -> IntValue<'ctx> { 37 | let lhs_result = self.gen_expression(scope, &operation.left); 38 | let rhs_result = self.gen_expression(scope, &operation.right); 39 | self.gen_op_int(&lhs_result, &rhs_result, &operation.op) 40 | } 41 | 42 | pub fn gen_op_int(&mut self, lhs: &IntValue<'ctx>, rhs: &IntValue<'ctx>, op: &ast::Operator) -> IntValue<'ctx> { 43 | match *op { 44 | ast::Operator::Plus => self.builder.build_int_add(*lhs, *rhs, "add"), 45 | ast::Operator::Minus => self.builder.build_int_sub(*lhs, *rhs, "sub"), 46 | ast::Operator::Mul => self.builder.build_int_mul(*lhs, *rhs, "mul"), 47 | ast::Operator::Div => self.builder.build_int_signed_div(*lhs, *rhs, "div"), 48 | } 49 | } 50 | 51 | pub fn gen_expression(&mut self, scope: &Scope<'ctx>, expression: &Box) -> IntValue<'ctx> { 52 | match &**expression { 53 | ast::Expression::LiteralInt(literal_int) => self.gen_literal_int(&literal_int), 54 | ast::Expression::Identifier(identifier) => match scope[&identifier.name] { 55 | BasicValueEnum::IntValue(value) => value, 56 | _ => panic!("function returned type other than int, no types yet"), 57 | }, 58 | ast::Expression::FunctionCall(function_call) => self.gen_function_call(scope, &function_call), 59 | ast::Expression::Op(operation) => self.gen_op_expression(scope, &operation), 60 | } 61 | } 62 | 63 | pub fn gen_function_call(&mut self, scope: &Scope<'ctx>, function_call: &ast::FunctionCall) -> IntValue<'ctx> { 64 | let fn_value = self.module.get_function(&function_call.name.name).unwrap(); 65 | let mut arguments = Vec::new(); 66 | for expression in (&function_call.arguments).into_iter() { 67 | arguments.push(BasicValueEnum::IntValue(self.gen_expression(scope, &expression))); 68 | } 69 | 70 | let result = self 71 | .builder 72 | .build_call(fn_value, &arguments, &function_call.name.name) 73 | .try_as_basic_value() 74 | .left() 75 | .unwrap(); 76 | match result { 77 | BasicValueEnum::IntValue(value) => value, 78 | _ => panic!("function returned type other than int, no types yet"), 79 | } 80 | } 81 | 82 | // Generates a FunctionValue for an `ast::Function`. This does not genereate a body, 83 | // that task is left to the `gen_function` function. The reason this is split 84 | // between two functions is that first all signatures are generated and then all bodies. This 85 | // allows bodies to reference `FunctionValue` wherever they are declared in the file. 86 | pub fn gen_signature(&mut self, function: &ast::Function) -> FunctionValue { 87 | let mut args = Vec::new(); 88 | for _ in &function.arguments { 89 | args.push(self.context.i64_type().into()); 90 | } 91 | let fn_type = self.context.i64_type().fn_type(&args, false); 92 | let fn_value = self.module.add_function(&function.name.name, fn_type, None); 93 | fn_value 94 | } 95 | 96 | pub fn gen_function(&mut self, function: &ast::Function) { 97 | let fn_value = self.module.get_function(&function.name.name).unwrap(); 98 | let basic_block = self.context.append_basic_block(fn_value, "entry"); 99 | 100 | self.builder.position_at_end(basic_block); 101 | 102 | let mut scope = self.scope.clone(); 103 | for (i, param) in (&function.arguments).into_iter().enumerate() { 104 | scope.insert(param.name.name.to_string(), fn_value.get_nth_param(i.try_into().unwrap()).unwrap()); 105 | } 106 | let body = &function.block; 107 | let return_value = self.gen_expression(&scope, &body.expression); 108 | self.builder.build_return(Some(&return_value)); 109 | } 110 | 111 | pub fn gen_module(&mut self, module: ast::Module) { 112 | // generate all signatures before the fuction bodies 113 | for function in &module.functions { 114 | self.gen_signature(&function); 115 | } 116 | for function in module.functions { 117 | self.gen_function(&function); 118 | } 119 | } 120 | 121 | pub fn dump(&self) -> String { 122 | self.module.print_to_string().to_string() 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | use crate::ast; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum TypingError { 6 | #[error("unknown named type")] 7 | TypeDoesNotExist { identifier: ast::Identifier }, 8 | #[error("identifier is not type")] 9 | IdentifierIsNotType { identifier: ast::Identifier }, 10 | #[error("argument length mismatch")] 11 | ArgumentLengthMismatch { 12 | // TODO: add position 13 | }, 14 | #[error("type mismatch")] 15 | TypeMismatch { 16 | type_one: ast::TypeUsage, 17 | type_two: ast::TypeUsage, 18 | }, 19 | #[error("unknown field name")] 20 | UnknownFieldName { identifier: ast::Identifier }, 21 | #[error("cannot assign to method")] 22 | CannotAssignToMethod { identifier: ast::Identifier }, 23 | #[error("multiple field name matches")] 24 | MultipleFieldName { identifier: ast::Identifier }, 25 | #[error("attribute gotten of non-struct")] 26 | AttributeOfNonstruct { identifier: ast::Identifier }, 27 | #[error("name is not a struct, cannot instaniate")] 28 | NotAStructLiteral { identifier: ast::Identifier }, 29 | #[error("struct literal fields mismatch")] 30 | StructLiteralFieldsMismatch { struct_name: ast::Identifier }, 31 | #[error("missing trait function")] 32 | MissingTraitFunction { 33 | struct_name: ast::Identifier, 34 | function_name: ast::Identifier, 35 | }, 36 | #[error("function not in trait")] 37 | FunctionNotInTrait { function_name: ast::Identifier }, 38 | #[error("impl trait must be trait")] 39 | ImplTraitMustBeTrait { trait_name: ast::Identifier }, 40 | #[error("function call used with non-function")] 41 | FunctionCallNotAFunction { 42 | // TODO: add position 43 | }, 44 | #[error("`if` condition must be bool")] 45 | IfConditionMustBeBool { 46 | // TODO: add position 47 | }, 48 | #[error("cannot use type as an expression")] 49 | TypeIsNotAnExpression { type_name: ast::Identifier }, 50 | #[error("wrong number of type parameters")] 51 | WrongNumberOfTypeParameters { 52 | // TODO: add position 53 | }, 54 | #[error("invalid use of alias")] 55 | InvalidUseofAlias, 56 | #[error("alias cannot have type parameters")] 57 | InvalidTypeParameterOnAlias { 58 | alias: ast::Identifier, 59 | }, 60 | #[error("type cannot be used for generic")] 61 | InvalidTypeForGeneric, 62 | #[error("multiple errors")] 63 | MultipleErrors { errors: Vec }, 64 | } 65 | -------------------------------------------------------------------------------- /src/grammar.lalrpop: -------------------------------------------------------------------------------- 1 | use crate::ast; 2 | 3 | grammar(id_generator: &ast::IdGenerator); 4 | 5 | match { 6 | r"[0-9]+", 7 | r"[0-9]+\.[0-9]+", 8 | r"[A-Za-z_][A-Za-z0-9_]*", 9 | ":", 10 | ";", 11 | "{", 12 | "}", 13 | "(", 14 | ")", 15 | "[", 16 | "]", 17 | "", 18 | ".", 19 | "+", 20 | "-", 21 | "*", 22 | "/", 23 | "fn", 24 | "return", 25 | "let", 26 | "true", 27 | "false", 28 | "if", 29 | "else", 30 | "=", 31 | "for", 32 | "type", 33 | "trait", 34 | "struct", 35 | "impl", 36 | ",", 37 | r"'(\\'|[^'])*'", 38 | r#""(\\"|[^"])*""#, 39 | 40 | r"\s*" => { }, 41 | r"//[^\n\r]*[\n\r]*" => { }, // `// comment` 42 | } 43 | 44 | 45 | pub LiteralInt: String = { 46 | => literal.to_string() 47 | }; 48 | 49 | pub SpannedLiteralInt: ast::LiteralInt = { 50 | > => ast::LiteralInt{value: literal_int, type_: ast::TypeUsage::new_builtin("i64".to_string())} 51 | }; 52 | 53 | pub LiteralFloat: String = { 54 | => literal.to_string() 55 | }; 56 | 57 | pub SpannedLiteralFloat: ast::LiteralFloat = { 58 | > => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("f64".to_string())} 59 | }; 60 | 61 | pub LiteralBool: String = { 62 | "true" => "true".to_string(), 63 | "false" => "false".to_string(), 64 | }; 65 | 66 | pub SpannedLiteralBool: ast::LiteralBool = { 67 | > => ast::LiteralBool{value: literal_bool, type_: ast::TypeUsage::new_builtin("bool".to_string())} 68 | }; 69 | 70 | pub LiteralString: String = { 71 | => String::from(&s[1..s.len()-1]), 72 | => String::from(&s[1..s.len()-1]), 73 | }; 74 | 75 | pub SpannedLiteralString: ast::LiteralString = { 76 | > => ast::LiteralString{value: literal_string, type_: ast::TypeUsage::new_builtin("String".to_string())} 77 | }; 78 | 79 | 80 | pub Identifier: String = { 81 | => i.to_string() 82 | }; 83 | 84 | pub GenericUsage: ast::GenericUsage = { 85 | "[" > "]" => ast::GenericUsage::new(&tp), 86 | }; 87 | 88 | pub LiteralStructField: (ast::Identifier, ast::Expression) = { 89 | ":" => (field, expr) 90 | }; 91 | 92 | pub LiteralStruct: ast::LiteralStruct = { 93 | "{" > "}" => { 94 | match gu { 95 | Some(tp) => { 96 | ast::LiteralStruct{ 97 | type_parameters: tp.clone(), 98 | name: i.clone(), 99 | fields: field_list, 100 | type_: ast::TypeUsage::new_named(&i, &tp), 101 | } 102 | }, 103 | None => { 104 | ast::LiteralStruct{ 105 | type_parameters: ast::GenericUsage::new(&[]), 106 | name: i.clone(), 107 | fields: field_list, 108 | type_: ast::TypeUsage::new_named(&i, &ast::GenericUsage::new(&[])), 109 | } 110 | } 111 | } 112 | } 113 | }; 114 | 115 | pub SpannedIdentifier: ast::Identifier = { 116 | > => ast::Identifier{name: i} 117 | }; 118 | 119 | pub FunctionCall: ast::FunctionCall = { 120 | "(" > ")" => ast::FunctionCall{source: source, arguments: args, type_: ast::TypeUsage::new_unknown(&id_generator)} 121 | }; 122 | 123 | pub StructGetter: ast::StructGetter = { 124 | "." => match gu { 125 | Some(tp) => ast::StructGetter{type_parameters: tp, source: source, attribute: field, type_: ast::TypeUsage::new_unknown(&id_generator)}, 126 | None => ast::StructGetter{type_parameters: ast::GenericUsage::Unknown, source: source, attribute: field, type_: ast::TypeUsage::new_unknown(&id_generator)}, 127 | } 128 | }; 129 | 130 | pub VariableUsage: ast::VariableUsage = { 131 | => match gu { 132 | Some(tp) => ast::VariableUsage{name: identifier, type_parameters: tp.clone(), type_: ast::TypeUsage::new_unknown(&id_generator)}, 133 | None => ast::VariableUsage{name: identifier, type_parameters: ast::GenericUsage::Unknown, type_: ast::TypeUsage::new_unknown(&id_generator)}, 134 | } 135 | }; 136 | 137 | pub IfExpression: ast::IfExpression = { 138 | "if" "("")" => ast::IfExpression{condition: c, block: b, else_: None, type_: ast::TypeUsage::new_unknown(&id_generator)}, 139 | "if" "("")" "else" => ast::IfExpression{condition: c, block: b, else_: Some(e), type_: ast::TypeUsage::new_unknown(&id_generator)}, 140 | }; 141 | 142 | pub Expression: ast::Expression = { 143 | "+" => { 144 | ast::Expression{ 145 | subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Plus, right: r})), 146 | type_: ast::TypeUsage::new_unknown(&id_generator), 147 | } 148 | }, 149 | "-" => { 150 | ast::Expression{ 151 | subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Minus, right: r})), 152 | type_: ast::TypeUsage::new_unknown(&id_generator), 153 | } 154 | }, 155 | Factor, 156 | }; 157 | 158 | pub Factor: ast::Expression = { 159 | "*" => { 160 | ast::Expression{ 161 | subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Mul, right: r})), 162 | type_: ast::TypeUsage::new_unknown(&id_generator), 163 | } 164 | }, 165 | "/" => { 166 | ast::Expression{ 167 | subexpression: Box::new(ast::Subexpression::Op(ast::Operation{left: l, op: ast::Operator::Div, right: r})), 168 | type_: ast::TypeUsage::new_unknown(&id_generator), 169 | } 170 | }, 171 | Term, 172 | }; 173 | 174 | pub Term: ast::Expression = { 175 | SpannedLiteralInt => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralInt(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 176 | SpannedLiteralFloat => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralFloat(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 177 | SpannedLiteralBool => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralBool(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 178 | SpannedLiteralString => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralString(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 179 | LiteralStruct => ast::Expression{subexpression: Box::new(ast::Subexpression::LiteralStruct(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 180 | FunctionCall => ast::Expression{subexpression: Box::new(ast::Subexpression::FunctionCall(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 181 | StructGetter => ast::Expression{subexpression: Box::new(ast::Subexpression::StructGetter(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 182 | VariableUsage => ast::Expression{subexpression: Box::new(ast::Subexpression::VariableUsage(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 183 | IfExpression => ast::Expression{subexpression: Box::new(ast::Subexpression::If(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 184 | Block => ast::Expression{subexpression: Box::new(ast::Subexpression::Block(<>)), type_: ast::TypeUsage::new_unknown(&id_generator)}, 185 | "(" ")" => e, 186 | }; 187 | 188 | 189 | pub ReturnStatement: ast::ReturnStatement = { 190 | "return" => ast::ReturnStatement{source: e} 191 | }; 192 | 193 | pub LetStatement: ast::LetStatement = { 194 | //TODO: support destructuring with tuples, when they exist. 195 | //TODO: add mut, weak 196 | "let" "=" => ast::LetStatement{variable_name: n, type_: ast::TypeUsage::new_unknown(&id_generator), expression: e}, 197 | "let" ":" "=" => ast::LetStatement{variable_name: n, type_: t, expression: e}, 198 | }; 199 | 200 | pub AssignmentStatement: ast::AssignmentStatement = { 201 | "=" => ast::AssignmentStatement{source: ast::AssignmentTarget::Variable(v), expression: e}, 202 | "=" => ast::AssignmentStatement{source: ast::AssignmentTarget::StructAttr(sg), expression: e}, 203 | }; 204 | 205 | pub Statement: ast::Statement = { 206 | ";" => ast::Statement::Return(r), 207 | ";" => ast::Statement::Let(l), 208 | ";" => ast::Statement::Assignment(a), 209 | ";" => ast::Statement::Expression(e), 210 | }; 211 | 212 | pub Block: ast::Block = { 213 | "{" )*> "}" => match e { 214 | None => ast::Block{statements: v, type_: ast::TypeUsage::new_unknown(&id_generator)}, 215 | Some(e) => { 216 | let mut v = v; 217 | v.push(ast::Statement::Expression(e)); 218 | ast::Block{statements: v, type_: ast::TypeUsage::new_unknown(&id_generator)} 219 | } 220 | } 221 | }; 222 | 223 | pub PartialNamedTypeUsage: ast::NamedTypeUsage = { 224 | => match gu { 225 | Some(tp) => ast::NamedTypeUsage{type_parameters: tp, name: n}, 226 | None => ast::NamedTypeUsage{type_parameters: ast::GenericUsage::Unknown, name: n}, 227 | }, 228 | }; 229 | 230 | pub NamedTypeUsage: ast::NamedTypeUsage = { 231 | => match gu { 232 | Some(tp) => ast::NamedTypeUsage{type_parameters: tp, name: n}, 233 | None => ast::NamedTypeUsage{type_parameters: ast::GenericUsage::new(&[]), name: n}, 234 | }, 235 | }; 236 | 237 | pub PartialTypeUsage: ast::TypeUsage = { 238 | => ast::TypeUsage::Named(n), 239 | "fn" "(" > ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::new_unit())}), 240 | "fn" "(" > ")" ":" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(rt)}), 241 | }; 242 | 243 | pub TypeUsage: ast::TypeUsage = { 244 | => ast::TypeUsage::Named(n), 245 | "fn" "(" > ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::new_unit())}), 246 | "fn" "(" > ")" ":" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(rt)}), 247 | }; 248 | 249 | pub VariableDeclaration: ast::VariableDeclaration = { 250 | ":" => ast::VariableDeclaration{name: i, type_: t}, 251 | }; 252 | 253 | pub GenericParameter: ast::GenericParameter = { 254 | => ast::GenericParameter{name: i, bounds: vec!()}, 255 | ":" > => ast::GenericParameter{name: i, bounds: bounds}, 256 | }; 257 | 258 | pub Generic: ast::Generic = { 259 | "[" > "]" => ast::Generic{parameters: p}, 260 | }; 261 | 262 | pub FunctionDeclaration: ast::FunctionDeclaration = { 263 | "fn" "(" > ")" => ast::FunctionDeclaration{name: n, generic: g, arguments: args, return_type: ast::new_unit()}, 264 | "fn" "(" > ")" ":" => ast::FunctionDeclaration{name: n, generic: g, arguments: args, return_type: rt}, 265 | "fn" "(" > ")" => ast::FunctionDeclaration{name: n, generic: ast::Generic{parameters: vec!()}, arguments: args, return_type: ast::new_unit()}, 266 | "fn" "(" > ")" ":" => ast::FunctionDeclaration{name: n, generic: ast::Generic{parameters: vec!()}, arguments: args, return_type: rt}, 267 | }; 268 | 269 | pub Function: ast::Function = { 270 | => ast::Function{declaration: d, block: b} 271 | }; 272 | 273 | pub StructField: ast::StructField = { 274 | ":" => ast::StructField{name: i, type_: t}, 275 | }; 276 | 277 | pub StructTypeDeclaration: ast::StructTypeDeclaration = { 278 | "type" "struct" "{" > "}" => match g { 279 | Some(generic) => ast::StructTypeDeclaration{name: i, generic: generic, fields: f}, 280 | None => ast::StructTypeDeclaration{name: i, generic: ast::Generic{parameters: vec!()}, fields: f}, 281 | } 282 | }; 283 | 284 | pub AliasTypeDeclaration: ast::AliasTypeDeclaration = { 285 | "type" "=" ";" => ast::AliasTypeDeclaration{name: i, replaces: t} 286 | }; 287 | 288 | pub TraitItem: ast::TraitItem = { 289 | ";" => ast::TraitItem::FunctionDeclaration(fd), 290 | => ast::TraitItem::Function(f), 291 | }; 292 | 293 | pub TraitTypeDeclaration: ast::TraitTypeDeclaration = { 294 | "type" "trait" "{" "}" => ast::TraitTypeDeclaration{name: i, generic: g, functions: ti}, 295 | "type" "trait" "{" "}" => ast::TraitTypeDeclaration{name: i, generic: ast::Generic{parameters: vec!()}, functions: ti}, 296 | }; 297 | 298 | pub TypeDeclaration: ast::TypeDeclaration = { 299 | => ast::TypeDeclaration::Struct(s), 300 | => ast::TypeDeclaration::Alias(a), 301 | => ast::TypeDeclaration::Trait(t), 302 | }; 303 | 304 | pub Impl: ast::Impl = { 305 | "impl" "{" "}" => { 306 | let generic = match g { 307 | Some(g) => g, 308 | None => ast::Generic{parameters: vec!()}, 309 | }; 310 | ast::Impl{generic: generic, trait_: None, struct_: s, functions: f} 311 | }, 312 | "impl" "for" "{" "}" => { 313 | let generic = match g { 314 | Some(g) => g, 315 | None => ast::Generic{parameters: vec!()}, 316 | }; 317 | ast::Impl{generic: generic, trait_: Some(t), struct_: s, functions: f} 318 | } 319 | }; 320 | 321 | pub ModuleItem: ast::ModuleItem = { 322 | => ast::ModuleItem::Function(f), 323 | => ast::ModuleItem::TypeDeclaration(td), 324 | => ast::ModuleItem::Impl(i), 325 | }; 326 | 327 | pub Module: ast::Module = { 328 | => ast::Module{items: i} 329 | }; 330 | 331 | // From https://lalrpop.github.io/lalrpop/tutorial/006_macros.html 332 | // Comma separated list of T with optional trailing comma 333 | Comma: Vec = { 334 | ",")*> => match e { 335 | None => v, 336 | Some(e) => { 337 | let mut v = v; 338 | v.push(e); 339 | v 340 | } 341 | } 342 | }; 343 | 344 | // PlusSeparated separated list of T with optional trailing comma 345 | PlusSeparated: Vec = { 346 | "+")*> => match e { 347 | None => v, 348 | Some(e) => { 349 | let mut v = v; 350 | v.push(e); 351 | v 352 | } 353 | } 354 | }; 355 | 356 | Spanned: ast::Spanned = { 357 | => ast::Spanned{span: ast::Span{left: l, right: r}, value: rule} 358 | }; 359 | -------------------------------------------------------------------------------- /src/interpreter.rs: -------------------------------------------------------------------------------- 1 | use crate::ast; 2 | use std::collections::HashMap; 3 | use std::sync::{Arc, Mutex}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub enum NumericValue { 7 | I8(i8), 8 | I16(i16), 9 | I32(i32), 10 | I64(i64), 11 | ISize(isize), 12 | 13 | U8(u8), 14 | U16(u16), 15 | U32(u32), 16 | U64(u64), 17 | USize(usize), 18 | 19 | F32(f32), 20 | F64(f64), 21 | } 22 | 23 | #[derive(Debug, Clone)] 24 | pub struct StructValue { 25 | source: ast::StructTypeDeclaration, 26 | fields: HashMap, 27 | } 28 | 29 | type BuiltinFunction = fn(Vec) -> Value; 30 | 31 | #[derive(Debug, Clone)] 32 | pub enum FunctionRef { 33 | User(ast::Function), 34 | Builtin(BuiltinFunction), 35 | } 36 | 37 | #[derive(Debug, Clone)] 38 | pub struct Function { 39 | pub partial: Vec, 40 | pub ref_: FunctionRef, 41 | } 42 | 43 | #[derive(Debug, Clone)] 44 | pub enum Value { 45 | Numeric(NumericValue), 46 | Bool(bool), 47 | String(String), 48 | Function(Function), 49 | Struct(Arc>), 50 | Unit, 51 | } 52 | 53 | #[derive(Debug, Clone)] 54 | pub enum NamedEntity { 55 | TypeDeclaration(ast::TypeDeclaration), 56 | Variable(Value), 57 | } 58 | 59 | #[derive(Debug, Clone)] 60 | struct Context { 61 | pub environment: HashMap, 62 | pub impls: HashMap, 63 | pub current_module: ast::Module, 64 | } 65 | 66 | impl Context { 67 | fn set_variable(&mut self, name: String, value: &Value) { 68 | self.environment.insert(name.to_string(), NamedEntity::Variable(value.clone())); 69 | } 70 | 71 | fn new_env(&self) -> Context { 72 | return Context::from_module(&self.current_module); 73 | } 74 | 75 | fn from_module(module: &ast::Module) -> Context { 76 | let mut ctx = Context { 77 | environment: create_builtins(), 78 | impls: HashMap::new(), 79 | current_module: module.clone(), 80 | }; 81 | 82 | for item in ctx.current_module.items.iter() { 83 | match item { 84 | ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => { 85 | ctx.environment.insert( 86 | struct_.name.name.value.to_string(), 87 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone())), 88 | ); 89 | } 90 | ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => { 91 | ctx.environment.insert( 92 | alias.name.name.value.to_string(), 93 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone())), 94 | ); 95 | } 96 | ast::ModuleItem::Function(function) => { 97 | ctx.environment.insert( 98 | function.declaration.name.name.value.to_string(), 99 | NamedEntity::Variable(Value::Function(Function { 100 | partial: vec![], 101 | ref_: FunctionRef::User(function.clone()), 102 | })), 103 | ); 104 | } 105 | ast::ModuleItem::Impl(impl_) => { 106 | ctx.impls.insert(impl_.struct_.name.name.value.to_string(), impl_.clone()); 107 | } 108 | _ => {} 109 | } 110 | } 111 | return ctx; 112 | } 113 | } 114 | 115 | fn create_builtins() -> HashMap { 116 | let mut result = HashMap::new(); 117 | result.insert( 118 | "i8".to_string(), 119 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 120 | name: "i8".to_string(), 121 | })), 122 | ); 123 | result.insert( 124 | "i16".to_string(), 125 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 126 | name: "i16".to_string(), 127 | })), 128 | ); 129 | result.insert( 130 | "i32".to_string(), 131 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 132 | name: "i32".to_string(), 133 | })), 134 | ); 135 | result.insert( 136 | "i64".to_string(), 137 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 138 | name: "i64".to_string(), 139 | })), 140 | ); 141 | result.insert( 142 | "isize".to_string(), 143 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 144 | name: "isize".to_string(), 145 | })), 146 | ); 147 | 148 | result.insert( 149 | "u8".to_string(), 150 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 151 | name: "u8".to_string(), 152 | })), 153 | ); 154 | result.insert( 155 | "u16".to_string(), 156 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 157 | name: "u16".to_string(), 158 | })), 159 | ); 160 | result.insert( 161 | "u32".to_string(), 162 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 163 | name: "u32".to_string(), 164 | })), 165 | ); 166 | result.insert( 167 | "u64".to_string(), 168 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 169 | name: "u64".to_string(), 170 | })), 171 | ); 172 | result.insert( 173 | "usize".to_string(), 174 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 175 | name: "usize".to_string(), 176 | })), 177 | ); 178 | 179 | result.insert( 180 | "f32".to_string(), 181 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 182 | name: "f32".to_string(), 183 | })), 184 | ); 185 | result.insert( 186 | "f64".to_string(), 187 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 188 | name: "f64".to_string(), 189 | })), 190 | ); 191 | 192 | result.insert( 193 | "bool".to_string(), 194 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 195 | name: "bool".to_string(), 196 | })), 197 | ); 198 | 199 | result.insert( 200 | "!".to_string(), 201 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 202 | name: "!".to_string(), 203 | })), 204 | ); 205 | result.insert( 206 | "unit".to_string(), 207 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 208 | name: "!".to_string(), 209 | })), 210 | ); 211 | result.insert( 212 | "String".to_string(), 213 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { 214 | name: "String".to_string(), 215 | })), 216 | ); 217 | 218 | return result; 219 | } 220 | 221 | pub enum ExpressionResult { 222 | Value(Value), 223 | Return(Value), 224 | } 225 | 226 | pub struct TreeWalkInterpreter {} 227 | 228 | impl TreeWalkInterpreter { 229 | pub fn with_module(self: &Self, module: &ast::Module) -> Value { 230 | let mut ctx = Context::from_module(module); 231 | 232 | let main = match &ctx.environment["main"] { 233 | NamedEntity::Variable(Value::Function(func)) => match &func.ref_ { 234 | FunctionRef::User(ref_) => ref_.clone(), 235 | _ => panic!("main should be a user defined function"), 236 | }, 237 | _ => panic!("main should be a user defined function"), 238 | }; 239 | 240 | return self.with_function(&mut ctx, &main); 241 | } 242 | 243 | fn with_function(self: &Self, ctx: &mut Context, function: &ast::Function) -> Value { 244 | let result = self.with_block(ctx, &function.block); 245 | return match result { 246 | ExpressionResult::Value(r) => r, 247 | ExpressionResult::Return(r) => r, 248 | }; 249 | } 250 | 251 | fn with_block(self: &Self, ctx: &mut Context, block: &ast::Block) -> ExpressionResult { 252 | let mut last = ExpressionResult::Value(Value::Unit); 253 | for statement in block.statements.iter() { 254 | let result = self.with_statement(ctx, statement); 255 | match result { 256 | ExpressionResult::Return(r) => { 257 | return ExpressionResult::Return(r); 258 | } 259 | ExpressionResult::Value(r) => { 260 | last = ExpressionResult::Value(r); 261 | } 262 | } 263 | } 264 | return last; 265 | } 266 | 267 | fn with_statement(self: &Self, ctx: &mut Context, statement: &ast::Statement) -> ExpressionResult { 268 | match statement { 269 | ast::Statement::Return(return_statement) => { 270 | let result = match self.with_expression(ctx, &return_statement.source) { 271 | ExpressionResult::Value(r) => r, 272 | ExpressionResult::Return(r) => { 273 | return ExpressionResult::Return(r); 274 | } 275 | }; 276 | return ExpressionResult::Return(result); 277 | } 278 | ast::Statement::Let(let_statement) => { 279 | let result = match self.with_expression(ctx, &let_statement.expression) { 280 | ExpressionResult::Value(r) => r, 281 | ExpressionResult::Return(r) => { 282 | return ExpressionResult::Return(r); 283 | } 284 | }; 285 | ctx.set_variable(let_statement.variable_name.name.value.to_string(), &result); 286 | return ExpressionResult::Value(Value::Unit); 287 | } 288 | ast::Statement::Assignment(assignment_statement) => { 289 | return self.with_assignment_statement(ctx, assignment_statement); 290 | } 291 | ast::Statement::Expression(expression) => { 292 | return self.with_expression(ctx, expression); 293 | } 294 | } 295 | } 296 | 297 | fn with_assignment_statement(self: &Self, ctx: &mut Context, statement: &ast::AssignmentStatement) -> ExpressionResult { 298 | let result = match self.with_expression(ctx, &statement.expression) { 299 | ExpressionResult::Value(r) => r, 300 | ExpressionResult::Return(r) => { 301 | return ExpressionResult::Return(r); 302 | } 303 | }; 304 | match &statement.source { 305 | ast::AssignmentTarget::Variable(variable) => { 306 | ctx.set_variable(variable.name.name.value.to_string(), &result); 307 | } 308 | ast::AssignmentTarget::StructAttr(struct_attr) => { 309 | let mut source = match self.with_expression(ctx, &struct_attr.source) { 310 | ExpressionResult::Value(r) => r, 311 | ExpressionResult::Return(r) => { 312 | return ExpressionResult::Return(r); 313 | } 314 | }; 315 | match &mut source { 316 | Value::Struct(s) => { 317 | let mut struct_ = s.lock().unwrap(); 318 | struct_.fields.insert(struct_attr.attribute.name.value.clone(), result); 319 | } 320 | _ => panic!("set attr on nonstruct, should never happen due to type system"), 321 | } 322 | } 323 | } 324 | return ExpressionResult::Value(Value::Unit); 325 | } 326 | 327 | fn with_expression(self: &Self, ctx: &mut Context, expression: &ast::Expression) -> ExpressionResult { 328 | match &*expression.subexpression { 329 | ast::Subexpression::LiteralInt(literal_int) => { 330 | let value: i64 = literal_int.value.value.parse().unwrap(); 331 | return ExpressionResult::Value(Value::Numeric(NumericValue::I64(value))); 332 | } 333 | ast::Subexpression::LiteralFloat(literal_float) => { 334 | let value: f64 = literal_float.value.value.parse().unwrap(); 335 | return ExpressionResult::Value(Value::Numeric(NumericValue::F64(value))); 336 | } 337 | ast::Subexpression::LiteralBool(literal_bool) => { 338 | let value: bool = if &literal_bool.value.value == "true" { true } else { false }; 339 | return ExpressionResult::Value(Value::Bool(value)); 340 | } 341 | ast::Subexpression::LiteralString(literal_string) => { 342 | let value: String = literal_string.value.value.to_string(); 343 | return ExpressionResult::Value(Value::String(value)); 344 | } 345 | ast::Subexpression::LiteralStruct(literal_struct) => { 346 | let declaration = match &ctx.environment[&literal_struct.name.name.value] { 347 | NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(declaration)) => declaration.clone(), 348 | _ => panic!("not a struct"), 349 | }; 350 | 351 | let mut fields = HashMap::new(); 352 | for field in declaration.fields.iter() { 353 | for (field_name, field_expression) in literal_struct.fields.iter() { 354 | if field.name.name.value == field_name.name.value { 355 | let field_result = match self.with_expression(ctx, field_expression) { 356 | ExpressionResult::Value(r) => r, 357 | ExpressionResult::Return(r) => { 358 | return ExpressionResult::Return(r); 359 | } 360 | }; 361 | fields.insert(field.name.name.value.to_string(), field_result); 362 | } 363 | } 364 | } 365 | return ExpressionResult::Value(Value::Struct(Arc::new(Mutex::new(StructValue { 366 | source: declaration.clone(), 367 | fields: fields, 368 | })))); 369 | } 370 | ast::Subexpression::FunctionCall(function_call) => { 371 | let source = match self.with_expression(ctx, &function_call.source) { 372 | ExpressionResult::Value(r) => r, 373 | ExpressionResult::Return(r) => { 374 | return ExpressionResult::Return(r); 375 | } 376 | }; 377 | let mut argument_values = vec![]; 378 | for arg in function_call.arguments.iter() { 379 | let argument_value = match self.with_expression(ctx, arg) { 380 | ExpressionResult::Value(r) => r, 381 | ExpressionResult::Return(r) => { 382 | return ExpressionResult::Return(r); 383 | } 384 | }; 385 | argument_values.push(argument_value); 386 | } 387 | match &source { 388 | Value::Function(function) => match &function.ref_ { 389 | FunctionRef::User(user_function) => { 390 | let mut fn_ctx = ctx.new_env(); 391 | let mut i = 0; 392 | for partial_arg in &function.partial { 393 | fn_ctx.set_variable( 394 | user_function.declaration.arguments[i].name.name.value.to_string(), 395 | &partial_arg.clone(), 396 | ); 397 | i = i + 1; 398 | } 399 | for argument_value in &argument_values { 400 | fn_ctx.set_variable( 401 | user_function.declaration.arguments[i].name.name.value.to_string(), 402 | &argument_value.clone(), 403 | ); 404 | } 405 | return ExpressionResult::Value(self.with_function(&mut fn_ctx, user_function)); 406 | } 407 | FunctionRef::Builtin(builtin_function) => { 408 | let all_values = function 409 | .partial 410 | .iter() 411 | .map(|val| val.clone()) 412 | .chain(argument_values.into_iter()) 413 | .collect(); 414 | return ExpressionResult::Value(builtin_function(all_values)); 415 | } 416 | }, 417 | _ => panic!("type error: function call source must be a function"), 418 | } 419 | } 420 | ast::Subexpression::VariableUsage(variable_usage) => { 421 | let variable_value = match &ctx.environment[&variable_usage.name.name.value] { 422 | NamedEntity::Variable(v) => v.clone(), 423 | _ => panic!("variable lookup of type"), 424 | }; 425 | return ExpressionResult::Value(variable_value); 426 | } 427 | ast::Subexpression::If(if_expression) => { 428 | let condition = match self.with_expression(ctx, &if_expression.condition) { 429 | ExpressionResult::Value(r) => r, 430 | ExpressionResult::Return(r) => { 431 | return ExpressionResult::Return(r); 432 | } 433 | }; 434 | 435 | match &condition { 436 | Value::Bool(cond) => { 437 | if cond.clone() { 438 | return self.with_block(ctx, &if_expression.block); 439 | } else { 440 | return match &if_expression.else_ { 441 | Some(else_) => self.with_block(ctx, else_), 442 | None => ExpressionResult::Value(Value::Unit), 443 | }; 444 | } 445 | } 446 | _ => panic!("TypeError: condition must be bool"), 447 | } 448 | } 449 | ast::Subexpression::StructGetter(struct_getter) => { 450 | let source = match self.with_expression(ctx, &struct_getter.source) { 451 | ExpressionResult::Value(r) => r, 452 | ExpressionResult::Return(r) => { 453 | return ExpressionResult::Return(r); 454 | } 455 | }; 456 | match &source { 457 | Value::Struct(struct_) => { 458 | let s = struct_.lock().unwrap(); 459 | if s.fields.contains_key(&struct_getter.attribute.name.value) { 460 | return ExpressionResult::Value(s.fields[&struct_getter.attribute.name.value].clone()); 461 | } 462 | for module_item in &ctx.current_module.items { 463 | match module_item { 464 | ast::ModuleItem::Impl(impl_) => { 465 | if impl_.struct_.name.name.value == s.source.name.name.value { 466 | for method in &impl_.functions { 467 | if method.declaration.name.name.value == struct_getter.attribute.name.value { 468 | // if first type matches, partial apply self 469 | if method.declaration.arguments.len() > 0 { 470 | match &method.declaration.arguments[0].type_ { 471 | ast::TypeUsage::Named(arg_named) => { 472 | if arg_named.name.name.value == s.source.name.name.value { 473 | return ExpressionResult::Value(Value::Function(Function { 474 | partial: vec![source.clone()], 475 | ref_: FunctionRef::User(method.clone()), 476 | })); 477 | } 478 | } 479 | _ => {} 480 | } 481 | } 482 | return ExpressionResult::Value(Value::Function(Function { 483 | partial: vec![], 484 | ref_: FunctionRef::User(method.clone()), 485 | })); 486 | } 487 | } 488 | } 489 | } 490 | _ => {} 491 | } 492 | } 493 | panic!("TypeError: Method not found"); 494 | } 495 | _ => { 496 | panic!("TypeError: struct getter used with non-struct"); 497 | } 498 | } 499 | } 500 | ast::Subexpression::Block(block) => { 501 | return self.with_block(ctx, block); 502 | } 503 | ast::Subexpression::Op(op) => { 504 | let left = match self.with_expression(ctx, &op.left) { 505 | ExpressionResult::Value(r) => r, 506 | ExpressionResult::Return(r) => { 507 | return ExpressionResult::Return(r); 508 | } 509 | }; 510 | let right = match self.with_expression(ctx, &op.right) { 511 | ExpressionResult::Value(r) => r, 512 | ExpressionResult::Return(r) => { 513 | return ExpressionResult::Return(r); 514 | } 515 | }; 516 | let result = match (&left, &op.op, &right) { 517 | //I 518 | (Value::Numeric(NumericValue::I8(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I8(r))) => { 519 | Value::Numeric(NumericValue::I8(l + r)) 520 | } 521 | (Value::Numeric(NumericValue::I8(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I8(r))) => { 522 | Value::Numeric(NumericValue::I8(l - r)) 523 | } 524 | (Value::Numeric(NumericValue::I8(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I8(r))) => { 525 | Value::Numeric(NumericValue::I8(l * r)) 526 | } 527 | (Value::Numeric(NumericValue::I8(l)), ast::Operator::Div, Value::Numeric(NumericValue::I8(r))) => { 528 | Value::Numeric(NumericValue::I8(l / r)) 529 | } 530 | 531 | (Value::Numeric(NumericValue::I16(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I16(r))) => { 532 | Value::Numeric(NumericValue::I16(l + r)) 533 | } 534 | (Value::Numeric(NumericValue::I16(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I16(r))) => { 535 | Value::Numeric(NumericValue::I16(l - r)) 536 | } 537 | (Value::Numeric(NumericValue::I16(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I16(r))) => { 538 | Value::Numeric(NumericValue::I16(l * r)) 539 | } 540 | (Value::Numeric(NumericValue::I16(l)), ast::Operator::Div, Value::Numeric(NumericValue::I16(r))) => { 541 | Value::Numeric(NumericValue::I16(l / r)) 542 | } 543 | 544 | (Value::Numeric(NumericValue::I32(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I32(r))) => { 545 | Value::Numeric(NumericValue::I32(l + r)) 546 | } 547 | (Value::Numeric(NumericValue::I32(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I32(r))) => { 548 | Value::Numeric(NumericValue::I32(l - r)) 549 | } 550 | (Value::Numeric(NumericValue::I32(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I32(r))) => { 551 | Value::Numeric(NumericValue::I32(l * r)) 552 | } 553 | (Value::Numeric(NumericValue::I32(l)), ast::Operator::Div, Value::Numeric(NumericValue::I32(r))) => { 554 | Value::Numeric(NumericValue::I32(l / r)) 555 | } 556 | 557 | (Value::Numeric(NumericValue::I64(l)), ast::Operator::Plus, Value::Numeric(NumericValue::I64(r))) => { 558 | Value::Numeric(NumericValue::I64(l + r)) 559 | } 560 | (Value::Numeric(NumericValue::I64(l)), ast::Operator::Minus, Value::Numeric(NumericValue::I64(r))) => { 561 | Value::Numeric(NumericValue::I64(l - r)) 562 | } 563 | (Value::Numeric(NumericValue::I64(l)), ast::Operator::Mul, Value::Numeric(NumericValue::I64(r))) => { 564 | Value::Numeric(NumericValue::I64(l * r)) 565 | } 566 | (Value::Numeric(NumericValue::I64(l)), ast::Operator::Div, Value::Numeric(NumericValue::I64(r))) => { 567 | Value::Numeric(NumericValue::I64(l / r)) 568 | } 569 | 570 | //U 571 | (Value::Numeric(NumericValue::U8(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U8(r))) => { 572 | Value::Numeric(NumericValue::U8(l + r)) 573 | } 574 | (Value::Numeric(NumericValue::U8(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U8(r))) => { 575 | Value::Numeric(NumericValue::U8(l - r)) 576 | } 577 | (Value::Numeric(NumericValue::U8(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U8(r))) => { 578 | Value::Numeric(NumericValue::U8(l * r)) 579 | } 580 | (Value::Numeric(NumericValue::U8(l)), ast::Operator::Div, Value::Numeric(NumericValue::U8(r))) => { 581 | Value::Numeric(NumericValue::U8(l / r)) 582 | } 583 | 584 | (Value::Numeric(NumericValue::U16(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U16(r))) => { 585 | Value::Numeric(NumericValue::U16(l + r)) 586 | } 587 | (Value::Numeric(NumericValue::U16(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U16(r))) => { 588 | Value::Numeric(NumericValue::U16(l - r)) 589 | } 590 | (Value::Numeric(NumericValue::U16(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U16(r))) => { 591 | Value::Numeric(NumericValue::U16(l * r)) 592 | } 593 | (Value::Numeric(NumericValue::U16(l)), ast::Operator::Div, Value::Numeric(NumericValue::U16(r))) => { 594 | Value::Numeric(NumericValue::U16(l / r)) 595 | } 596 | 597 | (Value::Numeric(NumericValue::U32(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U32(r))) => { 598 | Value::Numeric(NumericValue::U32(l + r)) 599 | } 600 | (Value::Numeric(NumericValue::U32(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U32(r))) => { 601 | Value::Numeric(NumericValue::U32(l - r)) 602 | } 603 | (Value::Numeric(NumericValue::U32(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U32(r))) => { 604 | Value::Numeric(NumericValue::U32(l * r)) 605 | } 606 | (Value::Numeric(NumericValue::U32(l)), ast::Operator::Div, Value::Numeric(NumericValue::U32(r))) => { 607 | Value::Numeric(NumericValue::U32(l / r)) 608 | } 609 | 610 | (Value::Numeric(NumericValue::U64(l)), ast::Operator::Plus, Value::Numeric(NumericValue::U64(r))) => { 611 | Value::Numeric(NumericValue::U64(l + r)) 612 | } 613 | (Value::Numeric(NumericValue::U64(l)), ast::Operator::Minus, Value::Numeric(NumericValue::U64(r))) => { 614 | Value::Numeric(NumericValue::U64(l - r)) 615 | } 616 | (Value::Numeric(NumericValue::U64(l)), ast::Operator::Mul, Value::Numeric(NumericValue::U64(r))) => { 617 | Value::Numeric(NumericValue::U64(l * r)) 618 | } 619 | (Value::Numeric(NumericValue::U64(l)), ast::Operator::Div, Value::Numeric(NumericValue::U64(r))) => { 620 | Value::Numeric(NumericValue::U64(l / r)) 621 | } 622 | 623 | //F 624 | (Value::Numeric(NumericValue::F32(l)), ast::Operator::Plus, Value::Numeric(NumericValue::F32(r))) => { 625 | Value::Numeric(NumericValue::F32(l + r)) 626 | } 627 | (Value::Numeric(NumericValue::F32(l)), ast::Operator::Minus, Value::Numeric(NumericValue::F32(r))) => { 628 | Value::Numeric(NumericValue::F32(l - r)) 629 | } 630 | (Value::Numeric(NumericValue::F32(l)), ast::Operator::Mul, Value::Numeric(NumericValue::F32(r))) => { 631 | Value::Numeric(NumericValue::F32(l * r)) 632 | } 633 | (Value::Numeric(NumericValue::F32(l)), ast::Operator::Div, Value::Numeric(NumericValue::F32(r))) => { 634 | Value::Numeric(NumericValue::F32(l / r)) 635 | } 636 | 637 | (Value::Numeric(NumericValue::F64(l)), ast::Operator::Plus, Value::Numeric(NumericValue::F64(r))) => { 638 | Value::Numeric(NumericValue::F64(l + r)) 639 | } 640 | (Value::Numeric(NumericValue::F64(l)), ast::Operator::Minus, Value::Numeric(NumericValue::F64(r))) => { 641 | Value::Numeric(NumericValue::F64(l - r)) 642 | } 643 | (Value::Numeric(NumericValue::F64(l)), ast::Operator::Mul, Value::Numeric(NumericValue::F64(r))) => { 644 | Value::Numeric(NumericValue::F64(l * r)) 645 | } 646 | (Value::Numeric(NumericValue::F64(l)), ast::Operator::Div, Value::Numeric(NumericValue::F64(r))) => { 647 | Value::Numeric(NumericValue::F64(l / r)) 648 | } 649 | 650 | //fail 651 | _ => panic!(""), 652 | }; 653 | return ExpressionResult::Value(result); 654 | } 655 | } 656 | } 657 | } 658 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // mod types; 2 | mod ast; 3 | mod errors; 4 | mod interpreter; 5 | mod trait_checking; 6 | mod type_alias_resolution; 7 | mod type_checking; 8 | #[macro_use] 9 | extern crate lalrpop_util; 10 | 11 | lalrpop_mod!(pub grammar); // synthesized by LALRPOP 12 | 13 | use std::fs; 14 | 15 | extern crate clap; 16 | use clap::{App, Arg}; 17 | 18 | fn main() { 19 | let matches = App::new("Boring Language Compiler") 20 | .version("0.0.1") 21 | .author("Andrew Segavac") 22 | .about("Compiles boring language files to LLVM IR.") 23 | .arg( 24 | Arg::with_name("OUTPUT") 25 | .short("o") 26 | .long("out") 27 | .value_name("OUTOUT") 28 | .help("Sets an output file") 29 | .takes_value(true), 30 | ) 31 | .arg(Arg::with_name("INPUT").help("Sets the input file").required(true).index(1)) 32 | .arg(Arg::with_name("v").short("v").multiple(true).help("Sets the level of verbosity")) 33 | .get_matches(); 34 | let input = matches.value_of("INPUT").unwrap(); 35 | 36 | let default_output = input.rsplitn(2, ".").collect::>().last().unwrap().clone(); 37 | let _output = matches.value_of("OUTPUT").unwrap_or(default_output); 38 | 39 | let contents = fs::read_to_string(input).expect("input file not found"); 40 | let unknown_id_gen = ast::IdGenerator::new("S"); 41 | let module_ast = grammar::ModuleParser::new().parse(&unknown_id_gen, &contents).unwrap(); //TODO: convert to error 42 | // println!("ast: {:#?}", &module_ast); 43 | let alias_resolver = type_alias_resolution::TypeAliasResolver {}; 44 | let resolved_ast = match alias_resolver.with_module(&module_ast) { 45 | Ok(r) => r, 46 | Err(err) => { 47 | println!("type checking error: {:#?}", &err); 48 | panic!("bad alias"); 49 | } 50 | }; 51 | 52 | // println!("resolved ast: {:#?}", &resolved_ast); 53 | let trait_checker = trait_checking::TraitChecker {}; 54 | match trait_checker.with_module(&resolved_ast) { 55 | Ok(_) => {} 56 | Err(err) => { 57 | println!("type checking error: {:#?}", &err); 58 | return; 59 | } 60 | } 61 | let type_checker = type_checking::TypeChecker {}; 62 | let type_checking_result = type_checker.with_module(&resolved_ast); 63 | match &type_checking_result { 64 | Ok((checked_ast, subst)) => { 65 | println!("checked ast: {:#?}", &checked_ast); 66 | println!("substitutions: {:#?}", &subst); 67 | let interpreter = interpreter::TreeWalkInterpreter {}; 68 | let result = interpreter.with_module(&checked_ast); 69 | println!("final result: {:#?}", &result); 70 | } 71 | Err(err) => { 72 | println!("type checking error: {:#?}", &err); 73 | } 74 | } 75 | 76 | // let context = Context::create(); 77 | // let mut code_gen = compiler::ModuleCodeGen::new(&context, "main".to_string()); 78 | // code_gen.gen_module(module_ast); 79 | // 80 | // let mut f = fs::File::create(output).expect("Unable to create out file"); 81 | // f.write_all(code_gen.dump().as_bytes()).expect("Unable to write data"); 82 | } 83 | 84 | #[test] 85 | fn grammar() { 86 | let id_gen = ast::IdGenerator::new("S"); 87 | assert!(grammar::LiteralIntParser::new().parse(&id_gen, "22").is_ok()); 88 | assert!(grammar::IdentifierParser::new().parse(&id_gen, "foo").is_ok()); 89 | assert!(grammar::LiteralIntParser::new().parse(&id_gen, "2a").is_err()); 90 | 91 | assert!(grammar::TermParser::new().parse(&id_gen, "22").is_ok()); 92 | assert!(grammar::TermParser::new().parse(&id_gen, "foo").is_ok()); 93 | 94 | assert!(grammar::ExpressionParser::new().parse(&id_gen, "22 * foo").is_ok()); 95 | assert!(grammar::ExpressionParser::new().parse(&id_gen, "22 * 33").is_ok()); 96 | assert!(grammar::ExpressionParser::new().parse(&id_gen, "(22 * 33) + 24").is_ok()); 97 | 98 | assert!(grammar::BlockParser::new().parse(&id_gen, "{ (22 * 33) + 24 }").is_ok()); 99 | assert!(grammar::BlockParser::new().parse(&id_gen, "{ (22 * 33) + 24; 25 }").is_ok()); 100 | // assert!(grammar::BlockParser::new().parse("{ (22 * 33) + 24\n 24 }").is_ok()); 101 | assert!(grammar::BlockParser::new().parse(&id_gen, "{ }").is_ok()); 102 | 103 | assert!(grammar::VariableDeclarationParser::new().parse(&id_gen, "foo: i32").is_ok()); 104 | assert!(grammar::VariableDeclarationParser::new().parse(&id_gen, "foo").is_err()); 105 | assert!(grammar::VariableDeclarationParser::new().parse(&id_gen, "1234").is_err()); 106 | 107 | assert!(grammar::FunctionParser::new() 108 | .parse(&id_gen, "fn add(a: i32, b: i32): i32 { a + b }") 109 | .is_ok()); 110 | assert!(grammar::FunctionParser::new() 111 | .parse(&id_gen, "fn random_dice_roll(): i32 { 4 }") 112 | .is_ok()); 113 | assert!(grammar::FunctionParser::new() 114 | .parse(&id_gen, "fn add(a: i32, b: i32): i32 { a + }") 115 | .is_err()); 116 | assert!(grammar::FunctionParser::new() 117 | .parse(&id_gen, "fn add(a: i32, b: i32): i32") 118 | .is_err()); 119 | 120 | assert!(grammar::FunctionCallParser::new().parse(&id_gen, "foo(1, 2)").is_ok()); 121 | 122 | assert!(grammar::ModuleParser::new() 123 | .parse(&id_gen, "fn add(a: i32, b: i32): i32 { a + b }") 124 | .is_ok()); 125 | assert!(grammar::ModuleParser::new() 126 | .parse( 127 | &id_gen, 128 | "fn add(a: i32, b: i32): i32 { a + b } fn subtract(a: i32, b: i32): i32 { a - b }" 129 | ) 130 | .is_ok()); 131 | } 132 | -------------------------------------------------------------------------------- /src/trait_checking.rs: -------------------------------------------------------------------------------- 1 | use crate::ast; 2 | use crate::errors; 3 | use std::collections::HashMap; 4 | 5 | pub type Result = std::result::Result; 6 | 7 | #[derive(Debug, Clone, PartialEq, Eq)] 8 | struct Context { 9 | pub environment_traits: HashMap, 10 | } 11 | 12 | fn create_builtins() -> HashMap { 13 | let result = HashMap::::new(); 14 | return result; 15 | } 16 | 17 | fn compare_struct_trait( 18 | struct_: &ast::TypeUsage, 19 | trait_: &ast::TypeUsage, 20 | struct_name: &ast::Identifier, 21 | trait_name: &ast::Identifier, 22 | ) -> Result<()> { 23 | match struct_ { 24 | ast::TypeUsage::Named(named) => match trait_ { 25 | ast::TypeUsage::Named(trait_named) => { 26 | if named.name.name.value == trait_named.name.name.value 27 | || (named.name.name.value == struct_name.name.value && trait_named.name.name.value == trait_name.name.value) 28 | { 29 | return Ok(()); 30 | } 31 | return Err(errors::TypingError::TypeMismatch { 32 | type_one: struct_.clone(), 33 | type_two: trait_.clone(), 34 | }); 35 | } 36 | ast::TypeUsage::Function(_) => { 37 | return Err(errors::TypingError::TypeMismatch { 38 | type_one: struct_.clone(), 39 | type_two: trait_.clone(), 40 | }); 41 | } 42 | _ => panic!("Unknown in function definition"), 43 | }, 44 | ast::TypeUsage::Function(function) => match trait_ { 45 | ast::TypeUsage::Named(_) => { 46 | return Err(errors::TypingError::TypeMismatch { 47 | type_one: struct_.clone(), 48 | type_two: trait_.clone(), 49 | }); 50 | } 51 | ast::TypeUsage::Function(trait_function) => { 52 | if function.arguments.len() != trait_function.arguments.len() { 53 | return Err(errors::TypingError::TypeMismatch { 54 | type_one: struct_.clone(), 55 | type_two: trait_.clone(), 56 | }); 57 | } 58 | for (i, _) in function.arguments.iter().enumerate() { 59 | compare_struct_trait(&function.arguments[i], &trait_function.arguments[i], struct_name, trait_name)?; 60 | } 61 | compare_struct_trait(&function.return_type, &trait_function.return_type, struct_name, trait_name)?; 62 | return Ok(()); 63 | } 64 | _ => panic!("Unknown in function definition"), 65 | }, 66 | _ => panic!("Unknown in function definition"), 67 | } 68 | } 69 | 70 | pub struct TraitChecker {} 71 | 72 | impl TraitChecker { 73 | pub fn with_module(self: &Self, module: &ast::Module) -> Result<()> { 74 | let mut ctx = Context { 75 | environment_traits: create_builtins(), 76 | }; 77 | 78 | for item in module.items.iter() { 79 | match item { 80 | ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Trait(trait_)) => { 81 | ctx.environment_traits.insert(trait_.name.name.value.to_string(), trait_.clone()); 82 | } 83 | _ => {} 84 | } 85 | } 86 | 87 | for item in module.items.iter() { 88 | match item { 89 | ast::ModuleItem::Impl(impl_) => { 90 | self.with_impl(&ctx, impl_)?; 91 | } 92 | _ => {} 93 | } 94 | } 95 | return Ok(()); 96 | } 97 | 98 | fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> Result<()> { 99 | // See if trait actually matches 100 | match &impl_.trait_ { 101 | Some(trait_) => { 102 | // assert trait functions satisfied 103 | if !ctx.environment_traits.contains_key(&trait_.name.name.value) { 104 | return Err(errors::TypingError::TypeDoesNotExist { 105 | identifier: trait_.name.clone(), 106 | }); 107 | } 108 | let trait_declaration = &ctx.environment_traits[&trait_.name.name.value]; 109 | for trait_item in trait_declaration.functions.iter() { 110 | match trait_item { 111 | ast::TraitItem::FunctionDeclaration(declaration) => { 112 | let mut found = false; 113 | for impl_function in impl_.functions.iter() { 114 | if impl_function.declaration.name.name.value == declaration.name.name.value { 115 | found = true; 116 | compare_struct_trait( 117 | &impl_function.declaration.to_type(), 118 | &declaration.to_type(), 119 | &impl_.struct_.name, 120 | &trait_.name, 121 | )?; 122 | } 123 | } 124 | if found == false { 125 | return Err(errors::TypingError::MissingTraitFunction { 126 | struct_name: impl_.struct_.name.clone(), 127 | function_name: declaration.name.clone(), 128 | }); 129 | } 130 | } 131 | ast::TraitItem::Function(function) => { 132 | // skip found check because it has a default 133 | for impl_function in impl_.functions.iter() { 134 | if impl_function.declaration.name.name.value == function.declaration.name.name.value { 135 | compare_struct_trait( 136 | &impl_function.declaration.to_type(), 137 | &function.declaration.to_type(), 138 | &impl_.struct_.name, 139 | &trait_.name, 140 | )?; 141 | } 142 | } 143 | } 144 | } 145 | } 146 | // assert all functions are in trait 147 | for impl_function in impl_.functions.iter() { 148 | let mut found = false; 149 | for trait_item in trait_declaration.functions.iter() { 150 | let declaration = match trait_item { 151 | ast::TraitItem::Function(function) => &function.declaration, 152 | ast::TraitItem::FunctionDeclaration(declaration) => &declaration, 153 | }; 154 | if impl_function.declaration.name.name.value == declaration.name.name.value { 155 | found = true; 156 | break; 157 | } 158 | } 159 | if found == false { 160 | return Err(errors::TypingError::FunctionNotInTrait { 161 | function_name: impl_function.declaration.name.clone(), 162 | }); 163 | } 164 | } 165 | } 166 | None => {} 167 | } 168 | // TODO: check for duplicate functions 169 | return Ok(()); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/type_alias_resolution.rs: -------------------------------------------------------------------------------- 1 | use crate::ast; 2 | use crate::errors; 3 | 4 | pub type Result = std::result::Result; 5 | 6 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 7 | struct Context { 8 | pub type_aliases: Vec, 9 | } 10 | 11 | fn resolve_type(ctx: &Context, type_: &ast::NamedTypeUsage) -> Result { 12 | let mut changed = true; 13 | let mut result = ast::TypeUsage::Named(type_.clone()); 14 | while changed { 15 | changed = false; 16 | let current = &result.clone(); 17 | match current { 18 | ast::TypeUsage::Named(named) => { 19 | for alias in ctx.type_aliases.iter() { 20 | if named.name.name.value == alias.name.name.value { // is alias, replace 21 | changed = true; 22 | result = alias.replaces.clone(); 23 | } 24 | } 25 | } 26 | _ => break, 27 | } 28 | } 29 | match &result { 30 | ast::TypeUsage::Named(named) => { 31 | match &named.type_parameters { 32 | ast::GenericUsage::Known(known) => { 33 | let mut result_params = vec!(); 34 | for param in known.parameters.iter() { 35 | result_params.push(process_type(ctx, param)?); 36 | } 37 | let mut new_named = named.clone(); 38 | new_named.type_parameters = ast::GenericUsage::new(&result_params); 39 | result = ast::TypeUsage::Named(new_named); 40 | }, 41 | _ => {} 42 | } 43 | }, 44 | ast::TypeUsage::Function(func) => { 45 | match &type_.type_parameters { 46 | ast::GenericUsage::Known(known) => { 47 | if known.parameters.len() > 0 { 48 | return Err(errors::TypingError::InvalidTypeParameterOnAlias{alias: type_.name.clone()}); 49 | } 50 | }, 51 | _ => {} //skip 52 | } 53 | }, 54 | _ => { 55 | panic!("alias of a non-type, not possible"); 56 | } 57 | } 58 | return Ok(result); 59 | } 60 | 61 | fn process_type(ctx: &Context, type_: &ast::TypeUsage) -> Result { 62 | match type_ { 63 | ast::TypeUsage::Named(named) => { 64 | return Ok(resolve_type(ctx, named)?); 65 | } 66 | ast::TypeUsage::Function(function) => { 67 | let mut arguments = vec!(); 68 | for a in function.arguments.iter() { 69 | arguments.push(process_type(ctx, &a.clone())?); 70 | } 71 | return Ok(ast::TypeUsage::Function(ast::FunctionTypeUsage { 72 | arguments: arguments, 73 | return_type: Box::new(process_type(ctx, &function.return_type.clone())?), 74 | })); 75 | } 76 | ast::TypeUsage::Unknown(unknown) => { 77 | return Ok(ast::TypeUsage::Unknown(unknown.clone())); 78 | }, 79 | ast::TypeUsage::Namespace(namespace) => { 80 | match namespace { 81 | ast::NamespaceTypeUsage::Type(named_type)=> { 82 | let result = resolve_type(ctx, named_type)?; 83 | match result { 84 | ast::TypeUsage::Named(named) => { 85 | return Ok(ast::TypeUsage::Namespace(ast::NamespaceTypeUsage::Type(named))); 86 | }, 87 | _ => { 88 | return Err(errors::TypingError::InvalidUseofAlias); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | pub struct TypeAliasResolver {} 98 | 99 | impl TypeAliasResolver { 100 | pub fn with_module(self: &Self, module: &ast::Module) -> Result { 101 | let mut ctx = Context { type_aliases: vec![] }; 102 | for item in module.items.iter() { 103 | match item { 104 | ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => { 105 | ctx.type_aliases.push(alias.clone()); 106 | } 107 | _ => {} 108 | } 109 | } 110 | 111 | let mut items = vec!(); 112 | for item in module.items.iter() { 113 | items.push(match item { 114 | ast::ModuleItem::Function(function) => ast::ModuleItem::Function(self.with_function(&ctx, function)?), 115 | ast::ModuleItem::TypeDeclaration(type_declaration) => { 116 | ast::ModuleItem::TypeDeclaration(self.with_type_declaration(&ctx, type_declaration)?) 117 | } 118 | ast::ModuleItem::Impl(impl_) => ast::ModuleItem::Impl(self.with_impl(&ctx, impl_)?), 119 | }); 120 | } 121 | 122 | return Ok(ast::Module { 123 | items: items, 124 | }); 125 | } 126 | 127 | fn with_function(self: &Self, ctx: &Context, function: &ast::Function) -> Result { 128 | return Ok(ast::Function { 129 | declaration: self.with_function_declaration(ctx, &function.declaration)?, 130 | block: self.with_block(ctx, &function.block)?, 131 | }); 132 | } 133 | 134 | fn with_function_declaration(self: &Self, ctx: &Context, declaration: &ast::FunctionDeclaration) -> Result { 135 | let mut arguments = vec!(); 136 | for arg in declaration.arguments.iter() { 137 | arguments.push(ast::VariableDeclaration { 138 | name: arg.name.clone(), 139 | type_: process_type(ctx, &arg.type_)?, 140 | }); 141 | } 142 | return Ok(ast::FunctionDeclaration { 143 | name: declaration.name.clone(), 144 | generic: declaration.generic.clone(), 145 | arguments: arguments, 146 | return_type: process_type(ctx, &declaration.return_type)?, 147 | }); 148 | } 149 | 150 | fn with_type_declaration(self: &Self, ctx: &Context, type_declaration: &ast::TypeDeclaration) -> Result { 151 | match type_declaration { 152 | ast::TypeDeclaration::Struct(struct_) => { 153 | return Ok(ast::TypeDeclaration::Struct(self.with_struct_declaration(ctx, struct_)?)); 154 | } 155 | ast::TypeDeclaration::Primitive(primitive) => { 156 | return Ok(ast::TypeDeclaration::Primitive(primitive.clone())); 157 | } 158 | ast::TypeDeclaration::Alias(alias) => { 159 | return Ok(ast::TypeDeclaration::Alias(alias.clone())); 160 | } 161 | ast::TypeDeclaration::Trait(trait_) => { 162 | return Ok(ast::TypeDeclaration::Trait(self.with_trait(ctx, trait_)?)); 163 | } 164 | } 165 | } 166 | 167 | fn with_struct_declaration(self: &Self, ctx: &Context, struct_: &ast::StructTypeDeclaration) -> Result { 168 | let mut fields = vec!(); 169 | for field in struct_.fields.iter() { 170 | fields.push(ast::StructField { 171 | name: field.name.clone(), 172 | type_: process_type(ctx, &field.type_)?, 173 | }); 174 | } 175 | return Ok(ast::StructTypeDeclaration { 176 | generic: struct_.generic.clone(), 177 | name: struct_.name.clone(), 178 | fields: fields, 179 | }); 180 | } 181 | 182 | fn with_trait(self: &Self, ctx: &Context, trait_: &ast::TraitTypeDeclaration) -> Result { 183 | let mut trait_ctx = ctx.clone(); 184 | trait_ctx.type_aliases.push(ast::AliasTypeDeclaration { 185 | name: ast::Identifier { 186 | name: ast::Spanned { 187 | span: ast::Span { left: 0, right: 0 }, //todo: figure out a sane value for these 188 | value: "Self".to_string(), 189 | }, 190 | }, 191 | replaces: ast::TypeUsage::Named(ast::NamedTypeUsage { 192 | type_parameters: ast::GenericUsage::Unknown, 193 | name: trait_.name.clone(), 194 | }), 195 | }); 196 | let mut functions = vec!(); 197 | for f in trait_.functions.iter() { 198 | functions.push(match f { 199 | ast::TraitItem::Function(function) => ast::TraitItem::Function(self.with_function(&trait_ctx, function)?), 200 | ast::TraitItem::FunctionDeclaration(function_declaration) => { 201 | ast::TraitItem::FunctionDeclaration(self.with_function_declaration(&trait_ctx, function_declaration)?) 202 | } 203 | }); 204 | } 205 | return Ok(ast::TraitTypeDeclaration { 206 | generic: trait_.generic.clone(), 207 | name: trait_.name.clone(), 208 | functions: functions, 209 | }); 210 | } 211 | 212 | fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> Result { 213 | let mut impl_ctx = ctx.clone(); 214 | impl_ctx.type_aliases.push(ast::AliasTypeDeclaration { 215 | name: ast::Identifier { 216 | name: ast::Spanned { 217 | span: ast::Span { left: 0, right: 0 }, //todo: figure out a sane value for these 218 | value: "Self".to_string(), 219 | }, 220 | }, 221 | replaces: ast::TypeUsage::Named(impl_.struct_.clone()), 222 | }); 223 | let mut functions = vec!(); 224 | for f in impl_.functions.iter() { 225 | functions.push(self.with_function(&impl_ctx, f)?); 226 | } 227 | return Ok(ast::Impl { 228 | generic: impl_.generic.clone(), 229 | trait_: impl_.trait_.clone(), 230 | struct_: impl_.struct_.clone(), 231 | functions: functions, 232 | }); 233 | } 234 | 235 | fn with_block(self: &Self, ctx: &Context, block: &ast::Block) -> Result { 236 | let mut statements = vec!(); 237 | for s in block.statements.iter() { 238 | statements.push(self.with_statement(ctx, s)?); 239 | } 240 | return Ok(ast::Block { 241 | statements: statements, 242 | type_: process_type(ctx, &block.type_)?, 243 | }); 244 | } 245 | 246 | fn with_statement(self: &Self, ctx: &Context, statement: &ast::Statement) -> Result { 247 | match statement { 248 | ast::Statement::Return(return_statement) => { 249 | return Ok(ast::Statement::Return(self.with_return_statement(ctx, return_statement)?)); 250 | } 251 | ast::Statement::Let(let_statement) => { 252 | return Ok(ast::Statement::Let(self.with_let_statement(ctx, let_statement)?)); 253 | } 254 | ast::Statement::Assignment(assignment_statement) => { 255 | return Ok(ast::Statement::Assignment(self.with_assignment_statement(ctx, assignment_statement)?)); 256 | } 257 | ast::Statement::Expression(expression) => { 258 | return Ok(ast::Statement::Expression(self.with_expression(ctx, expression)?)); 259 | } 260 | } 261 | } 262 | 263 | fn with_return_statement(self: &Self, ctx: &Context, statement: &ast::ReturnStatement) -> Result { 264 | return Ok(ast::ReturnStatement { 265 | source: self.with_expression(ctx, &statement.source)?, 266 | }); 267 | } 268 | 269 | fn with_let_statement(self: &Self, ctx: &Context, statement: &ast::LetStatement) -> Result { 270 | return Ok(ast::LetStatement { 271 | variable_name: statement.variable_name.clone(), 272 | expression: self.with_expression(ctx, &statement.expression)?, 273 | type_: process_type(ctx, &statement.type_)?, 274 | }); 275 | } 276 | 277 | fn with_assignment_statement(self: &Self, ctx: &Context, statement: &ast::AssignmentStatement) -> Result { 278 | return Ok(ast::AssignmentStatement { 279 | source: match &statement.source { 280 | ast::AssignmentTarget::Variable(variable) => ast::AssignmentTarget::Variable(ast::VariableUsage { 281 | type_parameters: variable.type_parameters.clone(), 282 | name: variable.name.clone(), 283 | type_: process_type(ctx, &variable.type_)?, 284 | }), 285 | ast::AssignmentTarget::StructAttr(struct_attr) => ast::AssignmentTarget::StructAttr(ast::StructGetter { 286 | type_parameters: struct_attr.type_parameters.clone(), 287 | source: self.with_expression(ctx, &struct_attr.source)?, 288 | attribute: struct_attr.attribute.clone(), 289 | type_: process_type(ctx, &struct_attr.type_)?, 290 | }), 291 | }, 292 | expression: self.with_expression(ctx, &statement.expression)?, 293 | }); 294 | } 295 | 296 | fn with_expression(self: &Self, ctx: &Context, expression: &ast::Expression) -> Result { 297 | return Ok(ast::Expression { 298 | subexpression: Box::new(match &*expression.subexpression { 299 | ast::Subexpression::LiteralInt(literal_int) => ast::Subexpression::LiteralInt(ast::LiteralInt { 300 | value: literal_int.value.clone(), 301 | type_: process_type(ctx, &literal_int.type_)?, 302 | }), 303 | ast::Subexpression::LiteralFloat(literal_float) => ast::Subexpression::LiteralFloat(ast::LiteralFloat { 304 | value: literal_float.value.clone(), 305 | type_: process_type(ctx, &literal_float.type_)?, 306 | }), 307 | ast::Subexpression::LiteralBool(literal_bool) => ast::Subexpression::LiteralBool(ast::LiteralBool { 308 | value: literal_bool.value.clone(), 309 | type_: process_type(ctx, &literal_bool.type_)?, 310 | }), 311 | ast::Subexpression::LiteralString(literal_string) => ast::Subexpression::LiteralString(ast::LiteralString { 312 | value: literal_string.value.clone(), 313 | type_: process_type(ctx, &literal_string.type_)?, 314 | }), 315 | ast::Subexpression::LiteralStruct(literal_struct) => { 316 | let result = resolve_type( 317 | ctx, 318 | &ast::NamedTypeUsage { 319 | type_parameters: literal_struct.type_parameters.clone(), 320 | name: literal_struct.name.clone(), 321 | }, 322 | )?; 323 | let new_name = match &result { 324 | ast::TypeUsage::Named(named) => named.name.clone(), 325 | _ => panic!("LiteralStruct resolved to non-named-type"), 326 | }; 327 | let mut fields = vec!(); 328 | for field in literal_struct.fields.iter() { 329 | fields.push((field.0.clone(), self.with_expression(ctx, &field.1)?)); 330 | } 331 | ast::Subexpression::LiteralStruct(ast::LiteralStruct { 332 | type_parameters: literal_struct.type_parameters.clone(), 333 | name: new_name.clone(), 334 | fields: fields, 335 | type_: process_type(ctx, &literal_struct.type_)?, 336 | }) 337 | } 338 | ast::Subexpression::FunctionCall(function_call) => { 339 | let mut arguments = vec!(); 340 | for arg in function_call.arguments.iter() { 341 | arguments.push(self.with_expression(ctx, arg)?); 342 | } 343 | ast::Subexpression::FunctionCall(ast::FunctionCall { 344 | source: self.with_expression(ctx, &function_call.source)?, 345 | arguments: arguments, 346 | type_: process_type(ctx, &function_call.type_)?, 347 | }) 348 | }, 349 | ast::Subexpression::VariableUsage(variable_usage) => ast::Subexpression::VariableUsage(ast::VariableUsage { 350 | name: variable_usage.name.clone(), 351 | type_parameters: variable_usage.type_parameters.clone(), 352 | type_: process_type(ctx, &variable_usage.type_)?, 353 | }), 354 | ast::Subexpression::If(if_expression) => ast::Subexpression::If(ast::IfExpression { 355 | condition: self.with_expression(ctx, &if_expression.condition)?, 356 | block: self.with_block(ctx, &if_expression.block)?, 357 | else_: match &if_expression.else_ { 358 | Some(else_) => Some(self.with_block(ctx, else_)?), 359 | None => None, 360 | }, 361 | type_: process_type(ctx, &if_expression.type_)?, 362 | }), 363 | ast::Subexpression::StructGetter(struct_getter) => ast::Subexpression::StructGetter(ast::StructGetter { 364 | type_parameters: struct_getter.type_parameters.clone(), 365 | source: self.with_expression(ctx, &struct_getter.source)?, 366 | attribute: struct_getter.attribute.clone(), 367 | type_: process_type(ctx, &struct_getter.type_)?, 368 | }), 369 | ast::Subexpression::Block(block) => ast::Subexpression::Block(self.with_block(ctx, &block)?), 370 | ast::Subexpression::Op(op) => ast::Subexpression::Op(ast::Operation { 371 | left: self.with_expression(ctx, &op.left)?, 372 | op: op.op.clone(), 373 | right: self.with_expression(ctx, &op.right)?, 374 | }), 375 | }), 376 | type_: process_type(ctx, &expression.type_)?, 377 | }); 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/type_checking.rs: -------------------------------------------------------------------------------- 1 | use crate::ast; 2 | use crate::errors; 3 | use std::collections::HashMap; 4 | use std::rc::Rc; 5 | 6 | pub type SubstitutionMap = HashMap; 7 | 8 | pub type Result = std::result::Result; 9 | 10 | #[derive(Debug, Clone, PartialEq, Eq)] 11 | pub struct TypeConstructor { 12 | generic: ast::Generic, 13 | type_usage: ast::TypeUsage, 14 | } 15 | 16 | fn type_meets_trait_bounds(ctx: &Context, type_: &ast::TypeUsage, bounds: &Vec) -> bool { 17 | for bound in bounds.iter() { 18 | let named = match type_ { 19 | ast::TypeUsage::Named(named) => named, 20 | ast::TypeUsage::Function(_) => { 21 | return false; 22 | }, 23 | ast::TypeUsage::Unknown(_) => { 24 | return true; // unknown this pass, skip, test once known 25 | }, 26 | ast::TypeUsage::Namespace(_) => { 27 | panic!("impossible"); 28 | } 29 | }; 30 | match &ctx.environment[&named.name.name.value] { 31 | NamedEntity::NamedType(named_type) => { 32 | let mut found = false; 33 | for impl_ in named_type.impls.iter() { 34 | if impl_.trait_ == Some(bound.name.value.to_string()) { 35 | found = true; 36 | break; 37 | } 38 | } 39 | if found == false { 40 | return false; 41 | } 42 | }, 43 | _ => { 44 | panic!("type is a variable, this should not happen"); 45 | } 46 | } 47 | } 48 | return true; 49 | } 50 | 51 | fn replace_generic_with_concrete(replace: &ast::Identifier, with_type: &ast::TypeUsage, in_type: &ast::TypeUsage) -> ast::TypeUsage { 52 | match in_type { 53 | ast::TypeUsage::Named(named) => { 54 | let mut result = named.clone(); 55 | if named.name.name.value == replace.name.value { 56 | return with_type.clone(); 57 | } 58 | result.type_parameters = match &named.type_parameters { 59 | ast::GenericUsage::Known(known) => { 60 | let mut param_result = vec!(); 61 | for param in known.parameters.iter() { 62 | param_result.push(replace_generic_with_concrete(replace, with_type, param)); 63 | } 64 | ast::GenericUsage::new(¶m_result) 65 | }, 66 | ast::GenericUsage::Unknown => { 67 | ast::GenericUsage::Unknown 68 | }, 69 | }; 70 | return ast::TypeUsage::Named(result); 71 | }, 72 | ast::TypeUsage::Function(func) => { 73 | let result = ast::TypeUsage::Function(ast::FunctionTypeUsage{ 74 | arguments: func.arguments.iter().map(|arg| { 75 | replace_generic_with_concrete(replace, with_type, arg) 76 | }).collect(), 77 | return_type: Box::new(replace_generic_with_concrete(replace, with_type, &func.return_type)), 78 | }); 79 | return result; 80 | }, 81 | _ => { 82 | // in_type is unknown, skip 83 | return in_type.clone(); 84 | }, 85 | }; 86 | } 87 | 88 | impl TypeConstructor { 89 | fn from_declaration(declaration: &ast::FunctionDeclaration) -> TypeConstructor { 90 | return TypeConstructor{ 91 | generic: declaration.generic.clone(), 92 | type_usage: declaration.to_type(), 93 | } 94 | } 95 | fn construct(&self, ctx: &Context, usage: &ast::GenericUsage) -> Result { 96 | match usage { 97 | ast::GenericUsage::Known(known_usage) => { 98 | let mut result = self.type_usage.clone(); 99 | if known_usage.parameters.len() != self.generic.parameters.len() { 100 | return Err(errors::TypingError::WrongNumberOfTypeParameters{}); 101 | } 102 | // 1. for arg in args, assert arg matches self.generic traits via impl name 103 | // 2. replace type usage names with arg types 104 | for i in 0..known_usage.parameters.len() { 105 | if !type_meets_trait_bounds(ctx, &known_usage.parameters[i], &self.generic.parameters[i].bounds) { 106 | return Err(errors::TypingError::InvalidTypeForGeneric); 107 | } 108 | result = replace_generic_with_concrete(&self.generic.parameters[i].name, &known_usage.parameters[i], &self.type_usage); 109 | } 110 | return Ok(result); 111 | }, 112 | ast::GenericUsage::Unknown => { 113 | // generate new Unknown Types for matching 114 | let mut result = self.type_usage.clone(); 115 | for param in self.generic.parameters.iter() { 116 | let id = ctx.id_generator.next(); 117 | let unknown = ast::TypeUsage::Unknown(ast::UnknownTypeUsage{name: id}); 118 | result = replace_generic_with_concrete(¶m.name, &unknown, &self.type_usage); 119 | } 120 | return Ok(result); 121 | } 122 | } 123 | } 124 | } 125 | 126 | #[derive(Debug, Clone, PartialEq, Eq)] 127 | pub struct EnvImpl { 128 | trait_: Option, 129 | functions: HashMap, 130 | } 131 | 132 | #[derive(Debug, Clone, PartialEq, Eq)] 133 | pub enum TypeType { 134 | Scalar, 135 | Trait, 136 | Struct, 137 | } 138 | 139 | #[derive(Debug, Clone, PartialEq, Eq)] 140 | pub struct EnvType { 141 | generic: ast::Generic, 142 | is_a: TypeType, 143 | fields: HashMap, 144 | impls: Vec, 145 | } 146 | 147 | impl EnvType { 148 | fn from_struct(struct_: &ast::StructTypeDeclaration) -> EnvType { 149 | return EnvType { 150 | generic: struct_.generic.clone(), 151 | is_a: TypeType::Struct, 152 | fields: struct_ 153 | .fields 154 | .iter() 155 | .map(|field| (field.name.name.value.to_string(), field.type_.clone())) 156 | .collect(), 157 | impls: vec![], 158 | }; 159 | } 160 | 161 | fn from_trait(trait_: &ast::TraitTypeDeclaration) -> EnvType { 162 | let mut functions = HashMap::new(); 163 | for func in trait_.functions.iter() { 164 | match func { 165 | ast::TraitItem::FunctionDeclaration(fd) => { 166 | functions.insert(fd.name.name.value.to_string(), TypeConstructor::from_declaration(&fd)); 167 | } 168 | ast::TraitItem::Function(f) => { 169 | functions.insert(f.declaration.name.name.value.to_string(), TypeConstructor::from_declaration(&f.declaration)); 170 | } 171 | } 172 | } 173 | let impl_ = EnvImpl { 174 | trait_: Some(trait_.name.name.value.to_string()), 175 | functions: functions, 176 | }; 177 | return EnvType { 178 | generic: trait_.generic.clone(), 179 | is_a: TypeType::Trait, 180 | fields: HashMap::new(), 181 | impls: vec![impl_], 182 | }; 183 | } 184 | 185 | fn type_construct(&self, ctx: &Context, usage: &ast::GenericUsage) -> Result { 186 | // steps 187 | // 1. Check if matches bounds, create unknowns if necessary. 188 | // 2. Replace all (Named+generics or function args/return) recursively. 189 | // 3. Return updated, plus known generic usage to replace any unknown usage. 190 | let known_usage = match usage { 191 | ast::GenericUsage::Known(known) => known.clone(), 192 | ast::GenericUsage::Unknown => { 193 | let mut new_unknowns = vec!(); 194 | for _ in 0..self.generic.parameters.len() { 195 | new_unknowns.push(ast::TypeUsage::new_unknown(&ctx.id_generator)); 196 | } 197 | ast::GenericInstantiation { 198 | parameters: new_unknowns.iter().map(|tp| tp.clone()).collect(), 199 | } 200 | }, 201 | }; 202 | if known_usage.parameters.len() != self.generic.parameters.len() { 203 | return Err(errors::TypingError::WrongNumberOfTypeParameters{}); 204 | } 205 | for i in 0..known_usage.parameters.len() { 206 | if !type_meets_trait_bounds(ctx, &known_usage.parameters[i], &self.generic.parameters[i].bounds) { 207 | return Err(errors::TypingError::InvalidTypeForGeneric); 208 | } 209 | } 210 | // Generic type matches, time to replace 211 | let mut result = self.clone(); 212 | for i in 0..known_usage.parameters.len() { 213 | let mut fields = HashMap::new(); 214 | for (k, v) in result.fields.iter() { 215 | let type_usage = replace_generic_with_concrete(&self.generic.parameters[i].name, &known_usage.parameters[i], &v); 216 | fields.insert(k.clone(), type_usage); 217 | } 218 | let mut impls = vec!(); 219 | for impl_ in result.impls.iter() { 220 | let mut functions = HashMap::new(); 221 | for (name, func) in impl_.functions.iter() { 222 | let type_usage = replace_generic_with_concrete(&self.generic.parameters[i].name, &known_usage.parameters[i], &func.type_usage); 223 | 224 | functions.insert(name.clone(), TypeConstructor{generic: func.generic.clone(), type_usage: type_usage}); 225 | } 226 | impls.push(EnvImpl{ 227 | trait_: impl_.trait_.clone(), 228 | functions: functions, 229 | }); 230 | } 231 | result = EnvType{ 232 | generic: ast::Generic{parameters: vec!()}, 233 | is_a: self.is_a.clone(), 234 | fields: fields, 235 | impls: impls, 236 | }; 237 | } 238 | return Ok(result); 239 | } 240 | } 241 | 242 | #[derive(Debug, Clone, PartialEq, Eq)] 243 | pub enum NamedEntity { 244 | NamedType(EnvType), 245 | Function(TypeConstructor), 246 | Variable(ast::TypeUsage), 247 | } 248 | 249 | fn create_builtins() -> HashMap { 250 | let mut result = HashMap::new(); 251 | result.insert( 252 | "i8".to_string(), 253 | NamedEntity::NamedType(EnvType { 254 | generic: ast::Generic{parameters: vec!()}, 255 | is_a: TypeType::Scalar, 256 | fields: HashMap::new(), 257 | impls: vec![], 258 | }), 259 | ); 260 | result.insert( 261 | "i16".to_string(), 262 | NamedEntity::NamedType(EnvType { 263 | generic: ast::Generic{parameters: vec!()}, 264 | is_a: TypeType::Scalar, 265 | fields: HashMap::new(), 266 | impls: vec![], 267 | }), 268 | ); 269 | result.insert( 270 | "i32".to_string(), 271 | NamedEntity::NamedType(EnvType { 272 | generic: ast::Generic{parameters: vec!()}, 273 | is_a: TypeType::Scalar, 274 | fields: HashMap::new(), 275 | impls: vec![], 276 | }), 277 | ); 278 | result.insert( 279 | "i64".to_string(), 280 | NamedEntity::NamedType(EnvType { 281 | generic: ast::Generic{parameters: vec!()}, 282 | is_a: TypeType::Scalar, 283 | fields: HashMap::new(), 284 | impls: vec![], 285 | }), 286 | ); 287 | result.insert( 288 | "isize".to_string(), 289 | NamedEntity::NamedType(EnvType { 290 | generic: ast::Generic{parameters: vec!()}, 291 | is_a: TypeType::Scalar, 292 | fields: HashMap::new(), 293 | impls: vec![], 294 | }), 295 | ); 296 | 297 | result.insert( 298 | "u8".to_string(), 299 | NamedEntity::NamedType(EnvType { 300 | generic: ast::Generic{parameters: vec!()}, 301 | is_a: TypeType::Scalar, 302 | fields: HashMap::new(), 303 | impls: vec![], 304 | }), 305 | ); 306 | result.insert( 307 | "u16".to_string(), 308 | NamedEntity::NamedType(EnvType { 309 | generic: ast::Generic{parameters: vec!()}, 310 | is_a: TypeType::Scalar, 311 | fields: HashMap::new(), 312 | impls: vec![], 313 | }), 314 | ); 315 | result.insert( 316 | "u32".to_string(), 317 | NamedEntity::NamedType(EnvType { 318 | generic: ast::Generic{parameters: vec!()}, 319 | is_a: TypeType::Scalar, 320 | fields: HashMap::new(), 321 | impls: vec![], 322 | }), 323 | ); 324 | result.insert( 325 | "u64".to_string(), 326 | NamedEntity::NamedType(EnvType { 327 | generic: ast::Generic{parameters: vec!()}, 328 | is_a: TypeType::Scalar, 329 | fields: HashMap::new(), 330 | impls: vec![], 331 | }), 332 | ); 333 | result.insert( 334 | "usize".to_string(), 335 | NamedEntity::NamedType(EnvType { 336 | generic: ast::Generic{parameters: vec!()}, 337 | is_a: TypeType::Scalar, 338 | fields: HashMap::new(), 339 | impls: vec![], 340 | }), 341 | ); 342 | 343 | result.insert( 344 | "f32".to_string(), 345 | NamedEntity::NamedType(EnvType { 346 | generic: ast::Generic{parameters: vec!()}, 347 | is_a: TypeType::Scalar, 348 | fields: HashMap::new(), 349 | impls: vec![], 350 | }), 351 | ); 352 | result.insert( 353 | "f64".to_string(), 354 | NamedEntity::NamedType(EnvType { 355 | generic: ast::Generic{parameters: vec!()}, 356 | is_a: TypeType::Scalar, 357 | fields: HashMap::new(), 358 | impls: vec![], 359 | }), 360 | ); 361 | 362 | result.insert( 363 | "bool".to_string(), 364 | NamedEntity::NamedType(EnvType { 365 | generic: ast::Generic{parameters: vec!()}, 366 | is_a: TypeType::Scalar, 367 | fields: HashMap::new(), 368 | impls: vec![], 369 | }), 370 | ); 371 | result.insert( 372 | "!".to_string(), 373 | NamedEntity::NamedType(EnvType { 374 | generic: ast::Generic{parameters: vec!()}, 375 | is_a: TypeType::Scalar, 376 | fields: HashMap::new(), 377 | impls: vec![], 378 | }), 379 | ); 380 | result.insert( 381 | "unit".to_string(), 382 | NamedEntity::NamedType(EnvType { 383 | generic: ast::Generic{parameters: vec!()}, 384 | is_a: TypeType::Scalar, 385 | fields: HashMap::new(), 386 | impls: vec![], 387 | }), 388 | ); 389 | result.insert( 390 | "String".to_string(), 391 | NamedEntity::NamedType(EnvType { 392 | generic: ast::Generic{parameters: vec!()}, 393 | is_a: TypeType::Scalar, 394 | fields: HashMap::new(), 395 | impls: vec![], 396 | }), 397 | ); 398 | return result; 399 | } 400 | 401 | enum StructAttr { 402 | Field(ast::TypeUsage), 403 | Method(TypeConstructor), 404 | } 405 | 406 | fn apply_self(type_name: &str, constructor: &TypeConstructor) -> TypeConstructor { 407 | match &constructor.type_usage { 408 | ast::TypeUsage::Function(func) => { 409 | if func.arguments.len() > 0 { 410 | match &func.arguments[0] { 411 | ast::TypeUsage::Named(named) => { 412 | if type_name == named.name.name.value { 413 | let result = ast::TypeUsage::Function(ast::FunctionTypeUsage { 414 | arguments: func.arguments[1..func.arguments.len()].iter().map(|arg| arg.clone()).collect(), 415 | return_type: func.return_type.clone(), 416 | }); 417 | return TypeConstructor{ 418 | generic: constructor.generic.clone(), 419 | type_usage: result, 420 | } 421 | } 422 | } 423 | _ => {} 424 | } 425 | } 426 | } 427 | _ => {} 428 | } 429 | return constructor.clone(); 430 | } 431 | 432 | fn get_attr(ctx: &Context, get_from: &NamedEntity, attribute: &ast::Identifier) -> Result { 433 | match get_from { 434 | NamedEntity::NamedType(env_type) => { 435 | if env_type.fields.contains_key(&attribute.name.value) { 436 | return Ok(StructAttr::Field(env_type.fields[&attribute.name.value].clone())); 437 | } 438 | let mut result = Vec::new(); 439 | for impl_ in env_type.impls.iter() { 440 | if impl_.functions.contains_key(&attribute.name.value) { 441 | result.push(impl_.functions[&attribute.name.value].clone()) 442 | } 443 | } 444 | if result.len() == 0 { 445 | return Err(errors::TypingError::UnknownFieldName { 446 | identifier: attribute.clone(), 447 | }); 448 | } 449 | if result.len() > 1 { 450 | return Err(errors::TypingError::MultipleFieldName { 451 | identifier: attribute.clone(), 452 | }); 453 | } 454 | return Ok(StructAttr::Method(result[0].clone())); 455 | } 456 | NamedEntity::Variable(type_) => match type_ { 457 | ast::TypeUsage::Named(named) => { 458 | let env_type = match &ctx.environment[&named.name.name.value] { 459 | NamedEntity::NamedType(env_type) => env_type, 460 | _ => panic!("variable has non-type as type"), 461 | }; 462 | let type_ = env_type.type_construct(ctx, &named.type_parameters)?; 463 | 464 | let attr = get_attr(ctx, &NamedEntity::NamedType(type_), attribute)?; 465 | let method = match attr { 466 | StructAttr::Field(field) => return Ok(StructAttr::Field(field)), 467 | StructAttr::Method(method) => method, 468 | }; 469 | return Ok(StructAttr::Method(apply_self(&named.name.name.value, &method))); 470 | } 471 | _ => { 472 | return Err(errors::TypingError::AttributeOfNonstruct { 473 | identifier: attribute.clone(), 474 | }); 475 | } 476 | }, 477 | NamedEntity::Function(_) => { 478 | return Err(errors::TypingError::AttributeOfNonstruct { 479 | identifier: attribute.clone(), 480 | }); 481 | } 482 | } 483 | } 484 | 485 | #[derive(Debug, Clone, PartialEq, Eq)] 486 | struct Context { 487 | pub current_function_return: Option, 488 | pub environment: HashMap, 489 | pub id_generator: Rc, 490 | } 491 | 492 | impl Context { 493 | fn add_variable(&self, name: String, type_usage: &ast::TypeUsage) -> Context { 494 | let mut ctx = self.clone(); 495 | ctx.environment.insert(name.to_string(), NamedEntity::Variable(type_usage.clone())); 496 | return ctx; 497 | } 498 | 499 | fn set_current_function_return(&self, function: &ast::TypeUsage) -> Context { 500 | let mut ctx = self.clone(); 501 | ctx.current_function_return = Some(function.clone()); 502 | return ctx; 503 | } 504 | 505 | fn add_generic(&self, generic: &ast::Generic) -> Result { 506 | let mut ctx = self.clone(); 507 | for parameter in generic.parameters.iter() { 508 | let mut env_type = EnvType{ 509 | generic: ast::Generic{ 510 | parameters: vec!(), 511 | }, 512 | is_a: TypeType::Trait, 513 | fields: HashMap::new(), 514 | impls: vec!(), 515 | }; 516 | for bound in parameter.bounds.iter() { 517 | if !self.environment.contains_key(&bound.name.value) { 518 | return Err(errors::TypingError::TypeDoesNotExist { 519 | identifier: bound.clone(), 520 | }); 521 | } 522 | match &self.environment[&bound.name.value] { 523 | NamedEntity::NamedType(named_type) => { 524 | env_type.impls.push(named_type.impls[0].clone()); 525 | }, 526 | _ => {} 527 | } 528 | }; 529 | ctx.environment.insert(parameter.name.name.value.clone(), NamedEntity::NamedType(env_type)); 530 | } 531 | return Ok(ctx); 532 | } 533 | 534 | fn add_impl(&self, impl_: &ast::Impl, traits: &HashMap) -> Result { 535 | let mut functions = HashMap::new(); 536 | for func in impl_.functions.iter() { 537 | functions.insert(func.declaration.name.name.value.to_string(), TypeConstructor::from_declaration(&func.declaration)); 538 | } 539 | // fill out defaults 540 | match &impl_.trait_ { 541 | Some(trait_) => { 542 | if !traits.contains_key(&trait_.name.name.value) { 543 | return Err(errors::TypingError::TypeDoesNotExist { 544 | identifier: trait_.name.clone(), 545 | }); 546 | } 547 | for func in traits[&trait_.name.name.value].functions.iter() { 548 | match func { 549 | ast::TraitItem::Function(default_function) => { 550 | if !functions.contains_key(&default_function.declaration.name.name.value) { 551 | functions.insert( 552 | default_function.declaration.name.name.value.to_string(), 553 | TypeConstructor::from_declaration(&default_function.declaration), 554 | ); 555 | } 556 | } 557 | _ => {} 558 | } 559 | } 560 | } 561 | None => {} 562 | } 563 | let mut result = self.clone(); 564 | let mut env_named = result.environment[&impl_.struct_.name.name.value].clone(); 565 | match &mut env_named { 566 | NamedEntity::NamedType(env_type) => { 567 | env_type.impls.push(EnvImpl { 568 | trait_: match &impl_.trait_ { 569 | Some(trait_) => Some(trait_.name.name.value.to_string()), 570 | None => None, 571 | }, 572 | functions: functions, 573 | }); 574 | result 575 | .environment 576 | .insert(impl_.struct_.name.name.value.to_string(), NamedEntity::NamedType(env_type.clone())); 577 | } 578 | NamedEntity::Variable(_) => { 579 | return Err(errors::TypingError::IdentifierIsNotType { 580 | identifier: impl_.struct_.name.clone(), 581 | }); 582 | } 583 | NamedEntity::Function(_) => { 584 | return Err(errors::TypingError::IdentifierIsNotType { 585 | identifier: impl_.struct_.name.clone(), 586 | }); 587 | } 588 | } 589 | return Ok(result); 590 | } 591 | } 592 | 593 | fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> { 594 | let result = match type_ { 595 | ast::TypeUsage::Named(named) => { 596 | if !ctx.environment.contains_key(&named.name.name.value) { 597 | return Err(errors::TypingError::TypeDoesNotExist { 598 | identifier: named.name.clone(), 599 | }); 600 | } 601 | match ctx.environment[&named.name.name.value] { 602 | NamedEntity::NamedType(_) => { 603 | // is a type 604 | } 605 | _ => { 606 | return Err(errors::TypingError::IdentifierIsNotType { 607 | identifier: named.name.clone(), 608 | }); 609 | } 610 | } 611 | } 612 | ast::TypeUsage::Unknown(_) => {} // do nothing 613 | ast::TypeUsage::Namespace(_) => {} 614 | ast::TypeUsage::Function(function) => { 615 | let mut errs = vec![]; 616 | for arg in function.arguments.iter() { 617 | match type_exists(ctx, arg) { 618 | Ok(_) => {} 619 | Err(err) => errs.push(err), 620 | }; 621 | } 622 | match type_exists(ctx, &function.return_type) { 623 | Ok(_) => {} 624 | Err(err) => errs.push(err), 625 | } 626 | if errs.len() > 0 { 627 | return Err(errors::TypingError::MultipleErrors { errors: errs }); 628 | } 629 | } 630 | }; 631 | return Ok(result); 632 | } 633 | 634 | fn apply_substitution(ctx: &Context, substitution: &SubstitutionMap, type_: &ast::TypeUsage) -> Result { 635 | let result = match type_ { 636 | ast::TypeUsage::Named(named) => ast::TypeUsage::Named(named.clone()), 637 | ast::TypeUsage::Unknown(unknown) => { 638 | if substitution.contains_key(&unknown.name) { 639 | substitution[&unknown.name].clone() 640 | } else { 641 | ast::TypeUsage::Unknown(unknown.clone()) 642 | } 643 | } 644 | ast::TypeUsage::Namespace(namespace) => { 645 | ast::TypeUsage::Namespace(namespace.clone()) 646 | } 647 | ast::TypeUsage::Function(function) => { 648 | let mut arguments = vec![]; 649 | for arg in function.arguments.iter() { 650 | arguments.push(apply_substitution(ctx, substitution, arg)?); 651 | } 652 | ast::TypeUsage::Function(ast::FunctionTypeUsage { 653 | arguments: arguments, 654 | return_type: Box::new(apply_substitution(ctx, substitution, &function.return_type)?), 655 | }) 656 | } 657 | }; 658 | type_exists(ctx, &result)?; 659 | return Ok(result); 660 | } 661 | 662 | fn compose_substitutions(ctx: &Context, s1: &SubstitutionMap, s2: &SubstitutionMap) -> Result { 663 | let mut result = SubstitutionMap::new(); 664 | for k in s2.keys() { 665 | result.insert(k.to_string(), apply_substitution(ctx, s1, &s2[k])?); 666 | } 667 | return Ok(s1.into_iter().map(|(k, v)| (k.clone(), v.clone())).chain(result).collect()); 668 | } 669 | 670 | fn unify(ctx: &Context, t1: &ast::TypeUsage, t2: &ast::TypeUsage) -> Result { 671 | match (t1, t2) { 672 | (ast::TypeUsage::Named(named1), ast::TypeUsage::Named(named2)) => { 673 | if named1.name.name.value == named2.name.name.value { 674 | let mut result = SubstitutionMap::new(); 675 | match (&named1.type_parameters, &named2.type_parameters) { 676 | (ast::GenericUsage::Known(known1), ast::GenericUsage::Known(known2)) => { 677 | if known1.parameters.len() != known2.parameters.len() { 678 | return Err(errors::TypingError::TypeMismatch { 679 | type_one: t1.clone(), 680 | type_two: t2.clone(), 681 | }); 682 | } 683 | for (i, _) in known1.parameters.iter().enumerate() { 684 | result = compose_substitutions( 685 | ctx, 686 | &result, 687 | &unify( 688 | ctx, 689 | &apply_substitution(ctx, &result, &known1.parameters[i])?, 690 | &apply_substitution(ctx, &result, &known2.parameters[i])?, 691 | )?, 692 | )?; 693 | } 694 | }, 695 | _ => { 696 | panic!("should never be unknown") 697 | }, 698 | } 699 | return Ok(result); 700 | } 701 | } 702 | _ => {} 703 | } 704 | match t1 { 705 | ast::TypeUsage::Unknown(unknown) => { 706 | return Ok(var_bind(&unknown.name, t2)); 707 | } 708 | _ => {} 709 | } 710 | match t2 { 711 | ast::TypeUsage::Unknown(unknown) => { 712 | return Ok(var_bind(&unknown.name, t1)); 713 | } 714 | _ => {} 715 | } 716 | match (t1, t2) { 717 | (ast::TypeUsage::Function(f1), ast::TypeUsage::Function(f2)) => { 718 | let mut result = unify(ctx, &*f1.return_type, &*f2.return_type)?; 719 | if f1.arguments.len() != f2.arguments.len() { 720 | panic!("Argument lengths don't match"); 721 | } 722 | for (i, _) in f1.arguments.iter().enumerate() { 723 | result = compose_substitutions( 724 | ctx, 725 | &result, 726 | &unify( 727 | ctx, 728 | &apply_substitution(ctx, &result, &f1.arguments[i])?, 729 | &apply_substitution(ctx, &result, &f2.arguments[i])?, 730 | )?, 731 | )?; 732 | } 733 | return Ok(result); 734 | } 735 | _ => {} 736 | } 737 | return Err(errors::TypingError::TypeMismatch { 738 | type_one: t1.clone(), 739 | type_two: t2.clone(), 740 | }); 741 | } 742 | 743 | fn var_bind(name: &str, t: &ast::TypeUsage) -> SubstitutionMap { 744 | match t { 745 | ast::TypeUsage::Unknown(unknown) => { 746 | if name == unknown.name { 747 | return SubstitutionMap::new(); 748 | } 749 | } 750 | _ => {} 751 | } 752 | if contains(t, name) { 753 | panic!("Type contains a reference to itself") 754 | } 755 | let mut substitution = SubstitutionMap::new(); 756 | substitution.insert(name.to_string(), t.clone()); 757 | return substitution; 758 | } 759 | 760 | fn contains(t: &ast::TypeUsage, name: &str) -> bool { 761 | match t { 762 | ast::TypeUsage::Named(_) => return false, 763 | ast::TypeUsage::Unknown(unknown) => unknown.name == name, 764 | ast::TypeUsage::Namespace(_) => return false, 765 | ast::TypeUsage::Function(f) => { 766 | if contains(&*f.return_type, name) { 767 | return true; 768 | } 769 | for arg in f.arguments.iter() { 770 | if contains(&arg.clone(), name) { 771 | return true; 772 | } 773 | } 774 | return false; 775 | } 776 | } 777 | } 778 | 779 | pub struct TypeChecker {} 780 | 781 | impl TypeChecker { 782 | pub fn with_module(self: &Self, module: &ast::Module) -> Result<(ast::Module, SubstitutionMap)> { 783 | let mut ctx = Context { 784 | environment: create_builtins(), 785 | current_function_return: None, 786 | id_generator: Rc::new(ast::IdGenerator::new("T")), 787 | }; 788 | 789 | let mut traits = HashMap::new(); 790 | 791 | for item in module.items.iter() { 792 | match item { 793 | ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => { 794 | ctx.environment.insert( 795 | struct_.name.name.value.to_string(), 796 | NamedEntity::NamedType(EnvType::from_struct(&struct_)), 797 | ); 798 | } 799 | ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Trait(trait_)) => { 800 | traits.insert(trait_.name.name.value.to_string(), trait_.clone()); 801 | ctx.environment.insert( 802 | trait_.name.name.value.to_string(), 803 | NamedEntity::NamedType(EnvType::from_trait(&trait_)), 804 | ); 805 | } 806 | ast::ModuleItem::Function(function) => { 807 | let function_type = ast::FunctionTypeUsage { 808 | arguments: function.declaration.arguments.iter().map(|arg| arg.type_.clone()).collect(), 809 | return_type: Box::new(function.declaration.return_type.clone()), 810 | }; 811 | ctx.environment.insert( 812 | function.declaration.name.name.value.to_string(), 813 | NamedEntity::Function(TypeConstructor{ 814 | generic: function.declaration.generic.clone(), 815 | type_usage: ast::TypeUsage::Function(function_type), 816 | }), 817 | ); 818 | } 819 | _ => {} 820 | } 821 | } 822 | 823 | for item in module.items.iter() { 824 | match item { 825 | ast::ModuleItem::Impl(impl_) => { 826 | if !ctx.environment.contains_key(&impl_.struct_.name.name.value) { 827 | return Err(errors::TypingError::IdentifierIsNotType { 828 | identifier: impl_.struct_.name.clone(), 829 | }); 830 | } 831 | ctx = ctx.add_impl(&impl_, &traits)?; 832 | } 833 | _ => {} 834 | } 835 | } 836 | 837 | let mut subst = SubstitutionMap::new(); 838 | let mut items = vec![]; 839 | for item in module.items.iter() { 840 | items.push(match item { 841 | ast::ModuleItem::Function(function) => { 842 | let (func, fn_subst) = self.with_function(&ctx, &subst, function)?; 843 | subst = compose_substitutions(&ctx, &subst, &fn_subst)?; 844 | ast::ModuleItem::Function(func) 845 | } 846 | ast::ModuleItem::TypeDeclaration(type_declaration) => { 847 | let (ty_decl, ty_subst) = self.with_type_declaration(&ctx, &subst, type_declaration)?; 848 | subst = compose_substitutions(&ctx, &subst, &ty_subst)?; 849 | ast::ModuleItem::TypeDeclaration(ty_decl) 850 | } 851 | ast::ModuleItem::Impl(impl_) => { 852 | let (impl_result, impl_subst) = self.with_impl(&ctx, &subst, impl_)?; 853 | // TODO: errors on generics not exist at global scope 854 | // subst = compose_substitutions(&ctx, &subst, &impl_subst)?; 855 | ast::ModuleItem::Impl(impl_result) 856 | } 857 | }); 858 | } 859 | let result = ast::Module { items: items }; 860 | return Ok((result, subst)); 861 | } 862 | 863 | fn with_function_declaration(self: &Self, ctx: &Context, declaration: &ast::FunctionDeclaration) -> Result { 864 | for arg in declaration.arguments.iter() { 865 | type_exists(ctx, &arg.type_)?; 866 | } 867 | type_exists(ctx, &declaration.return_type)?; 868 | return Ok(declaration.clone()); 869 | } 870 | 871 | fn with_function( 872 | self: &Self, 873 | ctx: &Context, 874 | incoming_substitutions: &SubstitutionMap, 875 | function: &ast::Function, 876 | ) -> Result<(ast::Function, SubstitutionMap)> { 877 | let gen_ctx = ctx.add_generic(&function.declaration.generic)?; 878 | let declaration = self.with_function_declaration(&gen_ctx, &function.declaration)?; 879 | // add args to env 880 | let mut function_ctx = gen_ctx.set_current_function_return(&declaration.return_type.clone()); 881 | for arg in declaration.arguments.iter() { 882 | function_ctx = function_ctx.add_variable(arg.name.name.value.to_string(), &arg.type_.clone()); 883 | } 884 | 885 | let (block, substitution) = self.with_block(&function_ctx, incoming_substitutions, &function.block)?; 886 | let mut substitution = compose_substitutions(&function_ctx, incoming_substitutions, &substitution)?; 887 | match &block.type_ { 888 | ast::TypeUsage::Named(named) => { 889 | if named.name.name.value != "!" { 890 | substitution = compose_substitutions( 891 | &function_ctx, 892 | &substitution, 893 | &unify(&function_ctx, &declaration.return_type, &block.type_)?, 894 | )?; 895 | } 896 | } 897 | _ => { 898 | substitution = compose_substitutions( 899 | &function_ctx, 900 | &substitution, 901 | &unify(&function_ctx, &declaration.return_type, &block.type_)?, 902 | )?; 903 | } 904 | } 905 | 906 | return Ok(( 907 | ast::Function { 908 | declaration: ast::FunctionDeclaration { 909 | generic: declaration.generic.clone(), 910 | name: declaration.name.clone(), 911 | arguments: declaration.arguments.iter().map(|arg| arg.clone()).collect(), 912 | return_type: declaration.return_type.clone(), 913 | }, 914 | block: block, 915 | }, 916 | substitution, 917 | )); 918 | } 919 | 920 | fn with_type_declaration( 921 | self: &Self, 922 | ctx: &Context, 923 | incoming_substitutions: &SubstitutionMap, 924 | type_declaration: &ast::TypeDeclaration, 925 | ) -> Result<(ast::TypeDeclaration, SubstitutionMap)> { 926 | match type_declaration { 927 | ast::TypeDeclaration::Struct(struct_) => { 928 | let result = self.with_struct_declaration(ctx, struct_)?; 929 | return Ok((ast::TypeDeclaration::Struct(result), SubstitutionMap::new())); 930 | } 931 | ast::TypeDeclaration::Primitive(primitive) => { 932 | return Ok((ast::TypeDeclaration::Primitive(primitive.clone()), SubstitutionMap::new())); 933 | } 934 | ast::TypeDeclaration::Alias(alias) => { 935 | return Ok((ast::TypeDeclaration::Alias(alias.clone()), SubstitutionMap::new())); 936 | } 937 | ast::TypeDeclaration::Trait(trait_) => { 938 | let (result, subst) = self.with_trait_declaration(ctx, incoming_substitutions, trait_)?; 939 | return Ok((ast::TypeDeclaration::Trait(result), subst)); 940 | } 941 | } 942 | } 943 | 944 | fn with_trait_declaration( 945 | self: &Self, 946 | ctx: &Context, 947 | incoming_substitutions: &SubstitutionMap, 948 | trait_: &ast::TraitTypeDeclaration, 949 | ) -> Result<(ast::TraitTypeDeclaration, SubstitutionMap)> { 950 | let gen_ctx = ctx.add_generic(&trait_.generic)?; 951 | let mut substitutions = incoming_substitutions.clone(); 952 | let mut result_functions = vec![]; 953 | for item in &trait_.functions { 954 | match item { 955 | ast::TraitItem::FunctionDeclaration(declaration) => { 956 | let result_declaration = self.with_function_declaration(&gen_ctx, declaration)?; 957 | result_functions.push(ast::TraitItem::FunctionDeclaration(result_declaration)); 958 | } 959 | ast::TraitItem::Function(function) => { 960 | let (function_result, susbt) = self.with_function(&gen_ctx, incoming_substitutions, function)?; 961 | substitutions = compose_substitutions(ctx, &substitutions, &susbt)?; 962 | result_functions.push(ast::TraitItem::Function(function_result)); 963 | } 964 | } 965 | } 966 | Ok(( 967 | ast::TraitTypeDeclaration { 968 | generic: trait_.generic.clone(), 969 | name: trait_.name.clone(), 970 | functions: result_functions, 971 | }, 972 | substitutions, 973 | )) 974 | } 975 | 976 | fn with_struct_declaration(self: &Self, ctx: &Context, struct_: &ast::StructTypeDeclaration) -> Result { 977 | let struct_ctx = ctx.add_generic(&struct_.generic)?; 978 | let mut fields = vec![]; 979 | for field in struct_.fields.iter() { 980 | type_exists(&struct_ctx, &field.type_)?; 981 | fields.push(ast::StructField { 982 | name: field.name.clone(), 983 | type_: field.type_.clone(), 984 | }); 985 | } 986 | return Ok(ast::StructTypeDeclaration { 987 | generic: struct_.generic.clone(), 988 | name: struct_.name.clone(), 989 | fields: fields, 990 | }); 991 | } 992 | 993 | fn with_impl( 994 | self: &Self, 995 | ctx: &Context, 996 | incoming_substitutions: &SubstitutionMap, 997 | impl_: &ast::Impl, 998 | ) -> Result<(ast::Impl, SubstitutionMap)> { 999 | let impl_ctx = ctx.add_generic(&impl_.generic)?; 1000 | 1001 | let mut substitutions = incoming_substitutions.clone(); 1002 | type_exists( 1003 | &impl_ctx, 1004 | &ast::TypeUsage::new_named(&impl_.struct_.name.clone(), &ast::GenericUsage::Unknown), 1005 | )?; 1006 | let mut functions = vec![]; 1007 | for function in impl_.functions.iter() { 1008 | let (result, function_subs) = self.with_function(&impl_ctx, &substitutions, function)?; 1009 | substitutions = compose_substitutions(&impl_ctx, &substitutions, &function_subs)?; 1010 | functions.push(result); 1011 | } 1012 | return Ok(( 1013 | ast::Impl { 1014 | generic: impl_.generic.clone(), 1015 | trait_: impl_.trait_.clone(), 1016 | struct_: impl_.struct_.clone(), 1017 | functions: functions, 1018 | }, 1019 | substitutions, 1020 | )); 1021 | } 1022 | 1023 | fn with_block( 1024 | self: &Self, 1025 | ctx: &Context, 1026 | incoming_substitutions: &SubstitutionMap, 1027 | block: &ast::Block, 1028 | ) -> Result<(ast::Block, SubstitutionMap)> { 1029 | let mut substitutions = incoming_substitutions.clone(); 1030 | let mut block_ctx = ctx.clone(); 1031 | // if return it's always never 1032 | // if last is expression it's that else unit 1033 | let mut has_return = false; 1034 | for statement in block.statements.iter() { 1035 | match statement { 1036 | ast::Statement::Return(_) => { 1037 | has_return = true; 1038 | } 1039 | _ => {} 1040 | } 1041 | } 1042 | let mut statements = vec![]; 1043 | for s in block.statements.iter() { 1044 | let (statement_ctx, result, statement_substitutions) = self.with_statement(&block_ctx, &substitutions, s)?; 1045 | block_ctx = statement_ctx; 1046 | substitutions = compose_substitutions(&block_ctx, &substitutions, &statement_substitutions)?; 1047 | statements.push(result); 1048 | } 1049 | if !has_return { 1050 | match block.statements.last().unwrap() { 1051 | ast::Statement::Expression(expr) => { 1052 | substitutions = compose_substitutions(&block_ctx, &substitutions, &unify(&block_ctx, &block.type_, &expr.type_)?)?; 1053 | } 1054 | _ => { 1055 | substitutions = compose_substitutions(&block_ctx, &substitutions, &unify(&block_ctx, &block.type_, &ast::new_unit())?)?; 1056 | } 1057 | } 1058 | } 1059 | let result_type = if has_return { 1060 | ast::new_never() 1061 | } else { 1062 | apply_substitution(&block_ctx, &substitutions, &block.type_)? 1063 | }; 1064 | let block_result = ast::Block { 1065 | statements: statements, 1066 | type_: result_type, 1067 | }; 1068 | return Ok((block_result, substitutions)); 1069 | } 1070 | 1071 | fn with_statement( 1072 | self: &Self, 1073 | ctx: &Context, 1074 | incoming_substitutions: &SubstitutionMap, 1075 | statement: &ast::Statement, 1076 | ) -> Result<(Context, ast::Statement, SubstitutionMap)> { 1077 | match statement { 1078 | ast::Statement::Return(return_statement) => { 1079 | let (result, subst) = self.with_return_statement(ctx, incoming_substitutions, return_statement)?; 1080 | let subst = compose_substitutions(ctx, &incoming_substitutions, &subst)?; 1081 | return Ok((ctx.clone(), ast::Statement::Return(result), subst)); 1082 | } 1083 | ast::Statement::Let(let_statement) => { 1084 | let (let_ctx, result, subst) = self.with_let_statement(ctx, incoming_substitutions, let_statement)?; 1085 | let subst = compose_substitutions(ctx, &incoming_substitutions, &subst)?; 1086 | return Ok((let_ctx, ast::Statement::Let(result), subst)); 1087 | } 1088 | ast::Statement::Assignment(assignment_statement) => { 1089 | let (result, subst) = self.with_assignment_statement(ctx, incoming_substitutions, assignment_statement)?; 1090 | let subst = compose_substitutions(ctx, &incoming_substitutions, &subst)?; 1091 | return Ok((ctx.clone(), ast::Statement::Assignment(result), subst)); 1092 | } 1093 | ast::Statement::Expression(expression) => { 1094 | let (result, subst) = self.with_expression(ctx, incoming_substitutions, expression)?; 1095 | let subst = compose_substitutions(ctx, &incoming_substitutions, &subst)?; 1096 | return Ok((ctx.clone(), ast::Statement::Expression(result), subst)); 1097 | } 1098 | } 1099 | } 1100 | 1101 | fn with_return_statement( 1102 | self: &Self, 1103 | ctx: &Context, 1104 | incoming_substitutions: &SubstitutionMap, 1105 | statement: &ast::ReturnStatement, 1106 | ) -> Result<(ast::ReturnStatement, SubstitutionMap)> { 1107 | let (result, subst) = self.with_expression(ctx, incoming_substitutions, &statement.source)?; 1108 | let mut substitution = compose_substitutions(ctx, &incoming_substitutions, &subst)?; 1109 | let mut is_never = false; 1110 | match &result.type_ { 1111 | ast::TypeUsage::Named(named) => { 1112 | if named.name.name.value == "!" { 1113 | is_never = true; 1114 | } 1115 | } 1116 | _ => {} 1117 | } 1118 | if !is_never { 1119 | substitution = compose_substitutions( 1120 | ctx, 1121 | &subst, 1122 | &unify(ctx, &ctx.current_function_return.as_ref().unwrap(), &result.type_)?, 1123 | )?; 1124 | } 1125 | 1126 | return Ok((ast::ReturnStatement { source: result }, substitution)); 1127 | } 1128 | 1129 | fn with_let_statement( 1130 | self: &Self, 1131 | ctx: &Context, 1132 | incoming_substitutions: &SubstitutionMap, 1133 | statement: &ast::LetStatement, 1134 | ) -> Result<(Context, ast::LetStatement, SubstitutionMap)> { 1135 | let (result, subst) = self.with_expression(ctx, incoming_substitutions, &statement.expression)?; 1136 | let let_ctx = ctx.add_variable(statement.variable_name.name.value.clone(), &result.type_); 1137 | let substitution = compose_substitutions(ctx, &subst, &unify(ctx, &statement.type_, &result.type_)?)?; 1138 | return Ok(( 1139 | let_ctx, 1140 | ast::LetStatement { 1141 | variable_name: statement.variable_name.clone(), 1142 | expression: result, 1143 | type_: apply_substitution(ctx, &substitution, &statement.type_)?, 1144 | }, 1145 | substitution, 1146 | )); 1147 | } 1148 | 1149 | fn with_assignment_statement( 1150 | self: &Self, 1151 | ctx: &Context, 1152 | incoming_substitutions: &SubstitutionMap, 1153 | statement: &ast::AssignmentStatement, 1154 | ) -> Result<(ast::AssignmentStatement, SubstitutionMap)> { 1155 | let (expr, subst) = self.with_expression(ctx, incoming_substitutions, &statement.expression)?; 1156 | let mut substitution = compose_substitutions(ctx, &incoming_substitutions, &subst)?; 1157 | 1158 | let result_as = ast::AssignmentStatement { 1159 | source: match &statement.source { 1160 | ast::AssignmentTarget::Variable(variable) => { 1161 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable.type_, &expr.type_)?)?; 1162 | ast::AssignmentTarget::Variable(ast::VariableUsage { 1163 | name: variable.name.clone(), 1164 | type_parameters: variable.type_parameters.clone(), 1165 | type_: apply_substitution(ctx, &substitution, &variable.type_)?, 1166 | }) 1167 | } 1168 | ast::AssignmentTarget::StructAttr(struct_attr) => { 1169 | let (source, subst) = self.with_expression(ctx, &substitution, &struct_attr.source)?; 1170 | let mut subst = subst.clone(); 1171 | 1172 | let field_type = match get_attr(ctx, &NamedEntity::Variable(source.type_.clone()), &struct_attr.attribute)? { 1173 | StructAttr::Field(type_) => type_, 1174 | StructAttr::Method(_) => { 1175 | return Err(errors::TypingError::CannotAssignToMethod { 1176 | identifier: struct_attr.attribute.clone(), 1177 | }) 1178 | } 1179 | }; 1180 | 1181 | subst = compose_substitutions(ctx, &subst, &unify(ctx, &struct_attr.type_, &field_type)?)?; 1182 | 1183 | let substitution = compose_substitutions( 1184 | ctx, 1185 | &compose_substitutions(ctx, &substitution, &subst)?, 1186 | &unify(ctx, &struct_attr.type_, &expr.type_)?, 1187 | )?; 1188 | ast::AssignmentTarget::StructAttr(ast::StructGetter { 1189 | type_parameters: struct_attr.type_parameters.clone(), 1190 | source: source, 1191 | attribute: struct_attr.attribute.clone(), 1192 | type_: apply_substitution(ctx, &substitution, &struct_attr.type_)?, 1193 | }) 1194 | } 1195 | }, 1196 | expression: expr, 1197 | }; 1198 | return Ok((result_as, substitution)); 1199 | } 1200 | 1201 | fn with_expression( 1202 | self: &Self, 1203 | ctx: &Context, 1204 | incoming_substitutions: &SubstitutionMap, 1205 | expression: &ast::Expression, 1206 | ) -> Result<(ast::Expression, SubstitutionMap)> { 1207 | let mut substitution = incoming_substitutions.clone(); 1208 | let subexpression = Box::new(match &*expression.subexpression { 1209 | ast::Subexpression::LiteralInt(literal_int) => { 1210 | let (result, subst) = self.with_literal_int(ctx, &substitution, literal_int)?; 1211 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1212 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1213 | ast::Subexpression::LiteralInt(result) 1214 | } 1215 | ast::Subexpression::LiteralFloat(literal_float) => { 1216 | let (result, subst) = self.with_literal_float(ctx, &substitution, literal_float)?; 1217 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1218 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1219 | ast::Subexpression::LiteralFloat(result) 1220 | } 1221 | ast::Subexpression::LiteralBool(literal_bool) => { 1222 | let (result, subst) = self.with_literal_bool(ctx, &substitution, literal_bool)?; 1223 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1224 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1225 | ast::Subexpression::LiteralBool(result) 1226 | } 1227 | ast::Subexpression::LiteralString(literal_string) => { 1228 | let (result, subst) = self.with_literal_string(ctx, &substitution, literal_string)?; 1229 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1230 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1231 | ast::Subexpression::LiteralString(result) 1232 | } 1233 | ast::Subexpression::LiteralStruct(literal_struct) => { 1234 | let (result, subst) = self.with_literal_struct(ctx, &substitution, literal_struct)?; 1235 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1236 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1237 | ast::Subexpression::LiteralStruct(result) 1238 | } 1239 | ast::Subexpression::FunctionCall(function_call) => { 1240 | let (result, subst) = self.with_function_call(ctx, &substitution, function_call)?; 1241 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1242 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &function_call.type_)?)?; 1243 | ast::Subexpression::FunctionCall(result) 1244 | } 1245 | ast::Subexpression::VariableUsage(variable_usage) => { 1246 | let (result, subst) = self.with_variable_usage(ctx, &substitution, variable_usage)?; 1247 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1248 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &variable_usage.type_)?)?; 1249 | ast::Subexpression::VariableUsage(result) 1250 | } 1251 | ast::Subexpression::If(if_expression) => { 1252 | let (result, subst) = self.with_if(ctx, &substitution, if_expression)?; 1253 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1254 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1255 | ast::Subexpression::If(result) 1256 | } 1257 | ast::Subexpression::StructGetter(struct_getter) => { 1258 | let (result, subst) = self.with_struct_getter(ctx, &substitution, struct_getter)?; 1259 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1260 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1261 | ast::Subexpression::StructGetter(result) 1262 | } 1263 | ast::Subexpression::Block(block) => { 1264 | let (result, subst) = self.with_block_expression(ctx, &substitution, block)?; 1265 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1266 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.type_)?)?; 1267 | ast::Subexpression::Block(result) 1268 | } 1269 | ast::Subexpression::Op(op) => { 1270 | let (result, subst) = self.with_op(ctx, &substitution, op)?; 1271 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1272 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.left.type_)?)?; 1273 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &expression.type_, &result.right.type_)?)?; 1274 | ast::Subexpression::Op(result) 1275 | } 1276 | }); 1277 | 1278 | let expr = ast::Expression { 1279 | subexpression: subexpression, 1280 | type_: apply_substitution(ctx, &substitution, &expression.type_)?, 1281 | }; 1282 | return Ok((expr, substitution)); 1283 | } 1284 | 1285 | fn with_literal_int( 1286 | self: &Self, 1287 | ctx: &Context, 1288 | substitution: &SubstitutionMap, 1289 | literal_int: &ast::LiteralInt, 1290 | ) -> Result<(ast::LiteralInt, SubstitutionMap)> { 1291 | Ok(( 1292 | ast::LiteralInt { 1293 | value: literal_int.value.clone(), 1294 | type_: apply_substitution(ctx, &substitution, &literal_int.type_)?, 1295 | }, 1296 | substitution.clone(), 1297 | )) 1298 | } 1299 | 1300 | fn with_literal_float( 1301 | self: &Self, 1302 | ctx: &Context, 1303 | substitution: &SubstitutionMap, 1304 | literal_float: &ast::LiteralFloat, 1305 | ) -> Result<(ast::LiteralFloat, SubstitutionMap)> { 1306 | Ok(( 1307 | ast::LiteralFloat { 1308 | value: literal_float.value.clone(), 1309 | type_: apply_substitution(ctx, &substitution, &literal_float.type_)?, 1310 | }, 1311 | substitution.clone(), 1312 | )) 1313 | } 1314 | 1315 | fn with_literal_bool( 1316 | self: &Self, 1317 | ctx: &Context, 1318 | substitution: &SubstitutionMap, 1319 | literal_bool: &ast::LiteralBool, 1320 | ) -> Result<(ast::LiteralBool, SubstitutionMap)> { 1321 | Ok(( 1322 | ast::LiteralBool { 1323 | value: literal_bool.value.clone(), 1324 | type_: apply_substitution(ctx, &substitution, &literal_bool.type_)?, 1325 | }, 1326 | substitution.clone(), 1327 | )) 1328 | } 1329 | 1330 | fn with_literal_string( 1331 | self: &Self, 1332 | ctx: &Context, 1333 | substitution: &SubstitutionMap, 1334 | literal_string: &ast::LiteralString, 1335 | ) -> Result<(ast::LiteralString, SubstitutionMap)> { 1336 | Ok(( 1337 | ast::LiteralString { 1338 | value: literal_string.value.clone(), 1339 | type_: apply_substitution(ctx, &substitution, &literal_string.type_)?, 1340 | }, 1341 | substitution.clone(), 1342 | )) 1343 | } 1344 | 1345 | fn with_literal_struct( 1346 | self: &Self, 1347 | ctx: &Context, 1348 | substitution: &SubstitutionMap, 1349 | literal_struct: &ast::LiteralStruct, 1350 | ) -> Result<(ast::LiteralStruct, SubstitutionMap)> { 1351 | let mut substitution = substitution.clone(); 1352 | let struct_type = match &ctx.environment[&literal_struct.name.name.value] { 1353 | NamedEntity::NamedType(env_type) => match &env_type.is_a { 1354 | TypeType::Struct => env_type.type_construct(ctx, &literal_struct.type_parameters)?, 1355 | _ => { 1356 | return Err(errors::TypingError::NotAStructLiteral { 1357 | identifier: literal_struct.name.clone(), 1358 | }); 1359 | } 1360 | }, 1361 | _ => { 1362 | return Err(errors::TypingError::NotAStructLiteral { 1363 | identifier: literal_struct.name.clone(), 1364 | }); 1365 | } 1366 | }; 1367 | 1368 | if struct_type.fields.len() != literal_struct.fields.len() { 1369 | return Err(errors::TypingError::StructLiteralFieldsMismatch { 1370 | struct_name: literal_struct.name.clone(), 1371 | }); 1372 | } 1373 | let mut fields = vec![]; 1374 | for (type_field_name, type_field_type) in struct_type.fields.iter() { 1375 | let mut found = false; 1376 | let mut field_expression: Option = None; 1377 | let mut field_name: Option = None; 1378 | for field in literal_struct.fields.iter() { 1379 | if type_field_name == &field.0.name.value { 1380 | found = true; 1381 | let (result, subst) = self.with_expression(ctx, &substitution, &field.1)?; 1382 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1383 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, type_field_type, &result.type_)?)?; 1384 | field_expression = Some(result); 1385 | field_name = Some(field.0.clone()); 1386 | } 1387 | } 1388 | if !found { 1389 | return Err(errors::TypingError::StructLiteralFieldsMismatch { 1390 | struct_name: literal_struct.name.clone(), 1391 | }); 1392 | } 1393 | fields.push((field_name.unwrap(), field_expression.unwrap())); 1394 | } 1395 | Ok(( 1396 | ast::LiteralStruct { 1397 | type_parameters: literal_struct.type_parameters.clone(), 1398 | name: literal_struct.name.clone(), 1399 | fields: fields, 1400 | type_: apply_substitution(ctx, &substitution, &literal_struct.type_)?, 1401 | }, 1402 | substitution, 1403 | )) 1404 | } 1405 | 1406 | fn with_function_call( 1407 | self: &Self, 1408 | ctx: &Context, 1409 | substitution: &SubstitutionMap, 1410 | function_call: &ast::FunctionCall, 1411 | ) -> Result<(ast::FunctionCall, SubstitutionMap)> { 1412 | let mut substitution = substitution.clone(); 1413 | let (source, subst) = self.with_expression(ctx, &substitution, &function_call.source)?; 1414 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1415 | match &source.type_ { 1416 | ast::TypeUsage::Function(fn_type) => { 1417 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &function_call.type_, &*fn_type.return_type)?)?; 1418 | if function_call.arguments.len() != fn_type.arguments.len() { 1419 | return Err(errors::TypingError::ArgumentLengthMismatch {}); 1420 | } 1421 | } 1422 | ast::TypeUsage::Named(_) => { 1423 | return Err(errors::TypingError::FunctionCallNotAFunction {}); 1424 | } 1425 | _ => {} 1426 | } 1427 | let mut arguments = vec![]; 1428 | for (i, arg) in function_call.arguments.iter().enumerate() { 1429 | let (result, subst) = self.with_expression(ctx, &substitution, arg)?; 1430 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1431 | 1432 | match &source.type_ { 1433 | ast::TypeUsage::Function(fn_type) => { 1434 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &fn_type.arguments[i], &result.type_)?)?; 1435 | } 1436 | ast::TypeUsage::Named(_) => { 1437 | return Err(errors::TypingError::FunctionCallNotAFunction {}); 1438 | } 1439 | _ => {} 1440 | } 1441 | arguments.push(result); 1442 | } 1443 | Ok(( 1444 | ast::FunctionCall { 1445 | source: source.clone(), 1446 | arguments: arguments, 1447 | type_: apply_substitution(ctx, &substitution, &function_call.type_)?, 1448 | }, 1449 | substitution, 1450 | )) 1451 | } 1452 | 1453 | fn with_variable_usage( 1454 | self: &Self, 1455 | ctx: &Context, 1456 | substitution: &SubstitutionMap, 1457 | variable_usage: &ast::VariableUsage, 1458 | ) -> Result<(ast::VariableUsage, SubstitutionMap)> { 1459 | let mut substitution = substitution.clone(); 1460 | match &ctx.environment[&variable_usage.name.name.value] { 1461 | NamedEntity::NamedType(named_type) => { 1462 | let type_ = ast::TypeUsage::Namespace(ast::NamespaceTypeUsage::Type(ast::NamedTypeUsage{name: variable_usage.name.clone(), type_parameters: variable_usage.type_parameters.clone()})); 1463 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &type_)?)?; 1464 | } 1465 | NamedEntity::Variable(variable) => { 1466 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &variable)?)?; 1467 | } 1468 | NamedEntity::Function(function) => { 1469 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &function.construct(ctx, &variable_usage.type_parameters)?)?)?; 1470 | } 1471 | } 1472 | Ok(( 1473 | ast::VariableUsage { 1474 | name: variable_usage.name.clone(), 1475 | type_parameters: variable_usage.type_parameters.clone(), // Redundant to type 1476 | type_: apply_substitution(ctx, &substitution, &variable_usage.type_)?, 1477 | }, 1478 | substitution, 1479 | )) 1480 | } 1481 | 1482 | fn with_if( 1483 | self: &Self, 1484 | ctx: &Context, 1485 | substitution: &SubstitutionMap, 1486 | if_expression: &ast::IfExpression, 1487 | ) -> Result<(ast::IfExpression, SubstitutionMap)> { 1488 | let mut substitution = substitution.clone(); 1489 | let (condition, subst) = self.with_expression(ctx, &substitution, &if_expression.condition)?; 1490 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1491 | 1492 | let (block_result, subst) = self.with_block(ctx, &substitution, &if_expression.block)?; 1493 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1494 | 1495 | let else_ = match &if_expression.else_ { 1496 | Some(else_) => { 1497 | let (result, subst) = self.with_block(ctx, &substitution, else_)?; 1498 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1499 | Some(result) 1500 | } 1501 | None => None, 1502 | }; 1503 | 1504 | match &condition.type_ { 1505 | ast::TypeUsage::Named(named) => { 1506 | if named.name.name.value != "bool" { 1507 | return Err(errors::TypingError::IfConditionMustBeBool {}); 1508 | } 1509 | } 1510 | ast::TypeUsage::Function(_) => { 1511 | return Err(errors::TypingError::IfConditionMustBeBool {}); 1512 | } 1513 | _ => {} 1514 | }; 1515 | 1516 | let mut never_count = 0; 1517 | match &block_result.type_ { 1518 | ast::TypeUsage::Named(named) => { 1519 | if named.name.name.value != "!" { 1520 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &if_expression.type_, &block_result.type_)?)?; 1521 | } else { 1522 | never_count += 1; 1523 | } 1524 | } 1525 | _ => { 1526 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &if_expression.type_, &block_result.type_)?)?; 1527 | } 1528 | }; 1529 | 1530 | match &else_ { 1531 | Some(else_block) => { 1532 | match &else_block.type_ { 1533 | ast::TypeUsage::Named(named) => { 1534 | if named.name.name.value != "!" { 1535 | substitution = 1536 | compose_substitutions(ctx, &substitution, &unify(ctx, &if_expression.type_, &else_block.type_)?)?; 1537 | } else { 1538 | never_count += 1; 1539 | } 1540 | } 1541 | _ => { 1542 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &if_expression.type_, &else_block.type_)?)?; 1543 | } 1544 | }; 1545 | } 1546 | None => { 1547 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &if_expression.type_, &ast::new_unit())?)?; 1548 | } 1549 | } 1550 | 1551 | let result_type = if never_count == 2 { 1552 | ast::new_never() 1553 | } else { 1554 | apply_substitution(ctx, &substitution, &if_expression.type_)? 1555 | }; 1556 | 1557 | Ok(( 1558 | ast::IfExpression { 1559 | condition: condition, 1560 | block: block_result, 1561 | else_: else_, 1562 | type_: result_type, 1563 | }, 1564 | substitution, 1565 | )) 1566 | } 1567 | 1568 | fn with_struct_getter( 1569 | self: &Self, 1570 | ctx: &Context, 1571 | substitution: &SubstitutionMap, 1572 | struct_getter: &ast::StructGetter, 1573 | ) -> Result<(ast::StructGetter, SubstitutionMap)> { 1574 | let mut substitution = substitution.clone(); 1575 | let (source, subst) = self.with_expression(ctx, &substitution, &struct_getter.source)?; 1576 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1577 | 1578 | let field_type = match get_attr(ctx, &NamedEntity::Variable(source.type_.clone()), &struct_getter.attribute)? { 1579 | StructAttr::Field(type_) => type_, 1580 | StructAttr::Method(constructor) => constructor.construct(ctx, &struct_getter.type_parameters)?, 1581 | }; 1582 | 1583 | substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &struct_getter.type_, &field_type)?)?; 1584 | 1585 | Ok(( 1586 | ast::StructGetter { 1587 | type_parameters: struct_getter.type_parameters.clone(), 1588 | source: source, 1589 | attribute: struct_getter.attribute.clone(), 1590 | type_: apply_substitution(ctx, &substitution, &struct_getter.type_)?, 1591 | }, 1592 | substitution, 1593 | )) 1594 | } 1595 | 1596 | fn with_block_expression( 1597 | self: &Self, 1598 | ctx: &Context, 1599 | substitution: &SubstitutionMap, 1600 | block: &ast::Block, 1601 | ) -> Result<(ast::Block, SubstitutionMap)> { 1602 | let mut substitution = substitution.clone(); 1603 | let (result, subst) = self.with_block(ctx, &substitution, &block)?; 1604 | substitution = compose_substitutions(ctx, &substitution, &subst)?; 1605 | Ok((result, substitution)) 1606 | } 1607 | 1608 | fn with_op( 1609 | self: &Self, 1610 | ctx: &Context, 1611 | substitution: &SubstitutionMap, 1612 | op: &ast::Operation, 1613 | ) -> Result<(ast::Operation, SubstitutionMap)> { 1614 | let mut substitution = substitution.clone(); 1615 | let (expr_left, subst_left) = self.with_expression(ctx, &substitution, &op.left)?; 1616 | let (expr_right, subst_right) = self.with_expression(ctx, &substitution, &op.right)?; 1617 | substitution = compose_substitutions(ctx, &substitution, &subst_left)?; 1618 | substitution = compose_substitutions(ctx, &substitution, &subst_right)?; 1619 | Ok(( 1620 | ast::Operation { 1621 | left: expr_left, 1622 | op: op.op.clone(), 1623 | right: expr_right, 1624 | }, 1625 | substitution, 1626 | )) 1627 | } 1628 | } 1629 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 2 | pub enum Signedness { 3 | Signed, 4 | Unsigned, 5 | } 6 | 7 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 8 | pub enum IntBitness { 9 | X8, 10 | X16, 11 | X32, 12 | X64, 13 | X128, 14 | } 15 | 16 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 17 | pub enum FloatBitness { 18 | X32, 19 | X64, 20 | X128, 21 | } 22 | 23 | #[derive(Clone, Eq, PartialEq, Hash)] 24 | pub struct IntTypeDef { 25 | pub signedness: Signedness, 26 | pub bitness: IntBitness, 27 | } 28 | 29 | #[derive(Clone, PartialEq, Eq, Hash)] 30 | pub struct FloatTypeDef { 31 | pub bitness: FloatBitness, 32 | } 33 | 34 | #[derive(Clone, PartialEq, Eq, Hash)] 35 | pub struct FunctionTypeDef { 36 | pub arguments: Vec, 37 | pub return_type: Box, 38 | } 39 | 40 | #[derive(Clone, PartialEq, Eq, Hash)] 41 | pub enum Type { 42 | Bool, 43 | Int(IntTypeDef), 44 | Float(FloatTypeDef), 45 | Function(FunctionTypeDef), 46 | // String(StringTypeDef), 47 | // Struct(StructTypeDef), 48 | // Trait(TraitTypeDef), 49 | // Void, 50 | // Never, 51 | } 52 | 53 | /// Used for places where type info may or may not be solved. 54 | #[derive(Clone, Eq, PartialEq, Hash)] 55 | pub enum SpecifiedType { 56 | Unknown, 57 | Type(Type), 58 | } 59 | --------------------------------------------------------------------------------