├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── examples ├── eratosthenes.glc ├── fib.glc └── mandelbrot.glc └── src ├── main.rs ├── parser ├── ast.rs ├── glacier.pest └── mod.rs ├── repl.rs ├── tests ├── mod.rs └── tests │ ├── 1_arithmetic.glc │ ├── 2_cf1.glc │ └── 3_cf2.glc └── vm ├── bytecode.rs ├── memory.rs ├── mod.rs ├── value.rs └── vm_bc.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | *.iml 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "arrayvec" 7 | version = "0.7.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.14" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 16 | dependencies = [ 17 | "hermit-abi", 18 | "libc", 19 | "winapi", 20 | ] 21 | 22 | [[package]] 23 | name = "autocfg" 24 | version = "1.1.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 27 | 28 | [[package]] 29 | name = "bitflags" 30 | version = "1.3.2" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 33 | 34 | [[package]] 35 | name = "block-buffer" 36 | version = "0.7.3" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 39 | dependencies = [ 40 | "block-padding", 41 | "byte-tools", 42 | "byteorder", 43 | "generic-array", 44 | ] 45 | 46 | [[package]] 47 | name = "block-padding" 48 | version = "0.1.5" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 51 | dependencies = [ 52 | "byte-tools", 53 | ] 54 | 55 | [[package]] 56 | name = "byte-tools" 57 | version = "0.3.1" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 60 | 61 | [[package]] 62 | name = "byteorder" 63 | version = "1.4.3" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 66 | 67 | [[package]] 68 | name = "clap" 69 | version = "3.2.12" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" 72 | dependencies = [ 73 | "atty", 74 | "bitflags", 75 | "clap_derive", 76 | "clap_lex", 77 | "indexmap", 78 | "once_cell", 79 | "strsim", 80 | "termcolor", 81 | "textwrap", 82 | ] 83 | 84 | [[package]] 85 | name = "clap_derive" 86 | version = "3.2.7" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902" 89 | dependencies = [ 90 | "heck", 91 | "proc-macro-error", 92 | "proc-macro2", 93 | "quote", 94 | "syn", 95 | ] 96 | 97 | [[package]] 98 | name = "clap_lex" 99 | version = "0.2.4" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 102 | dependencies = [ 103 | "os_str_bytes", 104 | ] 105 | 106 | [[package]] 107 | name = "digest" 108 | version = "0.8.1" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 111 | dependencies = [ 112 | "generic-array", 113 | ] 114 | 115 | [[package]] 116 | name = "fake-simd" 117 | version = "0.1.2" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 120 | 121 | [[package]] 122 | name = "generic-array" 123 | version = "0.12.4" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 126 | dependencies = [ 127 | "typenum", 128 | ] 129 | 130 | [[package]] 131 | name = "glacier2" 132 | version = "0.1.0" 133 | dependencies = [ 134 | "arrayvec", 135 | "clap", 136 | "lazy_static", 137 | "pest", 138 | "pest_derive", 139 | ] 140 | 141 | [[package]] 142 | name = "hashbrown" 143 | version = "0.12.2" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022" 146 | 147 | [[package]] 148 | name = "heck" 149 | version = "0.4.0" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 152 | 153 | [[package]] 154 | name = "hermit-abi" 155 | version = "0.1.19" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 158 | dependencies = [ 159 | "libc", 160 | ] 161 | 162 | [[package]] 163 | name = "indexmap" 164 | version = "1.9.1" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" 167 | dependencies = [ 168 | "autocfg", 169 | "hashbrown", 170 | ] 171 | 172 | [[package]] 173 | name = "lazy_static" 174 | version = "1.4.0" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 177 | 178 | [[package]] 179 | name = "libc" 180 | version = "0.2.126" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 183 | 184 | [[package]] 185 | name = "maplit" 186 | version = "1.0.2" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" 189 | 190 | [[package]] 191 | name = "once_cell" 192 | version = "1.13.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" 195 | 196 | [[package]] 197 | name = "opaque-debug" 198 | version = "0.2.3" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 201 | 202 | [[package]] 203 | name = "os_str_bytes" 204 | version = "6.1.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" 207 | 208 | [[package]] 209 | name = "pest" 210 | version = "2.1.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" 213 | dependencies = [ 214 | "ucd-trie", 215 | ] 216 | 217 | [[package]] 218 | name = "pest_derive" 219 | version = "2.1.0" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" 222 | dependencies = [ 223 | "pest", 224 | "pest_generator", 225 | ] 226 | 227 | [[package]] 228 | name = "pest_generator" 229 | version = "2.1.3" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" 232 | dependencies = [ 233 | "pest", 234 | "pest_meta", 235 | "proc-macro2", 236 | "quote", 237 | "syn", 238 | ] 239 | 240 | [[package]] 241 | name = "pest_meta" 242 | version = "2.1.3" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" 245 | dependencies = [ 246 | "maplit", 247 | "pest", 248 | "sha-1", 249 | ] 250 | 251 | [[package]] 252 | name = "proc-macro-error" 253 | version = "1.0.4" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 256 | dependencies = [ 257 | "proc-macro-error-attr", 258 | "proc-macro2", 259 | "quote", 260 | "syn", 261 | "version_check", 262 | ] 263 | 264 | [[package]] 265 | name = "proc-macro-error-attr" 266 | version = "1.0.4" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 269 | dependencies = [ 270 | "proc-macro2", 271 | "quote", 272 | "version_check", 273 | ] 274 | 275 | [[package]] 276 | name = "proc-macro2" 277 | version = "1.0.40" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" 280 | dependencies = [ 281 | "unicode-ident", 282 | ] 283 | 284 | [[package]] 285 | name = "quote" 286 | version = "1.0.20" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" 289 | dependencies = [ 290 | "proc-macro2", 291 | ] 292 | 293 | [[package]] 294 | name = "sha-1" 295 | version = "0.8.2" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" 298 | dependencies = [ 299 | "block-buffer", 300 | "digest", 301 | "fake-simd", 302 | "opaque-debug", 303 | ] 304 | 305 | [[package]] 306 | name = "strsim" 307 | version = "0.10.0" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 310 | 311 | [[package]] 312 | name = "syn" 313 | version = "1.0.98" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" 316 | dependencies = [ 317 | "proc-macro2", 318 | "quote", 319 | "unicode-ident", 320 | ] 321 | 322 | [[package]] 323 | name = "termcolor" 324 | version = "1.1.3" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 327 | dependencies = [ 328 | "winapi-util", 329 | ] 330 | 331 | [[package]] 332 | name = "textwrap" 333 | version = "0.15.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" 336 | 337 | [[package]] 338 | name = "typenum" 339 | version = "1.15.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" 342 | 343 | [[package]] 344 | name = "ucd-trie" 345 | version = "0.1.3" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" 348 | 349 | [[package]] 350 | name = "unicode-ident" 351 | version = "1.0.1" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" 354 | 355 | [[package]] 356 | name = "version_check" 357 | version = "0.9.4" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 360 | 361 | [[package]] 362 | name = "winapi" 363 | version = "0.3.9" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 366 | dependencies = [ 367 | "winapi-i686-pc-windows-gnu", 368 | "winapi-x86_64-pc-windows-gnu", 369 | ] 370 | 371 | [[package]] 372 | name = "winapi-i686-pc-windows-gnu" 373 | version = "0.4.0" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 376 | 377 | [[package]] 378 | name = "winapi-util" 379 | version = "0.1.5" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 382 | dependencies = [ 383 | "winapi", 384 | ] 385 | 386 | [[package]] 387 | name = "winapi-x86_64-pc-windows-gnu" 388 | version = "0.4.0" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 391 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "glacier2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | lazy_static = "1.4.0" 10 | pest = "2.1.3" 11 | pest_derive = "2.1.0" 12 | arrayvec = "0.7.2" 13 | clap = { version = "3.2.12", features = ["derive"] } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Glacier Programming Language 2 | 3 | Rust implementation of the Glacier 2 programming language. 4 | 5 | ## History 6 | 7 | The Glacier programming language is a successor of my previous programming language, Gorilla. 8 | Gorilla was written in Golang, but due to the implementation's bad memory management and slow speed, I started a new 9 | project called Glacier. 10 | 11 | Glacier 1 was merely a clone of Gorilla written in Rust. There is a 3x speed improvement, but still not optimal. 12 | 13 | The hope of Glacier 2 is to utilize the best methods for an interpreter. Mark-and-sweep GC, global allocator, simply 14 | bytecodes, and many more are the features I am aiming to use. 15 | 16 | ## Design 17 | 18 | - Simplicity 19 | - Ruby-style syntax with as few native types as possible. 20 | - Speed 21 | - Compiled to GlacierVM bytecode and executed by a fast VM. 22 | - JIT will be implemented in the future. 23 | - Safety 24 | - No undefined behaviours. 25 | -------------------------------------------------------------------------------- /examples/eratosthenes.glc: -------------------------------------------------------------------------------- 1 | n = 30000 2 | 3 | A = [true] * n 4 | 5 | i = 2 6 | L = n ** 0.5 7 | while i <= L 8 | if A[i] 9 | j = i * i 10 | while j < n 11 | A[j] = false 12 | j = j + i 13 | end 14 | end 15 | i = i + 1 16 | end 17 | 18 | c = 0 19 | k = 2 20 | while k < n 21 | if A[k]: c = c + 1 22 | k = k + 1 23 | end 24 | 25 | echo c 26 | -------------------------------------------------------------------------------- /examples/fib.glc: -------------------------------------------------------------------------------- 1 | # This script Computes the Nth fibonacci number modulo MOD in O(log N) time 2 | 3 | # Fib[N] 4 | N = 854441722044072015 # F[N] = 952625812 mod MOD 5 | 6 | # Modulo 7 | MOD = 1000000007 8 | 9 | ans = [[1, 0], [0, 1]] 10 | m = [[1, 1], [1, 0]] 11 | res = [[0, 0], [0, 0]] 12 | 13 | while N > 0 14 | if N % 2 == 1 15 | res[0][0] = (ans[0][0] * m[0][0] + ans[0][1] * m[1][0]) % MOD 16 | res[0][1] = (ans[0][0] * m[0][1] + ans[0][1] * m[1][1]) % MOD 17 | res[1][0] = (ans[1][0] * m[0][0] + ans[1][1] * m[1][0]) % MOD 18 | res[1][1] = (ans[1][1] * m[1][1] + ans[1][0] * m[0][1]) % MOD 19 | 20 | ans[0][0] = res[0][0]; ans[0][1] = res[0][1]; ans[1][0] = res[1][0]; ans[1][1] = res[1][1] 21 | end 22 | 23 | res[0][0] = (m[0][0] * m[0][0] + m[0][1] * m[1][0]) % MOD 24 | res[0][1] = (m[0][0] * m[0][1] + m[0][1] * m[1][1]) % MOD 25 | res[1][0] = (m[1][0] * m[0][0] + m[1][1] * m[1][0]) % MOD 26 | res[1][1] = (m[1][1] * m[1][1] + m[1][0] * m[0][1]) % MOD 27 | 28 | m[0][0] = res[0][0]; m[0][1] = res[0][1]; m[1][0] = res[1][0]; m[1][1] = res[1][1] 29 | 30 | N = N / 2 31 | end 32 | 33 | echo ans[0][1] 34 | -------------------------------------------------------------------------------- /examples/mandelbrot.glc: -------------------------------------------------------------------------------- 1 | # Draws a 80x41 image of Mandelbrot Set with 55 iterations 2 | 3 | y = 1.0 4 | while y >= -1.0 5 | line = ["."] * 80 6 | 7 | x = -2.0 8 | i = 0 9 | while x <= 0.5 10 | zr = 0.0 11 | zi = 0.0 12 | 13 | cr = x 14 | ci = y 15 | 16 | k = 0 17 | while k < 55 18 | zr2 = zr * zr 19 | zi2 = zi * zi 20 | if zr2 + zi2 > 4.0 21 | break 22 | end 23 | zi = 2.0 * zr * zi + ci 24 | zr = zr2 - zi2 + cr 25 | k = k + 1 26 | end 27 | 28 | ab = zr * zr + zi * zi 29 | 30 | if ab <= 4.0 31 | line[i] = "*" 32 | end 33 | 34 | x = x + 0.0315 35 | i = i + 1 36 | end 37 | 38 | echo line * "" 39 | 40 | y = y - 0.05 41 | end -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | pub mod parser; 2 | pub mod repl; 3 | mod tests; 4 | pub mod vm; 5 | 6 | use crate::repl::Repl; 7 | use parser::*; 8 | use std::fs::File; 9 | use std::io::Read; 10 | use vm::*; 11 | 12 | use crate::vm_bc::VM; 13 | use clap::Parser; 14 | /// Simple program to greet a person 15 | #[derive(Parser, Debug)] 16 | #[clap(author, version, about, long_about = None)] 17 | struct Args { 18 | #[clap(value_parser)] 19 | file: Option, 20 | } 21 | 22 | fn main() { 23 | let args = Args::parse(); 24 | 25 | if let Some(path) = args.file { 26 | let file = File::open(path); 27 | if let Ok(mut file) = file { 28 | let mut source = String::new(); 29 | file.read_to_string(&mut source).unwrap(); 30 | let mut vm = VM::default(); 31 | vm.set_source(source.clone()); 32 | 33 | let ast_ = parse(source.as_str()); 34 | if let Ok(ast_) = ast_ { 35 | vm.compile(&ast_); 36 | 37 | if let Some(e) = &vm.error { 38 | println!("{e}"); 39 | return; 40 | } 41 | 42 | vm.optimize(); 43 | 44 | // println!("{}", vm.disassemble()); 45 | 46 | vm.execute(); 47 | if let Some(e) = &vm.error { 48 | println!("{e}"); 49 | } 50 | } else if let Err(e) = ast_ { 51 | if let pest::error::LineColLocation::Span(start, end) = e.line_col { 52 | let line_str = source.split('\n').nth(start.0 - 1).unwrap(); 53 | println!( 54 | "At Line {}:\n{}\n{}{}\nSyntax Error", 55 | start.0, 56 | line_str, 57 | " ".repeat(start.1 - 1), 58 | "^".repeat(end.1.min(line_str.len()) - start.1), 59 | ); 60 | } else if let pest::error::LineColLocation::Pos(pos) = e.line_col { 61 | let line_str = source.split('\n').nth(pos.0 - 1).unwrap(); 62 | println!( 63 | "At Line {}:\n{}\n{}^\nSyntax Error", 64 | pos.0, 65 | line_str, 66 | " ".repeat(pos.1 - 1), 67 | ); 68 | } 69 | } 70 | } 71 | } else { 72 | let mut repl_ = Repl::default(); 73 | repl_.run(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/parser/ast.rs: -------------------------------------------------------------------------------- 1 | use pest::Span; 2 | 3 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 4 | pub struct AstSpan { 5 | pub start: usize, 6 | pub end: usize, 7 | } 8 | 9 | impl<'a> From> for AstSpan { 10 | fn from(span: Span) -> Self { 11 | Self { 12 | start: span.start(), 13 | end: span.end(), 14 | } 15 | } 16 | } 17 | 18 | #[derive(Debug, Clone)] 19 | pub enum Expression<'a> { 20 | String_(String_), 21 | Int(Integer<'a>), 22 | Float(Float<'a>), 23 | Bool(Bool), 24 | Array(Array<'a>), 25 | GetVar(GetVar<'a>), 26 | SetVar(Box>), 27 | Infix(Box>), 28 | Prefix(Box>), 29 | Index(Box>), 30 | 31 | If(Box>), 32 | While(Box>), 33 | Do(Box>), 34 | } 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct If<'a> { 38 | pub cond: Expression<'a>, 39 | pub body: Program<'a>, 40 | pub other: Program<'a>, 41 | pub pos: AstSpan, 42 | } 43 | 44 | #[derive(Debug, Clone)] 45 | pub struct While<'a> { 46 | pub cond: Expression<'a>, 47 | pub body: Program<'a>, 48 | pub pos: AstSpan, 49 | } 50 | 51 | #[derive(Debug, Clone)] 52 | pub struct Do<'a> { 53 | pub body: Program<'a>, 54 | pub pos: AstSpan, 55 | } 56 | 57 | #[derive(Debug, Clone)] 58 | pub struct Infix<'a> { 59 | pub left: Expression<'a>, 60 | pub operator: &'a str, 61 | pub right: Expression<'a>, 62 | pub pos: AstSpan, 63 | } 64 | 65 | #[derive(Debug, Clone)] 66 | pub struct Prefix<'a> { 67 | pub operator: &'a str, 68 | pub right: Expression<'a>, 69 | pub pos: AstSpan, 70 | } 71 | 72 | #[derive(Debug, Clone)] 73 | pub struct String_ { 74 | pub value: String, 75 | pub pos: AstSpan, 76 | } 77 | 78 | #[derive(Debug, Clone)] 79 | pub struct Integer<'a> { 80 | pub value: &'a str, 81 | pub pos: AstSpan, 82 | } 83 | 84 | #[derive(Debug, Clone)] 85 | pub struct Float<'a> { 86 | pub value: &'a str, 87 | pub pos: AstSpan, 88 | } 89 | 90 | #[derive(Debug, Clone)] 91 | pub struct Bool { 92 | pub value: bool, 93 | pub pos: AstSpan, 94 | } 95 | 96 | #[derive(Debug, Clone)] 97 | pub struct Array<'a> { 98 | pub values: Vec>, 99 | pub pos: AstSpan, 100 | } 101 | 102 | #[derive(Debug, Clone)] 103 | pub struct GetVar<'a> { 104 | pub name: &'a str, 105 | pub pos: AstSpan, 106 | } 107 | 108 | #[derive(Debug, Clone)] 109 | pub struct SetVar<'a> { 110 | pub name: &'a str, 111 | pub value: Expression<'a>, 112 | pub pos: AstSpan, 113 | } 114 | 115 | #[derive(Debug, Clone)] 116 | pub struct Index<'a> { 117 | pub callee: Expression<'a>, 118 | pub index: Expression<'a>, 119 | pub pos: AstSpan, 120 | } 121 | 122 | #[derive(Debug, Clone)] 123 | pub enum Statement<'a> { 124 | ExprStmt(ExprStmt<'a>), 125 | DebugPrint(DebugPrint<'a>), 126 | EchoPrint(EchoPrint<'a>), 127 | Break(Break), 128 | Next(Next), 129 | PointerAssign(Box>), 130 | } 131 | 132 | #[derive(Debug, Clone)] 133 | pub struct ExprStmt<'a> { 134 | pub expr: Expression<'a>, 135 | pub pos: AstSpan, 136 | } 137 | 138 | #[derive(Debug, Clone)] 139 | pub struct DebugPrint<'a> { 140 | pub expr: Expression<'a>, 141 | pub pos: AstSpan, 142 | } 143 | 144 | #[derive(Debug, Clone)] 145 | pub struct EchoPrint<'a> { 146 | pub expr: Expression<'a>, 147 | pub pos: AstSpan, 148 | } 149 | 150 | #[derive(Debug, Clone)] 151 | pub struct Break { 152 | pub pos: AstSpan, 153 | } 154 | 155 | #[derive(Debug, Clone)] 156 | pub struct Next { 157 | pub pos: AstSpan, 158 | } 159 | 160 | #[derive(Debug, Clone)] 161 | pub struct PointerAssign<'a> { 162 | pub ptr: Expression<'a>, 163 | pub value: Expression<'a>, 164 | pub pos: AstSpan, 165 | } 166 | 167 | pub type Program<'a> = Vec>; 168 | -------------------------------------------------------------------------------- /src/parser/glacier.pest: -------------------------------------------------------------------------------- 1 | program = _{ SOI ~ TERMINATOR* ~ (stmt ~ TERMINATOR+) * ~ stmt? ~ EOI } 2 | 3 | TERMINATOR = _{ NEWLINE | ";" } 4 | WHITESPACE = _{ " " | "\t" } 5 | COMMENT = _{ ("#" ~ (!NEWLINE ~ ANY)*) } 6 | 7 | integer = @{ ASCII_DIGIT{1, 32} } 8 | float = @{ ASCII_DIGIT{1, 32} ~ "." ~ ASCII_DIGIT{1, 32} } 9 | idt = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } 10 | identifier = @{ !(keyword ~ !(ASCII_ALPHANUMERIC)) ~ idt } 11 | keyword = @{ 12 | "if" | "else" | "while" | "do" | "end" | "debug" | "echo" | "false" | "true" | "break" | "next" 13 | } 14 | 15 | string_literal = @{ "\"" ~ literal_char* ~ "\"" } 16 | literal_char = { escape_sequence | (!"\"" ~ ANY) } 17 | escape_sequence = _{ "\\\\" | "\\\"" | "\\\'" | "\\n" | "\\r" | "\\t" | "\\0" } 18 | 19 | block = { 20 | (TERMINATOR* ~ (stmt ~ TERMINATOR+) * ~ stmt? ~ "end") | (":" ~ NEWLINE* ~ stmt) 21 | } 22 | 23 | no_else_block = { 24 | (TERMINATOR* ~ (stmt ~ TERMINATOR+) * ~ stmt? ~ "else") | (":"? ~ stmt ~ "else") 25 | } 26 | 27 | condition_if = { 28 | "if" ~ NEWLINE* ~ expression ~ NEWLINE* ~ block 29 | } 30 | 31 | condition_ifelse = { 32 | "if" ~ NEWLINE* ~ expression ~ NEWLINE* ~ no_else_block ~ NEWLINE* ~ NEWLINE* ~ block 33 | } 34 | 35 | do_block = { 36 | "do" ~ NEWLINE* ~ block 37 | } 38 | 39 | while_loop = { 40 | "while" ~ NEWLINE* ~ expression ~ NEWLINE* ~ block 41 | } 42 | 43 | indexing = { 44 | "[" ~ NEWLINE* ~ expression ~ NEWLINE* ~ "]" 45 | } 46 | 47 | suffix = { 48 | term ~ (indexing)+ 49 | } 50 | 51 | false_expr = { "false" } 52 | true_expr = { "true" } 53 | 54 | term = _{ string_literal | float | integer | false_expr | true_expr | identifier | array | "(" ~ expression ~ ")" } 55 | 56 | prefix = { prefix_operators+ ~ term } 57 | 58 | assign = { identifier ~ "=" ~ expression } 59 | 60 | array = { "[" ~ NEWLINE* ~ (expression ~ NEWLINE* ~ ","? ~ NEWLINE*)* ~ "]" } 61 | 62 | // condition_ifelse must be before condition_if 63 | expression_inner = _{ while_loop | condition_ifelse | condition_if | do_block | assign | suffix | prefix | term } 64 | 65 | add = { "+" } 66 | sub = { "-" } 67 | mul = { "*" } 68 | div = { "/" } 69 | modulo = { "%" } 70 | exp = { "**" } 71 | dbeq = { "==" } 72 | neq = { "!=" } 73 | gt = { ">" } 74 | lt = { "<" } 75 | gteq = { ">=" } 76 | lteq = { "<=" } 77 | bang = { "!" } 78 | log_and = { "&&" } 79 | log_or = { "||" } 80 | 81 | operators = _{ 82 | exp | add | sub | mul | div | modulo 83 | | dbeq | neq | gteq | lteq | gt | lt 84 | | log_and | log_or 85 | } 86 | 87 | prefix_operators = _{ 88 | add | sub | bang 89 | } 90 | 91 | infix = _{ expression_inner ~ (operators ~ expression_inner)+ } 92 | 93 | debug_print = { "debug" ~ expression } 94 | echo_print = { "echo" ~ expression } 95 | 96 | break_stmt = { "break" } 97 | next_stmt = { "next" } 98 | 99 | suffix_assign = { 100 | term ~ (indexing)+ ~ "=" ~ expression 101 | } 102 | 103 | expression = { infix | expression_inner } 104 | 105 | expression_stmt = { expression } 106 | 107 | stmt = _{ 108 | debug_print 109 | | echo_print 110 | | break_stmt 111 | | next_stmt 112 | | suffix_assign 113 | | expression_stmt 114 | } -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | use lazy_static::*; 2 | use pest::iterators::{Pair, Pairs}; 3 | use pest::Parser; 4 | use pest::prec_climber::*; 5 | use pest_derive::*; 6 | 7 | use ast::*; 8 | use Rule::*; 9 | 10 | pub mod ast; 11 | 12 | lazy_static! { 13 | static ref PREC_CLIMBER: PrecClimber = { 14 | use Assoc::*; 15 | 16 | PrecClimber::new(vec![ 17 | Operator::new(log_or, Left), 18 | Operator::new(log_and, Left), 19 | Operator::new(dbeq, Left) | Operator::new(neq, Left), 20 | Operator::new(gt, Left) 21 | | Operator::new(lt, Left) 22 | | Operator::new(gteq, Left) 23 | | Operator::new(lteq, Left), 24 | Operator::new(add, Left) | Operator::new(sub, Left), 25 | Operator::new(mul, Left) | Operator::new(div, Left) | Operator::new(modulo, Left), 26 | Operator::new(exp, Right), 27 | ]) 28 | }; 29 | } 30 | 31 | #[derive(Parser)] 32 | #[grammar = "parser/glacier.pest"] 33 | pub struct GlacierParser; 34 | 35 | fn infix<'a>(lhs: Expression<'a>, op: Pair<'a, Rule>, rhs: Expression<'a>) -> Expression<'a> { 36 | Expression::Infix(Box::new(Infix { 37 | left: lhs, 38 | operator: op.as_str(), 39 | right: rhs, 40 | pos: op.as_span().into(), 41 | })) 42 | } 43 | 44 | fn others(pair: Pair) -> Expression { 45 | match pair.as_rule() { 46 | Rule::string_literal => Expression::String_(String_ { 47 | value: { 48 | let mut s = String::new(); 49 | let mut escape_mode = false; 50 | for c in pair.as_str()[1..pair.as_str().len() - 1].chars() { 51 | if escape_mode { 52 | match c { 53 | 'n' => s.push('\n'), 54 | 't' => s.push('\t'), 55 | 'r' => s.push('\r'), 56 | '0' => s.push('\0'), 57 | '"' => s.push('"'), 58 | '\'' => s.push('\''), 59 | '\\' => s.push('\\'), 60 | _ => (), 61 | } 62 | escape_mode = false; 63 | } else if c == '\\' { 64 | escape_mode = true; 65 | } else { 66 | s.push(c); 67 | } 68 | } 69 | s 70 | }, 71 | pos: pair.as_span().into(), 72 | }), 73 | 74 | Rule::integer => Expression::Int(Integer { 75 | value: pair.as_str(), 76 | pos: pair.as_span().into(), 77 | }), 78 | 79 | Rule::float => Expression::Float(Float { 80 | value: pair.as_str(), 81 | pos: pair.as_span().into(), 82 | }), 83 | 84 | Rule::array => { 85 | let inner = pair.clone().into_inner(); 86 | Expression::Array(Array { 87 | values: inner.map(parse_expression).collect(), 88 | pos: pair.as_span().into(), 89 | }) 90 | } 91 | 92 | Rule::false_expr => Expression::Bool(Bool { 93 | value: false, 94 | pos: pair.as_span().into(), 95 | }), 96 | Rule::true_expr => Expression::Bool(Bool { 97 | value: true, 98 | pos: pair.as_span().into(), 99 | }), 100 | 101 | Rule::identifier => Expression::GetVar(GetVar { 102 | name: pair.as_str(), 103 | pos: pair.as_span().into(), 104 | }), 105 | 106 | Rule::assign => { 107 | let mut inner = pair.clone().into_inner(); 108 | let name = inner.next().unwrap().as_str(); 109 | let res = inner.next().unwrap(); 110 | Expression::SetVar(Box::new(SetVar { 111 | name, 112 | value: parse_expression(res), 113 | pos: pair.as_span().into(), 114 | })) 115 | } 116 | 117 | Rule::condition_if => { 118 | let mut inner = pair.clone().into_inner(); 119 | let cond = inner.next().unwrap(); 120 | let res = inner.next().unwrap(); 121 | Expression::If(Box::new(If { 122 | cond: parse_expression(cond), 123 | body: parse_program(res.into_inner()), 124 | other: vec![], 125 | pos: pair.as_span().into(), 126 | })) 127 | } 128 | 129 | Rule::condition_ifelse => { 130 | let mut inner = pair.clone().into_inner(); 131 | let cond = inner.next().unwrap(); 132 | let res = inner.next().unwrap(); 133 | let other = inner.next().unwrap(); 134 | Expression::If(Box::new(If { 135 | cond: parse_expression(cond), 136 | body: parse_program(res.into_inner()), 137 | other: parse_program(other.into_inner()), 138 | pos: pair.as_span().into(), 139 | })) 140 | } 141 | 142 | Rule::while_loop => { 143 | let mut inner = pair.clone().into_inner(); 144 | let cond = inner.next().unwrap(); 145 | let res = inner.next().unwrap(); 146 | Expression::While(Box::new(While { 147 | cond: parse_expression(cond), 148 | body: parse_program(res.into_inner()), 149 | pos: pair.as_span().into(), 150 | })) 151 | } 152 | 153 | Rule::do_block => { 154 | let mut inner = pair.clone().into_inner(); 155 | let res = inner.next().unwrap(); 156 | Expression::Do(Box::new(Do { 157 | body: parse_program(res.into_inner()), 158 | pos: pair.as_span().into(), 159 | })) 160 | } 161 | 162 | Rule::prefix => { 163 | let mut inner: Vec> = pair.clone().into_inner().collect(); 164 | let last = inner.pop().unwrap(); 165 | let mut right = parse_expression(last); 166 | 167 | while let Some(x) = inner.pop() { 168 | right = Expression::Prefix(Box::new(Prefix { 169 | operator: x.as_str(), 170 | right, 171 | pos: pair.as_span().into(), 172 | })) 173 | } 174 | 175 | right 176 | } 177 | Rule::suffix => { 178 | let mut inner = pair.clone().into_inner(); 179 | let res = inner.next().unwrap(); 180 | let mut args_iter = inner; 181 | 182 | let n = args_iter.next().unwrap(); 183 | let mut callee = match n.as_rule() { 184 | Rule::indexing => Expression::Index(Box::new(Index { 185 | callee: parse_expression(res), 186 | index: parse_expression(n.into_inner().next().unwrap()), 187 | pos: pair.as_span().into(), 188 | })), 189 | _ => unreachable!(), 190 | }; 191 | 192 | for xx in args_iter { 193 | callee = match xx.as_rule() { 194 | Rule::indexing => Expression::Index(Box::new(Index { 195 | callee, 196 | index: parse_expression(xx.into_inner().next().unwrap()), 197 | pos: pair.as_span().into(), 198 | })), 199 | _ => unreachable!(), 200 | } 201 | } 202 | 203 | callee 204 | } 205 | 206 | Rule::expression => climb(pair), 207 | _ => { 208 | dbg!(pair.as_rule()); 209 | unreachable!() 210 | } 211 | } 212 | } 213 | 214 | pub fn climb(pair: Pair) -> Expression { 215 | //dbg!(&pair); 216 | PREC_CLIMBER.climb(pair.into_inner(), others, infix) 217 | } 218 | 219 | fn parse_expression(pair: Pair) -> Expression { 220 | let inner = pair.clone().into_inner().count() != 0; 221 | let res = if inner && pair.clone().as_rule() == Rule::expression { 222 | climb(pair) 223 | } else { 224 | others(pair) 225 | }; 226 | 227 | res 228 | } 229 | 230 | fn parse_statement(pair: Pair) -> Statement { 231 | match pair.as_rule() { 232 | Rule::expression_stmt => { 233 | let p = pair.into_inner().next().unwrap(); 234 | let s = p.clone().as_span().into(); 235 | Statement::ExprStmt(ExprStmt { 236 | expr: parse_expression(p), 237 | pos: s, 238 | }) 239 | } 240 | Rule::debug_print => { 241 | let p = pair.into_inner().next().unwrap(); 242 | let s = p.clone().as_span().into(); 243 | Statement::DebugPrint(DebugPrint { 244 | expr: parse_expression(p), 245 | pos: s, 246 | }) 247 | } 248 | Rule::echo_print => { 249 | let p = pair.into_inner().next().unwrap(); 250 | let s = p.clone().as_span().into(); 251 | Statement::EchoPrint(EchoPrint { 252 | expr: parse_expression(p), 253 | pos: s, 254 | }) 255 | } 256 | Rule::break_stmt => Statement::Break(Break { 257 | pos: pair.as_span().into(), 258 | }), 259 | Rule::next_stmt => Statement::Next(Next { 260 | pos: pair.as_span().into(), 261 | }), 262 | 263 | Rule::suffix_assign => { 264 | let mut inner = pair.clone().into_inner(); 265 | let res = inner.next().unwrap(); 266 | let mut args_iter = inner.peekable(); 267 | 268 | let n = args_iter.next().unwrap(); 269 | let mut callee = match n.as_rule() { 270 | Rule::indexing => Expression::Index(Box::new(Index { 271 | callee: parse_expression(res), 272 | index: parse_expression(n.into_inner().next().unwrap()), 273 | pos: pair.as_span().into(), 274 | })), 275 | _ => unreachable!(), 276 | }; 277 | 278 | while let Some(xx) = args_iter.next() { 279 | if args_iter.peek().is_some() { 280 | callee = match xx.as_rule() { 281 | Rule::indexing => Expression::Index(Box::new(Index { 282 | callee, 283 | index: parse_expression(xx.into_inner().next().unwrap()), 284 | pos: pair.as_span().into(), 285 | })), 286 | _ => unreachable!(), 287 | } 288 | } else { 289 | return Statement::PointerAssign(Box::new(PointerAssign { 290 | ptr: callee, 291 | value: parse_expression(xx), 292 | pos: pair.as_span().into(), 293 | })); 294 | } 295 | } 296 | 297 | unreachable!() 298 | } 299 | 300 | _ => unreachable!(), 301 | } 302 | } 303 | 304 | fn parse_program(res: Pairs) -> Program { 305 | let mut ast = vec![]; 306 | for pair in res { 307 | match pair.as_rule() { 308 | Rule::stmt 309 | | Rule::expression_stmt 310 | | Rule::debug_print 311 | | Rule::echo_print 312 | | Rule::break_stmt 313 | | Rule::next_stmt 314 | | Rule::suffix_assign => ast.push(parse_statement(pair)), 315 | _ => {} 316 | } 317 | } 318 | ast 319 | } 320 | 321 | pub fn parse(code: &str) -> Result> { 322 | let res = GlacierParser::parse(Rule::program, code); 323 | match res { 324 | Ok(res) => { 325 | let ast = parse_program(res); 326 | Ok(ast) 327 | } 328 | Err(e) => Err(e), 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /src/repl.rs: -------------------------------------------------------------------------------- 1 | use crate::parser::*; 2 | use crate::vm::*; 3 | 4 | use std::io; 5 | use std::io::Write; 6 | 7 | #[derive(Debug, Default)] 8 | pub struct Repl { 9 | pub vm: vm_bc::VM, 10 | } 11 | 12 | impl Repl { 13 | pub fn run(&mut self) { 14 | println!("REPL for Glacier 2.0 dev"); 15 | 16 | self.vm.repl_mode = true; 17 | 18 | loop { 19 | print!("> "); 20 | io::stdout().flush().unwrap(); 21 | 22 | let mut input = String::new(); 23 | io::stdin().read_line(&mut input).unwrap(); 24 | 25 | input = input.trim().to_string(); 26 | 27 | if input == ":quit" { 28 | break; 29 | } 30 | 31 | self.vm.error = None; 32 | self.vm.set_source(input.clone()); 33 | 34 | let ast_ = parse(input.as_str()); 35 | if let Ok(ast_) = ast_ { 36 | // dbg!(&ast_); 37 | 38 | self.vm.compile(&ast_); 39 | 40 | if let Some(e) = &self.vm.error { 41 | println!("{e}"); 42 | continue; 43 | } 44 | 45 | self.vm.optimize(); 46 | 47 | // println!("{}", self.vm.disassemble()); 48 | // println!("{:?}", self.vm.current_compiler); 49 | 50 | self.vm.execute(); 51 | 52 | // println!("{:?}", self.vm.stack); 53 | 54 | if let Some(e) = &self.vm.error { 55 | println!("{e}"); 56 | continue; 57 | } 58 | 59 | if let Some(lp) = &self.vm.last_popped { 60 | if let value::Value::Null = unsafe { &**lp } { 61 | } else { 62 | println!("#>> {}", unsafe { &**lp }.debug_format()); 63 | } 64 | } 65 | } else if let Err(e) = ast_ { 66 | if let pest::error::LineColLocation::Span(start, end) = e.line_col { 67 | let line_str = input.split('\n').nth(start.0 - 1).unwrap(); 68 | println!( 69 | "At Line {}:\n{}\n{}{}\nSyntax Error", 70 | start.0, 71 | line_str, 72 | " ".repeat(start.1 - 1), 73 | "^".repeat(end.1.min(line_str.len()) - start.1), 74 | ); 75 | } else if let pest::error::LineColLocation::Pos(pos) = e.line_col { 76 | let line_str = input.split('\n').nth(pos.0 - 1).unwrap(); 77 | println!( 78 | "At Line {}:\n{}\n{}^\nSyntax Error", 79 | pos.0, 80 | line_str, 81 | " ".repeat(pos.1 - 1), 82 | ); 83 | } 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod testcases { 3 | use crate::value::Value; 4 | use crate::{parse, VM}; 5 | 6 | fn test_file(content: String, expected: Value) -> bool { 7 | let mut vm = VM { 8 | repl_mode: true, 9 | ..Default::default() 10 | }; 11 | vm.set_source(content.clone()); 12 | 13 | let ast_ = parse(content.as_str()); 14 | if let Ok(ast_) = ast_ { 15 | vm.compile(&ast_); 16 | 17 | if let Some(e) = &vm.error { 18 | println!("{e}"); 19 | return false; 20 | } 21 | 22 | vm.optimize(); 23 | vm.execute(); 24 | 25 | if let Some(e) = &vm.error { 26 | println!("{e}"); 27 | return false; 28 | } 29 | 30 | if let Some(lp) = &vm.last_popped { 31 | if unsafe { &**lp }.is_equal(&expected) { 32 | true 33 | } else { 34 | println!( 35 | "Expected: {:?}, got: {:?}", 36 | expected.debug_format(), 37 | unsafe { &**lp }.debug_format() 38 | ); 39 | false 40 | } 41 | } else { 42 | println!( 43 | "Expected: {:?}, got: {:?}", 44 | expected.debug_format(), 45 | "nothing" 46 | ); 47 | false 48 | } 49 | } else { 50 | println!("Syntax Error"); 51 | false 52 | } 53 | } 54 | 55 | #[test] 56 | fn test_all() { 57 | let testsuite = [ 58 | (include_str!("tests/1_arithmetic.glc"), Value::Int(682)), 59 | (include_str!("tests/2_cf1.glc"), Value::Int(5)), 60 | (include_str!("tests/3_cf2.glc"), Value::Bool(true)), 61 | ]; 62 | 63 | let mut i = 1; 64 | for (content, expected) in testsuite.iter() { 65 | println!("Testing: {i}"); 66 | assert!(test_file(content.to_string(), expected.clone())); 67 | i += 1; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/tests/tests/1_arithmetic.glc: -------------------------------------------------------------------------------- 1 | # test 1: arithmetic 2 | # expected: 682 3 | 4 | a = 38 - -12307 + 123 / 8 - 2 * (4 - 7) + 9123 * 2 * -3 - 1 5 | b = a % -122 6 | c = a % 38 7 | d = 2 * -b * --c 8 | 9 | d / 4 -------------------------------------------------------------------------------- /src/tests/tests/2_cf1.glc: -------------------------------------------------------------------------------- 1 | # test 2: if/else 2 | # expected: 5 3 | 4 | k = 1 5 | if 3 >= 3: k = 3 6 | if -9 == 1: k = 2 7 | 8 | if k < 3 9 | k = 4 10 | else 11 | k = 5 12 | end 13 | 14 | k 15 | -------------------------------------------------------------------------------- /src/tests/tests/3_cf2.glc: -------------------------------------------------------------------------------- 1 | # test 3: while loop 2 | # expected: true 3 | 4 | k = -4 5 | c = 0 6 | while k / 2 < 10 7 | c = c + k * k 8 | k = k + 1 9 | end 10 | 11 | a1 = c == 2500 12 | 13 | k = 0 14 | c = 0 15 | s = 0 16 | while k < 10000 17 | k = k + 1 18 | if k % 5 == 0: next 19 | if k % 127 20 | s = s + 1 21 | else 22 | break 23 | end 24 | c = c + k % 3 25 | end 26 | 27 | a2 = c == 100 28 | a3 = s == 101 29 | 30 | a1 && a2 && a3 31 | -------------------------------------------------------------------------------- /src/vm/bytecode.rs: -------------------------------------------------------------------------------- 1 | pub type Byte = u16; 2 | // Size doesn't really matter 3 | pub type ByteCodes = Vec; 4 | 5 | macro_rules! bytecodes_internal { 6 | () => {}; 7 | ($x:ident, $i:expr) => { 8 | pub const $x: Byte = $i; 9 | }; 10 | ($x:ident; $($y:ident);+, $i:expr) => { 11 | pub const $x: Byte = $i; 12 | bytecodes_internal!($($y);+, $i+1); 13 | }; 14 | } 15 | 16 | macro_rules! bytecodes { 17 | () => {}; 18 | 19 | ($x:ident;) => { 20 | pub const $x: Byte = 0; 21 | }; 22 | ($x:ident; $($y:ident);+;) => { 23 | pub const $x: Byte = 0; 24 | bytecodes_internal!($($y);+, 1); 25 | } 26 | } 27 | 28 | bytecodes! { 29 | // NOOP 30 | // Does nothing, just placeholder 31 | NOOP; 32 | 33 | // POP_LAST 34 | // Stack: [a] -> [] 35 | // Pops a 36 | POP_LAST; 37 | 38 | // REPLACE address 39 | // Stack: [addr, ..., a] -> [a, ...] 40 | // Pops a and puts its shallow copy at address 41 | REPLACE; 42 | 43 | // SET_IN_PLACE 44 | // Stack: [a, b] -> [a] 45 | // Pops b and puts its shallow copy in a 46 | SET_IN_PLACE; 47 | 48 | // LOAD_CONST address 49 | // Stack: [] -> [value] 50 | // Loads const[address] onto stack 51 | LOAD_CONST; 52 | 53 | // LOAD_LOCAL address 54 | // Stack: stack[address]=value -> [value] 55 | // Loads stack[address] onto stack 56 | LOAD_LOCAL; 57 | 58 | // MAKE_ARRAY length 59 | // Stack: [d, c, b, a] -> [ARR] 60 | // Pops `length` items and pushes array 61 | MAKE_ARRAY; 62 | 63 | // DEBUG_PRINT 64 | // Stack: [a] -> [] 65 | // Debug prints a 66 | DEBUG_PRINT; 67 | 68 | // ECHO_PRINT 69 | // Stack: [a] -> [] 70 | // Prints a 71 | ECHO_PRINT; 72 | 73 | // JUMP_IF_FALSE address 74 | // Stack: [a] -> [] 75 | // Jumps to address if stack if falsy 76 | JUMP_IF_FALSE; 77 | 78 | // JUMP_IF_FALSE_NO_POP address 79 | // Stack: [a] -> [a] 80 | // Jumps to address if stack if falsy, but don't pop 81 | JUMP_IF_FALSE_NO_POP; 82 | 83 | // JUMP address 84 | // Stack: [] -> [] 85 | // Jumps to address 86 | JUMP; 87 | 88 | // GET 89 | // Stack: [a, b] -> [c] 90 | // Pushes c = a[b] 91 | GET; 92 | 93 | // UNARY_NEG 94 | // Stack: [a] -> [-a] 95 | // Negates a 96 | UNARY_NEG; 97 | // UNARY_NOT 98 | // Stack: [a] -> [!a] 99 | // Not a 100 | UNARY_NOT; 101 | 102 | // BINARY_ADD 103 | // Stack: [a, b] -> [a + b] 104 | // Adds b to a 105 | BINARY_ADD; 106 | // BINARY_SUB 107 | // Stack: [a, b] -> [a - b] 108 | // Subtracts b from a 109 | BINARY_SUB; 110 | // BINARY_MUL 111 | // Stack: [a, b] -> [a * b] 112 | // Multiplies a by b 113 | BINARY_MUL; 114 | // BINARY_DIV 115 | // Stack: [a, b] -> [a / b] 116 | // Divides a by b 117 | BINARY_DIV; 118 | // BINARY_MOD 119 | // Stack: [a, b] -> [a % b] 120 | // a MOD b 121 | BINARY_MOD; 122 | // BINARY_EXP 123 | // Stack: [a, b] -> [a ** b] 124 | // a EXP b 125 | BINARY_EXP; 126 | 127 | // BINARY_EQ 128 | // Stack: [a, b] -> [a == b] 129 | // a == b 130 | BINARY_EQ; 131 | // BINARY_NE 132 | // Stack: [a, b] -> [a != b] 133 | // a != b 134 | BINARY_NE; 135 | 136 | // BINARY_LT 137 | // Stack: [a, b] -> [a < b] 138 | // a < b 139 | BINARY_LT; 140 | // BINARY_LE 141 | // Stack: [a, b] -> [a <= b] 142 | // a <= b 143 | BINARY_LE; 144 | // BINARY_GT 145 | // Stack: [a, b] -> [a > b] 146 | // a > b 147 | BINARY_GT; 148 | // BINARY_GE 149 | // Stack: [a, b] -> [a >= b] 150 | // a >= b 151 | BINARY_GE; 152 | } 153 | 154 | pub fn operands(bytecode: Byte) -> usize { 155 | match bytecode { 156 | LOAD_CONST | LOAD_LOCAL | REPLACE | JUMP_IF_FALSE | JUMP_IF_FALSE_NO_POP | JUMP => 1, 157 | _ => 0, 158 | } 159 | } 160 | 161 | pub fn bytecode_name(bytecode: Byte) -> &'static str { 162 | match bytecode { 163 | NOOP => "NOOP", 164 | POP_LAST => "POP_LAST", 165 | REPLACE => "REPLACE", 166 | SET_IN_PLACE => "SET_IN_PLACE", 167 | LOAD_CONST => "LOAD_CONST", 168 | LOAD_LOCAL => "LOAD_LOCAL", 169 | MAKE_ARRAY => "MAKE_ARRAY", 170 | JUMP_IF_FALSE => "JUMP_IF_FALSE", 171 | JUMP_IF_FALSE_NO_POP => "JUMP_IF_FALSE_NO_POP", 172 | JUMP => "JUMP", 173 | DEBUG_PRINT => "DEBUG_PRINT", 174 | GET => "GET", 175 | UNARY_NEG => "UNARY_NEG", 176 | UNARY_NOT => "UNARY_NOT", 177 | BINARY_ADD => "BINARY_ADD", 178 | BINARY_SUB => "BINARY_SUB", 179 | BINARY_MUL => "BINARY_MUL", 180 | BINARY_DIV => "BINARY_DIV", 181 | BINARY_MOD => "BINARY_MOD", 182 | BINARY_EQ => "BINARY_EQ", 183 | BINARY_NE => "BINARY_NE", 184 | BINARY_LT => "BINARY_LT", 185 | BINARY_LE => "BINARY_LE", 186 | BINARY_GT => "BINARY_GT", 187 | BINARY_GE => "BINARY_GE", 188 | _ => "UNKNOWN", 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/vm/memory.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::{alloc, dealloc, Layout}; 2 | use std::collections::hash_map::HashMap; 3 | use std::sync::Mutex; 4 | 5 | use lazy_static::lazy_static; 6 | 7 | use crate::vm::value::Value; 8 | 9 | static LAYOUT: Layout = Layout::new::(); 10 | 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 12 | #[repr(u8)] 13 | pub enum GCItemState { 14 | White, 15 | Grey, 16 | Black, 17 | Persistent, 18 | } 19 | 20 | lazy_static! { 21 | pub static ref ALL_ALLOCATIONS: Mutex> = Mutex::new(HashMap::new()); 22 | } 23 | 24 | pub static mut LAST_ALLOCATED: usize = 0; 25 | 26 | pub const GC_FORCE_COLLECT: usize = 1 << 19; 27 | 28 | pub fn alloc_value_ptr() -> *mut Value { 29 | let ptr = unsafe { alloc(LAYOUT) as *mut Value }; 30 | ALL_ALLOCATIONS 31 | .lock() 32 | .unwrap() 33 | .insert(ptr as usize, GCItemState::White); 34 | ptr 35 | } 36 | 37 | pub fn alloc_new_value(val: Value) -> *mut Value { 38 | let ptr = alloc_value_ptr(); 39 | unsafe { 40 | *ptr = val; 41 | } 42 | ptr 43 | } 44 | 45 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 46 | pub fn mark(node: *mut Value) { 47 | let mut all_allocations = ALL_ALLOCATIONS.lock().unwrap(); 48 | 49 | let mut grey_objects = Vec::new(); 50 | 51 | if let Some(item) = all_allocations.get_mut(&(node as usize)) { 52 | if *item == GCItemState::White { 53 | *item = GCItemState::Grey; 54 | grey_objects.push(node); 55 | } 56 | } 57 | 58 | while !grey_objects.is_empty() { 59 | let g = grey_objects.pop().unwrap(); 60 | if let Some(item) = all_allocations.get_mut(&(g as usize)) { 61 | if *item == GCItemState::Grey { 62 | *item = GCItemState::Black; 63 | 64 | if let Some(children) = unsafe { (*g).referenced_children() } { 65 | for child in children { 66 | if let Some(item) = all_allocations.get_mut(&(child as usize)) { 67 | if *item == GCItemState::White { 68 | *item = GCItemState::Grey; 69 | grey_objects.push(child); 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | pub fn sweep() { 80 | let mut all_allocations = ALL_ALLOCATIONS.lock().unwrap(); 81 | let mut to_remove = Vec::new(); 82 | for (ptr, state) in all_allocations.iter() { 83 | if *state == GCItemState::White { 84 | unsafe { 85 | dealloc(*ptr as *mut u8, LAYOUT); 86 | } 87 | to_remove.push(*ptr); 88 | } 89 | } 90 | 91 | // println!("Swept {} items", to_remove.len()); 92 | 93 | for ptr in to_remove { 94 | all_allocations.remove(&ptr); 95 | } 96 | 97 | for (_, state) in all_allocations.iter_mut() { 98 | *state = GCItemState::White; 99 | } 100 | 101 | unsafe { 102 | LAST_ALLOCATED = all_allocations.len(); 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /src/vm/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bytecode; 2 | pub mod memory; 3 | pub mod value; 4 | pub mod vm_bc; 5 | -------------------------------------------------------------------------------- /src/vm/value.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::not_unsafe_ptr_arg_deref)] 2 | 3 | use crate::memory::alloc_new_value; 4 | 5 | pub enum BinOpResult { 6 | Ok(*mut Value), 7 | Error(String), 8 | NoMatch, 9 | } 10 | 11 | #[repr(C)] 12 | #[derive(Debug, Clone, PartialEq)] 13 | pub enum Value { 14 | Float(f64), 15 | Int(i64), 16 | String(String), 17 | Bool(bool), 18 | Null, 19 | 20 | Array(Vec<*mut Value>), 21 | } 22 | 23 | impl Value { 24 | pub fn debug_format(&self) -> String { 25 | match self { 26 | Value::Float(f) => format!("{f:?}"), 27 | Value::Int(i) => format!("{i:?}"), 28 | Value::String(s) => format!("{s:?}"), 29 | Value::Bool(b) => format!("{b:?}"), 30 | Value::Null => "null".to_string(), 31 | 32 | Value::Array(a) => format!( 33 | "[{}]", 34 | a.iter() 35 | .map(|v| unsafe { &**v }.debug_format()) 36 | .collect::>() 37 | .join(", ") 38 | ), 39 | } 40 | } 41 | 42 | pub fn print_format(&self) -> String { 43 | match self { 44 | Value::Float(f) => format!("{f}"), 45 | Value::Int(i) => format!("{i}"), 46 | Value::String(s) => s.to_string(), 47 | Value::Bool(b) => format!("{b}"), 48 | Value::Null => "null".to_string(), 49 | 50 | Value::Array(a) => format!( 51 | "[{}]", 52 | a.iter() 53 | .map(|v| unsafe { &**v }.debug_format()) 54 | .collect::>() 55 | .join(", ") 56 | ), 57 | } 58 | } 59 | 60 | pub fn type_name(&self) -> &'static str { 61 | match self { 62 | Value::Float(_) => "float", 63 | Value::Int(_) => "int", 64 | Value::String(_) => "string", 65 | Value::Bool(_) => "bool", 66 | Value::Null => "null", 67 | 68 | Value::Array(_) => "array", 69 | } 70 | } 71 | 72 | pub fn regular_copy_to(&mut self, dest: *mut Value) -> *mut Value { 73 | unsafe { 74 | *dest = self.clone(); 75 | } 76 | 77 | dest 78 | } 79 | 80 | pub fn shallow_copy(&mut self) -> *mut Value { 81 | match self { 82 | Value::Array(_) => self as *mut Value, 83 | _ => alloc_new_value(self.clone()), 84 | } 85 | } 86 | 87 | pub fn deep_copy(&mut self) -> *mut Value { 88 | match self { 89 | Value::Array(a) => alloc_new_value(Value::Array( 90 | a.iter() 91 | .map(|v| unsafe { &mut **v }.deep_copy()) 92 | .collect(), 93 | )), 94 | _ => alloc_new_value(self.clone()), 95 | } 96 | } 97 | 98 | pub fn referenced_children(&self) -> Option> { 99 | match self { 100 | Value::Array(a) => Some(a.clone()), 101 | _ => None, 102 | } 103 | } 104 | 105 | pub fn is_truthy(&self) -> bool { 106 | match self { 107 | Value::Float(f) => *f != 0.0, 108 | Value::Int(i) => *i != 0, 109 | Value::String(s) => !s.is_empty(), 110 | Value::Bool(b) => *b, 111 | Value::Null => false, 112 | 113 | Value::Array(a) => !a.is_empty(), 114 | } 115 | } 116 | 117 | pub fn is_equal(&self, other: &Value) -> bool { 118 | match (self, other) { 119 | (Value::Float(f1), Value::Float(f2)) => f1 == f2, 120 | (Value::Int(i1), Value::Int(i2)) => i1 == i2, 121 | (Value::String(s1), Value::String(s2)) => *s1 == *s2, 122 | (Value::Bool(b1), Value::Bool(b2)) => b1 == b2, 123 | (Value::Null, Value::Null) => true, 124 | 125 | (Value::Array(a1), Value::Array(a2)) => { 126 | if a1.len() != a2.len() { 127 | return false; 128 | } 129 | 130 | for (v1, v2) in a1.iter().zip(a2.iter()) { 131 | if !unsafe { &**v1 }.is_equal(unsafe { &**v2 }) { 132 | return false; 133 | } 134 | } 135 | 136 | true 137 | } 138 | 139 | _ => false, 140 | } 141 | } 142 | 143 | pub fn binary_add(&self, other: &Value) -> BinOpResult { 144 | match (self, other) { 145 | (Value::Float(f1), Value::Float(f2)) => { 146 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 + f2))) 147 | } 148 | (Value::Int(i1), Value::Int(i2)) => { 149 | BinOpResult::Ok(alloc_new_value(Value::Int(i1 + i2))) 150 | } 151 | (Value::Int(i1), Value::Float(f1)) => { 152 | BinOpResult::Ok(alloc_new_value(Value::Float(*i1 as f64 + f1))) 153 | } 154 | (Value::Float(f1), Value::Int(i1)) => { 155 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 + *i1 as f64))) 156 | } 157 | 158 | (Value::String(s1), Value::String(s2)) => { 159 | BinOpResult::Ok(alloc_new_value(Value::String(s1.clone() + s2))) 160 | } 161 | 162 | (Value::Array(a1), Value::Array(a2)) => { 163 | let mut new_array = a1.clone(); 164 | new_array.extend(a2.clone()); 165 | 166 | BinOpResult::Ok(alloc_new_value(Value::Array(new_array))) 167 | } 168 | 169 | _ => BinOpResult::NoMatch, 170 | } 171 | } 172 | 173 | pub fn binary_sub(&self, other: &Value) -> BinOpResult { 174 | match (self, other) { 175 | (Value::Float(f1), Value::Float(f2)) => { 176 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 - f2))) 177 | } 178 | (Value::Int(i1), Value::Int(i2)) => { 179 | BinOpResult::Ok(alloc_new_value(Value::Int(i1 - i2))) 180 | } 181 | 182 | (Value::Int(i1), Value::Float(f1)) => { 183 | BinOpResult::Ok(alloc_new_value(Value::Float(*i1 as f64 - f1))) 184 | } 185 | (Value::Float(f1), Value::Int(i1)) => { 186 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 - *i1 as f64))) 187 | } 188 | 189 | _ => BinOpResult::NoMatch, 190 | } 191 | } 192 | 193 | pub fn binary_mul(&self, other: &Value) -> BinOpResult { 194 | match (self, other) { 195 | (Value::Float(f1), Value::Float(f2)) => { 196 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 * f2))) 197 | } 198 | (Value::Int(i1), Value::Int(i2)) => { 199 | BinOpResult::Ok(alloc_new_value(Value::Int(i1 * i2))) 200 | } 201 | 202 | (Value::Int(i1), Value::Float(f1)) => { 203 | BinOpResult::Ok(alloc_new_value(Value::Float(*i1 as f64 * f1))) 204 | } 205 | (Value::Float(f1), Value::Int(i1)) => { 206 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 * *i1 as f64))) 207 | } 208 | 209 | // Shallow repetition 210 | (Value::Array(a), Value::Int(i)) => { 211 | if *i < 0 { 212 | return BinOpResult::Error("Array shallow repetition multiplier must be nonnegative".to_string()); 213 | } 214 | 215 | let mut arr = Vec::new(); 216 | for _ in 0..*i { 217 | for v in a.iter() { 218 | unsafe { 219 | arr.push((**v).shallow_copy()); 220 | } 221 | } 222 | } 223 | 224 | BinOpResult::Ok(alloc_new_value(Value::Array(arr))) 225 | } 226 | 227 | (Value::Array(a), Value::String(s)) => { 228 | let mut ss = Vec::with_capacity(a.len()); 229 | unsafe { 230 | for x in a { 231 | ss.push((**x).print_format()); 232 | } 233 | } 234 | BinOpResult::Ok(alloc_new_value(Value::String(ss.join(s)))) 235 | } 236 | 237 | _ => BinOpResult::NoMatch, 238 | } 239 | } 240 | 241 | pub fn binary_div(&self, other: &Value) -> BinOpResult { 242 | match (self, other) { 243 | (Value::Float(f1), Value::Float(f2)) => { 244 | if *f2 == 0.0 { 245 | BinOpResult::Error(format!("Division By Zero: {} / 0.0", *f1)) 246 | } else { 247 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 / *f2))) 248 | } 249 | } 250 | (Value::Int(i1), Value::Int(i2)) => { 251 | if *i2 == 0 { 252 | BinOpResult::Error(format!("Division By Zero: {} / 0", *i1)) 253 | } else { 254 | BinOpResult::Ok(alloc_new_value(Value::Int(i1 / *i2))) 255 | } 256 | } 257 | 258 | (Value::Int(i1), Value::Float(f1)) => { 259 | if *f1 == 0.0 { 260 | BinOpResult::Error(format!("Division By Zero: {} / 0.0", *i1)) 261 | } else { 262 | BinOpResult::Ok(alloc_new_value(Value::Float(*i1 as f64 / f1))) 263 | } 264 | } 265 | (Value::Float(f1), Value::Int(i1)) => { 266 | if *i1 == 0 { 267 | BinOpResult::Error(format!("Division By Zero: {} / 0", *f1)) 268 | } else { 269 | BinOpResult::Ok(alloc_new_value(Value::Float(f1 / *i1 as f64))) 270 | } 271 | } 272 | 273 | _ => BinOpResult::NoMatch, 274 | } 275 | } 276 | 277 | pub fn binary_mod(&self, other: &Value) -> BinOpResult { 278 | match (self, other) { 279 | (Value::Float(f1), Value::Float(f2)) => { 280 | if *f2 == 0.0 { 281 | BinOpResult::Error(format!("Modulo By Zero: {} % 0.0", *f1)) 282 | } else { 283 | BinOpResult::Ok(alloc_new_value(Value::Float((f1 % *f2 + *f2) % *f2))) 284 | } 285 | } 286 | (Value::Int(i1), Value::Int(i2)) => { 287 | if *i2 == 0 { 288 | BinOpResult::Error(format!("Modulo By Zero: {} % 0", *i1)) 289 | } else { 290 | BinOpResult::Ok(alloc_new_value(Value::Int((i1 % *i2 + *i2) % *i2))) 291 | } 292 | } 293 | (Value::Int(i1), Value::Float(f1)) => { 294 | if *f1 == 0.0 { 295 | BinOpResult::Error(format!("Modulo By Zero: {} % 0.0", *i1)) 296 | } else { 297 | BinOpResult::Ok(alloc_new_value(Value::Float(((*i1 as f64) % f1 + f1) % f1))) 298 | } 299 | } 300 | (Value::Float(f1), Value::Int(i1)) => { 301 | if *i1 == 0 { 302 | BinOpResult::Error(format!("Modulo By Zero: {} % 0", *f1)) 303 | } else { 304 | BinOpResult::Ok(alloc_new_value(Value::Float((f1 % (*i1 as f64) + *i1 as f64) % *i1 as f64))) 305 | } 306 | } 307 | 308 | _ => BinOpResult::NoMatch, 309 | } 310 | } 311 | 312 | pub fn binary_exp(&self, other: &Value) -> BinOpResult { 313 | match (self, other) { 314 | (Value::Float(f1), Value::Float(f2)) => { 315 | BinOpResult::Ok(alloc_new_value(Value::Float(f1.powf(*f2)))) 316 | } 317 | (Value::Int(i1), Value::Int(i2)) => { 318 | BinOpResult::Ok(alloc_new_value(Value::Float((*i1 as f64).powf(*i2 as f64)))) 319 | } 320 | (Value::Int(i1), Value::Float(f1)) => { 321 | BinOpResult::Ok(alloc_new_value(Value::Float((*i1 as f64).powf(*f1)))) 322 | } 323 | (Value::Float(f1), Value::Int(i1)) => { 324 | BinOpResult::Ok(alloc_new_value(Value::Float(f1.powf(*i1 as f64)))) 325 | } 326 | 327 | // Deep repetition 328 | (Value::Array(a), Value::Int(i)) => { 329 | if *i < 0 { 330 | return BinOpResult::Error("Array deep repetition multiplier must be nonnegative".to_string()); 331 | } 332 | 333 | let mut arr = Vec::new(); 334 | for _ in 0..*i { 335 | for v in a.iter() { 336 | unsafe { 337 | arr.push((**v).deep_copy()); 338 | } 339 | } 340 | } 341 | 342 | BinOpResult::Ok(alloc_new_value(Value::Array(arr))) 343 | } 344 | 345 | _ => BinOpResult::NoMatch, 346 | } 347 | } 348 | 349 | pub fn binary_lt(&self, other: &Value) -> BinOpResult { 350 | match (self, other) { 351 | (Value::Float(f1), Value::Float(f2)) => { 352 | BinOpResult::Ok(alloc_new_value(Value::Bool(f1 < f2))) 353 | } 354 | (Value::Int(i1), Value::Int(i2)) => { 355 | BinOpResult::Ok(alloc_new_value(Value::Bool(i1 < i2))) 356 | } 357 | (Value::Int(i1), Value::Float(f1)) => { 358 | BinOpResult::Ok(alloc_new_value(Value::Bool((*i1 as f64) < *f1))) 359 | } 360 | (Value::Float(f1), Value::Int(i1)) => { 361 | BinOpResult::Ok(alloc_new_value(Value::Bool(*f1 < (*i1 as f64)))) 362 | } 363 | (Value::String(s1), Value::String(s2)) => { 364 | BinOpResult::Ok(alloc_new_value(Value::Bool(s1 < s2))) 365 | } 366 | 367 | _ => BinOpResult::NoMatch, 368 | } 369 | } 370 | 371 | pub fn binary_gt(&self, other: &Value) -> BinOpResult { 372 | match (self, other) { 373 | (Value::Float(f1), Value::Float(f2)) => { 374 | BinOpResult::Ok(alloc_new_value(Value::Bool(f1 > f2))) 375 | } 376 | (Value::Int(i1), Value::Int(i2)) => { 377 | BinOpResult::Ok(alloc_new_value(Value::Bool(i1 > i2))) 378 | } 379 | (Value::Int(i1), Value::Float(f1)) => { 380 | BinOpResult::Ok(alloc_new_value(Value::Bool((*i1 as f64) > *f1))) 381 | } 382 | (Value::Float(f1), Value::Int(i1)) => { 383 | BinOpResult::Ok(alloc_new_value(Value::Bool(*f1 > (*i1 as f64)))) 384 | } 385 | (Value::String(s1), Value::String(s2)) => { 386 | BinOpResult::Ok(alloc_new_value(Value::Bool(s1 > s2))) 387 | } 388 | 389 | _ => BinOpResult::NoMatch, 390 | } 391 | } 392 | 393 | pub fn binary_le(&self, other: &Value) -> BinOpResult { 394 | match (self, other) { 395 | (Value::Float(f1), Value::Float(f2)) => { 396 | BinOpResult::Ok(alloc_new_value(Value::Bool(f1 <= f2))) 397 | } 398 | (Value::Int(i1), Value::Int(i2)) => { 399 | BinOpResult::Ok(alloc_new_value(Value::Bool(i1 <= i2))) 400 | } 401 | (Value::Int(i1), Value::Float(f1)) => { 402 | BinOpResult::Ok(alloc_new_value(Value::Bool((*i1 as f64) <= *f1))) 403 | } 404 | (Value::Float(f1), Value::Int(i1)) => { 405 | BinOpResult::Ok(alloc_new_value(Value::Bool(*f1 <= (*i1 as f64)))) 406 | } 407 | (Value::String(s1), Value::String(s2)) => { 408 | BinOpResult::Ok(alloc_new_value(Value::Bool(s1 <= s2))) 409 | } 410 | 411 | _ => BinOpResult::NoMatch, 412 | } 413 | } 414 | 415 | pub fn binary_ge(&self, other: &Value) -> BinOpResult { 416 | match (self, other) { 417 | (Value::Float(f1), Value::Float(f2)) => { 418 | BinOpResult::Ok(alloc_new_value(Value::Bool(f1 >= f2))) 419 | } 420 | (Value::Int(i1), Value::Int(i2)) => { 421 | BinOpResult::Ok(alloc_new_value(Value::Bool(i1 >= i2))) 422 | } 423 | (Value::Int(i1), Value::Float(f1)) => { 424 | BinOpResult::Ok(alloc_new_value(Value::Bool((*i1 as f64) >= *f1))) 425 | } 426 | (Value::Float(f1), Value::Int(i1)) => { 427 | BinOpResult::Ok(alloc_new_value(Value::Bool(*f1 >= (*i1 as f64)))) 428 | } 429 | (Value::String(s1), Value::String(s2)) => { 430 | BinOpResult::Ok(alloc_new_value(Value::Bool(s1 >= s2))) 431 | } 432 | 433 | _ => BinOpResult::NoMatch, 434 | } 435 | } 436 | 437 | pub fn get_element(&self, index: *mut Value) -> Result<*mut Value, String> { 438 | unsafe { 439 | match self { 440 | Value::Array(a) => { 441 | if let Value::Int(i) = *index { 442 | if i < 0 { 443 | Err(format!("Negative index not supported: {i}")) 444 | } else if let Some(v) = a.get(i as usize) { 445 | Ok(*v) 446 | } else { 447 | Err(format!("Index out of range: {i}")) 448 | } 449 | } else { 450 | Err(format!( 451 | "Array index must be an integer, not {}", 452 | (*index).type_name() 453 | )) 454 | } 455 | } 456 | Value::String(s) => { 457 | if let Value::Int(i) = *index { 458 | if i < 0 { 459 | Err(format!("Negative index not supported: {i}")) 460 | } else if let Some(c) = s.chars().nth(i as usize) { 461 | Ok(alloc_new_value(Value::String(c.to_string()))) 462 | } else { 463 | Err(format!("Index out of range: {i}")) 464 | } 465 | } else { 466 | Err(format!( 467 | "String index must be an integer, not {}", 468 | (*index).type_name() 469 | )) 470 | } 471 | } 472 | _ => Err(format!("Cannot get element from type {}", self.type_name())), 473 | } 474 | } 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /src/vm/vm_bc.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fmt::Write; 3 | 4 | use arrayvec::ArrayVec; 5 | 6 | use crate::ast::*; 7 | use crate::value::*; 8 | 9 | use super::bytecode::*; 10 | use super::memory::*; 11 | 12 | pub const GC_TRIGGER: usize = 1 << 20; 13 | 14 | pub const BYTECODE_CAP: usize = 32768; 15 | pub const CONSTANT_SIZE: usize = 4096; 16 | pub const LOCAL_SIZE: usize = 4096; 17 | pub const SCOPE_SIZE: usize = 512; 18 | pub const STACK_SIZE: usize = 1 << 16; 19 | 20 | pub const BOOL_FALSE_CONSTANT: usize = 0; 21 | pub const BOOL_TRUE_CONSTANT: usize = 1; 22 | pub const NULL_CONSTANT: usize = 2; 23 | 24 | #[derive(Debug, Clone, Default)] 25 | pub struct Compiler { 26 | pub local_map: ArrayVec, SCOPE_SIZE>, 27 | pub scope_depth: usize, 28 | pub count: usize, 29 | } 30 | 31 | #[derive(Debug, Clone)] 32 | pub struct VM { 33 | pub source: String, 34 | 35 | pub bytecodes: Vec, 36 | // (Start, End) 37 | pub lines: Vec, 38 | pub pc: usize, 39 | 40 | pub constants: ArrayVec, 41 | pub constant_hash_int: HashMap, 42 | 43 | pub break_jump_patches: Vec>, 44 | pub next_jump_patches: Vec>, 45 | 46 | pub current_compiler: Compiler, 47 | 48 | pub stack: ArrayVec<*mut Value, STACK_SIZE>, 49 | 50 | pub last_popped: Option<*mut Value>, 51 | pub repl_mode: bool, 52 | 53 | pub error: Option, 54 | } 55 | 56 | impl Default for VM { 57 | fn default() -> Self { 58 | let mut v = VM { 59 | source: String::new(), 60 | 61 | bytecodes: Vec::with_capacity(BYTECODE_CAP), 62 | lines: Vec::with_capacity(BYTECODE_CAP), 63 | pc: 0, 64 | 65 | constants: ArrayVec::new(), 66 | constant_hash_int: HashMap::new(), 67 | 68 | break_jump_patches: Vec::new(), 69 | next_jump_patches: Vec::new(), 70 | 71 | current_compiler: Default::default(), 72 | 73 | stack: ArrayVec::new(), 74 | 75 | last_popped: None, 76 | repl_mode: false, 77 | 78 | error: None, 79 | }; 80 | 81 | v.constants.push(Value::Bool(false)); 82 | v.constants.push(Value::Bool(true)); 83 | v.constants.push(Value::Null); 84 | 85 | v.current_compiler.local_map.push(HashMap::new()); 86 | 87 | v 88 | } 89 | } 90 | 91 | impl VM { 92 | pub fn set_source(&mut self, source: String) { 93 | self.source = source; 94 | } 95 | 96 | #[inline(always)] 97 | fn span_to_line(&self, span: AstSpan) -> usize { 98 | self.source[..span.start].matches('\n').count() 99 | } 100 | 101 | fn get_nl_pos(&self, line: usize) -> usize { 102 | if line == 0 { 103 | return 0; 104 | } 105 | let mut count = 0; 106 | for (i, ch) in self.source.chars().enumerate() { 107 | if ch == '\n' { 108 | count += 1; 109 | if count == line { 110 | return i + 1; 111 | } 112 | } 113 | } 114 | unreachable!(); 115 | } 116 | 117 | pub fn compile_error(&mut self, span: AstSpan, message: String) { 118 | let line = self.span_to_line(span); 119 | let line_str = &self.source.split('\n').nth(line).unwrap(); 120 | let start = self.get_nl_pos(line); 121 | 122 | self.error = Some(format!( 123 | "At Line {}:\n{}\n{}{}\nCompile-time Error:\n {}", 124 | line + 1, 125 | line_str, 126 | " ".repeat(span.start - start), 127 | "^".repeat(span.end - span.start), 128 | message 129 | )); 130 | } 131 | 132 | pub fn runtime_error(&mut self, message: String) { 133 | let span = self.lines[self.pc]; 134 | let line = self.span_to_line(span); 135 | let line_str = &self.source.split('\n').nth(line).unwrap(); 136 | let start = self.get_nl_pos(line); 137 | self.error = Some(format!( 138 | "At Line {}:\n{}\n{}{}\nRuntime Error:\n {}", 139 | line + 1, 140 | line_str, 141 | " ".repeat(span.start - start), 142 | "^".repeat(span.end - span.start), 143 | message 144 | )); 145 | } 146 | } 147 | 148 | // Compilation 149 | impl VM { 150 | fn push_bytecode(&mut self, bytecode: Byte, span: AstSpan) { 151 | self.bytecodes.push(bytecode); 152 | self.lines.push(AstSpan { 153 | start: span.start, 154 | end: span.end, 155 | }); 156 | } 157 | 158 | pub fn begin_scope(&mut self) { 159 | self.current_compiler.scope_depth += 1; 160 | self.current_compiler.local_map.push(HashMap::new()); 161 | } 162 | 163 | pub fn end_scope(&mut self) { 164 | self.current_compiler.scope_depth -= 1; 165 | self.current_compiler.count -= self.current_compiler.local_map.pop().unwrap().len(); 166 | } 167 | 168 | pub fn add_local(&mut self, name: String) -> usize { 169 | for i in (0..=self.current_compiler.scope_depth).rev() { 170 | if let Some(index) = self.current_compiler.local_map[i].get(&name) { 171 | return *index; 172 | } 173 | } 174 | self.current_compiler.local_map[self.current_compiler.scope_depth] 175 | .insert(name, self.current_compiler.count); 176 | self.current_compiler.count += 1; 177 | self.current_compiler.count - 1 178 | } 179 | 180 | pub fn resolve_local(&mut self, name: String) -> Option { 181 | for i in (0..=self.current_compiler.scope_depth).rev() { 182 | if let Some(index) = self.current_compiler.local_map[i].get(&name) { 183 | return Some(*index); 184 | } 185 | } 186 | None 187 | } 188 | 189 | pub fn compile(&mut self, program: &Program) { 190 | self.lines.clear(); 191 | self.bytecodes.clear(); 192 | while self.current_compiler.local_map.len() > 1 { 193 | self.current_compiler.local_map.pop(); 194 | } 195 | self.current_compiler.count = self.current_compiler.local_map[0].len(); 196 | self.compile_program(program); 197 | } 198 | 199 | pub fn compile_program(&mut self, program: &Program) -> bool { 200 | for stmt in program { 201 | if !self.compile_statement(stmt) { 202 | return false; 203 | } 204 | } 205 | true 206 | } 207 | 208 | pub fn compile_statement(&mut self, statement: &Statement) -> bool { 209 | match statement { 210 | Statement::ExprStmt(e) => { 211 | if !self.compile_expression(&e.expr) { 212 | return false; 213 | }; 214 | self.push_bytecode(POP_LAST, e.pos); 215 | } 216 | Statement::DebugPrint(e) => { 217 | if !self.compile_expression(&e.expr) { 218 | return false; 219 | }; 220 | self.push_bytecode(DEBUG_PRINT, e.pos); 221 | } 222 | Statement::EchoPrint(e) => { 223 | if !self.compile_expression(&e.expr) { 224 | return false; 225 | }; 226 | self.push_bytecode(ECHO_PRINT, e.pos); 227 | } 228 | Statement::Break(b) => { 229 | self.push_bytecode(JUMP, b.pos); 230 | if let Some(patches) = self.break_jump_patches.last_mut() { 231 | patches.push(self.bytecodes.len()); 232 | } else { 233 | self.compile_error( 234 | b.pos, 235 | "Break statement outside of loop is not allowed".to_string(), 236 | ); 237 | return false; 238 | } 239 | self.push_bytecode(0, b.pos); 240 | } 241 | Statement::Next(b) => { 242 | self.push_bytecode(JUMP, b.pos); 243 | if let Some(patches) = self.next_jump_patches.last_mut() { 244 | patches.push(self.bytecodes.len()); 245 | } else { 246 | self.compile_error( 247 | b.pos, 248 | "Next statement outside of loop is not allowed".to_string(), 249 | ); 250 | return false; 251 | } 252 | self.push_bytecode(0, b.pos); 253 | } 254 | Statement::PointerAssign(ptr) => { 255 | if !self.compile_expression(&ptr.ptr) { 256 | return false; 257 | } 258 | 259 | if !self.compile_expression(&ptr.value) { 260 | return false; 261 | } 262 | 263 | self.push_bytecode(SET_IN_PLACE, ptr.pos); 264 | } 265 | } 266 | true 267 | } 268 | 269 | pub fn compile_expression(&mut self, expression: &Expression) -> bool { 270 | match expression { 271 | Expression::String_(s) => { 272 | if self 273 | .constants 274 | .try_push(Value::String(s.value.to_string())) 275 | .is_err() 276 | { 277 | self.compile_error(s.pos, format!("Constant exceeds limit of {CONSTANT_SIZE}")); 278 | return false; 279 | } 280 | 281 | self.push_bytecode(LOAD_CONST, s.pos); 282 | self.push_bytecode(self.constants.len() as Byte - 1, s.pos); 283 | } 284 | 285 | Expression::Int(num) => { 286 | let val = num.value.parse::(); 287 | if let Ok(val) = val { 288 | let index; 289 | if let Some(k) = self.constant_hash_int.get(&val) { 290 | index = *k; 291 | } else if self.constants.try_push(Value::Int(val)).is_err() { 292 | self.compile_error( 293 | num.pos, 294 | format!("Constant exceeds limit of {CONSTANT_SIZE}"), 295 | ); 296 | return false; 297 | } else { 298 | index = self.constants.len() as Byte - 1; 299 | self.constant_hash_int.insert(val, index); 300 | } 301 | self.push_bytecode(LOAD_CONST, num.pos); 302 | self.push_bytecode(index, num.pos); 303 | } else { 304 | self.compile_error(num.pos, "Integer literal too large".to_string()); 305 | return false; 306 | } 307 | } 308 | 309 | Expression::Float(num) => { 310 | let val = num.value.parse::(); 311 | if let Ok(val) = val { 312 | if self.constants.try_push(Value::Float(val)).is_err() { 313 | self.compile_error( 314 | num.pos, 315 | format!("Constant exceeds limit of {CONSTANT_SIZE}"), 316 | ); 317 | return false; 318 | } 319 | let index = self.constants.len() as Byte - 1; 320 | self.push_bytecode(LOAD_CONST, num.pos); 321 | self.push_bytecode(index, num.pos); 322 | } else { 323 | self.compile_error(num.pos, "Integer literal too large".to_string()); 324 | return false; 325 | } 326 | } 327 | 328 | Expression::Bool(b) => { 329 | let index = if b.value { 330 | BOOL_TRUE_CONSTANT 331 | } else { 332 | BOOL_FALSE_CONSTANT 333 | }; 334 | self.push_bytecode(LOAD_CONST, b.pos); 335 | self.push_bytecode(index as Byte, b.pos); 336 | } 337 | 338 | Expression::Array(a) => { 339 | for x in a.values.iter().rev() { 340 | self.compile_expression(x); 341 | } 342 | self.push_bytecode(MAKE_ARRAY, a.pos); 343 | self.push_bytecode(a.values.len() as Byte, a.pos); 344 | } 345 | 346 | Expression::GetVar(get) => { 347 | let res = self.resolve_local(get.name.to_string()); 348 | if let Some(index) = res { 349 | self.push_bytecode(LOAD_LOCAL, get.pos); 350 | self.push_bytecode(index as Byte, get.pos); 351 | } else { 352 | self.compile_error(get.pos, format!("Variable '{}' is not defined", get.name)); 353 | return false; 354 | } 355 | } 356 | 357 | Expression::SetVar(var) => { 358 | let replace = self.add_local(var.name.to_string()); 359 | 360 | if !self.compile_expression(&var.value) { 361 | return false; 362 | } 363 | 364 | self.push_bytecode(REPLACE, var.pos); 365 | self.push_bytecode(replace as Byte, var.pos); 366 | self.push_bytecode(LOAD_LOCAL, var.pos); 367 | self.push_bytecode(replace as Byte, var.pos); 368 | } 369 | 370 | Expression::Infix(infix) => { 371 | match infix.operator { 372 | "&&" => { 373 | if !self.compile_expression(&infix.left) { 374 | return false; 375 | } 376 | 377 | self.push_bytecode(JUMP_IF_FALSE_NO_POP, infix.pos); 378 | let patch_loc = self.bytecodes.len(); 379 | self.push_bytecode(0, infix.pos); 380 | 381 | // pop left operand 382 | self.push_bytecode(POP_LAST, infix.pos); 383 | 384 | if !self.compile_expression(&infix.right) { 385 | return false; 386 | } 387 | self.bytecodes[patch_loc] = self.bytecodes.len() as Byte; 388 | } 389 | "||" => { 390 | if !self.compile_expression(&infix.left) { 391 | return false; 392 | } 393 | 394 | self.push_bytecode(JUMP_IF_FALSE_NO_POP, infix.pos); 395 | let patch_loc_1 = self.bytecodes.len(); 396 | self.push_bytecode(0, infix.pos); 397 | 398 | self.push_bytecode(JUMP, infix.pos); 399 | let patch_loc_2 = self.bytecodes.len(); 400 | self.push_bytecode(0, infix.pos); 401 | 402 | self.bytecodes[patch_loc_1] = self.bytecodes.len() as Byte; 403 | 404 | self.push_bytecode(POP_LAST, infix.pos); 405 | if !self.compile_expression(&infix.right) { 406 | return false; 407 | } 408 | 409 | self.bytecodes[patch_loc_2] = self.bytecodes.len() as Byte; 410 | } 411 | _ => { 412 | if !self.compile_expression(&infix.left) { 413 | return false; 414 | } 415 | if !self.compile_expression(&infix.right) { 416 | return false; 417 | } 418 | match infix.operator { 419 | "+" => { 420 | self.push_bytecode(BINARY_ADD, infix.pos); 421 | } 422 | "-" => { 423 | self.push_bytecode(BINARY_SUB, infix.pos); 424 | } 425 | "*" => { 426 | self.push_bytecode(BINARY_MUL, infix.pos); 427 | } 428 | "/" => { 429 | self.push_bytecode(BINARY_DIV, infix.pos); 430 | } 431 | "%" => { 432 | self.push_bytecode(BINARY_MOD, infix.pos); 433 | } 434 | "**" => { 435 | self.push_bytecode(BINARY_EXP, infix.pos); 436 | } 437 | 438 | "==" => { 439 | self.push_bytecode(BINARY_EQ, infix.pos); 440 | } 441 | "!=" => { 442 | self.push_bytecode(BINARY_NE, infix.pos); 443 | } 444 | "<" => { 445 | self.push_bytecode(BINARY_LT, infix.pos); 446 | } 447 | "<=" => { 448 | self.push_bytecode(BINARY_LE, infix.pos); 449 | } 450 | ">" => { 451 | self.push_bytecode(BINARY_GT, infix.pos); 452 | } 453 | ">=" => { 454 | self.push_bytecode(BINARY_GE, infix.pos); 455 | } 456 | 457 | _ => { 458 | self.compile_error( 459 | infix.pos, 460 | format!("Unsupported Operand: {}", infix.operator), 461 | ); 462 | return false; 463 | } 464 | } 465 | } 466 | } 467 | } 468 | 469 | Expression::Prefix(prefix) => match prefix.operator { 470 | "-" => { 471 | if !self.compile_expression(&prefix.right) { 472 | return false; 473 | } 474 | self.push_bytecode(UNARY_NEG, prefix.pos); 475 | } 476 | "!" => { 477 | if !self.compile_expression(&prefix.right) { 478 | return false; 479 | } 480 | self.push_bytecode(UNARY_NOT, prefix.pos); 481 | } 482 | _ => { 483 | self.compile_error( 484 | prefix.pos, 485 | format!("Unsupported Operand: {}", prefix.operator), 486 | ); 487 | return false; 488 | } 489 | }, 490 | 491 | Expression::Index(indexing) => { 492 | if !self.compile_expression(&indexing.callee) { 493 | return false; 494 | } 495 | 496 | if !self.compile_expression(&indexing.index) { 497 | return false; 498 | } 499 | 500 | self.push_bytecode(GET, indexing.pos); 501 | } 502 | 503 | Expression::If(iff) => { 504 | // Compile Condition 505 | if !self.compile_expression(&iff.cond) { 506 | return false; 507 | } 508 | 509 | // Jump to else if false 510 | self.push_bytecode(JUMP_IF_FALSE, iff.pos); 511 | let patch_loc = self.bytecodes.len(); 512 | self.push_bytecode(0, iff.pos); 513 | 514 | if !iff.body.is_empty() { 515 | // Compile then block 516 | self.begin_scope(); 517 | if !self.compile_program(&iff.body) { 518 | return false; 519 | } 520 | self.end_scope(); 521 | 522 | // If there is a result, don't pop it 523 | if self.bytecodes.last() == Some(&POP_LAST) { 524 | self.bytecodes.pop(); 525 | self.lines.pop(); 526 | } else { 527 | self.push_bytecode(LOAD_CONST, iff.pos); 528 | self.push_bytecode(NULL_CONSTANT as Byte, iff.pos); 529 | } 530 | } else { 531 | self.push_bytecode(LOAD_CONST, iff.pos); 532 | self.push_bytecode(NULL_CONSTANT as Byte, iff.pos); 533 | } 534 | 535 | // Jump to end 536 | self.push_bytecode(JUMP, iff.pos); 537 | let patch_loc_2 = self.bytecodes.len(); 538 | self.push_bytecode(0, iff.pos); 539 | 540 | // Patch jump 1 541 | self.bytecodes[patch_loc] = self.bytecodes.len() as Byte; 542 | 543 | if iff.other.is_empty() { 544 | self.push_bytecode(LOAD_CONST, iff.pos); 545 | self.push_bytecode(NULL_CONSTANT as Byte, iff.pos); 546 | 547 | self.bytecodes[patch_loc_2] = self.bytecodes.len() as Byte; 548 | } else { 549 | if !iff.other.is_empty() { 550 | // Compile else block 551 | self.begin_scope(); 552 | if !self.compile_program(&iff.other) { 553 | return false; 554 | } 555 | self.end_scope(); 556 | 557 | // If there is a result, don't pop it 558 | if self.bytecodes.last() == Some(&POP_LAST) { 559 | self.bytecodes.pop(); 560 | self.lines.pop(); 561 | } else { 562 | self.push_bytecode(LOAD_CONST, iff.pos); 563 | self.push_bytecode(NULL_CONSTANT as Byte, iff.pos); 564 | } 565 | } else { 566 | self.push_bytecode(LOAD_CONST, iff.pos); 567 | self.push_bytecode(NULL_CONSTANT as Byte, iff.pos); 568 | } 569 | 570 | self.bytecodes[patch_loc_2] = self.bytecodes.len() as Byte; 571 | } 572 | } 573 | 574 | Expression::While(w) => { 575 | self.break_jump_patches.push(Vec::new()); 576 | self.next_jump_patches.push(Vec::new()); 577 | 578 | let loop_start = self.bytecodes.len(); 579 | 580 | if !self.compile_expression(&w.cond) { 581 | return false; 582 | } 583 | 584 | self.push_bytecode(JUMP_IF_FALSE, w.pos); 585 | let patch_loc = self.bytecodes.len(); 586 | self.push_bytecode(0, w.pos); 587 | 588 | self.begin_scope(); 589 | if !self.compile_program(&w.body) { 590 | return false; 591 | } 592 | self.end_scope(); 593 | 594 | self.push_bytecode(JUMP, w.pos); 595 | self.push_bytecode(loop_start as Byte, w.pos); 596 | 597 | self.bytecodes[patch_loc] = self.bytecodes.len() as Byte; 598 | 599 | let list = self.break_jump_patches.pop().unwrap(); 600 | for i in list { 601 | self.bytecodes[i] = self.bytecodes.len() as Byte; 602 | } 603 | 604 | let list = self.next_jump_patches.pop().unwrap(); 605 | for i in list { 606 | self.bytecodes[i] = loop_start as Byte; 607 | } 608 | 609 | self.push_bytecode(LOAD_CONST, w.pos); 610 | self.push_bytecode(NULL_CONSTANT as Byte, w.pos); 611 | } 612 | 613 | Expression::Do(d) => { 614 | self.begin_scope(); 615 | if !self.compile_program(&d.body) { 616 | return false; 617 | } 618 | self.end_scope(); 619 | if self.bytecodes.last() == Some(&POP_LAST) { 620 | self.bytecodes.pop(); 621 | self.lines.pop(); 622 | } else { 623 | self.push_bytecode(LOAD_CONST, d.pos); 624 | self.push_bytecode(NULL_CONSTANT as Byte, d.pos); 625 | } 626 | } 627 | } 628 | 629 | true 630 | } 631 | 632 | pub fn optimize(&mut self) { 633 | let mut i = 0; 634 | while i < self.bytecodes.len() { 635 | let b = self.bytecodes[i]; 636 | if b == LOAD_LOCAL || b == LOAD_CONST { 637 | // LOAD xxxx POP 638 | // This has no effect at all, except at end of file. 639 | if i + 2 < self.bytecodes.len() - 1 && self.bytecodes[i + 2] == POP_LAST { 640 | for j in i..=i + 2 { 641 | self.bytecodes[j] = NOOP; 642 | } 643 | i += 3; 644 | continue; 645 | } 646 | } 647 | 648 | i += 1; 649 | i += operands(b); 650 | } 651 | } 652 | 653 | pub fn disassemble(&self) -> String { 654 | let mut s = String::new(); 655 | let mut pc = 0; 656 | while pc < self.bytecodes.len() { 657 | let old_pc = pc; 658 | let byte = self.bytecodes[pc]; 659 | 660 | if byte == NOOP { 661 | pc += 1; 662 | continue; 663 | } 664 | 665 | let mut args: Vec = vec![]; 666 | match byte { 667 | LOAD_CONST => { 668 | pc += 1; 669 | let address = self.bytecodes[pc] as usize; 670 | args.push(format!( 671 | "{:04x} ({})", 672 | address, 673 | self.constants[address].debug_format() 674 | )); 675 | } 676 | 677 | LOAD_LOCAL | MAKE_ARRAY | REPLACE | JUMP_IF_FALSE | JUMP_IF_FALSE_NO_POP | JUMP => { 678 | pc += 1; 679 | let address = self.bytecodes[pc] as usize; 680 | args.push(format!("{address:04x}")); 681 | } 682 | 683 | _ => (), 684 | } 685 | 686 | s.write_fmt(format_args!( 687 | "{:04x}: {} {}\n", 688 | old_pc, 689 | bytecode_name(byte), 690 | args.join(", ") 691 | )) 692 | .unwrap(); 693 | 694 | pc += 1; 695 | } 696 | s 697 | } 698 | } 699 | 700 | // Execution 701 | impl VM { 702 | #[inline(always)] 703 | fn read_bytecode(&mut self) -> Byte { 704 | self.pc += 1; 705 | self.bytecodes[self.pc - 1] 706 | } 707 | 708 | pub fn execute(&mut self) { 709 | while self.stack.len() > self.current_compiler.count { 710 | self.stack.pop(); 711 | } 712 | self.last_popped = None; 713 | self.pc = 0; 714 | 715 | let mut iteration = 0; 716 | 717 | while self.pc < self.bytecodes.len() { 718 | if iteration == GC_TRIGGER { 719 | self.gc_recollect(); 720 | iteration = 0; 721 | } 722 | if ALL_ALLOCATIONS.lock().unwrap().len() - unsafe { LAST_ALLOCATED } >= GC_FORCE_COLLECT 723 | { 724 | self.gc_recollect(); 725 | } 726 | 727 | unsafe { 728 | let bc = self.read_bytecode(); 729 | match bc { 730 | // General 731 | NOOP => { 732 | // Do nothing 733 | } 734 | 735 | POP_LAST => { 736 | let v = self.stack.pop(); 737 | if self.repl_mode { 738 | self.last_popped = v; 739 | } 740 | } 741 | 742 | REPLACE => { 743 | let index = self.read_bytecode(); 744 | let v = self.stack.pop().unwrap(); 745 | while self.stack.len() <= index as usize { 746 | self.stack 747 | .push(&mut self.constants[NULL_CONSTANT] as *mut Value); 748 | } 749 | self.stack[index as usize] = (*v).shallow_copy(); 750 | } 751 | 752 | SET_IN_PLACE => { 753 | let v = self.stack.pop().unwrap(); 754 | let p = self.stack.pop().unwrap(); 755 | (*v).regular_copy_to(p); 756 | self.stack.push(v); 757 | } 758 | 759 | LOAD_CONST => { 760 | let index = self.read_bytecode(); 761 | if self 762 | .stack 763 | .try_push(alloc_new_value(self.constants[index as usize].clone())) 764 | .is_err() 765 | { 766 | self.runtime_error("Stack overflow".to_string()); 767 | return; 768 | } 769 | } 770 | 771 | LOAD_LOCAL => { 772 | let index = self.read_bytecode(); 773 | self.stack.push(*self.stack.get(index as usize).unwrap()); 774 | } 775 | 776 | MAKE_ARRAY => { 777 | let length = self.read_bytecode() as usize; 778 | let mut array = Vec::with_capacity(length); 779 | for _ in 0..length { 780 | array.push(self.stack.pop().unwrap()); 781 | } 782 | self.stack 783 | .push_unchecked(alloc_new_value(Value::Array(array))); 784 | } 785 | 786 | JUMP_IF_FALSE => { 787 | let address = self.read_bytecode(); 788 | if !(*self.stack.pop().unwrap()).is_truthy() { 789 | self.pc = address as usize; 790 | } 791 | } 792 | 793 | JUMP_IF_FALSE_NO_POP => { 794 | let address = self.read_bytecode(); 795 | if !(**self.stack.last().unwrap()).is_truthy() { 796 | self.pc = address as usize; 797 | } 798 | } 799 | 800 | JUMP => { 801 | let address = self.read_bytecode(); 802 | self.pc = address as usize; 803 | } 804 | 805 | DEBUG_PRINT => { 806 | let value = self.stack.pop().unwrap(); 807 | println!("{}", (*value).debug_format()); 808 | } 809 | 810 | ECHO_PRINT => { 811 | let value = self.stack.pop().unwrap(); 812 | println!("{}", (*value).print_format()); 813 | } 814 | 815 | GET => { 816 | let index = self.stack.pop().unwrap(); 817 | let callee = self.stack.pop().unwrap(); 818 | 819 | let res = (*callee).get_element(index); 820 | if res.is_err() { 821 | self.runtime_error(res.err().unwrap()); 822 | return; 823 | } 824 | 825 | self.stack.push(res.unwrap()); 826 | } 827 | 828 | // Prefix operators 829 | UNARY_NEG => { 830 | let value = &*self.stack.pop().unwrap(); 831 | match value { 832 | Value::Bool(_) => { 833 | self.runtime_error( 834 | "Unsupported Unary operation: -bool (Hint: Use !bool instead)" 835 | .to_string(), 836 | ); 837 | return; 838 | } 839 | Value::Int(i) => { 840 | // We just popped an element, so there should be an empty space on the stack. 841 | self.stack.push_unchecked(alloc_new_value(Value::Int( 842 | i.saturating_neg(), 843 | ))); 844 | } 845 | Value::Float(f) => { 846 | self.stack.push_unchecked(alloc_new_value(Value::Float(-*f))); 847 | } 848 | _ => { 849 | self.runtime_error(format!( 850 | "Unsupported Unary operation: -{}", 851 | value.type_name() 852 | )); 853 | return; 854 | } 855 | } 856 | } 857 | 858 | UNARY_NOT => { 859 | let value = &*self.stack.pop().unwrap(); 860 | self.stack 861 | .push_unchecked(alloc_new_value(Value::Bool(!value.is_truthy()))); 862 | } 863 | 864 | // Infix operators 865 | BINARY_ADD => { 866 | let right = &*self.stack.pop().unwrap(); 867 | let left = &*self.stack.pop().unwrap(); 868 | let res = left.binary_add(right); 869 | if let BinOpResult::Ok(res) = res { 870 | self.stack.push_unchecked(res); 871 | } else if let BinOpResult::Error(e) = res { 872 | self.runtime_error(e); 873 | return; 874 | } else { 875 | self.runtime_error(format!( 876 | "Unsupported Binary operation: {} + {}", 877 | left.type_name(), 878 | right.type_name() 879 | )); 880 | return; 881 | } 882 | } 883 | 884 | BINARY_SUB => { 885 | let right = &*self.stack.pop().unwrap(); 886 | let left = &*self.stack.pop().unwrap(); 887 | let res = left.binary_sub(right); 888 | if let BinOpResult::Ok(res) = res { 889 | self.stack.push_unchecked(res); 890 | } else if let BinOpResult::Error(e) = res { 891 | self.runtime_error(e); 892 | return; 893 | } else { 894 | self.runtime_error(format!( 895 | "Unsupported Binary operation: {} - {}", 896 | left.type_name(), 897 | right.type_name() 898 | )); 899 | return; 900 | } 901 | } 902 | 903 | BINARY_MUL => { 904 | let right = &*self.stack.pop().unwrap(); 905 | let left = &*self.stack.pop().unwrap(); 906 | let res = left.binary_mul(right); 907 | if let BinOpResult::Ok(res) = res { 908 | self.stack.push_unchecked(res); 909 | } else if let BinOpResult::Error(e) = res { 910 | self.runtime_error(e); 911 | return; 912 | } else { 913 | self.runtime_error(format!( 914 | "Unsupported Binary operation: {} * {}", 915 | left.type_name(), 916 | right.type_name() 917 | )); 918 | return; 919 | } 920 | } 921 | 922 | BINARY_DIV => { 923 | let right = &*self.stack.pop().unwrap(); 924 | let left = &*self.stack.pop().unwrap(); 925 | let res = left.binary_div(right); 926 | if let BinOpResult::Ok(res) = res { 927 | self.stack.push_unchecked(res); 928 | } else if let BinOpResult::Error(e) = res { 929 | self.runtime_error(e); 930 | return; 931 | } else { 932 | self.runtime_error(format!( 933 | "Unsupported Binary operation: {} / {}", 934 | left.type_name(), 935 | right.type_name() 936 | )); 937 | return; 938 | } 939 | } 940 | 941 | BINARY_MOD => { 942 | let right = &*self.stack.pop().unwrap(); 943 | let left = &*self.stack.pop().unwrap(); 944 | let res = left.binary_mod(right); 945 | if let BinOpResult::Ok(res) = res { 946 | self.stack.push_unchecked(res); 947 | } else if let BinOpResult::Error(e) = res { 948 | self.runtime_error(e); 949 | return; 950 | } else { 951 | self.runtime_error(format!( 952 | "Unsupported Binary operation: {} % {}", 953 | left.type_name(), 954 | right.type_name() 955 | )); 956 | return; 957 | } 958 | } 959 | 960 | BINARY_EXP => { 961 | let right = &*self.stack.pop().unwrap(); 962 | let left = &*self.stack.pop().unwrap(); 963 | let res = left.binary_exp(right); 964 | if let BinOpResult::Ok(res) = res { 965 | self.stack.push_unchecked(res); 966 | } else if let BinOpResult::Error(e) = res { 967 | self.runtime_error(e); 968 | return; 969 | } else { 970 | self.runtime_error(format!( 971 | "Unsupported Binary operation: {} ** {}", 972 | left.type_name(), 973 | right.type_name() 974 | )); 975 | return; 976 | } 977 | } 978 | 979 | BINARY_EQ => { 980 | let right = &*self.stack.pop().unwrap(); 981 | let left = &*self.stack.pop().unwrap(); 982 | self.stack 983 | .push_unchecked(alloc_new_value(Value::Bool(left.is_equal(right)))); 984 | } 985 | 986 | BINARY_NE => { 987 | let right = &*self.stack.pop().unwrap(); 988 | let left = &*self.stack.pop().unwrap(); 989 | self.stack 990 | .push_unchecked(alloc_new_value(Value::Bool(!left.is_equal(right)))); 991 | } 992 | 993 | BINARY_LT => { 994 | let right = &*self.stack.pop().unwrap(); 995 | let left = &*self.stack.pop().unwrap(); 996 | let res = left.binary_lt(right); 997 | if let BinOpResult::Ok(res) = res { 998 | self.stack.push_unchecked(res); 999 | } else if let BinOpResult::Error(e) = res { 1000 | self.runtime_error(e); 1001 | return; 1002 | } else { 1003 | self.runtime_error(format!( 1004 | "Unsupported Binary operation: {} < {}", 1005 | left.type_name(), 1006 | right.type_name() 1007 | )); 1008 | return; 1009 | } 1010 | } 1011 | 1012 | BINARY_LE => { 1013 | let right = &*self.stack.pop().unwrap(); 1014 | let left = &*self.stack.pop().unwrap(); 1015 | let res = left.binary_le(right); 1016 | if let BinOpResult::Ok(res) = res { 1017 | self.stack.push_unchecked(res); 1018 | } else if let BinOpResult::Error(e) = res { 1019 | self.runtime_error(e); 1020 | return; 1021 | } else { 1022 | self.runtime_error(format!( 1023 | "Unsupported Binary operation: {} <= {}", 1024 | left.type_name(), 1025 | right.type_name() 1026 | )); 1027 | return; 1028 | } 1029 | } 1030 | 1031 | BINARY_GT => { 1032 | let right = &*self.stack.pop().unwrap(); 1033 | let left = &*self.stack.pop().unwrap(); 1034 | let res = left.binary_gt(right); 1035 | if let BinOpResult::Ok(res) = res { 1036 | self.stack.push_unchecked(res); 1037 | } else if let BinOpResult::Error(e) = res { 1038 | self.runtime_error(e); 1039 | return; 1040 | } else { 1041 | self.runtime_error(format!( 1042 | "Unsupported Binary operation: {} > {}", 1043 | left.type_name(), 1044 | right.type_name() 1045 | )); 1046 | return; 1047 | } 1048 | } 1049 | 1050 | BINARY_GE => { 1051 | let right = &*self.stack.pop().unwrap(); 1052 | let left = &*self.stack.pop().unwrap(); 1053 | let res = left.binary_ge(right); 1054 | if let BinOpResult::Ok(res) = res { 1055 | self.stack.push_unchecked(res); 1056 | } else if let BinOpResult::Error(e) = res { 1057 | self.runtime_error(e); 1058 | return; 1059 | } else { 1060 | self.runtime_error(format!( 1061 | "Unsupported Binary operation: {} >= {}", 1062 | left.type_name(), 1063 | right.type_name() 1064 | )); 1065 | return; 1066 | } 1067 | } 1068 | 1069 | // Invalid 1070 | _ => { 1071 | self.runtime_error(format!("Unknown bytecode: {bc}")); 1072 | return; 1073 | } 1074 | } 1075 | } 1076 | 1077 | iteration += 1; 1078 | } 1079 | 1080 | self.gc_recollect(); 1081 | } 1082 | 1083 | pub fn gc_recollect(&mut self) { 1084 | for item in self.stack.iter() { 1085 | mark(*item); 1086 | } 1087 | 1088 | if let Some(p) = self.last_popped { 1089 | mark(p); 1090 | } 1091 | 1092 | sweep(); 1093 | } 1094 | } 1095 | --------------------------------------------------------------------------------