├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── idea └── grammar.txt ├── images ├── europa-e.png └── europa.png ├── src ├── environment.rs ├── error.rs ├── functions │ ├── mod.rs │ ├── native.rs │ ├── traits.rs │ └── user.rs ├── interpreter.rs ├── lexer.rs ├── main.rs ├── nodes │ ├── expr.rs │ ├── mod.rs │ └── stmt.rs ├── parser.rs ├── repl.rs ├── resolver.rs ├── stdlib │ ├── clock.rs │ ├── io.rs │ ├── math.rs │ └── mod.rs ├── tests │ ├── files.rs │ └── mod.rs ├── token.rs └── types │ ├── array.rs │ ├── hash.rs │ ├── map.rs │ ├── mod.rs │ ├── module.rs │ ├── ops.rs │ └── tostring.rs └── test ├── main.eo ├── playground.eo └── programs ├── 10bottles.eo ├── fib.eo └── fizzbuzz.eo /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo check --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug", 11 | "program": "${workspaceFolder}/target/debug/europa-lang.exe", 12 | "args": ["test/playground.eo"], 13 | "preLaunchTask": "rust: cargo build", 14 | "cwd": "${workspaceFolder}" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 4, 3 | "editor.insertSpaces": true 4 | } -------------------------------------------------------------------------------- /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 = "android-tzdata" 7 | version = "0.1.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 10 | 11 | [[package]] 12 | name = "android_system_properties" 13 | version = "0.1.5" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 16 | dependencies = [ 17 | "libc", 18 | ] 19 | 20 | [[package]] 21 | name = "ansi_term" 22 | version = "0.12.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 25 | dependencies = [ 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "atty" 31 | version = "0.2.14" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 34 | dependencies = [ 35 | "hermit-abi", 36 | "libc", 37 | "winapi", 38 | ] 39 | 40 | [[package]] 41 | name = "autocfg" 42 | version = "1.0.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 45 | 46 | [[package]] 47 | name = "bitflags" 48 | version = "1.3.2" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 51 | 52 | [[package]] 53 | name = "bumpalo" 54 | version = "3.16.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 57 | 58 | [[package]] 59 | name = "cc" 60 | version = "1.1.14" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "50d2eb3cd3d1bf4529e31c215ee6f93ec5a3d536d9f578f93d9d33ee19562932" 63 | dependencies = [ 64 | "shlex", 65 | ] 66 | 67 | [[package]] 68 | name = "cfg-if" 69 | version = "1.0.0" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 72 | 73 | [[package]] 74 | name = "chrono" 75 | version = "0.4.38" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 78 | dependencies = [ 79 | "android-tzdata", 80 | "iana-time-zone", 81 | "js-sys", 82 | "num-traits", 83 | "wasm-bindgen", 84 | "windows-targets", 85 | ] 86 | 87 | [[package]] 88 | name = "clap" 89 | version = "2.34.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 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 = "clipboard-win" 104 | version = "4.2.2" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "3db8340083d28acb43451166543b98c838299b7e0863621be53a338adceea0ed" 107 | dependencies = [ 108 | "error-code", 109 | "str-buf", 110 | "winapi", 111 | ] 112 | 113 | [[package]] 114 | name = "core-foundation-sys" 115 | version = "0.8.7" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 118 | 119 | [[package]] 120 | name = "dirs-next" 121 | version = "2.0.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" 124 | dependencies = [ 125 | "cfg-if", 126 | "dirs-sys-next", 127 | ] 128 | 129 | [[package]] 130 | name = "dirs-sys-next" 131 | version = "0.1.2" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" 134 | dependencies = [ 135 | "libc", 136 | "redox_users", 137 | "winapi", 138 | ] 139 | 140 | [[package]] 141 | name = "endian-type" 142 | version = "0.1.2" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" 145 | 146 | [[package]] 147 | name = "error-code" 148 | version = "2.3.0" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff" 151 | dependencies = [ 152 | "libc", 153 | "str-buf", 154 | ] 155 | 156 | [[package]] 157 | name = "europa-lang" 158 | version = "0.0.0" 159 | dependencies = [ 160 | "chrono", 161 | "clap", 162 | "maplit", 163 | "rustyline", 164 | ] 165 | 166 | [[package]] 167 | name = "fd-lock" 168 | version = "3.0.2" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "a16910e685088843d53132b04e0f10a571fdb193224fc589685b3ba1ce4cb03d" 171 | dependencies = [ 172 | "cfg-if", 173 | "libc", 174 | "windows-sys", 175 | ] 176 | 177 | [[package]] 178 | name = "getrandom" 179 | version = "0.2.3" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" 182 | dependencies = [ 183 | "cfg-if", 184 | "libc", 185 | "wasi", 186 | ] 187 | 188 | [[package]] 189 | name = "hermit-abi" 190 | version = "0.1.19" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 193 | dependencies = [ 194 | "libc", 195 | ] 196 | 197 | [[package]] 198 | name = "iana-time-zone" 199 | version = "0.1.60" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" 202 | dependencies = [ 203 | "android_system_properties", 204 | "core-foundation-sys", 205 | "iana-time-zone-haiku", 206 | "js-sys", 207 | "wasm-bindgen", 208 | "windows-core", 209 | ] 210 | 211 | [[package]] 212 | name = "iana-time-zone-haiku" 213 | version = "0.1.2" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 216 | dependencies = [ 217 | "cc", 218 | ] 219 | 220 | [[package]] 221 | name = "js-sys" 222 | version = "0.3.70" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 225 | dependencies = [ 226 | "wasm-bindgen", 227 | ] 228 | 229 | [[package]] 230 | name = "libc" 231 | version = "0.2.158" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" 234 | 235 | [[package]] 236 | name = "log" 237 | version = "0.4.14" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 240 | dependencies = [ 241 | "cfg-if", 242 | ] 243 | 244 | [[package]] 245 | name = "maplit" 246 | version = "1.0.2" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 249 | 250 | [[package]] 251 | name = "memchr" 252 | version = "2.4.1" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 255 | 256 | [[package]] 257 | name = "memoffset" 258 | version = "0.6.5" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 261 | dependencies = [ 262 | "autocfg", 263 | ] 264 | 265 | [[package]] 266 | name = "nibble_vec" 267 | version = "0.1.0" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" 270 | dependencies = [ 271 | "smallvec", 272 | ] 273 | 274 | [[package]] 275 | name = "nix" 276 | version = "0.23.1" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" 279 | dependencies = [ 280 | "bitflags", 281 | "cc", 282 | "cfg-if", 283 | "libc", 284 | "memoffset", 285 | ] 286 | 287 | [[package]] 288 | name = "num-traits" 289 | version = "0.2.19" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 292 | dependencies = [ 293 | "autocfg", 294 | ] 295 | 296 | [[package]] 297 | name = "once_cell" 298 | version = "1.19.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 301 | 302 | [[package]] 303 | name = "proc-macro2" 304 | version = "1.0.86" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 307 | dependencies = [ 308 | "unicode-ident", 309 | ] 310 | 311 | [[package]] 312 | name = "quote" 313 | version = "1.0.37" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 316 | dependencies = [ 317 | "proc-macro2", 318 | ] 319 | 320 | [[package]] 321 | name = "radix_trie" 322 | version = "0.2.1" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" 325 | dependencies = [ 326 | "endian-type", 327 | "nibble_vec", 328 | ] 329 | 330 | [[package]] 331 | name = "redox_syscall" 332 | version = "0.2.10" 333 | source = "registry+https://github.com/rust-lang/crates.io-index" 334 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 335 | dependencies = [ 336 | "bitflags", 337 | ] 338 | 339 | [[package]] 340 | name = "redox_users" 341 | version = "0.4.0" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" 344 | dependencies = [ 345 | "getrandom", 346 | "redox_syscall", 347 | ] 348 | 349 | [[package]] 350 | name = "rustyline" 351 | version = "9.1.1" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "6c38cfbd0a4d7df7aab7cf53732d5d43449d0300358fd15cd4e8c8468a956aca" 354 | dependencies = [ 355 | "bitflags", 356 | "cfg-if", 357 | "clipboard-win", 358 | "dirs-next", 359 | "fd-lock", 360 | "libc", 361 | "log", 362 | "memchr", 363 | "nix", 364 | "radix_trie", 365 | "scopeguard", 366 | "smallvec", 367 | "unicode-segmentation", 368 | "unicode-width", 369 | "utf8parse", 370 | "winapi", 371 | ] 372 | 373 | [[package]] 374 | name = "scopeguard" 375 | version = "1.1.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 378 | 379 | [[package]] 380 | name = "shlex" 381 | version = "1.3.0" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 384 | 385 | [[package]] 386 | name = "smallvec" 387 | version = "1.7.0" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" 390 | 391 | [[package]] 392 | name = "str-buf" 393 | version = "1.0.5" 394 | source = "registry+https://github.com/rust-lang/crates.io-index" 395 | checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" 396 | 397 | [[package]] 398 | name = "strsim" 399 | version = "0.8.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 402 | 403 | [[package]] 404 | name = "syn" 405 | version = "2.0.75" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "f6af063034fc1935ede7be0122941bafa9bacb949334d090b77ca98b5817c7d9" 408 | dependencies = [ 409 | "proc-macro2", 410 | "quote", 411 | "unicode-ident", 412 | ] 413 | 414 | [[package]] 415 | name = "textwrap" 416 | version = "0.11.0" 417 | source = "registry+https://github.com/rust-lang/crates.io-index" 418 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 419 | dependencies = [ 420 | "unicode-width", 421 | ] 422 | 423 | [[package]] 424 | name = "unicode-ident" 425 | version = "1.0.12" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 428 | 429 | [[package]] 430 | name = "unicode-segmentation" 431 | version = "1.8.0" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" 434 | 435 | [[package]] 436 | name = "unicode-width" 437 | version = "0.1.8" 438 | source = "registry+https://github.com/rust-lang/crates.io-index" 439 | checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" 440 | 441 | [[package]] 442 | name = "utf8parse" 443 | version = "0.2.0" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" 446 | 447 | [[package]] 448 | name = "vec_map" 449 | version = "0.8.2" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 452 | 453 | [[package]] 454 | name = "wasi" 455 | version = "0.10.2+wasi-snapshot-preview1" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" 458 | 459 | [[package]] 460 | name = "wasm-bindgen" 461 | version = "0.2.93" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 464 | dependencies = [ 465 | "cfg-if", 466 | "once_cell", 467 | "wasm-bindgen-macro", 468 | ] 469 | 470 | [[package]] 471 | name = "wasm-bindgen-backend" 472 | version = "0.2.93" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 475 | dependencies = [ 476 | "bumpalo", 477 | "log", 478 | "once_cell", 479 | "proc-macro2", 480 | "quote", 481 | "syn", 482 | "wasm-bindgen-shared", 483 | ] 484 | 485 | [[package]] 486 | name = "wasm-bindgen-macro" 487 | version = "0.2.93" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 490 | dependencies = [ 491 | "quote", 492 | "wasm-bindgen-macro-support", 493 | ] 494 | 495 | [[package]] 496 | name = "wasm-bindgen-macro-support" 497 | version = "0.2.93" 498 | source = "registry+https://github.com/rust-lang/crates.io-index" 499 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 500 | dependencies = [ 501 | "proc-macro2", 502 | "quote", 503 | "syn", 504 | "wasm-bindgen-backend", 505 | "wasm-bindgen-shared", 506 | ] 507 | 508 | [[package]] 509 | name = "wasm-bindgen-shared" 510 | version = "0.2.93" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 513 | 514 | [[package]] 515 | name = "winapi" 516 | version = "0.3.9" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 519 | dependencies = [ 520 | "winapi-i686-pc-windows-gnu", 521 | "winapi-x86_64-pc-windows-gnu", 522 | ] 523 | 524 | [[package]] 525 | name = "winapi-i686-pc-windows-gnu" 526 | version = "0.4.0" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 529 | 530 | [[package]] 531 | name = "winapi-x86_64-pc-windows-gnu" 532 | version = "0.4.0" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 535 | 536 | [[package]] 537 | name = "windows-core" 538 | version = "0.52.0" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 541 | dependencies = [ 542 | "windows-targets", 543 | ] 544 | 545 | [[package]] 546 | name = "windows-sys" 547 | version = "0.28.0" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6" 550 | dependencies = [ 551 | "windows_aarch64_msvc 0.28.0", 552 | "windows_i686_gnu 0.28.0", 553 | "windows_i686_msvc 0.28.0", 554 | "windows_x86_64_gnu 0.28.0", 555 | "windows_x86_64_msvc 0.28.0", 556 | ] 557 | 558 | [[package]] 559 | name = "windows-targets" 560 | version = "0.52.6" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 563 | dependencies = [ 564 | "windows_aarch64_gnullvm", 565 | "windows_aarch64_msvc 0.52.6", 566 | "windows_i686_gnu 0.52.6", 567 | "windows_i686_gnullvm", 568 | "windows_i686_msvc 0.52.6", 569 | "windows_x86_64_gnu 0.52.6", 570 | "windows_x86_64_gnullvm", 571 | "windows_x86_64_msvc 0.52.6", 572 | ] 573 | 574 | [[package]] 575 | name = "windows_aarch64_gnullvm" 576 | version = "0.52.6" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 579 | 580 | [[package]] 581 | name = "windows_aarch64_msvc" 582 | version = "0.28.0" 583 | source = "registry+https://github.com/rust-lang/crates.io-index" 584 | checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" 585 | 586 | [[package]] 587 | name = "windows_aarch64_msvc" 588 | version = "0.52.6" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 591 | 592 | [[package]] 593 | name = "windows_i686_gnu" 594 | version = "0.28.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" 597 | 598 | [[package]] 599 | name = "windows_i686_gnu" 600 | version = "0.52.6" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 603 | 604 | [[package]] 605 | name = "windows_i686_gnullvm" 606 | version = "0.52.6" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 609 | 610 | [[package]] 611 | name = "windows_i686_msvc" 612 | version = "0.28.0" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" 615 | 616 | [[package]] 617 | name = "windows_i686_msvc" 618 | version = "0.52.6" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 621 | 622 | [[package]] 623 | name = "windows_x86_64_gnu" 624 | version = "0.28.0" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" 627 | 628 | [[package]] 629 | name = "windows_x86_64_gnu" 630 | version = "0.52.6" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 633 | 634 | [[package]] 635 | name = "windows_x86_64_gnullvm" 636 | version = "0.52.6" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 639 | 640 | [[package]] 641 | name = "windows_x86_64_msvc" 642 | version = "0.28.0" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" 645 | 646 | [[package]] 647 | name = "windows_x86_64_msvc" 648 | version = "0.52.6" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 651 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "europa-lang" 3 | version = "0.0.0" 4 | edition = "2018" 5 | description = "A fun language with no classes whatsoever" 6 | 7 | [dependencies] 8 | chrono = "0.4.38" 9 | clap = "^2.34" 10 | maplit = "^1.0" 11 | rustyline = "^9.1" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2021 Junhao "Jerry" Zhang 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Europa Lang](./images/europa.png) 2 | [Discord Server](https://discord.gg/csdaFGd5K9) 3 | 4 | # Europa Lang 5 | 6 | This language aims to be simple, minimal, and compact. There will not be any classes whatsoever, and importing other files should be painless. 7 | 8 | ## Example 9 | 10 | ```europa 11 | use Math; 12 | use Io; 13 | 14 | Io.println("Hello, World!"); 15 | var input = Io.stdin.readline(); 16 | Io.println("You said: " + input); 17 | 18 | Io.println("Random Number 0..1: " + Math.random()); 19 | 20 | var map = {{ 21 | a_map = 'strings can look like this too'; 22 | [1 + 1] = 'expression keys'; 23 | }}; 24 | 25 | Io.println(map['a_map']); 26 | Io.println(map.a_map); 27 | Io.println(map[2]); 28 | 29 | fn add_two(a, b) { 30 | return a + b; 31 | } 32 | 33 | var a = true; 34 | var b = true; 35 | 36 | while true { 37 | if a == b { 38 | break; 39 | } elif a != b { 40 | continue; 41 | } else { 42 | break; 43 | } 44 | } 45 | 46 | var array = [1, 2, 3]; 47 | for i in array { 48 | Io.println(i); 49 | } 50 | ``` 51 | 52 | ## Usage 53 | 54 | ```sh 55 | cargo run -- [file] 56 | ``` 57 | 58 | ## Credits 59 | 60 | - @justamirror and Dart for name and language design suggestions. 61 | - @CoolCoderSJ for creating the discord server, along with language design suggestions! 62 | - @SixBeeps for designing the Europa logo! 63 | -------------------------------------------------------------------------------- /idea/grammar.txt: -------------------------------------------------------------------------------- 1 | program => statement* EOF 2 | 3 | 4 | # statements 5 | statement => exprStmt | varDecl | block | ifStmt | whileStmt | doWhileStmt | breakStmt | continueStmt | fnDecl | returnStmt | useStmt 6 | 7 | exprStmt => ( expr ";" ) 8 | varDecl => "var" IDENTIFIER ("=" expr )? ";" 9 | 10 | ifStmt => "if" expr block ( "elif" expr block )* ( "else" block )? 11 | 12 | whileStmt => "while" expr block 13 | doWhileStmt => "do" block "while" expr ";" 14 | # forStmt => "for" ( ( IDENTIFIER "in" expr ) | ( "(" IDENTIFIER "in" expr ")" ) ) block 15 | 16 | breakStmt => "break" ";" 17 | continueStmt => "continue" ";" 18 | 19 | fnDecl => "fn" IDENTIFIER function 20 | returnStmt => "return" expr? ";" 21 | 22 | useStmt => "use" IDENTIFIER ( "." ( ( "{" ( IDENTIFIER "," )* IDENTIFIER "}" ) | IDENTIFIER | "*" ) ) ";" 23 | 24 | 25 | # expressions 26 | expr => range 27 | range => ternary ( ( ".." | ".=" ) ternary )? 28 | 29 | ternary => assignment ( "?" expr ":" ternary )? 30 | assignment => ( IDENTIFIER ( "=" | "+=" | "-=" | "*=" | "/=" | "**=" | "%=" ) assignment ) | or 31 | 32 | or => and ( "or" and )* 33 | and => equality ( "and" equality )* 34 | 35 | equality => comparison ( ( "==" | "!=" ) comparison )* 36 | comparison => addition ( ( ">" | "<" | ">=" | "<=" ) addition )* 37 | 38 | addition => times ( ( "-" | "+" ) times )* 39 | times => unary ( ( "*" | "/" | "%" ) unary )* 40 | 41 | unary => ( ( "!" | "-" ) unary ) | call 42 | call => primary ( "(" args? ")" | "[" expr "]" | "." IDENTIFIER )* 43 | 44 | primary => NUMBER | STRING | IDENTIFIER | "true" | "false" | "nil" | "(" expr ")" | block | ifStmt | array | maps 45 | 46 | block => "{" statement* "}" 47 | array => "[" ( expr "," )* ( expr )? "]" 48 | maps => "{{" ( expr ":" expr? "," )* ( expr ":" expr? )? "}}" 49 | 50 | args => ( expr | IDENTIFIER "=" expr ) ( "," ( expr | IDENTIFER "=" expr ) )* 51 | params => ( IDENTIFIER | IDENTIFIER "=" expr ) ( "," ( IDENTIFIER | IDENTIFIER "=" expr ) )* 52 | 53 | function => "(" params? ")" block 54 | 55 | 56 | IDENTIFIER => [ _ a-z A-Z ]? [ _ a-z A-Z 0-9 ]* 57 | NUMBER => [ 0-9 _ ]+ ( "." [ 0-9 _ ]+ )? 58 | STRING => ( "\"" ( [ ^"\"" ]+ ) "\"" ) | ( "'" ( [ ^"'" ]+ ) "'" ) | ( "`"+ ( [ ^"`"+ ]+ ) "`"+ ) -------------------------------------------------------------------------------- /images/europa-e.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/europalang/Europa-Lang/993eb9fe1ff3a69eae2ac90c2e5e0559df606b80/images/europa-e.png -------------------------------------------------------------------------------- /images/europa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/europalang/Europa-Lang/993eb9fe1ff3a69eae2ac90c2e5e0559df606b80/images/europa.png -------------------------------------------------------------------------------- /src/environment.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | error::{Error, ErrorType}, 5 | token::{TType, Token}, 6 | types::Type, 7 | }; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct Environment { 11 | scopes: Vec>, 12 | } 13 | 14 | impl Environment { 15 | pub fn new() -> Self { 16 | Self { 17 | scopes: vec![HashMap::new()], 18 | } 19 | } 20 | 21 | pub fn push_scope(&mut self) { 22 | self.scopes.push(HashMap::new()); 23 | } 24 | 25 | pub fn pop_scope(&mut self) { 26 | self.scopes.pop(); 27 | } 28 | 29 | pub fn get(&self, tok: &Token) -> Result { 30 | if let TType::Identifier(name) = &tok.ttype { 31 | let mut scopes = self.scopes.clone(); 32 | scopes.reverse(); 33 | 34 | for scope in scopes { 35 | if scope.contains_key(name) { 36 | return Ok(scope[name].clone()); 37 | } 38 | } 39 | 40 | Err(Error::new( 41 | tok.lineinfo, 42 | format!("Undefined variable {}.", name), 43 | ErrorType::TypeError, 44 | )) 45 | } else { 46 | panic!(); 47 | } 48 | } 49 | 50 | pub fn get_at(&mut self, distance: usize, name: &Token) -> Type { 51 | if let TType::Identifier(n) = &name.ttype { 52 | self.ancestor(distance).get(n).unwrap().clone() 53 | } else { 54 | panic!() 55 | } 56 | } 57 | 58 | fn ancestor(&mut self, distance: usize) -> &mut HashMap { 59 | let distance = self.scopes.len() - 1 - distance; 60 | 61 | &mut self.scopes[distance] 62 | } 63 | 64 | pub fn define(&mut self, name: &String, val: &Type) { 65 | self.scopes 66 | .last_mut() 67 | .unwrap() 68 | .insert(name.clone(), val.clone()); 69 | } 70 | 71 | pub fn assign(&mut self, name: &Token, val: &Type) -> Result<(), Error> { 72 | if let TType::Identifier(n) = name.ttype.clone() { 73 | let mut scopes = self.scopes.clone(); 74 | scopes.reverse(); 75 | 76 | for (i, scope) in scopes.iter().enumerate() { 77 | if scope.contains_key(&n) { 78 | let mut scope = self.scopes[i].clone(); 79 | scope.insert(n, val.clone()); 80 | self.scopes[i] = scope; 81 | return Ok(()); 82 | } 83 | } 84 | 85 | Err(Error::new( 86 | name.lineinfo, 87 | format!("Undefined variable {}.", n), 88 | ErrorType::ReferenceError, 89 | )) 90 | } else { 91 | panic!(); 92 | } 93 | } 94 | 95 | pub fn assign_at(&mut self, distance: usize, name: &Token, val: &Type) -> Result<(), Error> { 96 | if let TType::Identifier(n) = &name.ttype { 97 | self.ancestor(distance).insert(n.clone(), val.clone()); 98 | } else { 99 | panic!(); 100 | } 101 | 102 | Ok(()) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::types::Type; 2 | 3 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 4 | pub struct LineInfo { 5 | pub line: i32, 6 | pub col: i32, 7 | } 8 | 9 | impl LineInfo { 10 | pub fn new(line: i32, col: i32) -> Self { 11 | Self { line, col } 12 | } 13 | } 14 | 15 | #[derive(Clone, Debug, PartialEq)] 16 | pub enum ErrorType { 17 | MathError, 18 | TypeError, 19 | SyntaxError, 20 | ReferenceError, 21 | 22 | Break, 23 | Continue, 24 | Return(Type), 25 | } 26 | 27 | #[derive(Clone, Debug)] 28 | pub enum ErrorNote { 29 | Note(String), 30 | Expect(LineInfo, String), 31 | } 32 | 33 | #[derive(Clone, Debug)] 34 | pub struct Error { 35 | info: LineInfo, 36 | pub error_type: ErrorType, 37 | error: String, 38 | notes: Vec, 39 | } 40 | 41 | impl Error { 42 | pub fn new(info: LineInfo, error: String, error_type: ErrorType) -> Self { 43 | Self { 44 | info, 45 | error, 46 | error_type, 47 | notes: Vec::new(), 48 | } 49 | } 50 | 51 | pub fn new_n(info: LineInfo, error: String, error_type: ErrorType, notes: Vec) -> Self { 52 | Self { 53 | info, 54 | error, 55 | error_type, 56 | notes, 57 | } 58 | } 59 | 60 | pub fn display(&self, code: &String) { 61 | let line = self.info.line as usize; 62 | let col = self.info.col as usize; 63 | 64 | let message = format!( 65 | "[\x1b[1m{}\x1b[0m:\x1b[1m{}\x1b[0m] \x1b[1m{:?}\x1b[0m: \x1b[31m\x1b[1m{}\x1b[0m", 66 | line, col, self.error_type, self.error 67 | ); 68 | let gutter = format!("\x1b[1m{}\x1b[0m | ", line); 69 | let editor = format!( 70 | "{}{}", 71 | gutter, 72 | code.split('\n').collect::>()[line - 1] 73 | ); 74 | let notes: String = if self.notes.len() > 0 { 75 | format!("\n{}", self.notes.iter().map(|o| match o { 76 | ErrorNote::Note(x) => format!("\n\x1b[1m\x1b[96mNote\x1b[0m: {}", x), 77 | ErrorNote::Expect(t, x) => { 78 | let line = t.line as usize; 79 | let col = t.col as usize; 80 | 81 | let gutter = format!("\x1b[1m{}\x1b[0m | ", line); 82 | let editor = format!( 83 | "{}{}", 84 | gutter, 85 | code.split('\n').collect::>()[line - 1] 86 | ); 87 | format!( 88 | "\n\x1b[1m\x1b[33mNote\x1b[0m: {} 89 | {edt} 90 | \x1b[33m{arrow:=>length$} this\x1b[0m", 91 | x, 92 | edt = editor, 93 | length = col + gutter.len() - 8, // - \x1b[1m\x1b[0m 94 | arrow = '^', 95 | ) 96 | } 97 | }).collect::>().join("")) 98 | } else { 99 | "".into() 100 | }; 101 | 102 | eprintln!( 103 | "{msg} 104 | {edt} 105 | \x1b[94m{arrow:->length$} here\x1b[0m{notes}", 106 | msg = message, 107 | edt = editor, 108 | length = col + gutter.len() - 8, // - \x1b[1m\x1b[0m 109 | arrow = '^', 110 | notes = notes 111 | ); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/functions/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{interpreter::Interpreter, types::Type}; 2 | 3 | use std::{fmt::{self, Display}, collections::HashMap}; 4 | 5 | pub use self::{ 6 | native::Func, 7 | traits::{Call, FResult}, 8 | user::FuncCallable, 9 | }; 10 | 11 | mod native; 12 | mod traits; 13 | mod user; 14 | 15 | #[derive(Debug, Clone)] 16 | pub enum FuncType { 17 | Native(Func), 18 | User(FuncCallable), 19 | } 20 | 21 | impl Call for FuncType { 22 | fn arity(&self) -> usize { 23 | match self { 24 | Self::Native(n) => n.arity(), 25 | Self::User(n) => n.arity(), 26 | } 27 | } 28 | 29 | fn call(&self, interpreter: &mut Interpreter, args: Vec, opt_args: HashMap) -> FResult { 30 | match self { 31 | Self::Native(n) => n.call(interpreter, args, opt_args), 32 | Self::User(n) => n.call(interpreter, args, opt_args), 33 | } 34 | } 35 | 36 | fn to_string(&self) -> String { 37 | match self { 38 | Self::Native(n) => n.to_string(), 39 | Self::User(n) => n.to_string(), 40 | } 41 | } 42 | 43 | fn name(&self) -> String { 44 | match self { 45 | Self::Native(n) => n.name(), 46 | Self::User(n) => n.name(), 47 | } 48 | } 49 | } 50 | 51 | impl Display for FuncType { 52 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 53 | write!(f, "fn {} [{} args]", self.name(), self.arity()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/functions/native.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Debug, rc::Rc, collections::HashMap}; 2 | 3 | use crate::{error::Error, interpreter::Interpreter, types::Type}; 4 | 5 | use super::traits::{Call, FResult}; 6 | 7 | // native functions 8 | #[derive(Clone)] 9 | pub struct Func { 10 | name: String, 11 | args: usize, 12 | exec: Rc, HashMap) -> Result>, 13 | } 14 | 15 | impl Func { 16 | pub fn new( 17 | name: &str, 18 | func: Rc, HashMap) -> Result>, 19 | args: usize, 20 | ) -> Self { 21 | Self { 22 | name: name.to_string(), 23 | exec: func, 24 | args, 25 | } 26 | } 27 | } 28 | 29 | impl Call for Func { 30 | fn arity(&self) -> usize { 31 | self.args 32 | } 33 | 34 | fn call(&self, interpreter: &mut Interpreter, args: Vec, opt_args: HashMap) -> FResult { 35 | (self.exec)(interpreter, args, opt_args) 36 | } 37 | 38 | fn to_string(&self) -> String { 39 | "".into() 40 | } 41 | 42 | fn name(&self) -> String { 43 | self.name.clone() // dirty hack 44 | } 45 | } 46 | 47 | impl Debug for Func { 48 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 49 | write!(f, "Native Function") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/functions/traits.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{error::Error, interpreter::Interpreter, types::Type}; 4 | 5 | pub type FResult = Result; 6 | 7 | pub trait Call { 8 | fn arity(&self) -> usize; 9 | fn call(&self, interpreter: &mut Interpreter, args: Vec, opt_args: HashMap) -> FResult; 10 | fn to_string(&self) -> String; 11 | fn name(&self) -> String; 12 | } 13 | -------------------------------------------------------------------------------- /src/functions/user.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Debug, collections::HashMap}; 2 | 3 | use crate::{ 4 | interpreter::Interpreter, 5 | nodes::stmt::Stmt, 6 | token::{TType, Token}, 7 | types::Type, 8 | error::ErrorType 9 | }; 10 | 11 | use super::traits::{Call, FResult}; 12 | 13 | // user-defined functions 14 | #[derive(Clone)] 15 | pub struct FuncCallable { 16 | name: Token, 17 | args: Vec, 18 | optional_args: HashMap, 19 | block: Vec, 20 | } 21 | 22 | impl FuncCallable { 23 | pub fn new(name: Token, args: Vec, optional_args: HashMap, block: Vec) -> Self { 24 | Self { name, args, optional_args, block } 25 | } 26 | } 27 | 28 | impl Call for FuncCallable { 29 | fn arity(&self) -> usize { 30 | self.args.len() 31 | } 32 | 33 | fn call(&self, interpreter: &mut Interpreter, args: Vec, opt_args: HashMap) -> FResult { 34 | interpreter.environ.push_scope(); 35 | 36 | for (i, name) in self.args.iter().enumerate() { 37 | match &name.ttype { 38 | TType::Identifier(n) => interpreter.environ.define(&n, &args[i]), 39 | _ => panic!(), 40 | } 41 | } 42 | 43 | 44 | for (name, val) in self.optional_args.iter() { 45 | interpreter.environ.define(name, match &opt_args.get(name) { 46 | Some(t) => t, 47 | None => val 48 | }); 49 | } 50 | 51 | let out = interpreter.eval_block(&self.block, false); 52 | interpreter.environ.pop_scope(); 53 | 54 | return match out { 55 | Ok(v) => match v { 56 | Some(v) => Ok(v), 57 | _ => Ok(Type::Nil) 58 | }, 59 | Err(e) => match e.error_type { 60 | ErrorType::Return(v) => Ok(v), 61 | _ => Err(e) 62 | } 63 | }; 64 | 65 | // if let Err(e) = out { 66 | // if let ErrorType::Return(v) = e.error_type { 67 | // return Ok(v) 68 | // } 69 | 70 | // return Err(e) 71 | // } 72 | 73 | // Ok(Type::Nil) 74 | } 75 | 76 | fn to_string(&self) -> String { 77 | format!( 78 | "", 79 | match &self.name.ttype { 80 | TType::Identifier(s) => s, 81 | _ => panic!(), 82 | } 83 | ) 84 | .into() 85 | } 86 | 87 | fn name(&self) -> String { 88 | match &self.name.ttype { 89 | TType::Identifier(name) => name.clone(), 90 | _ => panic!(), 91 | } 92 | } 93 | } 94 | 95 | impl Debug for FuncCallable { 96 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 97 | write!(f, "Native Function") 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/interpreter.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, collections::HashMap, rc::Rc}; 2 | 3 | use crate::{ 4 | environment::Environment, 5 | error::{Error, ErrorType, LineInfo}, 6 | functions::{Call, FuncCallable, FuncType}, 7 | nodes::{ 8 | expr::Expr, 9 | stmt::{ImportType, Stmt}, 10 | }, 11 | stdlib::Stdlib, 12 | token::{TType, Token}, 13 | types::{array::Array, module::Module}, 14 | types::{map::Map, Type}, 15 | }; 16 | 17 | type IResult = Result; 18 | // type SResult = Result<(), Error>; 19 | 20 | #[derive(Clone)] 21 | pub struct Interpreter { 22 | pub nodes: Vec, 23 | pub environ: Environment, 24 | pub locals: HashMap, 25 | pub stdlib: Stdlib, 26 | } 27 | 28 | impl Interpreter { 29 | // static methods 30 | pub fn new(nodes: Vec, environ: Environment) -> Self { 31 | Self { 32 | nodes, 33 | environ, 34 | locals: HashMap::new(), 35 | stdlib: Stdlib::new(), 36 | } 37 | } 38 | 39 | pub fn init(&mut self) -> IResult { 40 | let mut eval = Type::Nil; 41 | for stmt in self.nodes.clone() { 42 | eval = self.eval_stmt(&stmt)?; 43 | } 44 | 45 | Ok(eval) 46 | } 47 | 48 | // eval 49 | fn eval_stmt(&mut self, node: &Stmt) -> IResult { 50 | match node { 51 | Stmt::ExprStmt(s) => self.eval_expr(s), 52 | Stmt::VarDecl(decls) => { 53 | for (name, val) in decls { 54 | let val = self.eval_expr(&val)?; 55 | self.environ.define(&name, &val); 56 | } 57 | Ok(Type::Nil) 58 | } 59 | Stmt::Block(stmts) => { 60 | self.eval_block(stmts, false)?; 61 | Ok(Type::Nil) 62 | } 63 | Stmt::IfStmt(cond, true_br, elif_brs, else_br) => { 64 | Ok(self.eval_if(cond, true_br, elif_brs, else_br)?) 65 | } 66 | Stmt::WhileStmt(cond, block) => { 67 | loop { 68 | let cond = self.eval_expr(cond)?; 69 | if !self.is_truthy(&cond) { 70 | break; 71 | } 72 | 73 | let out = self.eval_block(block, false); 74 | 75 | if let Err(e) = out { 76 | if e.error_type == ErrorType::Break { 77 | break; 78 | } 79 | 80 | if e.error_type == ErrorType::Continue { 81 | continue; 82 | } 83 | 84 | return Err(e); 85 | } 86 | } 87 | 88 | Ok(Type::Nil) 89 | } 90 | Stmt::Break(t) => Err(Error::new( 91 | t.lineinfo, 92 | "Break statements can only be inside loops.".into(), 93 | ErrorType::Break, 94 | )), 95 | Stmt::Continue(t) => Err(Error::new( 96 | t.lineinfo, 97 | "Continue statements can only be inside loops.".into(), 98 | ErrorType::Continue, 99 | )), 100 | Stmt::Return(t, val) => { 101 | let expr; 102 | if let Some(v) = val { 103 | expr = self.eval_expr(v)?; 104 | } else { 105 | expr = Type::Nil; 106 | } 107 | 108 | Err(Error::new( 109 | t.lineinfo, 110 | "Return statements can only be inside functions.".into(), 111 | ErrorType::Return(expr), 112 | )) 113 | } 114 | Stmt::Function(name, args, optional_args, block) => { 115 | let var_name = match &name.ttype { 116 | TType::Identifier(x) => x, 117 | _ => panic!(), 118 | }; 119 | 120 | let mut opt_args = HashMap::new(); 121 | 122 | for (tok, expr) in optional_args { 123 | opt_args.insert( 124 | match &tok.ttype { 125 | TType::Identifier(name) => name.clone(), 126 | _ => panic!(), 127 | }, 128 | self.eval_expr(expr)?, 129 | ); 130 | } 131 | 132 | self.environ.define( 133 | &var_name, 134 | &Type::Func(FuncType::User(FuncCallable::new( 135 | name.clone(), 136 | args.clone(), 137 | opt_args.clone(), 138 | block.clone(), 139 | ))), 140 | ); 141 | 142 | Ok(Type::Nil) 143 | } 144 | Stmt::UseStmt(module, import_type) => { 145 | if let TType::Identifier(name) = &module.ttype { 146 | let module = &self.stdlib.mods[name].fns; 147 | 148 | match &import_type { 149 | ImportType::Star => { 150 | for (name, func) in module { 151 | self.environ.define(name, &func.clone()); 152 | } 153 | } 154 | 155 | ImportType::Multiple(fns) => { 156 | for fn_name in fns { 157 | let name_string = match &fn_name.ttype { 158 | TType::Identifier(s) => s, 159 | _ => panic!(), 160 | }; 161 | 162 | let maybe_func = module.get(name_string); 163 | 164 | match maybe_func { 165 | Some(func) => { 166 | self.environ.define(name_string, &func.clone()); 167 | } 168 | _ => {} 169 | } 170 | } 171 | } 172 | ImportType::Mod => self.environ.define( 173 | name, 174 | &Type::Module(Module::new(name.clone(), module.clone())), 175 | ), 176 | } 177 | 178 | Ok(Type::Nil) 179 | } else { 180 | panic!(); 181 | } 182 | } 183 | } 184 | } 185 | 186 | fn eval_expr(&mut self, node: &Expr) -> IResult { 187 | match node { 188 | Expr::Binary(left, tok, right) => { 189 | let lval = self.eval_expr(&left.as_ref())?; 190 | let rval = self.eval_expr(&right.as_ref())?; 191 | 192 | Ok(match tok.ttype { 193 | TType::Plus => self.out(&lval.add(&rval), &tok)?, 194 | TType::Minus => self.out(&lval.sub(&rval), &tok)?, 195 | TType::Times => self.out(&lval.mult(&rval), &tok)?, 196 | TType::Divide => self.out(&lval.div(&rval), &tok)?, 197 | TType::Mod => self.out(&lval.modulo(&rval), &tok)?, 198 | TType::Pow => self.out(&lval.pow(&rval), &tok)?, 199 | 200 | TType::EqEq => Type::Bool(lval == rval), 201 | TType::NotEq => Type::Bool(lval != rval), 202 | 203 | TType::Less => Type::Bool(lval < rval), 204 | TType::Greater => Type::Bool(lval > rval), 205 | TType::LessEq => Type::Bool(lval <= rval), 206 | TType::GreaterEq => Type::Bool(lval >= rval), 207 | _ => panic!(), 208 | }) 209 | } 210 | Expr::Grouping(expr) => Ok(self.eval_expr(&expr.as_ref())?), 211 | Expr::Literal(val) => Ok(val.clone()), 212 | Expr::Unary(tok, right) => { 213 | let rval = self.eval_expr(&right.as_ref())?; 214 | 215 | match tok.ttype { 216 | TType::Not => Ok(Type::Bool(self.is_truthy(&rval))), 217 | TType::Minus => match rval { 218 | Type::Float(v) => Ok(Type::Float(-v)), 219 | _ => Err(Error::new( 220 | tok.lineinfo, 221 | "Only numbers can be negated.".into(), 222 | ErrorType::TypeError, 223 | )), 224 | }, 225 | _ => panic!(), 226 | } 227 | } 228 | Expr::Variable(v) => { 229 | let some_key = self.locals.get(&v.lineinfo); 230 | 231 | if let Some(key) = some_key { 232 | Ok(self.environ.get_at(*key, v)) 233 | } else { 234 | self.environ.get(v) 235 | } 236 | } 237 | Expr::Assign(k, v) => { 238 | let val = self.eval_expr(&v)?; 239 | 240 | self.assign(k, &val) 241 | } 242 | Expr::Block(stmts) => Ok(self.eval_block(stmts, true)?.unwrap()), 243 | Expr::Logical(left, tok, right) => { 244 | let lval = self.eval_expr(left)?; 245 | 246 | if tok.ttype == TType::Or { 247 | if self.is_truthy(&lval) { 248 | return Ok(lval); 249 | } 250 | } else { 251 | if !self.is_truthy(&lval) { 252 | return Ok(lval); 253 | } 254 | } 255 | 256 | self.eval_expr(right) 257 | } 258 | Expr::Ternary(condition, true_br, else_br) => { 259 | let cond = self.eval_expr(condition)?; 260 | 261 | if self.is_truthy(&cond) { 262 | return Ok(self.eval_expr(true_br)?); 263 | } 264 | 265 | Ok(self.eval_expr(else_br)?) 266 | } 267 | Expr::Call(func, tok, args, optional_args) => { 268 | let callee = self.eval_expr(func)?; 269 | 270 | let mut params: Vec = Vec::new(); 271 | let mut opt_params: HashMap = HashMap::new(); 272 | for arg in args { 273 | params.push(self.eval_expr(arg)?); 274 | } 275 | 276 | for (tok, expr) in optional_args { 277 | opt_params.insert(tok.clone(), self.eval_expr(expr)?); 278 | } 279 | 280 | if let Type::Func(func) = callee { 281 | let ar = func.arity(); 282 | 283 | if params.len() != ar { 284 | return Err(Error::new( 285 | tok.lineinfo, 286 | format!( 287 | "Expected {} argument{}, but got {}.", 288 | ar, 289 | if ar == 1 { "" } else { "s" }, 290 | params.len() 291 | ) 292 | .into(), 293 | ErrorType::TypeError, 294 | )); 295 | } 296 | 297 | return func.call(self, params, opt_params); 298 | } else { 299 | return Err(Error::new( 300 | tok.lineinfo, 301 | "Only functions can be called.".into(), 302 | ErrorType::TypeError, 303 | )); 304 | } 305 | } 306 | Expr::IfExpr(cond, true_br, elif_brs, else_br) => { 307 | Ok(self.eval_if(cond, true_br, elif_brs, else_br)?) 308 | } 309 | Expr::Get(val, tok, key) => { 310 | let v = self.eval_expr(val)?; 311 | let k = self.eval_expr(key)?; 312 | 313 | self.out(&v.index(k), &tok) 314 | } 315 | Expr::Array(itms) => { 316 | let mut out: Vec = Vec::new(); 317 | 318 | for itm in itms { 319 | out.push(self.eval_expr(itm)?); 320 | } 321 | 322 | Ok(Type::Array(Rc::new(RefCell::new(Array::new(out))))) 323 | } 324 | Expr::Range(left, tok, right, inclusive) => { 325 | let left = self.eval_expr(left)?; 326 | let right = self.eval_expr(right)?; 327 | 328 | if let (Type::Float(l), Type::Float(r)) = (left, right) { 329 | let mut out: Vec = Vec::new(); 330 | let (l, r) = (l.floor() as i32, r.floor() as i32); 331 | 332 | if l > r { 333 | if *inclusive { 334 | for i in (r..=l).rev() { 335 | out.push(Type::Float(i as f32)); 336 | } 337 | } else { 338 | for i in (r + 1..=l).rev() { 339 | out.push(Type::Float(i as f32)); 340 | } 341 | } 342 | } else { 343 | if *inclusive { 344 | for i in l..=r { 345 | out.push(Type::Float(i as f32)); 346 | } 347 | } else { 348 | for i in l..r { 349 | out.push(Type::Float(i as f32)); 350 | } 351 | } 352 | } 353 | 354 | return Ok(Type::Array(Rc::new(RefCell::new(Array::new(out))))); 355 | } else { 356 | Err(Error::new( 357 | tok.lineinfo, 358 | "Ranges can only contain numbers.".into(), 359 | ErrorType::TypeError, 360 | )) 361 | } 362 | } 363 | Expr::Map(v) => { 364 | let mut out = HashMap::new(); 365 | 366 | for (key, value) in v { 367 | let key = self.eval_expr(key)?.to_string(); 368 | let value = self.eval_expr(value)?; 369 | 370 | out.insert(key, value); 371 | } 372 | 373 | Ok(Type::Map(Rc::new(RefCell::new(Map::new(out))))) 374 | } 375 | Expr::Set(var, brack, i, val) => { 376 | let collection = self.eval_expr(var)?; 377 | let i = self.eval_expr(i)?; 378 | let val = self.eval_expr(val)?; 379 | 380 | self.out(&collection.assign(i, val), brack) 381 | } 382 | Expr::Prop(var, prop) => { 383 | let module = self.eval_expr(var)?; 384 | match module { 385 | Type::Module(module) => { 386 | // correct 387 | let prop_string = match &prop.ttype { 388 | TType::Identifier(v) => v, 389 | _ => panic!(), 390 | }; 391 | 392 | let maybe_fn = module.fns.get(prop_string); 393 | 394 | if let Some(out) = maybe_fn { 395 | Ok(out.clone()) 396 | } else { 397 | Err(Error::new( 398 | prop.lineinfo, 399 | format!( 400 | "The item '{}' does not exist in the module '{}'.", 401 | prop_string, module.name 402 | ) 403 | .into(), 404 | ErrorType::TypeError, 405 | )) 406 | } 407 | } 408 | _ => Err(Error::new( 409 | prop.lineinfo, 410 | "Only modules have properties.".into(), 411 | ErrorType::TypeError, 412 | )), 413 | } 414 | } 415 | } 416 | } 417 | 418 | pub fn eval_block(&mut self, block: &Vec, ret_val: bool) -> Result, Error> { 419 | self.environ.push_scope(); 420 | 421 | let mut val = Type::Nil; 422 | for stmt in block { 423 | if ret_val { 424 | val = match self.eval_stmt(stmt) { 425 | Ok(v) => v, 426 | Err(e) => { 427 | // this could be a return 428 | self.environ.pop_scope(); 429 | return Err(e); 430 | } 431 | }; 432 | } else { 433 | match self.eval_stmt(stmt) { 434 | Ok(v) => v, 435 | Err(e) => { 436 | self.environ.pop_scope(); 437 | return Err(e); 438 | } 439 | }; 440 | } 441 | } 442 | 443 | self.environ.pop_scope(); 444 | 445 | if ret_val { 446 | Ok(Some(val)) 447 | } else { 448 | Ok(None) 449 | } 450 | } 451 | 452 | fn eval_if( 453 | &mut self, 454 | cond: &Expr, 455 | true_br: &Vec, 456 | elif_brs: &Vec<(Expr, Vec)>, 457 | else_br: &Option>, 458 | ) -> IResult { 459 | let cond_val = self.eval_expr(cond)?; 460 | 461 | if self.is_truthy(&cond_val) { 462 | return Ok(self.eval_block(true_br, true)?.unwrap()); 463 | } 464 | if elif_brs.len() != 0 { 465 | for (cond, elif_block) in elif_brs { 466 | let cond_val = self.eval_expr(cond)?; 467 | if self.is_truthy(&cond_val) { 468 | return Ok(self.eval_block(elif_block, true)?.unwrap()); 469 | } 470 | } 471 | } 472 | if let Some(else_block) = else_br { 473 | return Ok(self.eval_block(else_block, true)?.unwrap()); 474 | } 475 | 476 | Ok(Type::Nil) 477 | } 478 | 479 | // util 480 | fn out(&self, val: &Result, tok: &Token) -> Result { 481 | match val { 482 | Ok(r) => Ok(r.clone()), 483 | Err(t) => return Err(Error::new(tok.lineinfo, t.clone().0, t.clone().1)), 484 | } 485 | } 486 | 487 | fn is_truthy(&self, v: &Type) -> bool { 488 | match v { 489 | Type::Nil => false, 490 | Type::Bool(v) => *v, 491 | _ => true, 492 | } 493 | } 494 | 495 | fn assign(&mut self, var: &Token, val: &Type) -> Result { 496 | let some_key = self.locals.get(&var.lineinfo); 497 | 498 | if let Some(key) = some_key { 499 | self.environ.assign_at(*key, var, val)?; 500 | } else { 501 | self.environ.assign(var, val)?; 502 | } 503 | 504 | Ok(val.clone()) 505 | } 506 | 507 | pub fn resolve(&mut self, tok: Token, depth: usize) { 508 | self.locals.insert(tok.lineinfo, depth); 509 | } 510 | } 511 | -------------------------------------------------------------------------------- /src/lexer.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Error, ErrorNote, ErrorType, LineInfo}; 2 | use crate::token::{TType, Token}; 3 | 4 | use maplit::hashmap; 5 | use std::char::from_u32 as char_from_u32; 6 | use std::collections::HashMap; 7 | 8 | pub struct Lexer { 9 | code: String, 10 | chars: Vec, 11 | i: usize, // index 12 | info: LineInfo, 13 | tokens: Vec, 14 | keywords: HashMap, 15 | } 16 | 17 | impl Lexer { 18 | pub fn new(code: &String) -> Self { 19 | Self { 20 | code: code.to_string(), 21 | chars: code.chars().collect(), 22 | i: 0, 23 | info: LineInfo::new(1, 0), 24 | tokens: vec![], 25 | keywords: hashmap! { 26 | "true".into() => TType::True, 27 | "false".into() => TType::False, 28 | "nil".into() => TType::Nil, 29 | "fn".into() => TType::Fn, 30 | "return".into() => TType::Return, 31 | "var".into() => TType::Var, 32 | "use".into() => TType::Use, 33 | "do".into() => TType::Do, 34 | "while".into() => TType::While, 35 | "for".into() => TType::For, 36 | "in".into() => TType::In, 37 | "break".into() => TType::Break, 38 | "continue".into() => TType::Continue, 39 | "or".into() => TType::Or, 40 | "and".into() => TType::And, 41 | "if".into() => TType::If, 42 | "else".into() => TType::Else, 43 | "elif".into() => TType::Elif, 44 | }, 45 | } 46 | } 47 | 48 | pub fn set_lineinfo(&mut self, info: LineInfo) { 49 | self.info = info; 50 | } 51 | 52 | pub fn init(&mut self) -> Result, Error> { 53 | while self.i < self.code.len() { 54 | self.lex_char()?; 55 | } 56 | 57 | self.append_token(TType::EOF); 58 | 59 | Ok(self.tokens.clone()) 60 | } 61 | 62 | pub fn lex_char(&mut self) -> Result<(), Error> { 63 | let char = self.peek(); 64 | self.next(); 65 | 66 | match char { 67 | '{' => { 68 | if self.get('{') { 69 | self.append_token(TType::LeftBBrace) 70 | } else { 71 | self.append_token(TType::LeftBrace) 72 | } 73 | } 74 | '}' => { 75 | if self.get('}') { 76 | self.append_token(TType::RightBBrace) 77 | } else { 78 | self.append_token(TType::RightBrace) 79 | } 80 | } 81 | '(' => self.append_token(TType::LeftParen), 82 | ')' => self.append_token(TType::RightParen), 83 | '[' => self.append_token(TType::LeftBrack), 84 | ']' => self.append_token(TType::RightBrack), 85 | 86 | '!' => { 87 | if self.get('=') { 88 | self.append_token(TType::NotEq) 89 | } else { 90 | self.append_token(TType::Not) 91 | } 92 | } 93 | '=' => { 94 | if self.get('=') { 95 | self.append_token(TType::EqEq) 96 | } else { 97 | self.append_token(TType::Eq) 98 | } 99 | } 100 | 101 | '>' => { 102 | if self.get('=') { 103 | self.append_token(TType::GreaterEq) 104 | } else { 105 | self.append_token(TType::Greater) 106 | } 107 | } 108 | '<' => { 109 | if self.get('=') { 110 | self.append_token(TType::LessEq) 111 | } else { 112 | self.append_token(TType::Less) 113 | } 114 | } 115 | 116 | '"' | '\'' => { 117 | let lf = self.info; 118 | let str_type = char; // " or ' 119 | let mut str = String::new(); 120 | 121 | while self.is_valid() && self.peek() != str_type { 122 | if self.peek() == '\n' { 123 | self.newline(); 124 | } 125 | 126 | if self.peek() == '\\' { 127 | self.next(); 128 | let ch = self.peek(); 129 | self.next(); 130 | 131 | str.push(match ch { 132 | 'n' => '\n', 133 | 'r' => '\r', 134 | 't' => '\t', 135 | 'a' => '\x07', 136 | 'b' => '\x08', 137 | 'e' => '\x1b', 138 | 'f' => '\x0c', 139 | 'v' => '\x0b', 140 | '\\' => '\\', 141 | '\'' => '\'', 142 | '\"' => '\"', 143 | '?' => '?', 144 | 'o' => u8::from_str_radix(self.read_n(3).as_str(), 8) 145 | .map_err(|_| ()) 146 | .and_then(|c| char_from_u32(c as u32).ok_or(())) 147 | .map_err(|_| { 148 | Error::new( 149 | self.info, 150 | "Invalid string escape.".into(), 151 | ErrorType::SyntaxError, 152 | ) 153 | })?, 154 | 'x' => u8::from_str_radix(self.read_n(2).as_str(), 16) 155 | .map_err(|_| ()) 156 | .and_then(|c| char_from_u32(c as u32).ok_or(())) 157 | .map_err(|_| { 158 | Error::new( 159 | self.info, 160 | "Invalid string escape.".into(), 161 | ErrorType::SyntaxError, 162 | ) 163 | })?, 164 | 'u' => u16::from_str_radix(self.read_n(4).as_str(), 16) 165 | .map_err(|_| ()) 166 | .and_then(|c| char_from_u32(c as u32).ok_or(())) 167 | .map_err(|_| { 168 | Error::new( 169 | self.info, 170 | "Invalid string escape.".into(), 171 | ErrorType::SyntaxError, 172 | ) 173 | })?, 174 | 'U' => u32::from_str_radix(self.read_n(8).as_str(), 16) 175 | .map_err(|_| ()) 176 | .and_then(|c| char_from_u32(c).ok_or(())) 177 | .map_err(|_| { 178 | Error::new( 179 | self.info, 180 | "Invalid string escape.".into(), 181 | ErrorType::SyntaxError, 182 | ) 183 | })?, 184 | _ => { 185 | return Err(Error::new( 186 | self.info, 187 | "Invalid string escape.".into(), 188 | ErrorType::SyntaxError, 189 | )) 190 | } 191 | }); 192 | } else { 193 | str.push(self.peek()); 194 | self.next(); 195 | } 196 | } 197 | 198 | if !self.is_valid() { 199 | return Err(Error::new_n( 200 | self.info, 201 | String::from("Unterminated string."), 202 | ErrorType::SyntaxError, 203 | vec![ErrorNote::Expect(lf, "String starts here.".into())], 204 | )); 205 | } 206 | 207 | self.next(); // " 208 | 209 | self.tokens.push(Token { 210 | ttype: TType::String(str), 211 | lineinfo: self.info, 212 | }); 213 | } 214 | 215 | ',' => self.append_token(TType::Comma), 216 | '.' => { 217 | if self.get('.') { 218 | self.append_token(TType::DotDot) 219 | } else if self.get('=') { 220 | self.append_token(TType::DotEq) 221 | } else { 222 | self.append_token(TType::Dot) 223 | } 224 | } 225 | ';' => self.append_token(TType::Semi), 226 | 227 | // ternary operator 228 | '?' => self.append_token(TType::Question), 229 | ':' => self.append_token(TType::Colon), 230 | 231 | // operators 232 | '+' => { 233 | if self.get('=') { 234 | self.append_token(TType::PlusEq) 235 | } else { 236 | self.append_token(TType::Plus) 237 | } 238 | } 239 | '-' => { 240 | if self.get('=') { 241 | self.append_token(TType::MinusEq) 242 | } else { 243 | self.append_token(TType::Minus) 244 | } 245 | } 246 | '*' => { 247 | if self.get('=') { 248 | self.append_token(TType::TimesEq) 249 | } else if self.get('*') { 250 | if self.get('=') { 251 | self.append_token(TType::PowEq) 252 | } else { 253 | self.append_token(TType::Pow) 254 | } 255 | } else { 256 | self.append_token(TType::Times) 257 | } 258 | } 259 | '%' => { 260 | if self.get('=') { 261 | self.append_token(TType::ModEq) 262 | } else { 263 | self.append_token(TType::Mod) 264 | } 265 | } 266 | '/' => { 267 | if self.get('=') { 268 | self.append_token(TType::DivideEq) 269 | } else if self.get('/') { 270 | while self.peek() != '\n' && self.is_valid() { 271 | self.next(); 272 | } 273 | } else if self.get('*') { 274 | let lf = self.info; 275 | 276 | while self.is_valid() && (self.peek() != '*' && self.peek_n(1) != '/') { 277 | if self.peek() == '\n' { 278 | self.newline(); 279 | } 280 | 281 | self.next(); 282 | } 283 | 284 | if !self.is_valid() { 285 | return Err(Error::new_n( 286 | self.info, 287 | String::from("Unterminated multiline comment."), 288 | ErrorType::SyntaxError, 289 | vec![ErrorNote::Expect(lf, "Expected '*/' to match this.".into())], 290 | )); 291 | } 292 | 293 | self.next(); // * 294 | self.next() // / 295 | } else { 296 | self.append_token(TType::Divide) 297 | } 298 | } 299 | ' ' | '\r' | '\t' => (), 300 | '\n' => self.newline(), 301 | 302 | _ => { 303 | if self.is_alpha(char) { 304 | let mut name = String::from(char); 305 | while self.is_valid() && self.is_alphanum(self.peek()) { 306 | name += &self.peek().to_string(); 307 | self.next(); 308 | } 309 | 310 | if self.keywords.contains_key(&name) { 311 | self.tokens.push(Token { 312 | ttype: self.keywords[&name].clone(), 313 | lineinfo: self.info, 314 | }); 315 | } else { 316 | self.tokens.push(Token { 317 | ttype: TType::Identifier(name), 318 | lineinfo: self.info, 319 | }); 320 | } 321 | } else if self.is_number(char) { 322 | let mut num = String::from(char); 323 | 324 | while self.is_valid() && (self.is_number(self.peek()) || self.peek() == '_') { 325 | let n = self.peek(); 326 | if n != '_' { 327 | num += &n.to_string(); 328 | } 329 | self.next(); 330 | } 331 | 332 | if self.peek() == '.' && (self.peek_n(1) != '.' && self.peek_n(1) != '=') { 333 | num += &self.peek().to_string(); 334 | self.next(); // . 335 | 336 | while self.is_valid() && (self.is_number(self.peek()) || self.peek() == '_') 337 | { 338 | let n = self.peek(); 339 | if n != '_' { 340 | num += &n.to_string(); 341 | } 342 | self.next(); 343 | } 344 | } 345 | 346 | self.tokens.push(Token { 347 | ttype: TType::Number(num.parse().unwrap()), 348 | lineinfo: self.info, 349 | }); 350 | } else { 351 | return Err(Error::new( 352 | self.info, 353 | format!("Invalid token '{}'.", char), 354 | ErrorType::SyntaxError, 355 | )); 356 | } 357 | } 358 | }; 359 | Ok(()) 360 | } 361 | 362 | // characters 363 | fn is_alpha(&self, char: char) -> bool { 364 | ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' 365 | } 366 | 367 | fn is_number(&self, char: char) -> bool { 368 | '0' <= char && char <= '9' 369 | } 370 | 371 | fn is_alphanum(&self, char: char) -> bool { 372 | self.is_alpha(char) || self.is_number(char) 373 | } 374 | 375 | // advancing 376 | fn next(&mut self) { 377 | self.i += 1; 378 | self.info.col += 1; 379 | } 380 | 381 | fn newline(&mut self) { 382 | self.info.line += 1; 383 | self.info.col = 0; 384 | } 385 | 386 | // util 387 | fn append_token(&mut self, token: TType) { 388 | self.tokens.push(Token { 389 | ttype: token, 390 | lineinfo: self.info, 391 | }); 392 | } 393 | 394 | // lookahead 395 | fn get(&mut self, char: char) -> bool { 396 | if self.peek() != char { 397 | return false; 398 | } 399 | 400 | self.i += 1; 401 | true 402 | } 403 | 404 | fn peek(&self) -> char { 405 | if !self.is_valid() { 406 | return '\0'; 407 | } 408 | self.chars[self.i] 409 | } 410 | 411 | fn peek_n(&self, n: usize) -> char { 412 | if self.i + n >= self.chars.len() { 413 | return '\0'; 414 | } 415 | self.chars[self.i + n] 416 | } 417 | 418 | fn read_n(&mut self, n: usize) -> String { 419 | let mut string = String::new(); 420 | for _ in 0..n { 421 | string.push(self.peek()); 422 | self.next(); 423 | } 424 | 425 | string 426 | } 427 | 428 | fn is_valid(&self) -> bool { 429 | self.i < self.chars.len() 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate maplit; 2 | 3 | mod environment; 4 | mod error; 5 | mod functions; 6 | mod interpreter; 7 | mod lexer; 8 | mod nodes; 9 | mod parser; 10 | mod repl; 11 | mod resolver; 12 | mod tests; 13 | mod token; 14 | mod types; 15 | mod stdlib; 16 | 17 | use std::time::Instant; 18 | use std::{env, fs, process}; 19 | 20 | use interpreter::Interpreter; 21 | use lexer::Lexer; 22 | use parser::Parser; 23 | use resolver::Resolver; 24 | 25 | use crate::environment::Environment; 26 | use crate::error::Error; 27 | use crate::types::Type; 28 | 29 | use clap::{App, Arg}; 30 | 31 | const PROGRAM_NAME: &str = env!("CARGO_PKG_NAME"); 32 | const PROGRAM_VERSION: &str = env!("CARGO_PKG_VERSION"); 33 | const PROGRAM_AUTHORS: &str = env!("CARGO_PKG_AUTHORS"); 34 | const PROGRAM_ABOUT: &str = env!("CARGO_PKG_DESCRIPTION"); 35 | 36 | fn main() { 37 | let matches = App::new(PROGRAM_NAME) 38 | .version(PROGRAM_VERSION) 39 | .author(PROGRAM_AUTHORS) 40 | .about(PROGRAM_ABOUT) 41 | .arg( 42 | Arg::with_name("verbose") 43 | .short("v") 44 | .long("verbose") 45 | .help("Log extra information"), 46 | ) 47 | .arg( 48 | Arg::with_name("repl") 49 | .short("r") 50 | .long("repl") 51 | .help("Run file and continue to REPL"), 52 | ) 53 | .arg( 54 | Arg::with_name("eval") 55 | .short("e") 56 | .long("eval") 57 | .value_name("CODE") 58 | .help("Evaluate CODE instead of a file") 59 | .takes_value(true) 60 | .conflicts_with("FILE"), 61 | ) 62 | .arg(Arg::with_name("FILE").help("File to run").index(1)) 63 | .get_matches(); 64 | 65 | let verbose = matches.is_present("verbose"); 66 | 67 | let code = if let Some(file) = matches.value_of("FILE") { 68 | // run file contents 69 | 70 | fs::read_to_string(file).unwrap_or_else(|err| { 71 | eprintln!("Error reading file: {}", err); 72 | process::exit(1) 73 | }) 74 | } else if let Some(code) = matches.value_of("eval") { 75 | // run argument value 76 | 77 | String::from(code) 78 | } else { 79 | // no code to run, drop into repl 80 | 81 | println!("Welcome to the Europa interactive REPL.\nUse \".exit\" or \"use io.exit; exit(0);\" to exit."); 82 | 83 | // start no-context repl 84 | let environ = Environment::new(); 85 | repl::init(environ, verbose); 86 | 87 | return; 88 | }; 89 | 90 | // load and run code 91 | let mut environ = Environment::new(); 92 | match run_string(&code, &mut environ, verbose) { 93 | Err(e) => { 94 | e.display(&code); 95 | process::exit(1); 96 | } 97 | Ok(eval) => { 98 | if matches.is_present("repl") { 99 | println!("{:?}", eval); 100 | 101 | // drop into repl with environment 102 | repl::init(environ, verbose); 103 | } 104 | } 105 | } 106 | } 107 | 108 | // Loader for code, mutates Environment and returns evaluated (probably Nil) 109 | fn run_string( 110 | code: &String, 111 | environ: &mut Environment, 112 | verbose: bool, 113 | ) -> Result { 114 | // Tokenise code 115 | let mut time = Instant::now(); 116 | let tokens = Lexer::new(&code).init()?; 117 | 118 | if verbose { 119 | eprintln!("lexer {:?}", time.elapsed()); 120 | } 121 | 122 | // Turn tokens into AST 123 | time = Instant::now(); 124 | let tree = Parser::new(tokens).init()?; 125 | 126 | if verbose { 127 | eprintln!("parser {:?}", time.elapsed()); 128 | } 129 | 130 | // Create interpreter 131 | let mut interpreter = Interpreter::new(tree, environ.clone()); 132 | 133 | // Resolve variables 134 | time = Instant::now(); 135 | interpreter = Resolver::new(interpreter).init()?; 136 | 137 | if verbose { 138 | eprintln!("resolver {:?}", time.elapsed()); 139 | } 140 | 141 | // Run interpreter 142 | time = Instant::now(); 143 | let eval = interpreter.init()?; 144 | 145 | if verbose { 146 | eprintln!("interpreter {:?}", time.elapsed()); 147 | } 148 | 149 | *environ = interpreter.environ; 150 | 151 | Ok(eval) 152 | } 153 | -------------------------------------------------------------------------------- /src/nodes/expr.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::rc::Rc; 3 | 4 | use crate::nodes::stmt::Stmt; 5 | use crate::token::Token; 6 | use crate::types::Type; 7 | 8 | #[derive(Clone, Debug)] 9 | pub enum Expr { 10 | Assign(Token, Rc), 11 | Binary(Rc, Token, Rc), 12 | Grouping(Rc), 13 | Literal(Type), 14 | Unary(Token, Rc), 15 | Variable(Token), 16 | Block(Vec), 17 | Logical(Rc, Token, Rc), 18 | Ternary(Rc, Rc, Rc), 19 | Call(Rc, Token, Vec, HashMap), 20 | IfExpr(Rc, Vec, Vec<(Expr, Vec)>, Option>), 21 | Get(Rc, Token, Rc), 22 | Set(Rc, Token, Rc, Rc), 23 | Prop(Rc, Token), 24 | Array(Vec), 25 | Map(Vec<(Expr, Expr)>), 26 | Range(Rc, Token, Rc, bool), 27 | } 28 | -------------------------------------------------------------------------------- /src/nodes/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod expr; 2 | pub mod stmt; -------------------------------------------------------------------------------- /src/nodes/stmt.rs: -------------------------------------------------------------------------------- 1 | use crate::{nodes::expr::Expr, token::Token}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub enum Stmt { 5 | ExprStmt(Expr), 6 | VarDecl(Vec<(String, Expr)>), 7 | Block(Vec), 8 | IfStmt(Expr, Vec, Vec<(Expr, Vec)>, Option>), 9 | WhileStmt(Expr, Vec), 10 | Break(Token), 11 | Continue(Token), 12 | Return(Token, Option), 13 | Function(Token, Vec, Vec<(Token, Expr)>, Vec), 14 | UseStmt(Token, ImportType), 15 | } 16 | 17 | #[derive(Clone, Debug)] 18 | pub enum ImportType { 19 | Mod, 20 | Star, 21 | Multiple(Vec), 22 | } -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::rc::Rc; 3 | 4 | use crate::error::{Error, ErrorNote, ErrorType}; 5 | use crate::nodes::expr::Expr; 6 | use crate::nodes::stmt::{ImportType, Stmt}; 7 | use crate::token::{TType, Token}; 8 | use crate::types::Type; 9 | 10 | type PResult = Result; 11 | type SResult = Result; 12 | 13 | pub struct Parser { 14 | tokens: Vec, 15 | i: usize, 16 | } 17 | 18 | impl Parser { 19 | pub fn new(tokens: Vec) -> Self { 20 | Self { tokens, i: 0 } 21 | } 22 | 23 | pub fn init(&mut self) -> Result, Error> { 24 | let mut stmts: Vec = Vec::new(); 25 | 26 | while self.is_valid() { 27 | stmts.push(self.stmt()?); 28 | } 29 | 30 | Ok(stmts) 31 | } 32 | 33 | // recursive descent 34 | // statements 35 | fn stmt(&mut self) -> SResult { 36 | if self.get(&[TType::If]) { 37 | let (cond, if_br, elif_brs, else_br) = self.if_stmt()?; 38 | return Ok(Stmt::IfStmt(cond, if_br, elif_brs, else_br)); 39 | } 40 | if self.get(&[TType::Var]) { 41 | return self.var_decl(); 42 | } 43 | if self.get(&[TType::While]) { 44 | return self.while_stmt(); 45 | } 46 | if self.get(&[TType::Do]) { 47 | return self.dowhile_stmt(); 48 | } 49 | if self.get(&[TType::LeftBrace]) { 50 | return Ok(Stmt::Block(self.block()?)); 51 | } 52 | if self.get(&[TType::Break, TType::Continue, TType::Return]) { 53 | return self.controlflow_stmt(); 54 | } 55 | if self.get(&[TType::Fn]) { 56 | return self.fn_stmt(); 57 | } 58 | if self.get(&[TType::Use]) { 59 | return self.use_stmt(); 60 | } 61 | 62 | self.expr_stmt() 63 | } 64 | 65 | fn expr_stmt(&mut self) -> SResult { 66 | let expr = self.expr()?; 67 | 68 | if !self.get(&[TType::Semi]) { 69 | let semi = self.peek(); 70 | if semi.ttype != TType::RightBrace && semi.ttype != TType::EOF { 71 | return Err(Error::new_n( 72 | self.prev().lineinfo, 73 | "Expected ';' after statement.".into(), 74 | ErrorType::SyntaxError, 75 | vec![ErrorNote::Note( 76 | "The ';' is optional only if it is the last statement of a block.".into(), 77 | )], 78 | )); 79 | } 80 | } 81 | 82 | Ok(Stmt::ExprStmt(expr)) 83 | } 84 | 85 | fn if_stmt( 86 | &mut self, 87 | ) -> Result<(Expr, Vec, Vec<(Expr, Vec)>, Option>), Error> { 88 | let cond = self.expr()?; 89 | self.consume( 90 | TType::LeftBrace, 91 | "Expected '{' after if statement condition.".into(), 92 | )?; 93 | let true_br = self.block()?; 94 | let mut elif_brs: Vec<(Expr, Vec)> = Vec::new(); 95 | let else_br: Option>; 96 | 97 | if self.get(&[TType::Elif]) { 98 | loop { 99 | let elif_cond = self.expr()?; 100 | self.consume( 101 | TType::LeftBrace, 102 | "Expected '{' after elif statement condition.".into(), 103 | )?; 104 | elif_brs.push((elif_cond, self.block()?)); 105 | if !self.get(&[TType::Elif]) { 106 | break; 107 | } 108 | } 109 | } 110 | 111 | if self.get(&[TType::Else]) { 112 | self.consume(TType::LeftBrace, "Expected '{' after else keyword.".into())?; 113 | else_br = Some(self.block()?); 114 | } else { 115 | else_br = None; 116 | } 117 | 118 | Ok((cond, true_br, elif_brs, else_br)) 119 | } 120 | 121 | fn var_decl(&mut self) -> SResult { 122 | let mut vars = Vec::new(); 123 | 124 | loop { 125 | if let TType::Identifier(name) = self.next().ttype { 126 | let value = if self.get(&[TType::Eq]) { 127 | self.expr()? 128 | } else { 129 | Expr::Literal(Type::Nil) 130 | }; 131 | 132 | vars.push((name, value)); 133 | 134 | match self.next().ttype { 135 | TType::Semi => break, 136 | TType::Comma => continue, 137 | _ => { 138 | return Err(Error::new( 139 | self.prev().lineinfo, 140 | "Expected ',' or ';' after variable declaration.".into(), 141 | ErrorType::SyntaxError, 142 | )) 143 | } 144 | } 145 | } else { 146 | return Err(Error::new( 147 | self.prev().lineinfo, 148 | "Expected variable name".into(), 149 | ErrorType::SyntaxError, 150 | )); 151 | } 152 | } 153 | 154 | Ok(Stmt::VarDecl(vars)) 155 | } 156 | 157 | fn while_stmt(&mut self) -> SResult { 158 | let cond = self.expr()?; 159 | self.consume( 160 | TType::LeftBrace, 161 | "Expected '{' after while loop condition.".into(), 162 | )?; 163 | let body = self.block()?; 164 | 165 | Ok(Stmt::WhileStmt(cond, body)) 166 | } 167 | 168 | fn dowhile_stmt(&mut self) -> SResult { 169 | self.consume(TType::LeftBrace, "Expected '{' after do keyword.".into())?; 170 | let body = self.block()?; 171 | self.consume(TType::While, "Expected 'while' after do loop body.".into())?; 172 | let condition = self.expr()?; 173 | self.consume( 174 | TType::Semi, 175 | "Expected ';' after do while loop condition.".into(), 176 | )?; 177 | 178 | Ok(Stmt::Block(vec![ 179 | Stmt::Block(body.clone()), 180 | Stmt::WhileStmt(condition, body.clone()), 181 | ])) 182 | } 183 | 184 | fn controlflow_stmt(&mut self) -> SResult { 185 | let tok = self.prev(); 186 | let stype: String; 187 | 188 | let out = match tok.ttype { 189 | TType::Break => { 190 | stype = "break keyword".into(); 191 | Stmt::Break(tok) 192 | } 193 | TType::Continue => { 194 | stype = "continue keyword".into(); 195 | Stmt::Continue(tok) 196 | } 197 | TType::Return => { 198 | stype = "return keyword".into(); 199 | let val; 200 | 201 | if !self.check(TType::Semi) { 202 | val = Some(self.expr()?); 203 | } else { 204 | val = None; 205 | } 206 | 207 | Stmt::Return(tok, val) 208 | } 209 | _ => panic!(), 210 | }; 211 | 212 | self.consume(TType::Semi, format!("Expected ';' after {}", stype).into())?; 213 | Ok(out) 214 | } 215 | 216 | fn fn_stmt(&mut self) -> SResult { 217 | let name = self.peek(); 218 | 219 | if let TType::Identifier(_) = name.ttype { 220 | self.next(); 221 | let (params, optional_params, block) = self.finish_fn("function name".into())?; 222 | 223 | return Ok(Stmt::Function(name, params, optional_params, block)); 224 | } else { 225 | return Err(Error::new( 226 | name.lineinfo, 227 | "Expected function name after 'fn' keyword.".into(), 228 | ErrorType::SyntaxError, 229 | )); 230 | } 231 | } 232 | 233 | fn use_stmt(&mut self) -> SResult { 234 | let name = self.next(); 235 | 236 | let out = if matches!(name.ttype, TType::Identifier(_)) { 237 | // Stmt::UseStmt(name) 238 | if self.get(&[TType::Dot]) { 239 | // use io.* 240 | if self.get(&[TType::Times]) { 241 | Stmt::UseStmt(name, ImportType::Star) 242 | } else { 243 | // use io.{ 244 | if self.get(&[TType::LeftBrace]) { 245 | let mut fns = Vec::new(); 246 | 247 | while self.peek().ttype != TType::RightBrace { 248 | let next = self.next(); 249 | fns.push(match next.ttype { 250 | TType::Identifier(_) => next, 251 | _ => { 252 | return Err(Error::new( 253 | name.lineinfo, 254 | "Expected an identifier for the function name.".into(), 255 | ErrorType::SyntaxError, 256 | )) 257 | } 258 | }); 259 | 260 | if !self.get(&[TType::Comma]) && self.peek().ttype != TType::RightBrace 261 | { 262 | return Err(Error::new( 263 | self.peek().lineinfo, 264 | "Expected ',' after function value".into(), 265 | ErrorType::SyntaxError, 266 | )); 267 | } 268 | } 269 | 270 | self.next(); // } 271 | 272 | Stmt::UseStmt(name, ImportType::Multiple(fns)) 273 | 274 | // use io.println 275 | } else { 276 | let tok = self.next(); 277 | if let TType::Identifier(_) = tok.ttype { 278 | Stmt::UseStmt(name, ImportType::Multiple(vec![tok])) 279 | } else { 280 | return Err(Error::new( 281 | name.lineinfo, 282 | "Expected an identifier after '.'.".into(), 283 | ErrorType::SyntaxError, 284 | )); 285 | } 286 | } 287 | } 288 | } else { 289 | Stmt::UseStmt(name, ImportType::Mod) 290 | } 291 | } 292 | // todo: string syntax??? 293 | else { 294 | return Err(Error::new( 295 | name.lineinfo, 296 | "Expected an identifier or a string after use statement.".into(), 297 | ErrorType::SyntaxError, 298 | )); 299 | }; 300 | 301 | self.consume(TType::Semi, "Expected ';' after use statement.".into())?; 302 | 303 | Ok(out) 304 | } 305 | 306 | // expressions 307 | fn expr(&mut self) -> PResult { 308 | self.range() 309 | } 310 | 311 | fn range(&mut self) -> PResult { 312 | let mut expr = self.ternary()?; 313 | 314 | if self.get(&[TType::DotDot, TType::DotEq]) { 315 | let tok = self.prev(); 316 | let right = self.expr()?; 317 | 318 | let inclusive = match tok.ttype { 319 | TType::DotDot => false, 320 | TType::DotEq => true, 321 | _ => panic!(), 322 | }; 323 | 324 | expr = Expr::Range(Rc::new(expr), tok, Rc::new(right), inclusive); 325 | } 326 | 327 | Ok(expr) 328 | } 329 | 330 | fn ternary(&mut self) -> PResult { 331 | let mut expr = self.assign()?; 332 | 333 | if self.get(&[TType::Question]) { 334 | let true_br = self.expr()?; 335 | self.consume( 336 | TType::Colon, 337 | "Expected ':' after ternary if then expression.".into(), 338 | )?; 339 | let else_br = self.ternary()?; 340 | 341 | expr = Expr::Ternary(Rc::new(expr), Rc::new(true_br), Rc::new(else_br)); 342 | } 343 | 344 | Ok(expr) 345 | } 346 | 347 | fn assign(&mut self) -> PResult { 348 | let expr = self.or()?; 349 | 350 | // Set equal 351 | if self.get(&[ 352 | TType::Eq, 353 | TType::PlusEq, 354 | TType::MinusEq, 355 | TType::TimesEq, 356 | TType::DivideEq, 357 | TType::PowEq, 358 | TType::ModEq, 359 | ]) { 360 | let eq = self.prev(); 361 | let val = self.expr()?; 362 | 363 | let tok = if eq.ttype == TType::Eq { 364 | None 365 | } else { 366 | Some(Token { 367 | ttype: match eq.ttype { 368 | TType::PlusEq => TType::Plus, 369 | TType::MinusEq => TType::Minus, 370 | TType::TimesEq => TType::Times, 371 | TType::DivideEq => TType::Divide, 372 | TType::PowEq => TType::Pow, 373 | TType::ModEq => TType::Mod, 374 | _ => panic!(), 375 | }, 376 | ..eq 377 | }) 378 | }; 379 | 380 | if let Expr::Variable(var) = expr { 381 | return Ok(Expr::Assign( 382 | var.clone(), 383 | if let Some(t) = tok { 384 | Rc::new(Expr::Binary( 385 | Rc::new(Expr::Variable(var.clone())), 386 | t, 387 | Rc::new(val), 388 | )) 389 | } else { 390 | Rc::new(val) 391 | }, 392 | )); 393 | } else if let Expr::Get(ref var, ref brack, ref i) = expr { 394 | // var[idx] = val 395 | return Ok(Expr::Set( 396 | var.clone(), 397 | brack.clone(), 398 | i.clone(), 399 | if let Some(t) = tok { 400 | Rc::new(Expr::Binary(Rc::new(expr.clone()), t, Rc::new(val))) 401 | } else { 402 | Rc::new(val) 403 | }, 404 | )); 405 | } 406 | 407 | return Err(Error::new( 408 | eq.lineinfo, 409 | "Only variables and indices of arrays or maps can be assigned to.".into(), 410 | ErrorType::TypeError, 411 | )); 412 | } 413 | 414 | Ok(expr) 415 | } 416 | 417 | fn or(&mut self) -> PResult { 418 | let mut expr = self.and()?; 419 | 420 | while self.get(&[TType::Or]) { 421 | let op = self.prev(); 422 | let right = self.and()?; 423 | expr = Expr::Logical(Rc::new(expr), op, Rc::new(right)); 424 | } 425 | 426 | Ok(expr) 427 | } 428 | 429 | fn and(&mut self) -> PResult { 430 | let mut expr = self.equality()?; 431 | 432 | while self.get(&[TType::And]) { 433 | let op = self.prev(); 434 | let right = self.equality()?; 435 | expr = Expr::Logical(Rc::new(expr), op, Rc::new(right)); 436 | } 437 | 438 | Ok(expr) 439 | } 440 | 441 | fn equality(&mut self) -> PResult { 442 | let mut expr = self.comp()?; 443 | 444 | while self.get(&[TType::NotEq, TType::EqEq]) { 445 | let op = self.prev(); 446 | let right = self.comp()?; 447 | expr = Expr::Binary(Rc::new(expr), op, Rc::new(right)); 448 | } 449 | 450 | Ok(expr) 451 | } 452 | 453 | fn comp(&mut self) -> PResult { 454 | let mut expr = self.add()?; 455 | 456 | while self.get(&[TType::Greater, TType::Less, TType::GreaterEq, TType::LessEq]) { 457 | let op = self.prev(); 458 | let right = self.add()?; 459 | expr = Expr::Binary(Rc::new(expr), op, Rc::new(right)); 460 | } 461 | 462 | Ok(expr) 463 | } 464 | 465 | fn add(&mut self) -> PResult { 466 | let mut expr = self.mult()?; 467 | 468 | while self.get(&[TType::Minus, TType::Plus]) { 469 | let op = self.prev(); 470 | let right = self.mult()?; 471 | expr = Expr::Binary(Rc::new(expr), op, Rc::new(right)); 472 | } 473 | 474 | Ok(expr) 475 | } 476 | 477 | fn mult(&mut self) -> PResult { 478 | let mut expr = self.unary()?; 479 | 480 | while self.get(&[TType::Times, TType::Divide, TType::Mod]) { 481 | let op = self.prev(); 482 | let right = self.unary()?; 483 | expr = Expr::Binary(Rc::new(expr), op, Rc::new(right)); 484 | } 485 | 486 | Ok(expr) 487 | } 488 | 489 | fn unary(&mut self) -> PResult { 490 | if self.get(&[TType::Not, TType::Minus]) { 491 | let op = self.prev(); 492 | let right = self.unary()?; 493 | return Ok(Expr::Unary(op, Rc::new(right))); 494 | } 495 | 496 | self.call() 497 | } 498 | 499 | fn call(&mut self) -> PResult { 500 | let mut expr = self.primary()?; 501 | 502 | loop { 503 | if self.get(&[TType::LeftParen]) { 504 | expr = self.finish_call(&mut expr)?; 505 | } else if self.get(&[TType::LeftBrack]) { 506 | let tok = self.prev(); 507 | let val = self.expr()?; 508 | self.consume_n( 509 | TType::RightBrack, 510 | "Expected ']' after accessor value.".into(), 511 | vec![ErrorNote::Expect( 512 | tok.lineinfo, 513 | "Expected the ']' to match this.".into(), 514 | )], 515 | )?; 516 | expr = Expr::Get(Rc::new(expr), tok, Rc::new(val)); 517 | } else if self.get(&[TType::Dot]) { 518 | let name = self.next(); 519 | if let TType::Identifier(_) = &name.ttype { 520 | expr = Expr::Prop(Rc::new(expr), name) 521 | } else { 522 | return Err(Error::new( 523 | name.lineinfo, 524 | "Expected property name after '.'".into(), 525 | ErrorType::SyntaxError, 526 | )); 527 | } 528 | } else { 529 | break; 530 | } 531 | } 532 | 533 | Ok(expr) 534 | } 535 | 536 | fn primary(&mut self) -> PResult { 537 | if self.get(&[TType::False]) { 538 | return Ok(Expr::Literal(Type::Bool(false))); 539 | } 540 | if self.get(&[TType::True]) { 541 | return Ok(Expr::Literal(Type::Bool(true))); 542 | } 543 | if self.get(&[TType::Nil]) { 544 | return Ok(Expr::Literal(Type::Nil)); 545 | } 546 | if self.get(&[TType::LeftBrack]) { 547 | return self.array(); 548 | } 549 | if self.get(&[TType::LeftBBrace]) { 550 | return self.map(); 551 | } 552 | 553 | // 'statement-like' 554 | if self.get(&[TType::LeftBrace]) { 555 | return Ok(Expr::Block(self.block()?)); 556 | } 557 | 558 | if self.get(&[TType::If]) { 559 | let (cond, if_br, elif_brs, else_br) = self.if_stmt()?; 560 | return Ok(Expr::IfExpr(Rc::new(cond), if_br, elif_brs, else_br)); 561 | } 562 | 563 | if self.get(&[TType::LeftParen]) { 564 | let tok = self.prev(); 565 | let expr = self.expr()?; 566 | self.consume_n( 567 | TType::RightParen, 568 | String::from("Expected ')' after grouping expression."), 569 | vec![ErrorNote::Expect( 570 | tok.lineinfo, 571 | "Expected the ')' to match this.".into(), 572 | )], 573 | )?; 574 | return Ok(Expr::Grouping(Rc::new(expr))); 575 | } 576 | 577 | // literal-like 578 | self.next(); 579 | 580 | let tok = self.prev(); 581 | Ok(match &tok.ttype { 582 | TType::String(x) => Expr::Literal(Type::String(x.clone())), 583 | TType::Number(x) => Expr::Literal(Type::Float(*x)), 584 | TType::Identifier(_) => Expr::Variable(tok), 585 | _ => { 586 | return Err(Error::new( 587 | tok.lineinfo, 588 | format!("Unexpected token '{}'.", tok.ttype), 589 | ErrorType::SyntaxError, 590 | )); 591 | } 592 | }) 593 | } 594 | 595 | fn block(&mut self) -> Result, Error> { 596 | let tok = self.prev(); 597 | let mut stmts: Vec = Vec::new(); 598 | 599 | while !self.check(TType::RightBrace) && self.is_valid() { 600 | stmts.push(self.stmt()?); 601 | } 602 | 603 | self.consume_n( 604 | TType::RightBrace, 605 | "Expected '}' after block expression".into(), 606 | vec![ErrorNote::Expect( 607 | tok.lineinfo, 608 | "Expected '}' to match this.".into(), 609 | )], 610 | )?; 611 | 612 | Ok(stmts) 613 | } 614 | 615 | fn array(&mut self) -> PResult { 616 | let mut vals = Vec::new(); 617 | 618 | while self.peek().ttype != TType::RightBrack { 619 | vals.push(self.expr()?); 620 | 621 | if !self.get(&[TType::Comma]) && self.peek().ttype != TType::RightBrack { 622 | return Err(Error::new( 623 | self.peek().lineinfo, 624 | "Expected ',' after array value".into(), 625 | ErrorType::SyntaxError, 626 | )); 627 | } 628 | } 629 | 630 | self.next(); 631 | 632 | Ok(Expr::Array(vals)) 633 | } 634 | 635 | fn map(&mut self) -> PResult { 636 | // todo: enum 637 | // todo: identifiers 638 | let mut vals = Vec::new(); 639 | 640 | while self.peek().ttype != TType::RightBBrace { 641 | let key = self.expr()?; 642 | 643 | if self.get(&[TType::Comma]) || self.peek().ttype == TType::RightBBrace { 644 | vals.push((key.clone(), key)); 645 | continue; 646 | } 647 | 648 | self.consume(TType::Colon, "Expected ':' after key.".into())?; 649 | let value = self.expr()?; 650 | vals.push((key, value)); 651 | 652 | if !self.get(&[TType::Comma]) && self.peek().ttype != TType::RightBBrace { 653 | return Err(Error::new( 654 | self.peek().lineinfo, 655 | "Expected ',' after map value".into(), 656 | ErrorType::SyntaxError, 657 | )); 658 | } 659 | } 660 | 661 | self.next(); 662 | 663 | Ok(Expr::Map(vals)) 664 | } 665 | 666 | // util 667 | fn finish_call(&mut self, expr: &mut Expr) -> PResult { 668 | let mut args: Vec = Vec::new(); 669 | let mut optional_args: HashMap = HashMap::new(); 670 | 671 | if !self.check(TType::RightParen) { 672 | loop { 673 | match self.peek().ttype { 674 | TType::Identifier(name) => { 675 | if self.peek_n(1).ttype == TType::Eq { 676 | self.next(); // consume the name 677 | self.next(); // consume the eq 678 | optional_args.insert(name, self.expr()?); 679 | } else { 680 | args.push(self.expr()?) 681 | } 682 | } 683 | _ => args.push(self.expr()?), 684 | } 685 | 686 | if !self.get(&[TType::Comma]) { 687 | break; 688 | } 689 | } 690 | } 691 | 692 | let tok = self.consume(TType::RightParen, "Expected ')' after arguments.".into())?; 693 | Ok(Expr::Call(Rc::new(expr.clone()), tok, args, optional_args)) 694 | } 695 | 696 | fn finish_fn( 697 | &mut self, 698 | kind: String, 699 | ) -> Result<(Vec, Vec<(Token, Expr)>, Vec), Error> { 700 | let lineinfo = self 701 | .consume( 702 | TType::LeftParen, 703 | format!("Expected '(' after {}", kind).into(), 704 | )? 705 | .lineinfo; 706 | 707 | let mut params: Vec = Vec::new(); 708 | let mut optional_params: Vec<(Token, Expr)> = Vec::new(); 709 | 710 | if !self.check(TType::RightParen) { 711 | loop { 712 | let tok = self.peek(); 713 | 714 | if let TType::Identifier(_) = tok.ttype { 715 | self.next(); 716 | if self.get(&[TType::Eq]) { 717 | optional_params.push((tok, self.expr()?)); 718 | } else { 719 | params.push(tok); 720 | } 721 | } else { 722 | return Err(Error::new( 723 | tok.lineinfo, 724 | "Expected paramater name.".into(), 725 | ErrorType::SyntaxError, 726 | )); 727 | } 728 | 729 | if !self.get(&[TType::Comma]) { 730 | break; 731 | } 732 | } 733 | } 734 | 735 | self.consume_n( 736 | TType::RightParen, 737 | "Expected ')' after function paramaters.".into(), 738 | vec![ErrorNote::Expect( 739 | lineinfo, 740 | "Expected ')' to match this.".into(), 741 | )], 742 | )?; 743 | self.consume(TType::LeftBrace, "Expected '{' after ')'".into())?; 744 | 745 | let body = self.block()?; 746 | 747 | Ok((params, optional_params, body)) 748 | } 749 | 750 | // errors 751 | fn consume(&mut self, token: TType, error_message: String) -> Result { 752 | if self.check(token) { 753 | return Ok(self.next()); 754 | } 755 | 756 | self.synchronize(); 757 | Err(Error::new( 758 | self.peek().lineinfo, 759 | error_message, 760 | ErrorType::SyntaxError, 761 | )) 762 | } 763 | 764 | fn consume_n( 765 | &mut self, 766 | token: TType, 767 | error_message: String, 768 | notes: Vec, 769 | ) -> Result { 770 | if self.check(token) { 771 | return Ok(self.next()); 772 | } 773 | 774 | self.synchronize(); 775 | Err(Error::new_n( 776 | self.peek().lineinfo, 777 | error_message, 778 | ErrorType::SyntaxError, 779 | notes, 780 | )) 781 | } 782 | 783 | fn synchronize(&mut self) { 784 | self.next(); 785 | 786 | while self.is_valid() { 787 | if self.prev().ttype == TType::Semi { 788 | return; 789 | } 790 | 791 | match self.peek().ttype { 792 | TType::Use 793 | | TType::Fn 794 | | TType::Var 795 | | TType::For 796 | | TType::If 797 | | TType::Return 798 | | TType::While => { 799 | return; 800 | } 801 | _ => (), 802 | } 803 | 804 | self.next(); 805 | } 806 | } 807 | 808 | /// consume if the current token is in `tokens` 809 | fn get(&mut self, tokens: &[TType]) -> bool { 810 | for i in tokens.iter() { 811 | if self.check(i.clone()) { 812 | self.next(); 813 | return true; 814 | } 815 | } 816 | 817 | false 818 | } 819 | 820 | /// check if the current token is `token` 821 | fn check(&self, token: TType) -> bool { 822 | if !self.is_valid() { 823 | return false; 824 | } 825 | 826 | self.peek().ttype == token 827 | } 828 | 829 | /// get the current token 830 | fn peek(&self) -> Token { 831 | self.tokens[self.i].clone() 832 | } 833 | 834 | fn peek_n(&self, i: usize) -> Token { 835 | self.tokens[self.i + i].clone() 836 | } 837 | 838 | /// get the previous token 839 | fn prev(&self) -> Token { 840 | self.tokens[self.i - 1].clone() 841 | } 842 | 843 | /// consume the current token 844 | fn next(&mut self) -> Token { 845 | if self.is_valid() { 846 | self.i += 1; 847 | } 848 | 849 | self.prev() 850 | } 851 | 852 | /// check if the current token is EOF 853 | fn is_valid(&self) -> bool { 854 | self.peek().ttype != TType::EOF 855 | } 856 | } 857 | -------------------------------------------------------------------------------- /src/repl.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | use std::time::Instant; 3 | use std::{env, process}; 4 | 5 | use crate::environment::Environment; 6 | use crate::error::{Error, LineInfo}; 7 | use crate::interpreter::Interpreter; 8 | use crate::lexer::Lexer; 9 | use crate::parser::Parser; 10 | use crate::resolver::Resolver; 11 | use crate::token::{Token, TType}; 12 | use crate::types::Type; 13 | 14 | use rustyline::config::Configurer; 15 | use rustyline::error::ReadlineError; 16 | use rustyline::{ 17 | Cmd, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, 18 | KeyCode, KeyEvent, Modifiers, RepeatCount, 19 | }; 20 | 21 | pub fn init(mut environ: Environment, verbose: bool) { 22 | let mut code; let mut tokens; 23 | 24 | let history_file = if cfg!(windows) { 25 | if let Ok(dir) = env::var("USERPROFILE") { 26 | Some(format!("{}\\.europa_history", dir)) 27 | } else if let Ok(dir) = env::var("DEFAULTUSERPROFILE") { 28 | Some(format!("{}\\.europa_history", dir)) 29 | } else { None } 30 | } else if cfg!(unix) { 31 | if let Ok(home) = env::var("HOME") { 32 | Some(format!("{}/.europa_history", home)) 33 | } else { None } 34 | } else { None }; 35 | 36 | let mut rl = Editor::<()>::new(); 37 | rl.set_auto_add_history(true); 38 | rl.set_tab_stop(4); 39 | rl.set_indent_size(4); 40 | rl.bind_sequence( 41 | Event::KeySeq(vec![ KeyEvent(KeyCode::Tab, Modifiers::NONE) ].into()), 42 | EventHandler::Conditional(Box::new(TabEventHandler)), 43 | ); 44 | 45 | if let Some(history_file) = &history_file { 46 | if Path::new(&history_file).exists() { 47 | let _ = rl.load_history(&history_file); 48 | } 49 | } 50 | 51 | 'main_loop: loop { 52 | tokens = Vec::new(); 53 | code = String::new(); 54 | 55 | let mut line = 1; 56 | 57 | while tokens.len() == 0 || has_unclosed_brackets(&tokens) { 58 | let prompt = match tokens.len() { 59 | 0 => "> ", 60 | _ => { 61 | tokens.pop().unwrap(); // remove the EOF 62 | "... " 63 | }, 64 | }; 65 | 66 | let read = match rl.readline(prompt) { 67 | Ok(read) => read, 68 | Err(ReadlineError::Eof) => break 'main_loop, 69 | Err(ReadlineError::Interrupted) => continue 'main_loop, 70 | #[cfg(windows)] 71 | Err(ReadlineError::WindowResize) => continue, 72 | Err(err) => { 73 | eprintln!("Unexpected error: {}", err); 74 | process::exit(1); 75 | }, 76 | }; 77 | 78 | if read == ".exit" { 79 | break 'main_loop 80 | } 81 | 82 | if let Some(history_file) = &history_file { 83 | let _ = rl.append_history(&history_file); 84 | } 85 | 86 | code.push_str(&read); 87 | code.push('\n'); 88 | 89 | let mut lexer = Lexer::new(&read); 90 | lexer.set_lineinfo(LineInfo::new(line, 0)); 91 | match lexer.init() { 92 | Ok(mut lexed) => tokens.append(&mut lexed), 93 | Err(error) => { 94 | error.display(&read); 95 | continue 'main_loop 96 | }, 97 | } 98 | 99 | line += 1; 100 | } 101 | 102 | match run_code(&tokens, &mut environ, verbose) { 103 | Err(error) => error.display(&code), 104 | Ok(eval) => if eval != Type::Nil { 105 | println!("{}", eval); 106 | }, 107 | } 108 | } 109 | } 110 | 111 | fn run_code( 112 | code: &[Token], 113 | environ: &mut Environment, 114 | verbose: bool, 115 | ) -> Result { 116 | // Turn tokens into AST 117 | let mut time = Instant::now(); 118 | let tree = Parser::new(code.to_vec()).init()?; 119 | 120 | if verbose { 121 | eprintln!("parser {:?}", time.elapsed()); 122 | } 123 | 124 | // Create interpreter 125 | let mut interpreter = Interpreter::new(tree, environ.clone()); 126 | 127 | // Resolve variables 128 | time = Instant::now(); 129 | interpreter = Resolver::new(interpreter).init()?; 130 | 131 | if verbose { 132 | eprintln!("resolver {:?}", time.elapsed()); 133 | } 134 | 135 | // Run interpreter 136 | time = Instant::now(); 137 | let eval = interpreter.init()?; 138 | 139 | if verbose { 140 | eprintln!("interpreter {:?}", time.elapsed()); 141 | } 142 | 143 | *environ = interpreter.environ; 144 | 145 | Ok(eval) 146 | } 147 | 148 | fn has_unclosed_brackets(code: &[Token]) -> bool { 149 | #[derive(Clone, Copy, PartialEq)] 150 | enum BracketType { 151 | BBrace, 152 | Brace, 153 | Paren, 154 | Brack, 155 | } 156 | 157 | let mut stack = Vec::new(); 158 | 159 | for token in code { 160 | match token.ttype { 161 | TType::LeftBBrace => stack.push(BracketType::BBrace), 162 | TType::LeftBrace => stack.push(BracketType::Brace), 163 | TType::LeftParen => stack.push(BracketType::Paren), 164 | TType::LeftBrack => stack.push(BracketType::Brack), 165 | TType::RightBBrace => if stack.pop() != Some(BracketType::BBrace) { 166 | return false 167 | }, 168 | TType::RightBrace => if stack.pop() != Some(BracketType::Brace) { 169 | return false 170 | }, 171 | TType::RightParen => if stack.pop() != Some(BracketType::Paren) { 172 | return false 173 | }, 174 | TType::RightBrack => if stack.pop() != Some(BracketType::Brack) { 175 | return false 176 | }, 177 | _ => (), 178 | } 179 | } 180 | 181 | stack.len() > 0 182 | } 183 | 184 | #[derive(Clone, Copy)] 185 | struct TabEventHandler; 186 | 187 | impl ConditionalEventHandler for TabEventHandler { 188 | fn handle( 189 | &self, 190 | event: &Event, 191 | _n: RepeatCount, 192 | _p: bool, 193 | ctx: &EventContext<'_>, 194 | ) -> Option { 195 | if let Event::KeySeq(keys) = event { 196 | if let KeyEvent(KeyCode::Tab, Modifiers::NONE) = keys[0] { 197 | if ctx.line()[..ctx.pos()].chars().all(|ch| ch.is_whitespace()) { 198 | Some(Cmd::Insert(1, String::from("\t"))) 199 | } else { 200 | Some(Cmd::Complete) 201 | } 202 | } else { 203 | None 204 | } 205 | } else { 206 | None 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/resolver.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{Error, ErrorType}, 3 | interpreter::Interpreter, 4 | nodes::{ 5 | expr::Expr, 6 | stmt::{ImportType, Stmt}, 7 | }, 8 | token::{TType, Token}, 9 | }; 10 | 11 | pub struct Resolver { 12 | scopes: Vec>, 13 | interpreter: Interpreter, 14 | } 15 | 16 | impl Resolver { 17 | pub fn new(interpreter: Interpreter) -> Self { 18 | Self { 19 | interpreter, 20 | scopes: vec![], 21 | } 22 | } 23 | 24 | pub fn init(&mut self) -> Result { 25 | let nodes = self.interpreter.nodes.clone(); 26 | 27 | self.resolves(&nodes)?; 28 | 29 | Ok(self.interpreter.clone()) 30 | } 31 | 32 | // resolve 33 | fn resolve_stmt(&mut self, node: &Stmt) -> Result<(), Error> { 34 | match node { 35 | Stmt::ExprStmt(expr) => { 36 | self.resolve_expr(expr)?; 37 | } 38 | Stmt::VarDecl(vars) => { 39 | for (name, val) in vars { 40 | self.resolve_expr(val)?; 41 | self.define(name); 42 | } 43 | } 44 | Stmt::Block(stmts) => { 45 | self.resolves(stmts)?; 46 | } 47 | Stmt::IfStmt(cond, true_br, elif_brs, else_br) => { 48 | self.resolve_if(cond, true_br, elif_brs, else_br)?; 49 | } 50 | Stmt::WhileStmt(cond, body) => { 51 | self.resolve_expr(cond)?; 52 | self.resolves(body)?; 53 | } 54 | Stmt::Return(_, val) => { 55 | if let Some(v) = val { 56 | self.resolve_expr(v)?; 57 | } 58 | } 59 | Stmt::Function(name, args, optional_args, block) => { 60 | let func_name = match &name.ttype { 61 | TType::Identifier(s) => s, 62 | _ => panic!(), 63 | }; 64 | 65 | self.define(&func_name); 66 | 67 | self.begin_scope(); 68 | for param in args { 69 | let name = match ¶m.ttype { 70 | TType::Identifier(x) => x, 71 | _ => panic!(), 72 | }; 73 | 74 | self.define(&name); 75 | } 76 | 77 | for (param, expr) in optional_args { 78 | let name = match ¶m.ttype { 79 | TType::Identifier(x) => x, 80 | _ => panic!(), 81 | }; 82 | 83 | self.define(&name); 84 | self.resolve_expr(expr)?; 85 | } 86 | 87 | self.resolves(block)?; 88 | self.end_scope(); 89 | } 90 | Stmt::Break(_) => {} 91 | Stmt::Continue(_) => {} 92 | 93 | // todo 94 | Stmt::UseStmt(module, import_type) => { 95 | let lf = module.lineinfo; 96 | 97 | let name = match &module.ttype { 98 | TType::Identifier(n) => n, 99 | _ => panic!(), 100 | }; 101 | 102 | let stdlib = self.interpreter.stdlib.mods.clone(); 103 | let module = stdlib.get(name).clone(); 104 | 105 | if let Some(module) = module { 106 | let fns = &module.fns; 107 | 108 | match &import_type { 109 | ImportType::Star => { 110 | let keys = fns.keys(); 111 | for name in keys { 112 | self.define(&name); 113 | } 114 | } 115 | ImportType::Mod => { 116 | self.define(&name); 117 | } 118 | ImportType::Multiple(itms) => { 119 | for fn_name in itms { 120 | let name_string = match &fn_name.ttype { 121 | TType::Identifier(s) => s, 122 | _ => panic!(), 123 | }; 124 | 125 | let maybe_func = fns.get(name_string); 126 | 127 | match maybe_func { 128 | Some(_) => { 129 | self.define(&name_string); 130 | } 131 | None => { 132 | return Err(Error::new( 133 | lf, 134 | format!( 135 | "The item '{}' does not exist in the module '{}'.", 136 | name_string, name 137 | ) 138 | .into(), 139 | ErrorType::ReferenceError, 140 | )) 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } else { 147 | return Err(Error::new( 148 | lf, 149 | format!("Module '{}' not found.", name), 150 | ErrorType::ReferenceError, 151 | )); 152 | } 153 | } 154 | } 155 | 156 | Ok(()) 157 | } 158 | 159 | fn resolve_expr(&mut self, expr: &Expr) -> Result<(), Error> { 160 | match expr { 161 | Expr::Assign(var, val) => { 162 | self.resolve_local(var); 163 | self.resolve_expr(val)?; 164 | } 165 | Expr::Binary(left, _, right) => { 166 | self.resolve_expr(left)?; 167 | self.resolve_expr(right)?; 168 | } 169 | Expr::Grouping(expr) => self.resolve_expr(expr)?, 170 | Expr::Unary(_, expr) => { 171 | self.resolve_expr(expr)?; 172 | } 173 | Expr::Variable(var) => { 174 | self.resolve_local(var); 175 | } 176 | Expr::Block(stmts) => { 177 | self.resolves(stmts)?; 178 | } 179 | Expr::Logical(left, _, right) => { 180 | self.resolve_expr(left)?; 181 | self.resolve_expr(right)?; 182 | } 183 | Expr::Ternary(cond, left, right) => { 184 | self.resolve_expr(cond)?; 185 | self.resolve_expr(left)?; 186 | self.resolve_expr(right)?; 187 | } 188 | Expr::Call(call, _, args, optional_args) => { 189 | self.resolve_expr(call)?; 190 | 191 | for arg in args { 192 | self.resolve_expr(arg)?; 193 | } 194 | 195 | for (_, arg) in optional_args { 196 | self.resolve_expr(arg)?; 197 | } 198 | } 199 | Expr::IfExpr(cond, true_br, elif_brs, else_br) => { 200 | self.resolve_if(cond, true_br, elif_brs, else_br)?; 201 | } 202 | Expr::Literal(_) => {} 203 | Expr::Get(val, _, key) => { 204 | self.resolve_expr(val)?; 205 | self.resolve_expr(key)?; 206 | } 207 | Expr::Array(itms) => { 208 | for itm in itms { 209 | self.resolve_expr(itm)?; 210 | } 211 | } 212 | Expr::Range(left, _, right, _) => { 213 | self.resolve_expr(left)?; 214 | self.resolve_expr(right)?; 215 | } 216 | Expr::Map(map) => { 217 | for (key, value) in map { 218 | self.resolve_expr(key)?; 219 | self.resolve_expr(value)?; 220 | } 221 | } 222 | Expr::Set(var, _, i, val) => { 223 | self.resolve_expr(var)?; 224 | self.resolve_expr(i)?; 225 | self.resolve_expr(val)?; 226 | } 227 | Expr::Prop(var, _) => { 228 | self.resolve_expr(var)?; 229 | } 230 | } 231 | 232 | Ok(()) 233 | } 234 | 235 | // util 236 | fn begin_scope(&mut self) { 237 | self.scopes.push(Vec::new()); 238 | } 239 | 240 | fn end_scope(&mut self) { 241 | self.scopes.pop(); 242 | } 243 | 244 | // resolve 245 | fn resolves(&mut self, stmts: &Vec) -> Result<(), Error> { 246 | self.begin_scope(); 247 | for stmt in stmts { 248 | self.resolve_stmt(stmt)?; 249 | } 250 | self.end_scope(); 251 | 252 | Ok(()) 253 | } 254 | // resolve_local resolves a variable 255 | fn resolve_local(&mut self, name: &Token) { 256 | let var = match &name.ttype { 257 | TType::Identifier(v) => v, 258 | _ => panic!(), 259 | }; 260 | 261 | for i in (0..self.scopes.len()).rev() { 262 | if self.scopes[i].contains(var) { 263 | return self 264 | .interpreter 265 | .resolve(name.clone(), self.scopes.len() - 1 - i); 266 | } 267 | } 268 | } 269 | 270 | fn resolve_if( 271 | &mut self, 272 | cond: &Expr, 273 | true_br: &Vec, 274 | elif_brs: &Vec<(Expr, Vec)>, 275 | else_br: &Option>, 276 | ) -> Result<(), Error> { 277 | self.resolve_expr(cond)?; 278 | 279 | self.resolves(true_br)?; 280 | 281 | for (cond, block) in elif_brs { 282 | self.resolve_expr(cond)?; 283 | 284 | self.resolves(block)?; 285 | } 286 | 287 | if let Some(br) = else_br { 288 | self.resolves(br)?; 289 | } 290 | 291 | Ok(()) 292 | } 293 | 294 | // define 295 | fn define(&mut self, name: &String) { 296 | if self.scopes.is_empty() { 297 | return; 298 | } 299 | 300 | let len = self.scopes.len(); 301 | self.scopes[len - 1].push(name.clone()); 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /src/stdlib/clock.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | rc::Rc, 3 | time::{SystemTime, UNIX_EPOCH, Duration}, 4 | }; 5 | use chrono::{DateTime, Utc}; 6 | 7 | use maplit::hashmap; 8 | 9 | use crate::{ 10 | functions::{Func, FuncType}, 11 | native_func, 12 | types::module::Module, 13 | types::Type, 14 | }; 15 | 16 | pub fn new() -> Module { 17 | Module { 18 | name: "clock".into(), 19 | fns: hashmap! { 20 | "now".into() => native_func!(|_, _, _| { 21 | let start = SystemTime::now().duration_since(UNIX_EPOCH).expect("Error getting time."); 22 | Ok(Type::Float(start.as_millis() as f32)) 23 | }, 0), 24 | "fmt".into() => native_func!(|_, args, _| { 25 | 26 | let millis: f32 = match args[0] { 27 | Type::Float(value) => value, 28 | _ => { 29 | eprintln!("Invalid argument passed to clock.fmt!"); 30 | 0.0 31 | } 32 | }; 33 | let datetime: DateTime = (UNIX_EPOCH + Duration::from_millis(millis as u64)).into(); 34 | 35 | Ok(Type::String(datetime.format(args[1].to_string().as_str()).to_string())) 36 | }, 2) 37 | }, 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/stdlib/io.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | io::{stdin, stdout, Write}, 3 | rc::Rc, 4 | }; 5 | 6 | use maplit::hashmap; 7 | 8 | use crate::{ 9 | functions::{Func, FuncType}, 10 | native_func, 11 | types::module::Module, 12 | types::Type, 13 | }; 14 | 15 | pub fn new() -> Module { 16 | Module { 17 | name: "io".into(), 18 | fns: hashmap! { 19 | "println".into() => native_func!(|_, args, _| { 20 | println!("{}", args[0].to_string()); 21 | Ok(Type::Nil) 22 | }, 1), 23 | "print".into() => native_func!(|_, args, _| { 24 | print!("{}", args[0].to_string()); 25 | Ok(Type::Nil) 26 | }, 1), 27 | "flush".into() => native_func!(|_, _, _| { 28 | stdout().flush().expect("Failed to flush."); 29 | Ok(Type::Nil) 30 | }, 0), 31 | "readln".into() => native_func!(|_, args, _| { 32 | let msg = args[0].to_string(); 33 | print!("{}", msg); 34 | stdout().flush().expect("Failed to flush."); 35 | 36 | let mut out = String::new(); 37 | stdin().read_line(&mut out).expect("Failed to read user input."); 38 | 39 | Ok(Type::String(out.trim().to_string())) 40 | }, 1), 41 | "exit".into() => native_func!(|_, args, _| { 42 | std::process::exit(match args[0] { 43 | Type::Float(value) => value as i32, 44 | _ => { 45 | eprintln!("Expected a number for the exit code."); 46 | 0 47 | } 48 | }); 49 | }, 1), 50 | }, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/stdlib/math.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use maplit::hashmap; 4 | 5 | use crate::{ 6 | functions::{Func, FuncType}, 7 | native_func, 8 | types::module::Module, 9 | types::Type, 10 | }; 11 | 12 | pub fn new() -> Module { 13 | Module { 14 | name: "math".into(), 15 | fns: hashmap! { 16 | // nums 17 | "infinity".into() => Type::Float(f32::INFINITY), 18 | "nan".into() => Type::Float(f32::NAN), 19 | 20 | // funcs 21 | "sin".into() => native_func!(|_, _args, _| { 22 | Ok(Type::Float(3f32)) // ??????? 23 | }, 1) 24 | }, 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/stdlib/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | The stdlib is the entry point. It contains a hashmap of modules. 3 | Each module can contain a function bound to a function name. 4 | */ 5 | 6 | use std::collections::HashMap; 7 | 8 | use maplit::hashmap; 9 | 10 | use crate::types::module::Module; 11 | 12 | mod io; 13 | mod math; 14 | mod clock; 15 | 16 | /** 17 | Easier coding. 18 | 19 | # Usage 20 | ``` 21 | native_func!(|interpreter, args| { 22 | // ... 23 | }, 1) 24 | ``` 25 | */ 26 | #[macro_export] 27 | macro_rules! native_func { 28 | ($func:expr, $arity:expr) => { 29 | native_func!("", $func, $arity) 30 | }; 31 | 32 | ($name:literal, $func:expr, $arity:expr) => { 33 | Type::Func(FuncType::Native(Func::new( 34 | $name, 35 | Rc::new($func), 36 | $arity, 37 | ))) 38 | }; 39 | } 40 | 41 | #[derive(Clone)] 42 | pub struct Stdlib { 43 | pub mods: HashMap, 44 | } 45 | 46 | impl Stdlib { 47 | pub fn new() -> Self { 48 | Stdlib { 49 | mods: hashmap! { 50 | "io".into() => io::new(), 51 | "math".into() => math::new(), 52 | "clock".into() => clock::new(), 53 | }, 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/tests/files.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod programs { 3 | use crate::{environment::Environment, error::Error, run_string}; 4 | use std::{fs, process}; 5 | 6 | fn run_file(file: String) -> Result<(), Error> { 7 | let code = fs::read_to_string(file).unwrap_or_else(|err| { 8 | eprintln!("Error reading file: {}", err); 9 | process::exit(1) 10 | }); 11 | 12 | run_string(&code, &mut Environment::new(), false)?; 13 | 14 | Ok(()) 15 | } 16 | 17 | #[test] 18 | fn programs() { 19 | let programs = fs::read_dir("test/programs").unwrap(); 20 | 21 | for file in programs { 22 | let path = file.unwrap().path().display().to_string(); 23 | 24 | run_file(path).unwrap(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod files; 2 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use crate::error::LineInfo; 4 | 5 | #[derive(Debug, Clone, PartialEq)] 6 | pub enum TType { 7 | // delims 8 | LeftBBrace, // {{ 9 | RightBBrace, // }} 10 | LeftBrace, 11 | RightBrace, 12 | LeftParen, 13 | RightParen, 14 | LeftBrack, 15 | RightBrack, 16 | Comma, 17 | Dot, 18 | DotDot, 19 | DotEq, 20 | Semi, 21 | 22 | // comparison 23 | Not, 24 | EqEq, 25 | NotEq, 26 | Greater, 27 | GreaterEq, 28 | Less, 29 | LessEq, 30 | 31 | // assignment 32 | Eq, 33 | PlusEq, 34 | MinusEq, 35 | TimesEq, 36 | DivideEq, 37 | PowEq, 38 | ModEq, 39 | 40 | // operators 41 | Plus, 42 | Minus, 43 | Times, 44 | Divide, 45 | Pow, 46 | Mod, 47 | 48 | Colon, 49 | Question, 50 | 51 | // literals 52 | Identifier(String), 53 | String(String), 54 | Number(f32), 55 | True, 56 | False, 57 | Nil, 58 | 59 | // keywords 60 | Fn, 61 | Return, 62 | Var, 63 | Use, 64 | Do, 65 | While, 66 | For, 67 | In, 68 | Break, 69 | Continue, 70 | Or, 71 | And, 72 | If, 73 | Else, 74 | Elif, 75 | 76 | EOF, 77 | } 78 | 79 | #[derive(Debug, Clone, PartialEq)] 80 | pub struct Token { 81 | pub ttype: TType, 82 | pub lineinfo: LineInfo, 83 | } 84 | 85 | impl Display for TType { 86 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 87 | write!(f, "{:?}", self) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/types/array.rs: -------------------------------------------------------------------------------- 1 | use crate::error::ErrorType; 2 | 3 | use super::{ops::TResult, Type}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Array { 7 | pub arr: Vec, 8 | } 9 | 10 | impl Array { 11 | pub fn new(arr: Vec) -> Self { 12 | Self { arr } 13 | } 14 | 15 | pub fn get(&self, i: Type) -> TResult { 16 | let i = self.check_index(i)?; 17 | Ok(self.arr[i].clone()) 18 | } 19 | 20 | pub fn set(&mut self, i: Type, v: Type) -> Result<(), (String, ErrorType)> { 21 | let i = self.check_index(i)?; 22 | self.arr[i] = v.clone(); 23 | Ok(()) 24 | } 25 | 26 | fn check_index(&self, num: Type) -> Result { 27 | let len = self.arr.len(); 28 | 29 | match num { 30 | Type::Float(i) => { 31 | if i.is_infinite() || i.is_nan() || // infinite 32 | i.round() != i 33 | // not whole 34 | { 35 | return Err(( 36 | format!("Only whole numbers are valid index ranges (got {}).", i).into(), 37 | ErrorType::TypeError, 38 | )); 39 | } 40 | 41 | let idx; 42 | 43 | if i < 0f32 { 44 | idx = len as f32 + i; 45 | } else { 46 | idx = i; 47 | } 48 | 49 | if idx < 0f32 || idx as usize >= len { 50 | return Err(( 51 | format!("Index {} out of array range 0-{}.", i, len - 1).into(), 52 | ErrorType::ReferenceError, 53 | )); 54 | } 55 | 56 | Ok(idx as usize) 57 | } 58 | _ => { 59 | return Err(( 60 | "Arrays can only be indexed with numbers.".into(), 61 | ErrorType::TypeError, 62 | )) 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/types/hash.rs: -------------------------------------------------------------------------------- 1 | use super::Type; 2 | use std::cmp::Ordering; 3 | 4 | impl PartialEq for Type { 5 | fn eq(&self, other: &Self) -> bool { 6 | match (self, other) { 7 | (Type::String(a), Type::String(b)) => a == b, 8 | (Type::Float(a), Type::Float(b)) => a == b, 9 | (Type::Nil, Type::Nil) => true, 10 | (Type::Bool(a), Type::Bool(b)) => a == b, 11 | _ => false, 12 | } 13 | } 14 | } 15 | 16 | impl PartialOrd for Type { 17 | fn partial_cmp(&self, other: &Self) -> Option { 18 | match (self, other) { 19 | (Type::String(a), Type::String(b)) => a.len().partial_cmp(&b.len()), 20 | (Type::Float(a), Type::Float(b)) => a.partial_cmp(b), 21 | _ => None, 22 | } 23 | } 24 | } 25 | 26 | // todo: hash 27 | -------------------------------------------------------------------------------- /src/types/map.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, rc::Rc}; 2 | 3 | use crate::error::ErrorType; 4 | 5 | use super::{ops::TResult, Type}; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Map { 9 | pub map: HashMap, 10 | } 11 | 12 | impl Map { 13 | pub fn new(map: HashMap) -> Self { 14 | Self { map } 15 | } 16 | 17 | pub fn get(&self, key: Type) -> TResult { 18 | let key = self.check_index(key)?; 19 | let val = self.map.get(&key); 20 | 21 | match val { 22 | Some(val) => Ok(match val { 23 | Type::Array(v) => Type::Array(Rc::clone(v)), 24 | Type::Map(v) => Type::Map(Rc::clone(v)), 25 | _ => val.clone() 26 | }), 27 | _ => Err(( 28 | format!("'{}' is not a key in the map.", key,), 29 | ErrorType::ReferenceError, 30 | )), 31 | } 32 | } 33 | 34 | pub fn set(&mut self, key: Type, value: Type) -> Result<(), (String, ErrorType)> { 35 | let key = self.check_index(key)?; 36 | self.map.insert(key, value); 37 | Ok(()) 38 | } 39 | 40 | pub fn to_string(&self, idt: usize) -> String { 41 | let mut out = String::from("{{\n"); 42 | 43 | for (key, value) in &self.map { 44 | out += &format!("{}\"{}\": {},\n", " ".repeat(idt), key, match value { 45 | Type::Map(v) => { 46 | v.borrow().to_string(idt + 1) 47 | }, 48 | Type::String(v) => format!("\"{}\"", v), 49 | _ => value.to_string() 50 | }); 51 | } 52 | 53 | out += &" ".repeat(idt - 1); 54 | out += "}}"; 55 | 56 | out 57 | } 58 | 59 | fn check_index(&self, key: Type) -> Result { 60 | match key { 61 | _ => Ok(key.to_string()), 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/types/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use crate::functions::FuncType; 4 | use array::Array; 5 | use map::Map; 6 | use module::Module; 7 | 8 | pub mod map; 9 | pub mod tostring; 10 | pub mod ops; 11 | pub mod hash; 12 | pub mod array; 13 | pub mod module; 14 | 15 | 16 | #[derive(Debug, Clone)] 17 | pub enum Type { 18 | Float(f32), 19 | String(String), 20 | Bool(bool), 21 | Array(Rc>), 22 | Map(Rc>), 23 | Module(Module), 24 | Func(FuncType), 25 | Nil, 26 | } 27 | -------------------------------------------------------------------------------- /src/types/module.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use super::Type; 4 | 5 | use std::fmt::{ self, Display }; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Module { 9 | pub name: String, 10 | pub fns: HashMap, 11 | } 12 | 13 | impl Module { 14 | pub fn new(name: String, fns: HashMap) -> Self { 15 | Self { name, fns } 16 | } 17 | 18 | pub fn to_string(&self, idt: usize) -> String { 19 | let mut out = String::from(format!("mod {} {{\n", self.name)); 20 | 21 | for (key, _) in &self.fns { 22 | let itm = format!("{}()", key); 23 | out += &format!("{}{},\n", " ".repeat(idt), itm); 24 | } 25 | 26 | out += &" ".repeat(idt - 1); 27 | out += "}"; 28 | 29 | out 30 | } 31 | } 32 | 33 | impl Display for Module { 34 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 35 | write!(f, "mod {} [{} items]", self.name, self.fns.len()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/types/ops.rs: -------------------------------------------------------------------------------- 1 | use super::Type; 2 | use crate::error::ErrorType; 3 | 4 | pub type TResult = Result; 5 | 6 | impl Type { 7 | pub fn add(&self, other: &Type) -> TResult { 8 | if let (Self::Float(a), Self::Float(b)) = (self, other) { 9 | return Ok(Self::Float(a + b)); 10 | } 11 | 12 | if let (Self::String(a), Self::Float(b)) = (self, other) { 13 | return Ok(Self::String(format!("{}{}", a, b))); 14 | } 15 | 16 | if let (Self::Float(a), Self::String(b)) = (self, other) { 17 | return Ok(Self::String(format!("{}{}", a, b))); 18 | } 19 | 20 | if let (Self::String(a), Self::String(b)) = (self, other) { 21 | return Ok(Self::String(format!("{}{}", a, b))); 22 | } 23 | 24 | Err(( 25 | "Operator '+' can only be applied to strings and numbers.".into(), 26 | ErrorType::TypeError, 27 | )) 28 | } 29 | 30 | pub fn sub(&self, other: &Type) -> TResult { 31 | if let (Self::Float(a), Self::Float(b)) = (self, other) { 32 | return Ok(Self::Float(a - b)); 33 | } 34 | 35 | Err(( 36 | "Operator '-' can only be applied to numbers.".into(), 37 | ErrorType::TypeError, 38 | )) 39 | } 40 | 41 | pub fn mult(&self, other: &Type) -> TResult { 42 | if let (Self::Float(a), Self::Float(b)) = (self, other) { 43 | return Ok(Self::Float(a * b)); 44 | } 45 | 46 | if let (Self::String(a), Self::Float(b)) = (self, other) { 47 | if b.fract() != 0.0 { 48 | return Err(( 49 | "Operator '*' can only be applied to strings and integer numbers.".into(), 50 | ErrorType::TypeError, 51 | )); 52 | } 53 | return Ok(Self::String(a.repeat((*b) as usize))); 54 | } 55 | 56 | if let (Self::Float(a), Self::String(b)) = (self, other) { 57 | if a.fract() != 0.0 { 58 | return Err(( 59 | "Operator '*' can only be applied to strings and integer numbers.".into(), 60 | ErrorType::TypeError, 61 | )); 62 | } 63 | return Ok(Self::String(b.repeat((*a) as usize))); 64 | } 65 | 66 | Err(( 67 | "Operator '*' can only be applied to numbers, or numbers and strings.".into(), 68 | ErrorType::TypeError, 69 | )) 70 | } 71 | 72 | pub fn div(&self, other: &Type) -> TResult { 73 | if let (Self::Float(a), Self::Float(b)) = (self, other) { 74 | if *b == 0f32 { 75 | return Err(("Division by 0.".into(), ErrorType::MathError)); 76 | } 77 | return Ok(Self::Float(a / b)); 78 | } 79 | 80 | Err(( 81 | "Operator '/' can only be applied to numbers.".into(), 82 | ErrorType::TypeError, 83 | )) 84 | } 85 | 86 | pub fn modulo(&self, other: &Type) -> TResult { 87 | if let (Self::Float(a), Self::Float(b)) = (self, other) { 88 | if *b == 0f32 { 89 | return Err(("Division by 0.".into(), ErrorType::MathError)); 90 | } 91 | return Ok(Self::Float(a % b)); 92 | } 93 | 94 | Err(( 95 | "Operator '%' can only be applied to numbers.".into(), 96 | ErrorType::TypeError, 97 | )) 98 | } 99 | 100 | pub fn pow(&self, other: &Type) -> TResult { 101 | if let (Self::Float(a), Self::Float(b)) = (self, other) { 102 | if *b == 0f32 { 103 | return Err(("Division by 0.".into(), ErrorType::MathError)); 104 | } 105 | return Ok(Self::Float(a.powf(*b))); 106 | } 107 | 108 | Err(( 109 | "Operator '%' can only be applied to numbers.".into(), 110 | ErrorType::TypeError, 111 | )) 112 | } 113 | 114 | // arrays and maps 115 | pub fn index(&self, num: Type) -> TResult { 116 | match self { 117 | Self::Array(v) => v.borrow().get(num), 118 | Self::Map(v) => v.borrow().get(num), 119 | _ => Err(( 120 | "The [...] operator can only be applied to arrays and maps.".into(), 121 | ErrorType::TypeError, 122 | )), 123 | } 124 | } 125 | 126 | pub fn assign(&self, i: Type, value: Type) -> TResult { 127 | match self { 128 | Type::Array(v) => { 129 | v.borrow_mut().set(i, value.clone())?; 130 | Ok(value.clone()) 131 | } 132 | Type::Map(v) => { 133 | v.borrow_mut().set(i, value.clone())?; 134 | Ok(value.clone()) 135 | } 136 | _ => Err(( 137 | "The [...]= operator can only be applied to arrays and maps.".into(), 138 | ErrorType::ReferenceError, 139 | )), 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/types/tostring.rs: -------------------------------------------------------------------------------- 1 | use crate::functions::Call; 2 | 3 | use super::Type; 4 | 5 | use std::fmt::{ self, Display }; 6 | 7 | impl Type { 8 | // wtf is this???? 9 | pub fn to_string(&self) -> String { 10 | match self { 11 | Type::Array(v) => { 12 | let mut out = String::from('['); 13 | 14 | for (idx, val) in v.borrow().arr.iter().enumerate() { 15 | out += &val.to_string(); 16 | 17 | if idx < v.borrow().arr.len() - 1 { 18 | out += ", "; 19 | } 20 | } 21 | 22 | out + "]" 23 | } 24 | Type::Map(n) => { 25 | n.borrow().to_string(1) 26 | } 27 | Type::Nil => "nil".into(), 28 | Type::Float(n) => n.to_string(), 29 | Type::String(n) => n.clone(), 30 | Type::Bool(n) => n.to_string(), 31 | Type::Func(n) => Call::to_string(n), 32 | Type::Module(n) => { 33 | n.to_string(1) 34 | } 35 | } 36 | } 37 | } 38 | 39 | impl Display for Type { 40 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 41 | match self { 42 | Self::Array(items) => { 43 | let items = &items.borrow().arr; 44 | 45 | write!(f, "[")?; 46 | 47 | for (i, item) in items.iter().enumerate() { 48 | write!(f, "{}", item)?; 49 | 50 | if i + 1 < items.len() { 51 | write!(f, ", ")?; 52 | } 53 | } 54 | 55 | write!(f, "]") 56 | }, 57 | Self::Map(items) => { 58 | let items = &items.borrow().map; 59 | 60 | write!(f, "{{")?; 61 | 62 | for (key, value) in items.iter() { 63 | write!(f, "\n\t{}: {},", Self::String(key.clone()), value)?; 64 | } 65 | 66 | write!(f, "\n}}") 67 | }, 68 | Self::Nil => write!(f, "nil"), 69 | Self::Float(value) => write!(f, "{}", value), 70 | Self::String(value) => write!( 71 | f, 72 | "\"{}\"", 73 | value 74 | .replace('"', "\\\"") 75 | .replace('\n', "\\n") 76 | .replace('\r', "\\r"), 77 | ), 78 | Self::Bool(value) => write!(f, "{}", value), 79 | Self::Func(function) => write!(f, "{}", function), 80 | Self::Module(module) => write!(f, "{}", module), 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /test/main.eo: -------------------------------------------------------------------------------- 1 | {{ }} {} () [] , . 2 | ! == != 3 | > < >= <= 4 | + - * / ** % 5 | = += -= *= /= **= %= 6 | 7 | // comment 8 | /* 9 | multi comment 10 | */ 11 | 12 | "string" 'string' 12_56_6.4 1 true false nil 13 | ; 14 | fn return var use while for in break continue do or and -------------------------------------------------------------------------------- /test/playground.eo: -------------------------------------------------------------------------------- 1 | use io.println; 2 | 3 | println("a" * -5); 4 | println("a" * 5); 5 | println("a" * "a"); -------------------------------------------------------------------------------- /test/programs/10bottles.eo: -------------------------------------------------------------------------------- 1 | use io.println; 2 | 3 | var count = 10; 4 | while count > 0 { 5 | var bottle = "bottle" + (count == 1 ? "" : "s"); 6 | println(count + " " + bottle + " of beer on the wall, " + count + " " + bottle + " of beer."); 7 | count -= 1; 8 | var bottle2 = "bottle" + (count == 1 ? "" : "s"); 9 | println("Take one down, pass it around, " + count + " " + bottle2 + " of beer on the wall."); 10 | } -------------------------------------------------------------------------------- /test/programs/fib.eo: -------------------------------------------------------------------------------- 1 | use io.println; 2 | 3 | var a = 0; 4 | var b = 1; 5 | var c = 0; 6 | 7 | while a + b < 1000000 { 8 | println(b); 9 | c = b; 10 | b = a + b; 11 | a = c; 12 | } 13 | 14 | -------------------------------------------------------------------------------- /test/programs/fizzbuzz.eo: -------------------------------------------------------------------------------- 1 | use io.println; 2 | 3 | var i = 1; 4 | do { 5 | var x = if i % 3 == 0 { "Fizz " } else { "" } + if i % 5 == 0 { "Buzz" } else { "" }; 6 | println(x == "" ? i : x); 7 | } while (i += 1) < 101; --------------------------------------------------------------------------------