mirror of
https://github.com/MorizzG/rlox.git
synced 2025-12-06 04:12:42 +00:00
finished resolver (chapter 11) and started classes (chapter 12)
This commit is contained in:
parent
42c9f17399
commit
10540708d4
34 changed files with 1449 additions and 439 deletions
312
Cargo.lock
generated
312
Cargo.lock
generated
|
|
@ -2,6 +2,152 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.78"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"clap_derive",
|
||||||
|
"clap_lex",
|
||||||
|
"is-terminal",
|
||||||
|
"once_cell",
|
||||||
|
"strsim",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
|
||||||
|
dependencies = [
|
||||||
|
"os_str_bytes",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-lifetimes"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"io-lifetimes",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.139"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_str_bytes"
|
||||||
|
version = "6.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
|
@ -44,6 +190,30 @@ dependencies = [
|
||||||
"siphasher",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.50"
|
version = "1.0.50"
|
||||||
|
|
@ -81,16 +251,55 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
name = "rlox2"
|
name = "rlox2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"rlox2-frontend",
|
||||||
|
"rlox2-interpreter",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rlox2-frontend"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
"phf",
|
"phf",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rlox2-interpreter"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"itertools",
|
||||||
|
"rlox2-frontend",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.36.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4fdebc4b395b7fbb9ab11e462e20ed9051e7b16e42d24042c776eca0ac81b03"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.107"
|
version = "1.0.107"
|
||||||
|
|
@ -102,6 +311,15 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.38"
|
version = "1.0.38"
|
||||||
|
|
@ -127,3 +345,97 @@ name = "unicode-ident"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.42.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.42.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.42.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.42.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.42.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.42.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.42.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||||
|
|
|
||||||
16
Cargo.toml
16
Cargo.toml
|
|
@ -3,8 +3,18 @@ name = "rlox2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
[[bin]]
|
||||||
|
name = "rlox"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
members = ["frontend", "interpreter"]
|
||||||
|
|
||||||
|
[dependencies.rlox2-frontend]
|
||||||
|
path = "frontend"
|
||||||
|
|
||||||
|
[dependencies.rlox2-interpreter]
|
||||||
|
path = "interpreter"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
phf = { version = "0.11.1", features = ["macros"] }
|
clap = { version = "4.1.4", features = ["derive"] }
|
||||||
thiserror = "1.0"
|
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,18 @@ if_stmt -> "if" "(" expression ")" statement ( "else" statement )? ;
|
||||||
print_stmt -> "print" expression ";" ;
|
print_stmt -> "print" expression ";" ;
|
||||||
while_stmt -> "while" "(" expression ")" statement ;
|
while_stmt -> "while" "(" expression ")" statement ;
|
||||||
for_stmt -> "for" "(" (declaration | expr_stmt | ";") ";" expression? ";" expression ";" ")" statement ;
|
for_stmt -> "for" "(" (declaration | expr_stmt | ";") ";" expression? ";" expression ";" ")" statement ;
|
||||||
declaration -> var_decl | fun_decl ;
|
|
||||||
block -> "{" statement* "}" ;
|
block -> "{" statement* "}" ;
|
||||||
expr_Stmt -> expression ";" ;
|
expr_Stmt -> expression ";" ;
|
||||||
break -> "break" ";" ;
|
break -> "break" ";" ;
|
||||||
return -> "return" expression? ";" ;
|
return -> "return" expression? ";" ;
|
||||||
|
|
||||||
|
declaration -> var_decl | fun_decl | class_decl ;
|
||||||
|
|
||||||
var_decl -> "var" IDENTIFIER ( "=" expression )? ";"
|
var_decl -> "var" IDENTIFIER ( "=" expression )? ";"
|
||||||
fun_decl -> "fun" IDENTIFIER "(" parameters ")" block ;
|
fun_decl -> "fun" IDENTIFIER "(" parameters ")" block ;
|
||||||
|
class_decl -> "class" IDENTIFIER "{" method* "}" ;
|
||||||
|
|
||||||
|
method -> IDENTIFIER "(" parameters ")" block ;
|
||||||
|
|
||||||
expression -> assignment
|
expression -> assignment
|
||||||
|
|
||||||
|
|
@ -37,8 +41,8 @@ unary -> ( "!" | "-" ) unary | call ;
|
||||||
call -> primary ( "(" arguments? ")" )* ;
|
call -> primary ( "(" arguments? ")" )* ;
|
||||||
arguments -> expression ( "," expression )* ;
|
arguments -> expression ( "," expression )* ;
|
||||||
|
|
||||||
primary -> "(" expression ")" | IDENTIFIER | fun_expr | NUMBER | STRING | "true" | "false" | "nil" ;
|
primary -> "(" expression ")" | IDENTIFIER | lambda | NUMBER | STRING | "true" | "false" | "nil" ;
|
||||||
|
|
||||||
fun_expr -> "fun" "(" parameters ")" block ;
|
lambda -> "fun" "(" parameters ")" block ;
|
||||||
|
|
||||||
parameters -> ( IDENTIFIER ( "," IDENTIFIER )* )? ;
|
parameters -> ( IDENTIFIER ( "," IDENTIFIER )* )? ;
|
||||||
11
frontend/Cargo.toml
Normal file
11
frontend/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "rlox2-frontend"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
phf = { version = "0.11.1", features = ["macros"] }
|
||||||
|
thiserror = "1.0.38"
|
||||||
|
itertools = "0.10.5"
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
use phf::phf_map;
|
use phf::phf_map;
|
||||||
|
|
||||||
use crate::error::LexerError;
|
use super::{CodePos, LexerError, Token, TokenType};
|
||||||
use crate::misc::CodePos;
|
|
||||||
|
|
||||||
use super::{Token, TokenType};
|
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
|
@ -289,7 +286,7 @@ impl Lexer {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.errors.push(LexerError::InvalidNumberLiteral {
|
self.errors.push(LexerError::InvalidNumberLiteral {
|
||||||
lexeme,
|
lexeme,
|
||||||
msg: format!("{err}"),
|
msg: err.to_string(),
|
||||||
code_pos: self.code_pos,
|
code_pos: self.code_pos,
|
||||||
});
|
});
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
use std::fmt::{Debug, Display};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct CodePos {
|
pub struct CodePos {
|
||||||
pub line: u32,
|
pub line: u32,
|
||||||
|
|
@ -12,23 +10,14 @@ impl Default for CodePos {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CodePos {
|
impl std::fmt::Display for CodePos {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "line {}, col {}", self.line, self.col)
|
write!(f, "line {}, col {}", self.line, self.col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for CodePos {
|
impl std::fmt::Debug for CodePos {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}:{}", self.line, self.col)
|
write!(f, "{}:{}", self.line, self.col)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
|
||||||
|
|
||||||
pub fn indent(s: String) -> String {
|
|
||||||
s.split('\n')
|
|
||||||
.map(|line| format!("\t{line}"))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n")
|
|
||||||
}
|
|
||||||
19
frontend/src/lexer/error.rs
Normal file
19
frontend/src/lexer/error.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use super::CodePos;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum LexerError {
|
||||||
|
#[error("Unexpected character '{c}' at {code_pos}.")]
|
||||||
|
UnexpectedCharacter { c: char, code_pos: CodePos },
|
||||||
|
#[error("Unterminated string literal starting at {code_pos}.")]
|
||||||
|
UnterminatedStringLiteral { code_pos: CodePos },
|
||||||
|
#[error("Unterminated block comment starting at {code_pos}.")]
|
||||||
|
UnterminatedBlockComment { code_pos: CodePos },
|
||||||
|
#[error("Invalid number literal {lexeme} at {code_pos}: {msg}")]
|
||||||
|
InvalidNumberLiteral {
|
||||||
|
lexeme: String,
|
||||||
|
msg: String,
|
||||||
|
code_pos: CodePos,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
mod _lexer;
|
mod _lexer;
|
||||||
|
mod code_pos;
|
||||||
|
mod error;
|
||||||
mod token;
|
mod token;
|
||||||
|
|
||||||
pub use _lexer::scan_tokens;
|
pub use _lexer::scan_tokens;
|
||||||
|
pub use code_pos::CodePos;
|
||||||
|
pub use error::LexerError;
|
||||||
pub use token::{Token, TokenType};
|
pub use token::{Token, TokenType};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::misc::CodePos;
|
use super::CodePos;
|
||||||
|
|
||||||
#[allow(dead_code, clippy::upper_case_acronyms)]
|
#[allow(dead_code, clippy::upper_case_acronyms)]
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
2
frontend/src/lib.rs
Normal file
2
frontend/src/lib.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
||||||
45
frontend/src/parser/error.rs
Normal file
45
frontend/src/parser/error.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::lexer::{CodePos, Token};
|
||||||
|
|
||||||
|
use super::Expr;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ParserError {
|
||||||
|
#[error("Token stream ended unexpectedly.")]
|
||||||
|
TokenStreamEnded,
|
||||||
|
#[error("Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
|
||||||
|
ExpectedPrimary { token: Token },
|
||||||
|
#[error("Missing semicolon at {code_pos}")]
|
||||||
|
MissingSemicolon { code_pos: CodePos },
|
||||||
|
#[error("Expected variable name at {0}, got {token} instead", token.code_pos)]
|
||||||
|
ExpectedVarName { token: Token },
|
||||||
|
#[error("Can't assign to {expr} at {code_pos}")]
|
||||||
|
InvalidAssignment { expr: Expr, code_pos: CodePos },
|
||||||
|
#[error("Missing closing curly brace at {code_pos}")]
|
||||||
|
MissingRightBrace { code_pos: CodePos },
|
||||||
|
#[error("Missing closing parenthesis at {code_pos}")]
|
||||||
|
MissingRightParen { code_pos: CodePos },
|
||||||
|
#[error("Missing parenthesis after if at {code_pos}")]
|
||||||
|
MissingParenAfterIf { code_pos: CodePos },
|
||||||
|
#[error("Missing parenthesis after while at {code_pos}")]
|
||||||
|
MissingParenAfterWhile { code_pos: CodePos },
|
||||||
|
#[error("Missing parenthesis after for at {code_pos}")]
|
||||||
|
MissingParenAfterFor { code_pos: CodePos },
|
||||||
|
#[error("Call at {code_pos} has too many arguments")]
|
||||||
|
TooManyArguments { code_pos: CodePos },
|
||||||
|
#[error("{msg} at {code_pos}")]
|
||||||
|
MissingIdentifier { msg: String, code_pos: CodePos },
|
||||||
|
#[error("Missing arguments to function declaration at {code_pos}")]
|
||||||
|
MissingFunctionArgs { code_pos: CodePos },
|
||||||
|
#[error("Missing body to function declaration at {code_pos}")]
|
||||||
|
MissingFunctionBody { code_pos: CodePos },
|
||||||
|
#[error("Function declaration at {code_pos} has too many parameters")]
|
||||||
|
TooManyParams { code_pos: CodePos },
|
||||||
|
#[error("Return statement outside of function definition")]
|
||||||
|
InvalidReturn { code_pos: CodePos },
|
||||||
|
#[error("Break statement outside of loop")]
|
||||||
|
InvalidBreak { code_pos: CodePos },
|
||||||
|
#[error("Missing class body at {code_pos}")]
|
||||||
|
MissingClassBody { code_pos: CodePos },
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use super::Stmt;
|
use super::Stmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -28,8 +30,15 @@ pub enum Expr {
|
||||||
Variable {
|
Variable {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
Assignment {
|
LocalVariable {
|
||||||
name: String,
|
name: String,
|
||||||
|
level: usize,
|
||||||
|
},
|
||||||
|
GlobalVariable {
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
|
Assignment {
|
||||||
|
target: Box<Expr>,
|
||||||
value: Box<Expr>,
|
value: Box<Expr>,
|
||||||
},
|
},
|
||||||
Call {
|
Call {
|
||||||
|
|
@ -39,6 +48,7 @@ pub enum Expr {
|
||||||
Function {
|
Function {
|
||||||
name: String,
|
name: String,
|
||||||
param_names: Vec<String>,
|
param_names: Vec<String>,
|
||||||
|
closure_vars: Vec<(String, usize)>,
|
||||||
body: Box<Stmt>,
|
body: Box<Stmt>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -89,10 +99,10 @@ impl Expr {
|
||||||
Expr::Grouping { expr }
|
Expr::Grouping { expr }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assignment(name: impl Into<String>, value: Expr) -> Self {
|
pub fn assignment(target: Expr, value: Expr) -> Self {
|
||||||
let name = name.into();
|
let target = Box::new(target);
|
||||||
let value = Box::new(value);
|
let value = Box::new(value);
|
||||||
Expr::Assignment { name, value }
|
Expr::Assignment { target, value }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(callee: Expr, args: Vec<Expr>) -> Self {
|
pub fn call(callee: Expr, args: Vec<Expr>) -> Self {
|
||||||
|
|
@ -105,6 +115,7 @@ impl Expr {
|
||||||
Self::Function {
|
Self::Function {
|
||||||
name,
|
name,
|
||||||
param_names,
|
param_names,
|
||||||
|
closure_vars: Vec::new(),
|
||||||
body,
|
body,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -120,22 +131,24 @@ impl Display for Expr {
|
||||||
write!(f, "({op} {left} {right})")
|
write!(f, "({op} {left} {right})")
|
||||||
}
|
}
|
||||||
Expr::Grouping { expr } => write!(f, "(group {expr})"),
|
Expr::Grouping { expr } => write!(f, "(group {expr})"),
|
||||||
Expr::Variable { name } => write!(f, "{name}"),
|
Expr::Variable { name } => write!(f, "(var {name})"),
|
||||||
Expr::Assignment { name, value } => write!(f, "{name} = {value}"),
|
Expr::LocalVariable { name, level } => write!(f, "(var {name} local({level}))"),
|
||||||
Expr::Call { callee, args } => write!(
|
Expr::GlobalVariable { name } => write!(f, "(var {name} global)"),
|
||||||
f,
|
Expr::Assignment { target, value } => write!(f, "{target} = {value}"),
|
||||||
"({callee} {})",
|
Expr::Call { callee, args } => write!(f, "({callee} {})", args.iter().map(|arg| arg.to_string()).join(" ")),
|
||||||
args.iter()
|
|
||||||
.map(|arg| format!("{arg}"))
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(" ")
|
|
||||||
),
|
|
||||||
Expr::Function {
|
Expr::Function {
|
||||||
name,
|
name,
|
||||||
param_names,
|
param_names,
|
||||||
|
closure_vars,
|
||||||
body,
|
body,
|
||||||
} => {
|
} => {
|
||||||
write!(f, "fun {name}({}) {body}", param_names.join(", "))
|
if !closure_vars.is_empty() {
|
||||||
|
let closure_fmt = closure_vars.iter().map(|(name, _level)| name).join(", ");
|
||||||
|
|
||||||
|
write!(f, "fun [{closure_fmt}] {name}({}) {body}", param_names.join(", "))
|
||||||
|
} else {
|
||||||
|
write!(f, "fun {name}({}) {body}", param_names.join(", "))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
5
frontend/src/parser/misc.rs
Normal file
5
frontend/src/parser/misc.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
pub fn indent(s: String) -> String {
|
||||||
|
s.split('\n').map(|line| format!("\t{line}")).join("\n")
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
mod _parser;
|
mod error;
|
||||||
mod expr;
|
mod expr;
|
||||||
|
mod misc;
|
||||||
|
mod parse;
|
||||||
mod stmt;
|
mod stmt;
|
||||||
|
|
||||||
pub use _parser::parse_tokens;
|
pub use error::ParserError;
|
||||||
pub use expr::{BinaryOp, Expr, Literal, LogicalOp, UnaryOp};
|
pub use expr::{BinaryOp, Expr, Literal, LogicalOp, UnaryOp};
|
||||||
|
pub use parse::parse_tokens;
|
||||||
pub use stmt::Stmt;
|
pub use stmt::Stmt;
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
use std::vec::IntoIter;
|
use std::vec::IntoIter;
|
||||||
|
|
||||||
use crate::error::ParserError;
|
|
||||||
use crate::lexer::{Token, TokenType};
|
use crate::lexer::{Token, TokenType};
|
||||||
use crate::parser::expr::BinaryOp;
|
use crate::parser::expr::BinaryOp;
|
||||||
|
|
||||||
use super::expr::{Expr, UnaryOp};
|
use super::expr::{Expr, UnaryOp};
|
||||||
use super::{LogicalOp, Stmt};
|
use super::{LogicalOp, ParserError, Stmt};
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
|
@ -62,6 +61,9 @@ struct Parser {
|
||||||
token_iter: TokenIter,
|
token_iter: TokenIter,
|
||||||
|
|
||||||
parse_errors: Vec<ParserError>,
|
parse_errors: Vec<ParserError>,
|
||||||
|
|
||||||
|
is_in_function: bool,
|
||||||
|
is_in_loop: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
|
|
@ -69,6 +71,8 @@ impl Parser {
|
||||||
Parser {
|
Parser {
|
||||||
token_iter: TokenIter::new(tokens),
|
token_iter: TokenIter::new(tokens),
|
||||||
parse_errors: Vec::new(),
|
parse_errors: Vec::new(),
|
||||||
|
is_in_function: false,
|
||||||
|
is_in_loop: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,20 +146,29 @@ impl Parser {
|
||||||
TokenType::While => self.while_statement(),
|
TokenType::While => self.while_statement(),
|
||||||
TokenType::For => self.for_statement(),
|
TokenType::For => self.for_statement(),
|
||||||
TokenType::Var => self.var_declaration(),
|
TokenType::Var => self.var_declaration(),
|
||||||
|
TokenType::Class => self.class_declaration(),
|
||||||
TokenType::Fun => self.fun_declaration(),
|
TokenType::Fun => self.fun_declaration(),
|
||||||
TokenType::LeftBrace => self.block(),
|
TokenType::LeftBrace => self.block(),
|
||||||
TokenType::Break => {
|
TokenType::Break => {
|
||||||
|
let code_pos = self.peek_token().code_pos;
|
||||||
assert_eq!(self.next_token().token_type, TokenType::Break);
|
assert_eq!(self.next_token().token_type, TokenType::Break);
|
||||||
self.semicolon()?;
|
self.semicolon()?;
|
||||||
|
if !self.is_in_loop {
|
||||||
|
return Err(ParserError::InvalidBreak { code_pos });
|
||||||
|
}
|
||||||
Ok(Stmt::Break)
|
Ok(Stmt::Break)
|
||||||
}
|
}
|
||||||
TokenType::Return => {
|
TokenType::Return => {
|
||||||
|
let code_pos = self.peek_token().code_pos;
|
||||||
assert_eq!(self.next_token().token_type, TokenType::Return);
|
assert_eq!(self.next_token().token_type, TokenType::Return);
|
||||||
let expr = match self.peek_token().token_type {
|
let expr = match self.peek_token().token_type {
|
||||||
TokenType::Semicolon => Expr::nil(),
|
TokenType::Semicolon => Expr::nil(),
|
||||||
_ => self.expression()?,
|
_ => self.expression()?,
|
||||||
};
|
};
|
||||||
self.semicolon()?;
|
self.semicolon()?;
|
||||||
|
if !self.is_in_function {
|
||||||
|
return Err(ParserError::InvalidReturn { code_pos });
|
||||||
|
}
|
||||||
Ok(Stmt::return_stmt(expr))
|
Ok(Stmt::return_stmt(expr))
|
||||||
}
|
}
|
||||||
_ => self.expression_statement(),
|
_ => self.expression_statement(),
|
||||||
|
|
@ -202,7 +215,17 @@ impl Parser {
|
||||||
code_pos: token.code_pos,
|
code_pos: token.code_pos,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let body = self.statement()?;
|
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
|
||||||
|
|
||||||
|
let body = match self.statement() {
|
||||||
|
Ok(body) => body,
|
||||||
|
Err(err) => {
|
||||||
|
self.is_in_loop = is_in_loop;
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.is_in_loop = is_in_loop;
|
||||||
|
|
||||||
Ok(Stmt::while_stmt(condition, body))
|
Ok(Stmt::while_stmt(condition, body))
|
||||||
}
|
}
|
||||||
|
|
@ -239,7 +262,17 @@ impl Parser {
|
||||||
code_pos: token.code_pos,
|
code_pos: token.code_pos,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mut body = self.statement()?;
|
let is_in_loop = std::mem::replace(&mut self.is_in_loop, true);
|
||||||
|
|
||||||
|
let mut body = match self.statement() {
|
||||||
|
Ok(body) => body,
|
||||||
|
Err(err) => {
|
||||||
|
self.is_in_loop = is_in_loop;
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.is_in_loop = is_in_loop;
|
||||||
|
|
||||||
if let Some(increment) = increment {
|
if let Some(increment) = increment {
|
||||||
body = Stmt::Block {
|
body = Stmt::Block {
|
||||||
|
|
@ -295,6 +328,38 @@ impl Parser {
|
||||||
Ok(Stmt::var_decl(name, initializer))
|
Ok(Stmt::var_decl(name, initializer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn class_declaration(&mut self) -> ParserResult<Stmt> {
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::Class);
|
||||||
|
|
||||||
|
let name = self.identifier("Missing class name")?;
|
||||||
|
|
||||||
|
self.consume_token(TokenType::LeftBrace, |token| ParserError::MissingClassBody {
|
||||||
|
code_pos: token.code_pos,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mut methods = Vec::new();
|
||||||
|
|
||||||
|
while self.peek_token().token_type != TokenType::RightBrace {
|
||||||
|
let method_name = self.identifier("Expected method name").map_err(|err| {
|
||||||
|
if self.peek_token().token_type == TokenType::EOF {
|
||||||
|
ParserError::MissingRightBrace {
|
||||||
|
code_pos: self.peek_token().code_pos,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let method = self.fun_params_and_body(method_name)?;
|
||||||
|
|
||||||
|
methods.push(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(self.next_token().token_type, TokenType::RightBrace);
|
||||||
|
|
||||||
|
Ok(Stmt::Class { name, methods })
|
||||||
|
}
|
||||||
|
|
||||||
fn fun_declaration(&mut self) -> ParserResult<Stmt> {
|
fn fun_declaration(&mut self) -> ParserResult<Stmt> {
|
||||||
assert_eq!(self.next_token().token_type, TokenType::Fun);
|
assert_eq!(self.next_token().token_type, TokenType::Fun);
|
||||||
|
|
||||||
|
|
@ -331,7 +396,20 @@ impl Parser {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let body = self.block()?;
|
let is_in_function = std::mem::replace(&mut self.is_in_function, true);
|
||||||
|
let is_in_loop = std::mem::replace(&mut self.is_in_loop, false);
|
||||||
|
|
||||||
|
let body = match self.block() {
|
||||||
|
Ok(body) => body,
|
||||||
|
Err(err) => {
|
||||||
|
self.is_in_function = is_in_function;
|
||||||
|
self.is_in_loop = is_in_loop;
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.is_in_function = is_in_function;
|
||||||
|
self.is_in_loop = is_in_loop;
|
||||||
|
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
Ok(Expr::function(name, param_names, body))
|
Ok(Expr::function(name, param_names, body))
|
||||||
|
|
@ -415,7 +493,7 @@ impl Parser {
|
||||||
let value = self.assignment()?;
|
let value = self.assignment()?;
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Variable { name } => Ok(Expr::assignment(name, value)),
|
Expr::Variable { name } => Ok(Expr::assignment(Expr::Variable { name }, value)),
|
||||||
_ => Err(ParserError::InvalidAssignment { expr, code_pos }),
|
_ => Err(ParserError::InvalidAssignment { expr, code_pos }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::misc::indent;
|
use super::misc::indent;
|
||||||
|
|
||||||
use super::Expr;
|
use super::Expr;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -25,6 +24,10 @@ pub enum Stmt {
|
||||||
Block {
|
Block {
|
||||||
statements: Vec<Stmt>,
|
statements: Vec<Stmt>,
|
||||||
},
|
},
|
||||||
|
Class {
|
||||||
|
name: String,
|
||||||
|
methods: Vec<Expr>,
|
||||||
|
},
|
||||||
ExprStmt {
|
ExprStmt {
|
||||||
expr: Box<Expr>,
|
expr: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
|
@ -92,13 +95,13 @@ impl Display for Stmt {
|
||||||
writeln!(f, "if {condition}")?;
|
writeln!(f, "if {condition}")?;
|
||||||
match then_branch.as_ref() {
|
match then_branch.as_ref() {
|
||||||
Stmt::Block { .. } => write!(f, "{then_branch}")?,
|
Stmt::Block { .. } => write!(f, "{then_branch}")?,
|
||||||
_ => write!(f, "{}", indent(format!("{then_branch}")))?,
|
_ => write!(f, "{}", indent(then_branch.to_string()))?,
|
||||||
}
|
}
|
||||||
if let Some(else_branch) = else_branch {
|
if let Some(else_branch) = else_branch {
|
||||||
writeln!(f, "\nelse")?;
|
writeln!(f, "\nelse")?;
|
||||||
match else_branch.as_ref() {
|
match else_branch.as_ref() {
|
||||||
Stmt::Block { .. } => write!(f, "{else_branch}")?,
|
Stmt::Block { .. } => write!(f, "{else_branch}")?,
|
||||||
_ => write!(f, "{}", indent(format!("{else_branch}")))?,
|
_ => write!(f, "{}", indent(else_branch.to_string()))?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -107,7 +110,7 @@ impl Display for Stmt {
|
||||||
writeln!(f, "{condition}")?;
|
writeln!(f, "{condition}")?;
|
||||||
match body.as_ref() {
|
match body.as_ref() {
|
||||||
Stmt::Block { .. } => write!(f, "{body}")?,
|
Stmt::Block { .. } => write!(f, "{body}")?,
|
||||||
_ => write!(f, "{}", indent(format!("{body}")))?,
|
_ => write!(f, "{}", indent(body.to_string()))?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +118,14 @@ impl Display for Stmt {
|
||||||
Stmt::Block { statements } => {
|
Stmt::Block { statements } => {
|
||||||
writeln!(f, "{{")?;
|
writeln!(f, "{{")?;
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
write!(f, "{}", indent(format!("{statement}")))?;
|
writeln!(f, "{}", indent(statement.to_string()))?;
|
||||||
|
}
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
Stmt::Class { name, methods } => {
|
||||||
|
writeln!(f, "class {name} {{")?;
|
||||||
|
for method in methods {
|
||||||
|
writeln!(f, "{method}")?;
|
||||||
}
|
}
|
||||||
write!(f, "}}")
|
write!(f, "}}")
|
||||||
}
|
}
|
||||||
13
interpreter/Cargo.toml
Normal file
13
interpreter/Cargo.toml
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "rlox2-interpreter"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies.rlox2-frontend]
|
||||||
|
path = "../frontend"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
thiserror = "1.0.38"
|
||||||
|
itertools = "0.10.5"
|
||||||
19
interpreter/src/class.rs
Normal file
19
interpreter/src/class.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct LoxClass {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LoxClass {
|
||||||
|
pub fn new(name: impl Into<String>) -> Self {
|
||||||
|
let name = name.into();
|
||||||
|
LoxClass { name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for LoxClass {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "<class {}>", self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
209
interpreter/src/environment.rs
Normal file
209
interpreter/src/environment.rs
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use crate::error::RuntimeError;
|
||||||
|
|
||||||
|
use super::value::HeapedValue;
|
||||||
|
use super::{Runtime, Value};
|
||||||
|
|
||||||
|
pub type Scope = HashMap<String, HeapedValue>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Environment<'a> {
|
||||||
|
local_scopes: Vec<Scope>,
|
||||||
|
|
||||||
|
runtime: &'a mut Runtime,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Environment<'a> {
|
||||||
|
pub fn new(runtime: &'a mut Runtime) -> Self {
|
||||||
|
Environment {
|
||||||
|
// globals: HashMap::new(),
|
||||||
|
// frames: vec![Frame::new_global()],
|
||||||
|
local_scopes: Vec::new(),
|
||||||
|
|
||||||
|
runtime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn globals(&self) -> &HashMap<String, Value> {
|
||||||
|
self.runtime.globals()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enter_scope(&mut self) {
|
||||||
|
// self.current_frame_mut().enter_scope();
|
||||||
|
self.local_scopes.push(Scope::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_scope(&mut self, scope: Scope) {
|
||||||
|
self.local_scopes.push(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_scope(&mut self) {
|
||||||
|
// self.current_frame_mut().exit_scope();
|
||||||
|
self.local_scopes.pop().expect("Tried to pop global scope");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_global(&self, name: &str) -> Result<Value, RuntimeError> {
|
||||||
|
self.runtime.get_global(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local(&self, name: &str, level: usize) -> Result<Value, RuntimeError> {
|
||||||
|
if let Some(scope) = self.local_scopes.iter().rev().nth(level) {
|
||||||
|
if let Some(heap_value) = scope.get(name) {
|
||||||
|
return Ok(heap_value.get_clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG
|
||||||
|
println!("Name {name} not defined at level {level}");
|
||||||
|
println!("{self}");
|
||||||
|
|
||||||
|
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
|
||||||
|
// self.runtime.get_global(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define(&mut self, name: impl Into<String>, value: Value) {
|
||||||
|
if let Some(scope) = self.local_scopes.last_mut() {
|
||||||
|
let name = name.into();
|
||||||
|
scope.insert(name, HeapedValue::new(value));
|
||||||
|
} else {
|
||||||
|
self.runtime.define_global(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_global(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
|
||||||
|
self.runtime.assign_global(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign(&mut self, name: &str, value: Value, level: usize) -> Result<(), RuntimeError> {
|
||||||
|
if let Some(scope) = self.local_scopes.iter_mut().rev().nth(level) {
|
||||||
|
if let Some(heap_value) = scope.get_mut(name) {
|
||||||
|
heap_value.replace(value);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
|
||||||
|
// self.runtime.assign_global(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pub fn push_frame(&mut self, base_scope: Scope) {
|
||||||
|
self.frames.push(Frame::new(base_scope));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_frame(&mut self) {
|
||||||
|
self.frames.pop().expect("Tried to pop global frame");
|
||||||
|
} */
|
||||||
|
|
||||||
|
pub fn collect_closure(&self, closure_vars: &[(String, usize)]) -> Scope {
|
||||||
|
let mut closure_scope = Scope::new();
|
||||||
|
|
||||||
|
for (name, level) in closure_vars {
|
||||||
|
let heap_value = self
|
||||||
|
.local_scopes
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.nth(*level)
|
||||||
|
.unwrap()
|
||||||
|
.get(name)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let name = name.to_owned();
|
||||||
|
|
||||||
|
closure_scope.insert(name, heap_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
closure_scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Environment<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.runtime)?;
|
||||||
|
|
||||||
|
for (level, scope) in self.local_scopes.iter().enumerate() {
|
||||||
|
write!(f, "\nScope {level}:")?;
|
||||||
|
|
||||||
|
for (name, value) in scope.iter() {
|
||||||
|
write!(f, "\n\t{name} = {value}")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Environment<'_> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
self.runtime.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Environment<'_> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
self.runtime.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
self.runtime.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
/* struct Frame {
|
||||||
|
scopes: Vec<Scope>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Frame {
|
||||||
|
fn new(base_scope: Scope) -> Self {
|
||||||
|
Frame {
|
||||||
|
scopes: vec![base_scope],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_global() -> Self {
|
||||||
|
Frame { scopes: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_scope(&mut self) {
|
||||||
|
self.scopes.push(Scope::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_scope(&mut self) {
|
||||||
|
self.scopes.pop().expect("Tried to exit scope, but no scope to exit");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, name: &str) -> Option<Value> {
|
||||||
|
for scope in self.scopes.iter().rev() {
|
||||||
|
if let Some(heap_value) = scope.get(name) {
|
||||||
|
return Some(heap_value.get_clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_define(&mut self, name: impl Into<String> + Borrow<str>, value: Value) -> Result<(), Value> {
|
||||||
|
if let Some(scope) = self.scopes.last_mut() {
|
||||||
|
scope.insert(name.into(), HeapValue::new(value));
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_assign(&mut self, name: &str, value: Value) -> Result<(), Value> {
|
||||||
|
for scope in self.scopes.iter_mut().rev() {
|
||||||
|
if let Some(heap_value) = scope.get_mut(name) {
|
||||||
|
heap_value.replace(value);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(value)
|
||||||
|
}
|
||||||
|
} */
|
||||||
85
interpreter/src/error.rs
Normal file
85
interpreter/src/error.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
use itertools::Itertools;
|
||||||
|
use rlox2_frontend::lexer::LexerError;
|
||||||
|
use rlox2_frontend::parser::{BinaryOp, ParserError, UnaryOp};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::ResolverError;
|
||||||
|
use crate::Value;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum RuntimeError {
|
||||||
|
#[error("Unary operator {op} had invalid argument {arg}")]
|
||||||
|
UnaryOpInvalidArgument { op: UnaryOp, arg: Value },
|
||||||
|
#[error("Binary operator {op} had invalid arguments {left} and {right}")]
|
||||||
|
BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value },
|
||||||
|
#[error("Division by zero")]
|
||||||
|
DivisionByZero,
|
||||||
|
#[error("Name {name} is not defined")]
|
||||||
|
NameNotDefined { name: String },
|
||||||
|
#[error("Global {name} is not defined")]
|
||||||
|
GlobalNotDefined { name: String },
|
||||||
|
#[error("{callee} is not callable")]
|
||||||
|
NotCallable { callee: Value },
|
||||||
|
#[error("{name}() takes {arity} args, but {given} were given.")]
|
||||||
|
WrongArity { name: String, arity: usize, given: usize },
|
||||||
|
#[error("Extern function call to {name} failed: {msg}")]
|
||||||
|
ExtFunCallFailed { name: String, msg: String },
|
||||||
|
#[error("Uncaught break statement")]
|
||||||
|
Break,
|
||||||
|
#[error("Uncaught return statement")]
|
||||||
|
Return { value: Value },
|
||||||
|
#[error("Exit with exit code {exit_code}")]
|
||||||
|
Exit { exit_code: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[allow(clippy::enum_variant_names)]
|
||||||
|
pub enum LoxError {
|
||||||
|
#[error("{0}", format_multiple_errors(inner))]
|
||||||
|
LexerError { inner: Vec<LexerError> },
|
||||||
|
#[error("{0}", format_multiple_errors(inner))]
|
||||||
|
ParserError { inner: Vec<ParserError> },
|
||||||
|
#[error("{inner}")]
|
||||||
|
ResolverError { inner: ResolverError },
|
||||||
|
#[error("{inner}")]
|
||||||
|
RuntimeError { inner: RuntimeError },
|
||||||
|
#[error("Called exit() with exit code {exit_code}")]
|
||||||
|
Exit { exit_code: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_multiple_errors(errs: &Vec<impl std::error::Error>) -> String {
|
||||||
|
let msg = if errs.len() == 1 {
|
||||||
|
errs[0].to_string()
|
||||||
|
} else {
|
||||||
|
errs.iter().map(|err| err.to_string()).join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
msg
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<LexerError>> for LoxError {
|
||||||
|
fn from(lexer_errs: Vec<LexerError>) -> Self {
|
||||||
|
LoxError::LexerError { inner: lexer_errs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<ParserError>> for LoxError {
|
||||||
|
fn from(parser_errs: Vec<ParserError>) -> Self {
|
||||||
|
LoxError::ParserError { inner: parser_errs }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ResolverError> for LoxError {
|
||||||
|
fn from(resolver_err: ResolverError) -> Self {
|
||||||
|
LoxError::ResolverError { inner: resolver_err }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RuntimeError> for LoxError {
|
||||||
|
fn from(runtime_err: RuntimeError) -> Self {
|
||||||
|
match runtime_err {
|
||||||
|
RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code },
|
||||||
|
_ => LoxError::RuntimeError { inner: runtime_err },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
use crate::parser::Stmt;
|
use rlox2_frontend::parser::Stmt;
|
||||||
|
|
||||||
use super::environment::{Environment, Scope};
|
use super::environment::{Environment, Scope};
|
||||||
use super::interpret::EvalResult;
|
use super::interpret::EvalResult;
|
||||||
use super::Value;
|
use super::{Runtime, Value};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LoxFunction {
|
pub struct LoxFunction {
|
||||||
|
|
@ -15,12 +15,12 @@ pub struct LoxFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoxFunction {
|
impl LoxFunction {
|
||||||
pub fn new(name: impl Into<String>, param_names: Vec<String>, body: Stmt) -> Self {
|
pub fn new(name: impl Into<String>, closure: Scope, param_names: Vec<String>, body: Stmt) -> Self {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
let body = Box::new(body);
|
let body = Box::new(body);
|
||||||
LoxFunction {
|
LoxFunction {
|
||||||
name,
|
name,
|
||||||
closure: Scope::new(),
|
closure,
|
||||||
param_names,
|
param_names,
|
||||||
body,
|
body,
|
||||||
}
|
}
|
||||||
|
|
@ -71,10 +71,10 @@ impl LoxExternFunction {
|
||||||
LoxExternFunction { name, closure }
|
LoxExternFunction { name, closure }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register(self, env: &mut Environment) {
|
pub fn register(self, env: &mut Runtime) {
|
||||||
let name = self.name.clone();
|
let name = self.name.clone();
|
||||||
let fun = Value::extern_function(self);
|
let fun = Value::extern_function(self);
|
||||||
env.define(name, fun);
|
env.define_global(name, fun);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
|
pub fn call(&self, args: Vec<Value>, env: &mut Environment) -> EvalResult<Value> {
|
||||||
|
|
@ -1,26 +1,32 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use rlox2_frontend::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp};
|
||||||
|
|
||||||
use crate::error::RuntimeError;
|
use crate::error::RuntimeError;
|
||||||
use crate::parser::{BinaryOp, Expr, Literal, LogicalOp, Stmt, UnaryOp};
|
use crate::LoxClass;
|
||||||
|
|
||||||
use super::environment::Environment;
|
use super::environment::Environment;
|
||||||
use super::{LoxFunction, Value};
|
use super::{LoxFunction, Runtime, Value};
|
||||||
|
|
||||||
pub type EvalResult<T> = Result<T, RuntimeError>;
|
pub type EvalResult<T> = Result<T, RuntimeError>;
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
pub fn execute(statement: Stmt, env: &mut Environment) -> Result<(), RuntimeError> {
|
pub fn execute(statement: Stmt, runtime: &mut Runtime) -> Result<(), RuntimeError> {
|
||||||
statement.execute(env)
|
let mut env = Environment::new(runtime);
|
||||||
|
|
||||||
|
statement.eval(&mut env)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
/* trait Eval {
|
trait Eval {
|
||||||
fn eval(self, env: &mut Environment) -> EvalResult;
|
fn eval(&self, env: &mut Environment) -> EvalResult<Value>;
|
||||||
} */
|
}
|
||||||
|
|
||||||
impl Literal {
|
impl Eval for Literal {
|
||||||
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
|
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
|
||||||
let _ = env;
|
let _ = env;
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -32,7 +38,7 @@ impl Literal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Eval for Expr {
|
||||||
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
|
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
|
||||||
match self {
|
match self {
|
||||||
Expr::Literal { literal } => literal.eval(env),
|
Expr::Literal { literal } => literal.eval(env),
|
||||||
|
|
@ -104,10 +110,17 @@ impl Expr {
|
||||||
Ok(right)
|
Ok(right)
|
||||||
}
|
}
|
||||||
Expr::Grouping { expr } => expr.eval(env),
|
Expr::Grouping { expr } => expr.eval(env),
|
||||||
Expr::Variable { name } => env.get(name),
|
Expr::Variable { name } => panic!("Unresolved variable {name}"),
|
||||||
Expr::Assignment { name, value } => {
|
Expr::LocalVariable { name, level } => env.get_local(name, *level),
|
||||||
|
Expr::GlobalVariable { name } => env.get_global(name),
|
||||||
|
Expr::Assignment { target, value } => {
|
||||||
let value = value.eval(env)?;
|
let value = value.eval(env)?;
|
||||||
env.assign(name, value.clone())?;
|
|
||||||
|
match target.as_ref() {
|
||||||
|
Expr::LocalVariable { name, level } => env.assign(name, value.clone(), *level)?,
|
||||||
|
Expr::GlobalVariable { name } => env.assign_global(name, value.clone())?,
|
||||||
|
_ => panic!("Invalid assigment target {target}"),
|
||||||
|
}
|
||||||
Ok(value)
|
Ok(value)
|
||||||
}
|
}
|
||||||
Expr::Call { callee, args } => {
|
Expr::Call { callee, args } => {
|
||||||
|
|
@ -126,9 +139,11 @@ impl Expr {
|
||||||
Expr::Function {
|
Expr::Function {
|
||||||
name,
|
name,
|
||||||
param_names,
|
param_names,
|
||||||
|
closure_vars,
|
||||||
body,
|
body,
|
||||||
} => Ok(Value::function(LoxFunction::new(
|
} => Ok(Value::function(LoxFunction::new(
|
||||||
name,
|
name,
|
||||||
|
env.collect_closure(closure_vars),
|
||||||
param_names.clone(),
|
param_names.clone(),
|
||||||
body.as_ref().clone(),
|
body.as_ref().clone(),
|
||||||
))),
|
))),
|
||||||
|
|
@ -136,8 +151,8 @@ impl Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stmt {
|
impl Eval for Stmt {
|
||||||
fn execute(&self, env: &mut Environment) -> EvalResult<()> {
|
fn eval(&self, env: &mut Environment) -> EvalResult<Value> {
|
||||||
match self {
|
match self {
|
||||||
Stmt::Print { expr } => {
|
Stmt::Print { expr } => {
|
||||||
match expr.eval(env)? {
|
match expr.eval(env)? {
|
||||||
|
|
@ -145,8 +160,6 @@ impl Stmt {
|
||||||
Value::String(s) => println!("{s}"),
|
Value::String(s) => println!("{s}"),
|
||||||
val => println!("{val}"),
|
val => println!("{val}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Stmt::IfStmt {
|
Stmt::IfStmt {
|
||||||
condition,
|
condition,
|
||||||
|
|
@ -155,49 +168,54 @@ impl Stmt {
|
||||||
} => {
|
} => {
|
||||||
let condition = condition.eval(env)?;
|
let condition = condition.eval(env)?;
|
||||||
if condition.is_truthy() {
|
if condition.is_truthy() {
|
||||||
then_branch.execute(env)
|
then_branch.eval(env)?;
|
||||||
} else if let Some(else_branch) = else_branch {
|
} else if let Some(else_branch) = else_branch {
|
||||||
else_branch.execute(env)
|
else_branch.eval(env)?;
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::While { condition, body } => {
|
Stmt::While { condition, body } => {
|
||||||
while condition.eval(env)?.is_truthy() {
|
while condition.eval(env)?.is_truthy() {
|
||||||
match body.execute(env) {
|
match body.eval(env) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(RuntimeError::Break) => break,
|
Err(RuntimeError::Break) => break,
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Stmt::VarDecl { name, initializer } => {
|
Stmt::VarDecl { name, initializer } => {
|
||||||
let initializer = initializer.eval(env)?;
|
let initializer = initializer.eval(env)?;
|
||||||
env.define(name.as_ref(), initializer);
|
env.define(name, initializer);
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Stmt::Block { statements } => {
|
Stmt::Block { statements } => {
|
||||||
env.enter_scope();
|
env.enter_scope();
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
// on error the current frame gets destroyed anyways, so no need to exit scope
|
if let Err(err) = statement.eval(env) {
|
||||||
statement.execute(env)?;
|
env.exit_scope();
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
env.exit_scope();
|
env.exit_scope();
|
||||||
Ok(())
|
}
|
||||||
|
Stmt::Class { name, methods: _ } => {
|
||||||
|
env.define(name, Value::Nil);
|
||||||
|
|
||||||
|
let class = Value::class(LoxClass::new(name));
|
||||||
|
|
||||||
|
env.assign(name, class, 0)?;
|
||||||
}
|
}
|
||||||
Stmt::ExprStmt { expr } => {
|
Stmt::ExprStmt { expr } => {
|
||||||
// expr.eval(env)?;
|
// expr.eval(env)?;
|
||||||
// Ok(Value::Nil)
|
// Ok(Value::Nil)
|
||||||
expr.eval(env)?;
|
expr.eval(env)?;
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Stmt::Break => Err(RuntimeError::Break),
|
Stmt::Break => return Err(RuntimeError::Break),
|
||||||
Stmt::Return { expr } => {
|
Stmt::Return { expr } => {
|
||||||
let value = expr.eval(env)?;
|
let value = expr.eval(env)?;
|
||||||
Err(RuntimeError::Return { value })
|
return Err(RuntimeError::Return { value });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Value::Nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,20 +231,20 @@ impl LoxFunction {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
env.push_frame(self.closure().clone());
|
env.push_scope(self.closure().clone());
|
||||||
|
|
||||||
for (name, value) in std::iter::zip(self.param_names(), args) {
|
for (name, value) in std::iter::zip(self.param_names(), args) {
|
||||||
env.define(name.as_ref(), value);
|
env.define(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_val = match self.body().execute(env) {
|
let ret_val = match self.body().eval(env) {
|
||||||
Ok(()) => Value::Nil,
|
Ok(_) => Ok(Value::Nil),
|
||||||
Err(RuntimeError::Return { value }) => value,
|
Err(RuntimeError::Return { value }) => Ok(value),
|
||||||
Err(err) => return Err(err),
|
Err(err) => Err(err),
|
||||||
};
|
};
|
||||||
|
|
||||||
env.pop_frame();
|
env.exit_scope();
|
||||||
|
|
||||||
Ok(ret_val)
|
ret_val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
interpreter/src/lib.rs
Normal file
18
interpreter/src/lib.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
mod class;
|
||||||
|
mod environment;
|
||||||
|
mod error;
|
||||||
|
mod function;
|
||||||
|
mod interpret;
|
||||||
|
mod lox_std;
|
||||||
|
mod resolver;
|
||||||
|
mod run;
|
||||||
|
mod runtime;
|
||||||
|
mod value;
|
||||||
|
|
||||||
|
pub use class::LoxClass;
|
||||||
|
pub use error::{LoxError, RuntimeError};
|
||||||
|
pub use function::LoxFunction;
|
||||||
|
pub use resolver::{resolve, ResolverError};
|
||||||
|
pub use run::{run_file, run_repl};
|
||||||
|
pub use runtime::Runtime;
|
||||||
|
pub use value::Value;
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
use crate::error::RuntimeError;
|
use crate::error::RuntimeError;
|
||||||
|
|
||||||
use super::environment::Environment;
|
|
||||||
use super::function::{ExternFunClosure, LoxExternFunction};
|
use super::function::{ExternFunClosure, LoxExternFunction};
|
||||||
use super::value::HeapValue;
|
use super::{Runtime, Value};
|
||||||
use super::Value;
|
|
||||||
|
|
||||||
pub fn init_std(env: &mut Environment) {
|
pub fn init_std(env: &mut Runtime) {
|
||||||
input().register(env);
|
|
||||||
clock().register(env);
|
clock().register(env);
|
||||||
print_globals().register(env);
|
|
||||||
exit().register(env);
|
exit().register(env);
|
||||||
|
input().register(env);
|
||||||
|
print_env().register(env);
|
||||||
|
print_globals().register(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn input() -> LoxExternFunction {
|
fn input() -> LoxExternFunction {
|
||||||
|
|
@ -58,7 +57,7 @@ fn clock() -> LoxExternFunction {
|
||||||
fn print_globals() -> LoxExternFunction {
|
fn print_globals() -> LoxExternFunction {
|
||||||
let closure: ExternFunClosure = |args, env| match *args {
|
let closure: ExternFunClosure = |args, env| match *args {
|
||||||
[] => {
|
[] => {
|
||||||
let mut globals: Vec<(&String, &HeapValue)> = env.globals().iter().collect();
|
let mut globals: Vec<(&String, &Value)> = env.globals().iter().collect();
|
||||||
|
|
||||||
globals.sort_by_key(|&(name, _value)| name);
|
globals.sort_by_key(|&(name, _value)| name);
|
||||||
|
|
||||||
|
|
@ -77,6 +76,21 @@ fn print_globals() -> LoxExternFunction {
|
||||||
LoxExternFunction::new("print_globals", closure)
|
LoxExternFunction::new("print_globals", closure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_env() -> LoxExternFunction {
|
||||||
|
let closure: ExternFunClosure = |args, env| match *args {
|
||||||
|
[] => {
|
||||||
|
println!("{env}");
|
||||||
|
Ok(Value::Nil)
|
||||||
|
}
|
||||||
|
_ => Err(RuntimeError::ExtFunCallFailed {
|
||||||
|
name: "print_env".to_owned(),
|
||||||
|
msg: "print_env() takes no arguments".to_owned(),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
LoxExternFunction::new("print_env", closure)
|
||||||
|
}
|
||||||
|
|
||||||
fn exit() -> LoxExternFunction {
|
fn exit() -> LoxExternFunction {
|
||||||
let closure: ExternFunClosure = |args, _env| {
|
let closure: ExternFunClosure = |args, _env| {
|
||||||
match &*args {
|
match &*args {
|
||||||
11
interpreter/src/resolver/error.rs
Normal file
11
interpreter/src/resolver/error.rs
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ResolverError {
|
||||||
|
#[error("Can't read variable {name} in its own initializer.")]
|
||||||
|
VarInOwnInitializer { name: String },
|
||||||
|
#[error("Could not resolve variable {name}")]
|
||||||
|
UnresolvableVariable { name: String },
|
||||||
|
#[error("Return outside of function definition")]
|
||||||
|
ReturnOutsideFunction,
|
||||||
|
}
|
||||||
5
interpreter/src/resolver/mod.rs
Normal file
5
interpreter/src/resolver/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod error;
|
||||||
|
mod resolve;
|
||||||
|
|
||||||
|
pub use error::ResolverError;
|
||||||
|
pub use resolve::resolve;
|
||||||
276
interpreter/src/resolver/resolve.rs
Normal file
276
interpreter/src/resolver/resolve.rs
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use rlox2_frontend::parser::{Expr, Stmt};
|
||||||
|
|
||||||
|
use crate::Runtime;
|
||||||
|
use crate::{LoxError, ResolverError};
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
type ResolverScope = HashMap<String, ResolveStatus>;
|
||||||
|
type ResolverFrame = Vec<ResolverScope>;
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum ResolveStatus {
|
||||||
|
Declared,
|
||||||
|
Defined,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
|
pub fn resolve(statement: &mut Stmt, runtime: &mut Runtime) -> Result<(), LoxError> {
|
||||||
|
let global_names = runtime.globals().keys().cloned();
|
||||||
|
|
||||||
|
let mut resolver = Resolver::new(global_names);
|
||||||
|
|
||||||
|
resolver.resolve_stmt(statement)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Resolver {
|
||||||
|
global_scope: ResolverScope,
|
||||||
|
// local_scopes: Vec<ResolverScope>,
|
||||||
|
frames: Vec<ResolverFrame>,
|
||||||
|
|
||||||
|
closure_vars: HashMap<String, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Resolver {
|
||||||
|
fn new(global_names: impl Iterator<Item = String>) -> Self {
|
||||||
|
let mut global_scope = ResolverScope::new();
|
||||||
|
|
||||||
|
for name in global_names {
|
||||||
|
global_scope.insert(name, ResolveStatus::Defined);
|
||||||
|
}
|
||||||
|
|
||||||
|
Resolver {
|
||||||
|
global_scope,
|
||||||
|
frames: vec![ResolverFrame::new()],
|
||||||
|
closure_vars: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_scopes(&self) -> &ResolverFrame {
|
||||||
|
self.frames.last().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn local_scopes_mut(&mut self) -> &mut ResolverFrame {
|
||||||
|
self.frames.last_mut().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_scope(&mut self) {
|
||||||
|
self.local_scopes_mut().push(ResolverScope::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_scope(&mut self) {
|
||||||
|
self.local_scopes_mut()
|
||||||
|
.pop()
|
||||||
|
.expect("Tried to pop from empty ResolverFrame");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_frame(&mut self) {
|
||||||
|
self.frames.push(vec![ResolverScope::new()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop_frame(&mut self) {
|
||||||
|
self.frames.pop().expect("Tried to pop non-existant ResolverFrame");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare(&mut self, name: &str) {
|
||||||
|
let name = name.to_owned();
|
||||||
|
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
|
||||||
|
local_scope.insert(name, ResolveStatus::Declared);
|
||||||
|
} else {
|
||||||
|
self.global_scope.insert(name, ResolveStatus::Declared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn define(&mut self, name: &str) {
|
||||||
|
let name = name.to_owned();
|
||||||
|
if let Some(local_scope) = self.local_scopes_mut().last_mut() {
|
||||||
|
local_scope.insert(name, ResolveStatus::Defined);
|
||||||
|
} else {
|
||||||
|
self.global_scope.insert(name, ResolveStatus::Declared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(&self, name: &str) -> Option<ResolveStatus> {
|
||||||
|
if let Some(local_scope) = self.local_scopes().last() {
|
||||||
|
local_scope.get(name).cloned()
|
||||||
|
} else {
|
||||||
|
self.global_scope.get(name).cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_var(&mut self, name: &str) -> Result<Expr, ResolverError> {
|
||||||
|
let name = name.to_owned();
|
||||||
|
|
||||||
|
let mut level = 0;
|
||||||
|
|
||||||
|
for scope in self.local_scopes().iter().rev() {
|
||||||
|
if scope.contains_key(&name) {
|
||||||
|
return Ok(Expr::LocalVariable { name, level });
|
||||||
|
}
|
||||||
|
// DEBUG
|
||||||
|
level += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for frame in self.frames.iter().rev().skip(1) {
|
||||||
|
for scope in frame.iter().rev() {
|
||||||
|
if scope.contains_key(&name) {
|
||||||
|
if !self.closure_vars.contains_key(&name) {
|
||||||
|
// the level at which the closed-over variable will be collected from
|
||||||
|
// from the perspective of the parameter/closure scope i.e. the outmost of the function
|
||||||
|
self.closure_vars
|
||||||
|
.insert(name.clone(), level - self.local_scopes().len());
|
||||||
|
}
|
||||||
|
return Ok(Expr::LocalVariable {
|
||||||
|
name,
|
||||||
|
// distance from actual variable refernce to parameter/closure scope
|
||||||
|
level: self.local_scopes().len() - 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
level += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.global_scope.contains_key(&name) {
|
||||||
|
return Ok(Expr::GlobalVariable { name });
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(ResolverError::UnresolvableVariable { name })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_stmt(&mut self, stmt: &mut Stmt) -> Result<(), ResolverError> {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Print { expr } => self.resolve_expr(expr),
|
||||||
|
Stmt::IfStmt {
|
||||||
|
condition,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
} => {
|
||||||
|
self.resolve_expr(condition)?;
|
||||||
|
self.resolve_stmt(then_branch)?;
|
||||||
|
if let Some(else_branch) = else_branch {
|
||||||
|
self.resolve_stmt(else_branch)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::While { condition, body } => {
|
||||||
|
self.resolve_expr(condition)?;
|
||||||
|
self.resolve_stmt(body)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::VarDecl { name, initializer } => {
|
||||||
|
self.declare(name);
|
||||||
|
self.resolve_expr(initializer)?;
|
||||||
|
self.define(name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::Block { statements } => {
|
||||||
|
self.enter_scope();
|
||||||
|
for statement in statements.iter_mut() {
|
||||||
|
self.resolve_stmt(statement)?;
|
||||||
|
}
|
||||||
|
self.exit_scope();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::Class { name, methods: _ } => {
|
||||||
|
self.declare(name);
|
||||||
|
self.define(name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Stmt::ExprStmt { expr } => self.resolve_expr(expr),
|
||||||
|
Stmt::Break => Ok(()),
|
||||||
|
Stmt::Return { expr } => self.resolve_expr(expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_expr(&mut self, expr: &mut Expr) -> Result<(), ResolverError> {
|
||||||
|
match expr {
|
||||||
|
Expr::Literal { literal: _ } => Ok(()),
|
||||||
|
Expr::Unary { op: _, expr } => self.resolve_expr(expr),
|
||||||
|
Expr::Binary { left, op: _, right } => {
|
||||||
|
self.resolve_expr(left)?;
|
||||||
|
self.resolve_expr(right)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::Logical { left, op: _, right } => {
|
||||||
|
self.resolve_expr(left)?;
|
||||||
|
self.resolve_expr(right)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::Grouping { expr } => self.resolve_expr(expr),
|
||||||
|
Expr::Variable { name } => {
|
||||||
|
if let Some(ResolveStatus::Declared) = self.check(name) {
|
||||||
|
let name = name.clone();
|
||||||
|
return Err(ResolverError::VarInOwnInitializer { name });
|
||||||
|
}
|
||||||
|
|
||||||
|
*expr = self.resolve_var(name)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::LocalVariable { name, level: _ } => {
|
||||||
|
panic!("Tried to resolve variable {name} twice")
|
||||||
|
}
|
||||||
|
Expr::GlobalVariable { name } => {
|
||||||
|
panic!("Tried to resolve variable {name} twice")
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Assignment { target, value } => {
|
||||||
|
self.resolve_expr(value)?;
|
||||||
|
|
||||||
|
let target = target.as_mut();
|
||||||
|
if let Expr::Variable { name } = target {
|
||||||
|
*target = self.resolve_var(name)?;
|
||||||
|
} else {
|
||||||
|
panic!("Invalid assignment target {target}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::Call { callee, args } => {
|
||||||
|
self.resolve_expr(callee)?;
|
||||||
|
for arg in args.iter_mut() {
|
||||||
|
self.resolve_expr(arg)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Expr::Function {
|
||||||
|
name,
|
||||||
|
param_names,
|
||||||
|
closure_vars,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
let old_closure_names = std::mem::take(&mut self.closure_vars);
|
||||||
|
|
||||||
|
self.push_frame();
|
||||||
|
|
||||||
|
self.declare(name);
|
||||||
|
self.define(name);
|
||||||
|
|
||||||
|
for param_name in param_names {
|
||||||
|
self.declare(param_name);
|
||||||
|
self.define(param_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.resolve_stmt(body)?;
|
||||||
|
|
||||||
|
self.pop_frame();
|
||||||
|
|
||||||
|
let closure_names = std::mem::replace(&mut self.closure_vars, old_closure_names);
|
||||||
|
|
||||||
|
for closure_var in closure_names {
|
||||||
|
closure_vars.push(closure_var);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,28 +1,17 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
|
use rlox2_frontend::lexer::{scan_tokens, Token};
|
||||||
|
use rlox2_frontend::parser::parse_tokens;
|
||||||
|
|
||||||
use crate::error::LoxError;
|
use crate::error::LoxError;
|
||||||
use crate::interpreter::interpret::execute;
|
use crate::interpret::execute;
|
||||||
use crate::lexer::{scan_tokens, Token};
|
use crate::resolver::resolve;
|
||||||
use crate::parser::parse_tokens;
|
|
||||||
|
|
||||||
use super::environment::Environment;
|
use super::Runtime;
|
||||||
|
|
||||||
pub fn interpreter_main() {
|
pub fn run_file(runtime: &mut Runtime, script_path: &str) {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
|
||||||
|
|
||||||
match args.len() {
|
|
||||||
1 => run_repl(),
|
|
||||||
2 => run_file(&args[1]),
|
|
||||||
_ => {
|
|
||||||
eprintln!("Usage: rlox [script]");
|
|
||||||
std::process::exit(64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_file(script_path: &str) {
|
|
||||||
let source_code = std::fs::read_to_string(script_path).unwrap_or_else(|err| {
|
let source_code = std::fs::read_to_string(script_path).unwrap_or_else(|err| {
|
||||||
eprintln!("Reading script file {} failed: {}", script_path, err);
|
eprintln!("Reading script file {script_path} failed: {err}");
|
||||||
std::process::exit(66);
|
std::process::exit(66);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -31,14 +20,14 @@ fn run_file(script_path: &str) {
|
||||||
std::process::exit(65);
|
std::process::exit(65);
|
||||||
} */
|
} */
|
||||||
|
|
||||||
let mut env = Environment::new();
|
match run(&source_code, runtime) {
|
||||||
|
Ok(()) => {}
|
||||||
match run(&source_code, &mut env) {
|
|
||||||
Ok(()) => std::process::exit(0),
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("{err}");
|
eprintln!("{err}");
|
||||||
match err {
|
match err {
|
||||||
LoxError::LexerError { .. } | LoxError::ParserError { .. } => std::process::exit(65),
|
LoxError::LexerError { .. } | LoxError::ParserError { .. } | LoxError::ResolverError { .. } => {
|
||||||
|
std::process::exit(65)
|
||||||
|
}
|
||||||
LoxError::RuntimeError { .. } => std::process::exit(70),
|
LoxError::RuntimeError { .. } => std::process::exit(70),
|
||||||
LoxError::Exit { exit_code } => std::process::exit(exit_code),
|
LoxError::Exit { exit_code } => std::process::exit(exit_code),
|
||||||
}
|
}
|
||||||
|
|
@ -46,11 +35,9 @@ fn run_file(script_path: &str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_repl() {
|
pub fn run_repl(runtime: &mut Runtime) {
|
||||||
let stdin = std::io::stdin();
|
let stdin = std::io::stdin();
|
||||||
|
|
||||||
let mut env = Environment::new();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let mut input_buf = String::new();
|
let mut input_buf = String::new();
|
||||||
|
|
||||||
|
|
@ -59,7 +46,7 @@ fn run_repl() {
|
||||||
|
|
||||||
'inner: loop {
|
'inner: loop {
|
||||||
stdin.read_line(&mut input_buf).unwrap_or_else(|err| {
|
stdin.read_line(&mut input_buf).unwrap_or_else(|err| {
|
||||||
eprintln!("Could not read from stdin: {}", err);
|
eprintln!("Could not read from stdin: {err}");
|
||||||
std::process::exit(66);
|
std::process::exit(66);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -78,6 +65,11 @@ fn run_repl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
print!("< ");
|
print!("< ");
|
||||||
|
|
||||||
|
// let indentation = " ".repeat((num_open_braces + num_open_brackets + num_open_parens) as usize);
|
||||||
|
|
||||||
|
// print!("{indentation}");
|
||||||
|
|
||||||
std::io::stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,21 +79,20 @@ fn run_repl() {
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
match run(input_buf, &mut env) {
|
match run(input_buf, runtime) {
|
||||||
Ok(()) => {}
|
Ok(()) => {}
|
||||||
Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code),
|
Err(LoxError::Exit { exit_code }) => std::process::exit(exit_code),
|
||||||
Err(err) => eprintln!("{}", err),
|
Err(err) => eprintln!("{err}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(code_string: &str, env: &mut Environment) -> Result<(), LoxError> {
|
fn run(code_string: &str, runtime: &mut Runtime) -> Result<(), LoxError> {
|
||||||
let tokens: Vec<Token> = scan_tokens(code_string)?;
|
let tokens: Vec<Token> = scan_tokens(code_string)?;
|
||||||
|
|
||||||
/* let token_str = tokens
|
/* let token_str = tokens
|
||||||
.iter()
|
.iter()
|
||||||
.map(|token| format!("{token}"))
|
.map(|token| token.to_string())
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
println!("{token_str}"); */
|
println!("{token_str}"); */
|
||||||
|
|
@ -112,16 +103,14 @@ fn run(code_string: &str, env: &mut Environment) -> Result<(), LoxError> {
|
||||||
println!("{statement}");
|
println!("{statement}");
|
||||||
} */
|
} */
|
||||||
|
|
||||||
// let mut result = Value::Nil;
|
/* for statement in statements.iter() {
|
||||||
|
println!("{statement}");
|
||||||
for statement in statements {
|
|
||||||
execute(statement, env)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* match result {
|
|
||||||
Value::Nil => {}
|
|
||||||
result => println!("{result}"),
|
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
for mut statement in statements {
|
||||||
|
resolve(&mut statement, runtime)?;
|
||||||
|
execute(statement, runtime)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
101
interpreter/src/runtime.rs
Normal file
101
interpreter/src/runtime.rs
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::io::{stdin, stdout, Read, Write};
|
||||||
|
|
||||||
|
use crate::error::RuntimeError;
|
||||||
|
|
||||||
|
use super::lox_std::init_std;
|
||||||
|
use super::Value;
|
||||||
|
|
||||||
|
pub struct Runtime {
|
||||||
|
globals: HashMap<String, Value>,
|
||||||
|
|
||||||
|
in_stream: Box<dyn std::io::BufRead>,
|
||||||
|
out_stream: Box<dyn std::io::Write>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Runtime {
|
||||||
|
pub fn new(in_stream: Box<dyn std::io::Read>, out_stream: Box<dyn std::io::Write>) -> Self {
|
||||||
|
let in_stream = Box::new(std::io::BufReader::new(in_stream));
|
||||||
|
let mut runtime = Runtime {
|
||||||
|
globals: HashMap::new(),
|
||||||
|
in_stream,
|
||||||
|
out_stream,
|
||||||
|
};
|
||||||
|
|
||||||
|
init_std(&mut runtime);
|
||||||
|
|
||||||
|
runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn globals(&self) -> &HashMap<String, Value> {
|
||||||
|
&self.globals
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_global(&self, name: &str) -> Result<Value, RuntimeError> {
|
||||||
|
self.globals
|
||||||
|
.get(name)
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| RuntimeError::GlobalNotDefined { name: name.to_owned() })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define_global(&mut self, name: impl Into<String>, value: Value) {
|
||||||
|
let name = name.into();
|
||||||
|
self.globals.insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_global(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
|
||||||
|
if let Some(old_value) = self.globals.get_mut(name) {
|
||||||
|
*old_value = value;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(RuntimeError::GlobalNotDefined { name: name.to_owned() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Runtime {
|
||||||
|
fn default() -> Self {
|
||||||
|
Runtime::new(Box::new(std::io::BufReader::new(stdin())), Box::new(stdout()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Runtime {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("Runtime").field("globals", &self.globals).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Runtime {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "Globals:")?;
|
||||||
|
|
||||||
|
for (name, value) in self.globals.iter() {
|
||||||
|
write!(f, "\n\t{name} = {value}")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Runtime {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.out_stream.flush().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Read for Runtime {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
self.in_stream.read(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for Runtime {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
self.out_stream.write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
self.out_stream.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,11 +2,14 @@ use std::cell::UnsafeCell;
|
||||||
use std::fmt::{Debug, Display};
|
use std::fmt::{Debug, Display};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::LoxClass;
|
||||||
|
|
||||||
use super::function::LoxExternFunction;
|
use super::function::LoxExternFunction;
|
||||||
use super::LoxFunction;
|
use super::LoxFunction;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
Class(Rc<LoxClass>),
|
||||||
Function(Rc<LoxFunction>),
|
Function(Rc<LoxFunction>),
|
||||||
ExternFunction(Rc<LoxExternFunction>),
|
ExternFunction(Rc<LoxExternFunction>),
|
||||||
String(Rc<String>),
|
String(Rc<String>),
|
||||||
|
|
@ -16,6 +19,11 @@ pub enum Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
|
pub fn class(class: LoxClass) -> Self {
|
||||||
|
let class = Rc::new(class);
|
||||||
|
Value::Class(class)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn function(fun: LoxFunction) -> Self {
|
pub fn function(fun: LoxFunction) -> Self {
|
||||||
let fun = Rc::new(fun);
|
let fun = Rc::new(fun);
|
||||||
Value::Function(fun)
|
Value::Function(fun)
|
||||||
|
|
@ -39,6 +47,7 @@ impl Value {
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
Value::Class(class) => write!(f, "{class}"),
|
||||||
Value::Function(fun) => write!(f, "{fun}"),
|
Value::Function(fun) => write!(f, "{fun}"),
|
||||||
Value::ExternFunction(ext_fun) => write!(f, "{ext_fun}"),
|
Value::ExternFunction(ext_fun) => write!(f, "{ext_fun}"),
|
||||||
Value::String(s) => write!(f, "\"{s}\""),
|
Value::String(s) => write!(f, "\"{s}\""),
|
||||||
|
|
@ -66,14 +75,14 @@ impl PartialEq for Value {
|
||||||
/*====================================================================================================================*/
|
/*====================================================================================================================*/
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HeapValue {
|
pub struct HeapedValue {
|
||||||
inner: Rc<UnsafeCell<Value>>,
|
inner: Rc<UnsafeCell<Value>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HeapValue {
|
impl HeapedValue {
|
||||||
pub fn new(value: Value) -> Self {
|
pub fn new(value: Value) -> Self {
|
||||||
let inner = Rc::new(UnsafeCell::new(value));
|
let inner = Rc::new(UnsafeCell::new(value));
|
||||||
HeapValue { inner }
|
HeapedValue { inner }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_clone(&self) -> Value {
|
pub fn get_clone(&self) -> Value {
|
||||||
|
|
@ -93,13 +102,13 @@ impl HeapValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for HeapValue {
|
impl Debug for HeapedValue {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.debug_struct("HeapValue").field("inner", &self.get_clone()).finish()
|
f.debug_struct("HeapValue").field("inner", &self.get_clone()).finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for HeapValue {
|
impl Display for HeapedValue {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}", self.get_clone())
|
write!(f, "{}", self.get_clone())
|
||||||
}
|
}
|
||||||
125
src/error.rs
125
src/error.rs
|
|
@ -1,125 +0,0 @@
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::interpreter::Value;
|
|
||||||
use crate::lexer::Token;
|
|
||||||
use crate::misc::CodePos;
|
|
||||||
use crate::parser::{BinaryOp, Expr, UnaryOp};
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum LexerError {
|
|
||||||
#[error("Unexpected character '{c}' at {code_pos}.")]
|
|
||||||
UnexpectedCharacter { c: char, code_pos: CodePos },
|
|
||||||
#[error("Unterminated string literal starting at {code_pos}.")]
|
|
||||||
UnterminatedStringLiteral { code_pos: CodePos },
|
|
||||||
#[error("Unterminated block comment starting at {code_pos}.")]
|
|
||||||
UnterminatedBlockComment { code_pos: CodePos },
|
|
||||||
#[error("Invalid number literal {lexeme} at {code_pos}: {msg}")]
|
|
||||||
InvalidNumberLiteral {
|
|
||||||
lexeme: String,
|
|
||||||
msg: String,
|
|
||||||
code_pos: CodePos,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum ParserError {
|
|
||||||
#[error("Token stream ended unexpectedly.")]
|
|
||||||
TokenStreamEnded,
|
|
||||||
#[error("Expected a primary expression, but found a {token} token instead at {0}.", token.code_pos)]
|
|
||||||
ExpectedPrimary { token: Token },
|
|
||||||
#[error("Missing semicolon at {code_pos}")]
|
|
||||||
MissingSemicolon { code_pos: CodePos },
|
|
||||||
#[error("Expected variable name at {0}, got {token} instead", token.code_pos)]
|
|
||||||
ExpectedVarName { token: Token },
|
|
||||||
#[error("Can't assign to {expr} at {code_pos}")]
|
|
||||||
InvalidAssignment { expr: Expr, code_pos: CodePos },
|
|
||||||
#[error("Missing closing curly brace at {code_pos}")]
|
|
||||||
MissingRightBrace { code_pos: CodePos },
|
|
||||||
#[error("Missing closing parenthesis at {code_pos}")]
|
|
||||||
MissingRightParen { code_pos: CodePos },
|
|
||||||
#[error("Missing parenthesis after if at {code_pos}")]
|
|
||||||
MissingParenAfterIf { code_pos: CodePos },
|
|
||||||
#[error("Missing parenthesis after while at {code_pos}")]
|
|
||||||
MissingParenAfterWhile { code_pos: CodePos },
|
|
||||||
#[error("Missing parenthesis after for at {code_pos}")]
|
|
||||||
MissingParenAfterFor { code_pos: CodePos },
|
|
||||||
#[error("Call at {code_pos} has too many arguments")]
|
|
||||||
TooManyArguments { code_pos: CodePos },
|
|
||||||
#[error("{msg} at {code_pos}")]
|
|
||||||
MissingIdentifier { msg: String, code_pos: CodePos },
|
|
||||||
#[error("Missing arguments to function declaration at {code_pos}")]
|
|
||||||
MissingFunctionArgs { code_pos: CodePos },
|
|
||||||
#[error("Missing body to function declaration at {code_pos}")]
|
|
||||||
MissingFunctionBody { code_pos: CodePos },
|
|
||||||
#[error("Function declaration at {code_pos} has too many parameters")]
|
|
||||||
TooManyParams { code_pos: CodePos },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum RuntimeError {
|
|
||||||
#[error("Unary operator {op} had invalid argument {arg}")]
|
|
||||||
UnaryOpInvalidArgument { op: UnaryOp, arg: Value },
|
|
||||||
#[error("Binary operator {op} had invalid arguments {left} and {right}")]
|
|
||||||
BinaryOpInvalidArguments { left: Value, op: BinaryOp, right: Value },
|
|
||||||
#[error("Division by zero")]
|
|
||||||
DivisionByZero,
|
|
||||||
#[error("Name {name} is not defined")]
|
|
||||||
NameNotDefined { name: String },
|
|
||||||
#[error("{callee} is not callable")]
|
|
||||||
NotCallable { callee: Value },
|
|
||||||
#[error("{name}() takes {arity} args, but {given} were given.")]
|
|
||||||
WrongArity { name: String, arity: usize, given: usize },
|
|
||||||
#[error("Extern function call to {name} failed: {msg}")]
|
|
||||||
ExtFunCallFailed { name: String, msg: String },
|
|
||||||
#[error("Uncaught break statement")]
|
|
||||||
Break,
|
|
||||||
#[error("Uncaught return statement")]
|
|
||||||
Return { value: Value },
|
|
||||||
#[error("Exit with exit code {exit_code}")]
|
|
||||||
Exit { exit_code: i32 },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
#[allow(clippy::enum_variant_names)]
|
|
||||||
pub enum LoxError {
|
|
||||||
#[error("{0}", format_multiple_errors(inner))]
|
|
||||||
LexerError { inner: Vec<LexerError> },
|
|
||||||
#[error("{0}", format_multiple_errors(inner))]
|
|
||||||
ParserError { inner: Vec<ParserError> },
|
|
||||||
#[error("{inner}")]
|
|
||||||
RuntimeError { inner: RuntimeError },
|
|
||||||
#[error("Called exit() with exit code {exit_code}")]
|
|
||||||
Exit { exit_code: i32 },
|
|
||||||
}
|
|
||||||
|
|
||||||
fn format_multiple_errors(errs: &Vec<impl std::error::Error>) -> String {
|
|
||||||
let msg = if errs.len() == 1 {
|
|
||||||
format!("{}", errs[0])
|
|
||||||
} else {
|
|
||||||
let msgs: Vec<String> = errs.iter().map(|err| format!("{}", err)).collect();
|
|
||||||
msgs.join("\n")
|
|
||||||
};
|
|
||||||
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<LexerError>> for LoxError {
|
|
||||||
fn from(lexer_errs: Vec<LexerError>) -> Self {
|
|
||||||
LoxError::LexerError { inner: lexer_errs }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<ParserError>> for LoxError {
|
|
||||||
fn from(parser_errs: Vec<ParserError>) -> Self {
|
|
||||||
LoxError::ParserError { inner: parser_errs }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<RuntimeError> for LoxError {
|
|
||||||
fn from(runtime_err: RuntimeError) -> Self {
|
|
||||||
match runtime_err {
|
|
||||||
RuntimeError::Exit { exit_code } => LoxError::Exit { exit_code },
|
|
||||||
_ => LoxError::RuntimeError { inner: runtime_err },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
use std::borrow::Borrow;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::error::RuntimeError;
|
|
||||||
|
|
||||||
use super::lox_std::init_std;
|
|
||||||
use super::value::HeapValue;
|
|
||||||
use super::Value;
|
|
||||||
|
|
||||||
pub type Scope = HashMap<String, HeapValue>;
|
|
||||||
|
|
||||||
pub struct Environment {
|
|
||||||
globals: Scope,
|
|
||||||
|
|
||||||
// scopes: Vec<Scope>,
|
|
||||||
frames: Vec<Frame>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Environment {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut env = Environment {
|
|
||||||
globals: HashMap::new(),
|
|
||||||
frames: vec![Frame::new_global()],
|
|
||||||
};
|
|
||||||
|
|
||||||
init_std(&mut env);
|
|
||||||
|
|
||||||
env
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_frame(&self) -> &Frame {
|
|
||||||
self.frames.last().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn current_frame_mut(&mut self) -> &mut Frame {
|
|
||||||
self.frames.last_mut().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn globals(&self) -> &Scope {
|
|
||||||
&self.globals
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, name: &str) -> Result<Value, RuntimeError> {
|
|
||||||
if let Some(value) = self.current_frame().get(name) {
|
|
||||||
return Ok(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.globals.get(name) {
|
|
||||||
Some(heap_value) => Ok(heap_value.get_clone()),
|
|
||||||
None => Err(RuntimeError::NameNotDefined { name: name.to_owned() }),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn define(&mut self, name: impl Into<String> + Borrow<str>, value: Value) {
|
|
||||||
if let Err(value) = self.current_frame_mut().try_define(name.borrow(), value) {
|
|
||||||
self.globals.insert(name.into(), HeapValue::new(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), RuntimeError> {
|
|
||||||
let value = match self.current_frame_mut().try_assign(name, value) {
|
|
||||||
Ok(()) => return Ok(()),
|
|
||||||
Err(value) => value,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(var) = self.globals.get_mut(name) {
|
|
||||||
// *var = value;
|
|
||||||
var.replace(value);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(RuntimeError::NameNotDefined { name: name.to_owned() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enter_scope(&mut self) {
|
|
||||||
self.current_frame_mut().enter_scope();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit_scope(&mut self) {
|
|
||||||
self.current_frame_mut().exit_scope();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push_frame(&mut self, base_scope: Scope) {
|
|
||||||
self.frames.push(Frame::new(base_scope));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop_frame(&mut self) {
|
|
||||||
self.frames.pop().expect("Tried to pop global frame");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*====================================================================================================================*/
|
|
||||||
|
|
||||||
struct Frame {
|
|
||||||
scopes: Vec<Scope>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Frame {
|
|
||||||
fn new(base_scope: Scope) -> Self {
|
|
||||||
Frame {
|
|
||||||
scopes: vec![base_scope],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_global() -> Self {
|
|
||||||
Frame { scopes: Vec::new() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enter_scope(&mut self) {
|
|
||||||
self.scopes.push(Scope::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn exit_scope(&mut self) {
|
|
||||||
self.scopes.pop().expect("Tried to exit scope, but no scope to exit");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, name: &str) -> Option<Value> {
|
|
||||||
for scope in self.scopes.iter().rev() {
|
|
||||||
if let Some(heap_value) = scope.get(name) {
|
|
||||||
return Some(heap_value.get_clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_define(&mut self, name: impl Into<String> + Borrow<str>, value: Value) -> Result<(), Value> {
|
|
||||||
if let Some(scope) = self.scopes.last_mut() {
|
|
||||||
scope.insert(name.into(), HeapValue::new(value));
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn try_assign(&mut self, name: &str, value: Value) -> Result<(), Value> {
|
|
||||||
for scope in self.scopes.iter_mut().rev() {
|
|
||||||
if let Some(heap_value) = scope.get_mut(name) {
|
|
||||||
heap_value.replace(value);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
mod environment;
|
|
||||||
mod function;
|
|
||||||
mod interpret;
|
|
||||||
mod lox_std;
|
|
||||||
mod run;
|
|
||||||
mod value;
|
|
||||||
|
|
||||||
pub use function::LoxFunction;
|
|
||||||
pub use run::interpreter_main;
|
|
||||||
pub use value::Value;
|
|
||||||
34
src/main.rs
34
src/main.rs
|
|
@ -1,9 +1,31 @@
|
||||||
mod error;
|
use clap::{ArgAction, Parser};
|
||||||
mod interpreter;
|
use rlox2_interpreter::{run_file, run_repl, Runtime};
|
||||||
mod lexer;
|
|
||||||
mod misc;
|
#[derive(Parser, Debug)]
|
||||||
mod parser;
|
struct CliArgs {
|
||||||
|
#[arg()]
|
||||||
|
file_name: Option<String>,
|
||||||
|
|
||||||
|
#[arg(short, action = ArgAction::SetTrue)]
|
||||||
|
interactive: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
interpreter::interpreter_main();
|
interpreter_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interpreter_main() {
|
||||||
|
let cli_args = CliArgs::parse();
|
||||||
|
|
||||||
|
let mut runtime = Runtime::default();
|
||||||
|
|
||||||
|
if let Some(file_name) = cli_args.file_name {
|
||||||
|
run_file(&mut runtime, &file_name);
|
||||||
|
|
||||||
|
if cli_args.interactive {
|
||||||
|
run_repl(&mut runtime);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
run_repl(&mut runtime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue