├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── backlog-stuff.md ├── dominance.md ├── examples ├── basic.kb ├── explorer │ ├── Cargo.toml │ ├── res │ │ ├── OFL.txt │ │ └── SourceCodePro-Regular.ttf │ └── src │ │ ├── gui.rs │ │ ├── main.rs │ │ └── renderer.rs ├── fib.kb ├── life.kb ├── nested.kb ├── repl │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tuples.kb └── values.kb ├── ideas.md ├── main.md ├── readme.md └── src ├── bytecode.rs ├── compiler ├── analysis.rs ├── ast.rs ├── bbir.rs ├── bbir_builder.rs ├── codegen.rs ├── infer.rs ├── mod.rs ├── opt.rs ├── parser.rs └── transform.rs ├── index_vec.rs ├── lib.rs ├── packed_option.rs ├── value.rs └── vm.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | dump.md 3 | -------------------------------------------------------------------------------- /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 = "ahash" 7 | version = "0.7.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" 10 | dependencies = [ 11 | "getrandom", 12 | "once_cell", 13 | "version_check", 14 | ] 15 | 16 | [[package]] 17 | name = "ahash" 18 | version = "0.8.3" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" 21 | dependencies = [ 22 | "cfg-if", 23 | "once_cell", 24 | "version_check", 25 | ] 26 | 27 | [[package]] 28 | name = "arrayvec" 29 | version = "0.5.2" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" 32 | 33 | [[package]] 34 | name = "autocfg" 35 | version = "1.1.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 38 | 39 | [[package]] 40 | name = "bitflags" 41 | version = "1.3.2" 42 | source = "registry+https://github.com/rust-lang/crates.io-index" 43 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 44 | 45 | [[package]] 46 | name = "bumpalo" 47 | version = "3.12.0" 48 | source = "registry+https://github.com/rust-lang/crates.io-index" 49 | checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" 50 | 51 | [[package]] 52 | name = "cc" 53 | version = "1.0.79" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 56 | 57 | [[package]] 58 | name = "cfg-if" 59 | version = "1.0.0" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 62 | 63 | [[package]] 64 | name = "cmake" 65 | version = "0.1.49" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "db34956e100b30725f2eb215f90d4871051239535632f84fea3bc92722c66b7c" 68 | dependencies = [ 69 | "cc", 70 | ] 71 | 72 | [[package]] 73 | name = "convert_case" 74 | version = "0.4.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 77 | 78 | [[package]] 79 | name = "ctrlc" 80 | version = "3.2.4" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "1631ca6e3c59112501a9d87fd86f21591ff77acd31331e8a73f8d80a65bbdd71" 83 | dependencies = [ 84 | "nix 0.26.2", 85 | "windows-sys 0.42.0", 86 | ] 87 | 88 | [[package]] 89 | name = "cty" 90 | version = "0.2.2" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 93 | 94 | [[package]] 95 | name = "derive_more" 96 | version = "0.99.17" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 99 | dependencies = [ 100 | "convert_case", 101 | "proc-macro2", 102 | "quote", 103 | "rustc_version", 104 | "syn", 105 | ] 106 | 107 | [[package]] 108 | name = "dlib" 109 | version = "0.5.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" 112 | dependencies = [ 113 | "libloading", 114 | ] 115 | 116 | [[package]] 117 | name = "downcast-rs" 118 | version = "1.2.0" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 121 | 122 | [[package]] 123 | name = "errno" 124 | version = "0.2.8" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 127 | dependencies = [ 128 | "errno-dragonfly", 129 | "libc", 130 | "winapi", 131 | ] 132 | 133 | [[package]] 134 | name = "errno-dragonfly" 135 | version = "0.1.2" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 138 | dependencies = [ 139 | "cc", 140 | "libc", 141 | ] 142 | 143 | [[package]] 144 | name = "euclid" 145 | version = "0.22.7" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "b52c2ef4a78da0ba68fbe1fd920627411096d2ac478f7f4c9f3a54ba6705bade" 148 | dependencies = [ 149 | "num-traits", 150 | ] 151 | 152 | [[package]] 153 | name = "explorer" 154 | version = "0.1.0" 155 | dependencies = [ 156 | "fontdue", 157 | "kibi", 158 | "lru", 159 | "minifb", 160 | "ordered-float", 161 | "raqote", 162 | "windows-sys 0.45.0", 163 | ] 164 | 165 | [[package]] 166 | name = "fastrand" 167 | version = "1.9.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 170 | dependencies = [ 171 | "instant", 172 | ] 173 | 174 | [[package]] 175 | name = "fontdue" 176 | version = "0.7.2" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "6a62391ecb864cf12ed06b2af4eda2e609b97657950d6a8f06841b17726ab253" 179 | dependencies = [ 180 | "hashbrown 0.11.2", 181 | "ttf-parser", 182 | ] 183 | 184 | [[package]] 185 | name = "futures" 186 | version = "0.3.27" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" 189 | dependencies = [ 190 | "futures-channel", 191 | "futures-core", 192 | "futures-executor", 193 | "futures-io", 194 | "futures-sink", 195 | "futures-task", 196 | "futures-util", 197 | ] 198 | 199 | [[package]] 200 | name = "futures-channel" 201 | version = "0.3.27" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" 204 | dependencies = [ 205 | "futures-core", 206 | "futures-sink", 207 | ] 208 | 209 | [[package]] 210 | name = "futures-core" 211 | version = "0.3.27" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" 214 | 215 | [[package]] 216 | name = "futures-executor" 217 | version = "0.3.27" 218 | source = "registry+https://github.com/rust-lang/crates.io-index" 219 | checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" 220 | dependencies = [ 221 | "futures-core", 222 | "futures-task", 223 | "futures-util", 224 | ] 225 | 226 | [[package]] 227 | name = "futures-io" 228 | version = "0.3.27" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" 231 | 232 | [[package]] 233 | name = "futures-macro" 234 | version = "0.3.27" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" 237 | dependencies = [ 238 | "proc-macro2", 239 | "quote", 240 | "syn", 241 | ] 242 | 243 | [[package]] 244 | name = "futures-sink" 245 | version = "0.3.27" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" 248 | 249 | [[package]] 250 | name = "futures-task" 251 | version = "0.3.27" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" 254 | 255 | [[package]] 256 | name = "futures-util" 257 | version = "0.3.27" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" 260 | dependencies = [ 261 | "futures-channel", 262 | "futures-core", 263 | "futures-io", 264 | "futures-macro", 265 | "futures-sink", 266 | "futures-task", 267 | "memchr", 268 | "pin-project-lite", 269 | "pin-utils", 270 | "slab", 271 | ] 272 | 273 | [[package]] 274 | name = "getrandom" 275 | version = "0.2.8" 276 | source = "registry+https://github.com/rust-lang/crates.io-index" 277 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 278 | dependencies = [ 279 | "cfg-if", 280 | "libc", 281 | "wasi", 282 | ] 283 | 284 | [[package]] 285 | name = "hashbrown" 286 | version = "0.11.2" 287 | source = "registry+https://github.com/rust-lang/crates.io-index" 288 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 289 | dependencies = [ 290 | "ahash 0.7.6", 291 | ] 292 | 293 | [[package]] 294 | name = "hashbrown" 295 | version = "0.13.2" 296 | source = "registry+https://github.com/rust-lang/crates.io-index" 297 | checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" 298 | dependencies = [ 299 | "ahash 0.8.3", 300 | ] 301 | 302 | [[package]] 303 | name = "instant" 304 | version = "0.1.12" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 307 | dependencies = [ 308 | "cfg-if", 309 | "js-sys", 310 | "wasm-bindgen", 311 | "web-sys", 312 | ] 313 | 314 | [[package]] 315 | name = "io-lifetimes" 316 | version = "1.0.6" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "cfa919a82ea574332e2de6e74b4c36e74d41982b335080fa59d4ef31be20fdf3" 319 | dependencies = [ 320 | "libc", 321 | "windows-sys 0.45.0", 322 | ] 323 | 324 | [[package]] 325 | name = "js-sys" 326 | version = "0.3.61" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 329 | dependencies = [ 330 | "wasm-bindgen", 331 | ] 332 | 333 | [[package]] 334 | name = "kibi" 335 | version = "0.1.0" 336 | dependencies = [ 337 | "derive_more", 338 | ] 339 | 340 | [[package]] 341 | name = "lazy_static" 342 | version = "1.4.0" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 345 | 346 | [[package]] 347 | name = "libc" 348 | version = "0.2.139" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 351 | 352 | [[package]] 353 | name = "libloading" 354 | version = "0.7.4" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" 357 | dependencies = [ 358 | "cfg-if", 359 | "winapi", 360 | ] 361 | 362 | [[package]] 363 | name = "linux-raw-sys" 364 | version = "0.1.4" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 367 | 368 | [[package]] 369 | name = "log" 370 | version = "0.4.17" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 373 | dependencies = [ 374 | "cfg-if", 375 | ] 376 | 377 | [[package]] 378 | name = "lru" 379 | version = "0.10.0" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e" 382 | dependencies = [ 383 | "hashbrown 0.13.2", 384 | ] 385 | 386 | [[package]] 387 | name = "lyon_geom" 388 | version = "0.17.7" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "71d89ccbdafd83d259403e22061be27bccc3254bba65cdc5303250c4227c8c8e" 391 | dependencies = [ 392 | "arrayvec", 393 | "euclid", 394 | "num-traits", 395 | ] 396 | 397 | [[package]] 398 | name = "memchr" 399 | version = "2.5.0" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 402 | 403 | [[package]] 404 | name = "memoffset" 405 | version = "0.6.5" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 408 | dependencies = [ 409 | "autocfg", 410 | ] 411 | 412 | [[package]] 413 | name = "minifb" 414 | version = "0.24.0" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "c66a1fdd7e946fe33fe9725012e25836bba3655769bee9ee347cce7de3f396df" 417 | dependencies = [ 418 | "cc", 419 | "dlib", 420 | "futures", 421 | "instant", 422 | "js-sys", 423 | "lazy_static", 424 | "libc", 425 | "orbclient", 426 | "raw-window-handle 0.4.3", 427 | "serde", 428 | "serde_derive", 429 | "tempfile", 430 | "wasm-bindgen-futures", 431 | "wayland-client", 432 | "wayland-cursor", 433 | "wayland-protocols", 434 | "winapi", 435 | "x11-dl", 436 | ] 437 | 438 | [[package]] 439 | name = "minimal-lexical" 440 | version = "0.2.1" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 443 | 444 | [[package]] 445 | name = "nix" 446 | version = "0.24.3" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" 449 | dependencies = [ 450 | "bitflags", 451 | "cfg-if", 452 | "libc", 453 | "memoffset", 454 | ] 455 | 456 | [[package]] 457 | name = "nix" 458 | version = "0.26.2" 459 | source = "registry+https://github.com/rust-lang/crates.io-index" 460 | checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" 461 | dependencies = [ 462 | "bitflags", 463 | "cfg-if", 464 | "libc", 465 | "static_assertions", 466 | ] 467 | 468 | [[package]] 469 | name = "nom" 470 | version = "7.1.3" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 473 | dependencies = [ 474 | "memchr", 475 | "minimal-lexical", 476 | ] 477 | 478 | [[package]] 479 | name = "num-traits" 480 | version = "0.2.15" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 483 | dependencies = [ 484 | "autocfg", 485 | ] 486 | 487 | [[package]] 488 | name = "once_cell" 489 | version = "1.17.1" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 492 | 493 | [[package]] 494 | name = "orbclient" 495 | version = "0.3.43" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "974465c5e83cf9df05c1e4137b271d29035c902e39e5ad4c1939837e22160af8" 498 | dependencies = [ 499 | "cfg-if", 500 | "libc", 501 | "raw-window-handle 0.3.4", 502 | "redox_syscall", 503 | "sdl2", 504 | "sdl2-sys", 505 | "wasm-bindgen", 506 | "web-sys", 507 | ] 508 | 509 | [[package]] 510 | name = "ordered-float" 511 | version = "3.4.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "d84eb1409416d254e4a9c8fa56cc24701755025b458f0fcd8e59e1f5f40c23bf" 514 | dependencies = [ 515 | "num-traits", 516 | ] 517 | 518 | [[package]] 519 | name = "pin-project-lite" 520 | version = "0.2.9" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" 523 | 524 | [[package]] 525 | name = "pin-utils" 526 | version = "0.1.0" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 529 | 530 | [[package]] 531 | name = "pkg-config" 532 | version = "0.3.26" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" 535 | 536 | [[package]] 537 | name = "proc-macro2" 538 | version = "1.0.51" 539 | source = "registry+https://github.com/rust-lang/crates.io-index" 540 | checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" 541 | dependencies = [ 542 | "unicode-ident", 543 | ] 544 | 545 | [[package]] 546 | name = "quote" 547 | version = "1.0.23" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 550 | dependencies = [ 551 | "proc-macro2", 552 | ] 553 | 554 | [[package]] 555 | name = "raqote" 556 | version = "0.8.2" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "f5b6cb89f8be6a645e5834f3ad44a7960d12343d97b5b7fb07cb0365ae36aa2d" 559 | dependencies = [ 560 | "euclid", 561 | "lyon_geom", 562 | "sw-composite", 563 | "typed-arena", 564 | ] 565 | 566 | [[package]] 567 | name = "raw-window-handle" 568 | version = "0.3.4" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" 571 | dependencies = [ 572 | "libc", 573 | "raw-window-handle 0.4.3", 574 | ] 575 | 576 | [[package]] 577 | name = "raw-window-handle" 578 | version = "0.4.3" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" 581 | dependencies = [ 582 | "cty", 583 | ] 584 | 585 | [[package]] 586 | name = "redox_syscall" 587 | version = "0.2.16" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 590 | dependencies = [ 591 | "bitflags", 592 | ] 593 | 594 | [[package]] 595 | name = "repl" 596 | version = "0.1.0" 597 | dependencies = [ 598 | "ctrlc", 599 | "kibi", 600 | ] 601 | 602 | [[package]] 603 | name = "rustc_version" 604 | version = "0.4.0" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 607 | dependencies = [ 608 | "semver", 609 | ] 610 | 611 | [[package]] 612 | name = "rustix" 613 | version = "0.36.9" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" 616 | dependencies = [ 617 | "bitflags", 618 | "errno", 619 | "io-lifetimes", 620 | "libc", 621 | "linux-raw-sys", 622 | "windows-sys 0.45.0", 623 | ] 624 | 625 | [[package]] 626 | name = "scoped-tls" 627 | version = "1.0.1" 628 | source = "registry+https://github.com/rust-lang/crates.io-index" 629 | checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 630 | 631 | [[package]] 632 | name = "sdl2" 633 | version = "0.35.2" 634 | source = "registry+https://github.com/rust-lang/crates.io-index" 635 | checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a" 636 | dependencies = [ 637 | "bitflags", 638 | "lazy_static", 639 | "libc", 640 | "raw-window-handle 0.4.3", 641 | "sdl2-sys", 642 | ] 643 | 644 | [[package]] 645 | name = "sdl2-sys" 646 | version = "0.35.2" 647 | source = "registry+https://github.com/rust-lang/crates.io-index" 648 | checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0" 649 | dependencies = [ 650 | "cfg-if", 651 | "cmake", 652 | "libc", 653 | "version-compare", 654 | ] 655 | 656 | [[package]] 657 | name = "semver" 658 | version = "1.0.16" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" 661 | 662 | [[package]] 663 | name = "serde" 664 | version = "1.0.156" 665 | source = "registry+https://github.com/rust-lang/crates.io-index" 666 | checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" 667 | dependencies = [ 668 | "serde_derive", 669 | ] 670 | 671 | [[package]] 672 | name = "serde_derive" 673 | version = "1.0.156" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" 676 | dependencies = [ 677 | "proc-macro2", 678 | "quote", 679 | "syn", 680 | ] 681 | 682 | [[package]] 683 | name = "slab" 684 | version = "0.4.8" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" 687 | dependencies = [ 688 | "autocfg", 689 | ] 690 | 691 | [[package]] 692 | name = "smallvec" 693 | version = "1.10.0" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" 696 | 697 | [[package]] 698 | name = "static_assertions" 699 | version = "1.1.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 702 | 703 | [[package]] 704 | name = "sw-composite" 705 | version = "0.7.15" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "0b8d4f1dd38540e3f62c393ae78e874c94491c403025368183b018e3fb098b1f" 708 | 709 | [[package]] 710 | name = "syn" 711 | version = "1.0.107" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" 714 | dependencies = [ 715 | "proc-macro2", 716 | "quote", 717 | "unicode-ident", 718 | ] 719 | 720 | [[package]] 721 | name = "tempfile" 722 | version = "3.4.0" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" 725 | dependencies = [ 726 | "cfg-if", 727 | "fastrand", 728 | "redox_syscall", 729 | "rustix", 730 | "windows-sys 0.42.0", 731 | ] 732 | 733 | [[package]] 734 | name = "ttf-parser" 735 | version = "0.15.2" 736 | source = "registry+https://github.com/rust-lang/crates.io-index" 737 | checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" 738 | 739 | [[package]] 740 | name = "typed-arena" 741 | version = "2.0.2" 742 | source = "registry+https://github.com/rust-lang/crates.io-index" 743 | checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" 744 | 745 | [[package]] 746 | name = "unicode-ident" 747 | version = "1.0.6" 748 | source = "registry+https://github.com/rust-lang/crates.io-index" 749 | checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" 750 | 751 | [[package]] 752 | name = "version-compare" 753 | version = "0.1.1" 754 | source = "registry+https://github.com/rust-lang/crates.io-index" 755 | checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" 756 | 757 | [[package]] 758 | name = "version_check" 759 | version = "0.9.4" 760 | source = "registry+https://github.com/rust-lang/crates.io-index" 761 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 762 | 763 | [[package]] 764 | name = "wasi" 765 | version = "0.11.0+wasi-snapshot-preview1" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 768 | 769 | [[package]] 770 | name = "wasm-bindgen" 771 | version = "0.2.84" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 774 | dependencies = [ 775 | "cfg-if", 776 | "wasm-bindgen-macro", 777 | ] 778 | 779 | [[package]] 780 | name = "wasm-bindgen-backend" 781 | version = "0.2.84" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 784 | dependencies = [ 785 | "bumpalo", 786 | "log", 787 | "once_cell", 788 | "proc-macro2", 789 | "quote", 790 | "syn", 791 | "wasm-bindgen-shared", 792 | ] 793 | 794 | [[package]] 795 | name = "wasm-bindgen-futures" 796 | version = "0.4.34" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" 799 | dependencies = [ 800 | "cfg-if", 801 | "js-sys", 802 | "wasm-bindgen", 803 | "web-sys", 804 | ] 805 | 806 | [[package]] 807 | name = "wasm-bindgen-macro" 808 | version = "0.2.84" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 811 | dependencies = [ 812 | "quote", 813 | "wasm-bindgen-macro-support", 814 | ] 815 | 816 | [[package]] 817 | name = "wasm-bindgen-macro-support" 818 | version = "0.2.84" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 821 | dependencies = [ 822 | "proc-macro2", 823 | "quote", 824 | "syn", 825 | "wasm-bindgen-backend", 826 | "wasm-bindgen-shared", 827 | ] 828 | 829 | [[package]] 830 | name = "wasm-bindgen-shared" 831 | version = "0.2.84" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 834 | 835 | [[package]] 836 | name = "wayland-client" 837 | version = "0.29.5" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" 840 | dependencies = [ 841 | "bitflags", 842 | "downcast-rs", 843 | "libc", 844 | "nix 0.24.3", 845 | "scoped-tls", 846 | "wayland-commons", 847 | "wayland-scanner", 848 | "wayland-sys", 849 | ] 850 | 851 | [[package]] 852 | name = "wayland-commons" 853 | version = "0.29.5" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" 856 | dependencies = [ 857 | "nix 0.24.3", 858 | "once_cell", 859 | "smallvec", 860 | "wayland-sys", 861 | ] 862 | 863 | [[package]] 864 | name = "wayland-cursor" 865 | version = "0.29.5" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" 868 | dependencies = [ 869 | "nix 0.24.3", 870 | "wayland-client", 871 | "xcursor", 872 | ] 873 | 874 | [[package]] 875 | name = "wayland-protocols" 876 | version = "0.29.5" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" 879 | dependencies = [ 880 | "bitflags", 881 | "wayland-client", 882 | "wayland-commons", 883 | "wayland-scanner", 884 | ] 885 | 886 | [[package]] 887 | name = "wayland-scanner" 888 | version = "0.29.5" 889 | source = "registry+https://github.com/rust-lang/crates.io-index" 890 | checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" 891 | dependencies = [ 892 | "proc-macro2", 893 | "quote", 894 | "xml-rs", 895 | ] 896 | 897 | [[package]] 898 | name = "wayland-sys" 899 | version = "0.29.5" 900 | source = "registry+https://github.com/rust-lang/crates.io-index" 901 | checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" 902 | dependencies = [ 903 | "dlib", 904 | "lazy_static", 905 | "pkg-config", 906 | ] 907 | 908 | [[package]] 909 | name = "web-sys" 910 | version = "0.3.61" 911 | source = "registry+https://github.com/rust-lang/crates.io-index" 912 | checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" 913 | dependencies = [ 914 | "js-sys", 915 | "wasm-bindgen", 916 | ] 917 | 918 | [[package]] 919 | name = "winapi" 920 | version = "0.3.9" 921 | source = "registry+https://github.com/rust-lang/crates.io-index" 922 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 923 | dependencies = [ 924 | "winapi-i686-pc-windows-gnu", 925 | "winapi-x86_64-pc-windows-gnu", 926 | ] 927 | 928 | [[package]] 929 | name = "winapi-i686-pc-windows-gnu" 930 | version = "0.4.0" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 933 | 934 | [[package]] 935 | name = "winapi-x86_64-pc-windows-gnu" 936 | version = "0.4.0" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 939 | 940 | [[package]] 941 | name = "windows-sys" 942 | version = "0.42.0" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 945 | dependencies = [ 946 | "windows_aarch64_gnullvm", 947 | "windows_aarch64_msvc", 948 | "windows_i686_gnu", 949 | "windows_i686_msvc", 950 | "windows_x86_64_gnu", 951 | "windows_x86_64_gnullvm", 952 | "windows_x86_64_msvc", 953 | ] 954 | 955 | [[package]] 956 | name = "windows-sys" 957 | version = "0.45.0" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 960 | dependencies = [ 961 | "windows-targets", 962 | ] 963 | 964 | [[package]] 965 | name = "windows-targets" 966 | version = "0.42.1" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 969 | dependencies = [ 970 | "windows_aarch64_gnullvm", 971 | "windows_aarch64_msvc", 972 | "windows_i686_gnu", 973 | "windows_i686_msvc", 974 | "windows_x86_64_gnu", 975 | "windows_x86_64_gnullvm", 976 | "windows_x86_64_msvc", 977 | ] 978 | 979 | [[package]] 980 | name = "windows_aarch64_gnullvm" 981 | version = "0.42.1" 982 | source = "registry+https://github.com/rust-lang/crates.io-index" 983 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 984 | 985 | [[package]] 986 | name = "windows_aarch64_msvc" 987 | version = "0.42.1" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 990 | 991 | [[package]] 992 | name = "windows_i686_gnu" 993 | version = "0.42.1" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 996 | 997 | [[package]] 998 | name = "windows_i686_msvc" 999 | version = "0.42.1" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 1002 | 1003 | [[package]] 1004 | name = "windows_x86_64_gnu" 1005 | version = "0.42.1" 1006 | source = "registry+https://github.com/rust-lang/crates.io-index" 1007 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 1008 | 1009 | [[package]] 1010 | name = "windows_x86_64_gnullvm" 1011 | version = "0.42.1" 1012 | source = "registry+https://github.com/rust-lang/crates.io-index" 1013 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 1014 | 1015 | [[package]] 1016 | name = "windows_x86_64_msvc" 1017 | version = "0.42.1" 1018 | source = "registry+https://github.com/rust-lang/crates.io-index" 1019 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 1020 | 1021 | [[package]] 1022 | name = "x11-dl" 1023 | version = "2.21.0" 1024 | source = "registry+https://github.com/rust-lang/crates.io-index" 1025 | checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" 1026 | dependencies = [ 1027 | "libc", 1028 | "once_cell", 1029 | "pkg-config", 1030 | ] 1031 | 1032 | [[package]] 1033 | name = "xcursor" 1034 | version = "0.3.4" 1035 | source = "registry+https://github.com/rust-lang/crates.io-index" 1036 | checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" 1037 | dependencies = [ 1038 | "nom", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "xml-rs" 1043 | version = "0.8.4" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" 1046 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "kibi" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | members = [ 8 | "examples/*", 9 | ] 10 | 11 | 12 | [dependencies] 13 | derive_more = "0.99.17" 14 | 15 | [profile.release] 16 | debug = true 17 | 18 | -------------------------------------------------------------------------------- /backlog-stuff.md: -------------------------------------------------------------------------------- 1 | - todo: 2 | - revise mouse events. 3 | - bubbling. 4 | - multiple hovered widgets, one hot widget. 5 | - oh, events are propagated to ancestors only! 6 | - and if you have a child overlapping a sibling, the sibling doesn't get the event. 7 | - that's how overlays prevent `:hover` on the content beneath them. 8 | - so let's do dom style hovering. 9 | - parent is hovered, when child is hovered. 10 | - children are tested in reverse, stop on first hit. 11 | - last widget in hover list is the "hot" widget. 12 | - pointer events on by default. 13 | - events bubble to parents, unless propagation is stopped. 14 | - events have flag, whether current widget was target of event. 15 | - things like click are also propagated, even for buttons, but event handlers can of course stop propagation for specific buttons. common case util: `clicked_directly: clicked and is_target`. 16 | - consider making event (data) an enum. 17 | - be more official about "one event at a time". 18 | - otherwise stop_propagation could stop multiple events. 19 | - and it's just generally more intuitive. 20 | - how to make buttons show, when line is hovered? 21 | - could duplicate state. 22 | - could have `hidden` prop and set once have line events. 23 | - could have `widget_box_ex`, which passes a `None` event to the closure (with the hover state). 24 | - windows: 25 | - clipping. 26 | - scrolling. 27 | - code view as window with scrolling (optional, otherwise grow). 28 | - sub-tree skipping: 29 | - event mask. 30 | - pass `skip: bool` to widget_box. 31 | - also sub-tree `no_skip` override -> can set on root node for full update, ignores local caches. useful for stuff like theme, so you don't need fine grained dependency tracking. 32 | - gui skips child fn if there are no events in the sub-tree. 33 | - key must not be counter. 34 | - compute render children in intrinsic pass. 35 | - not sure this is actually all that useful. 36 | - things like decorations change pretty often, and they're somewhat expensive to cache (cause you'd need to diff the decos for each line). 37 | - so virtualization would be much more effective. 38 | - though could still help with layout & allocation overhead. 39 | - ui stuff: 40 | - box-sizing. 41 | - clip mouse, unless captured. 42 | - hit test on mouse up (cause capture may have prevented hover). 43 | - clip. 44 | - event bubbling. 45 | - change style props after widget creation (active highlight). 46 | - text background fill. 47 | - text widgets in text layout's render children list. 48 | - hit_test_range in `draw` before drawing text layout. 49 | - inline widgets. 50 | - text layout inline objects & layout function (store glyph advances). 51 | - text layout widget render children list (layout inline widgets first). 52 | - flex grow. 53 | - flex justify gaps. 54 | - cache calls to layout_pass: store given_size on widget (clear in intrinsic pass). 55 | - grid layout. 56 | - overlays? 57 | - for drop downs, tool tips, etc. 58 | - are like regular children (in some parent down the tree). 59 | - but are don't affect layout, 60 | - positioned relative to some other widget, 61 | - drawn on top of all other widgets. 62 | - text layout word wrapping. 63 | 64 | - todo: debug info. 65 | - node info for pc. 66 | - own value vs aliases. 67 | - store own values (opt) & node for each op. 68 | - well, own value is always node id. 69 | - so replace source info with node id. 70 | - then have flag for "is value": if true, the instr produces the value of its node id. 71 | - control flow info. 72 | - basic blocks + semantic annotations. 73 | - loop header/body. 74 | - if then/else branch. 75 | - do block body. 76 | - function body. 77 | - value query. 78 | - call stack. 79 | - getting register values from other stack frames. 80 | - extensible stack for host functions -> regs are the "slots". 81 | 82 | 83 | - general backlog: 84 | - module state. 85 | - inout params. 86 | - can't pass globals. 87 | - tuples. 88 | - destructuring. 89 | - tuple ret. 90 | - optional args & varargs. 91 | - del. 92 | - table -> map. 93 | - usable host api. 94 | - proper gc & memory allocation. 95 | - closures. 96 | - kbtf. 97 | - wasm-like transfer format. 98 | - unlimited registers (but limit to 127 for now). 99 | - later: typed registers. 100 | - easy to validate. 101 | - can be used for unchecked operations. 102 | - vm optimizations like inline caches are not exposed. 103 | - "close captures" instruction. 104 | 105 | - compiler backlog: 106 | - ast child visitors. 107 | - compile: 108 | - assert cfg has no critical edges. 109 | - validation: 110 | - phi maps & stmt lists uniquely owned. 111 | - return error instead of panicking. 112 | - trace logging: 113 | - for debugging & giving insight into what the compiler does. 114 | - debug info: 115 | - accurate source ranges. 116 | - local mapping. 117 | - no-elim stmt flag. 118 | - default return is empty tuple. 119 | - tuple return optimization. 120 | - parser: 121 | - token array. 122 | - remove eof token. 123 | - features: 124 | - numbers. 125 | - tables. 126 | - break, continue, return. 127 | - optional chaining. 128 | - impl-opt: 129 | - function::gc. 130 | - typed bb/stmt bit sets. 131 | - phi2 & call0-4. 132 | - track phi begin/end on bbs. 133 | - cssa: update terminator args. 134 | - tests. 135 | 136 | 137 | - vm backlog: 138 | - booleans. 139 | - integers. 140 | - or_else. 141 | - get_field. 142 | - function env capture. 143 | - impl-opt: 144 | - 16 byte align `Value`. 145 | - generic ops: inline(always). 146 | - stack & instruction "pointers". 147 | - hash slot prediction. 148 | - hidden classes? 149 | - tests. 150 | 151 | - low priority backlog: 152 | - `IfBlock` -> `ValueBlock` use for functions? 153 | - `_` for discarding values. 154 | - and_then: opposite of `??`. what operator? 155 | - maybe `&&` for and_then, `||` for or_else. 156 | - cssa: replace terminator args. 157 | - repl: support multiple stmts. 158 | - print value of all of them. 159 | 160 | - ideas: 161 | - reverse debugging: 162 | - start with snapshots + outputs of external functions. 163 | - perf wise, that would be a bit like a stop-the-world gc - not ideal, but good enough, maybe. 164 | - cancel snapshot if there's a non-serializable host function in the call stack. 165 | - resumable host code: 166 | - enables pausing code anywhere -> preemptive multi-tasking. 167 | - maybe using async for ergonomics. 168 | - issue: future captures `&mut Vm`. 169 | - serializable state machine would be better. 170 | - no unsafe required. 171 | - serializable. 172 | - also keep non-state-machine host functions, as they're faster (require no allocation for state). 173 | - related: host closures. 174 | - fast strcmp: ptr_eq first. 175 | - jump tables. 176 | - runtime, cross-module optimizations: 177 | - eg: inlining. 178 | - can always flush the optimizations, if module is reloaded or exported function is changed. 179 | - typed user data. 180 | - instanceof instruction. 181 | - coverage instruction. 182 | - with inline counter. 183 | - freezing & read-only refs. 184 | - get/set object properties. 185 | - `do` block constraints: 186 | - restrict variable "capture": `[&a, &mut b]`. 187 | - if use that, can't return from outer function either. maybe. 188 | 189 | 190 | -------------------------------------------------------------------------------- /dominance.md: -------------------------------------------------------------------------------- 1 | 2 | - defs: 3 | - dominates(a, b): all paths from entry to b flow through a. 4 | - strictly dominates(a, b): dom(a, b) ∧ a ≠ b 5 | - immediately dominates(a, b): sdom(a, b) ∧ ¬∃ c, sdom(a, c) ∧ sdom(c, b) 6 | - dominance frontier(a): { b, ¬sdom(a, b) ∧ ∃ p: pred(b), dom(a, p) } 7 | 8 | 9 | - compute idom: 10 | ``` 11 | doms = { nil for bb in bbs } 12 | doms[bb0] = bb0 13 | 14 | while changed: 15 | for bb in bbs.reverse_post_order: 16 | if bb == bb0: continue 17 | 18 | new_idom = first pred p, where doms[p] != nil 19 | for p in bb.pred: 20 | if p != new_idom and doms[p] != nil: 21 | new_idom = intersect(p, new_idom) 22 | 23 | doms[bb] = new_dom 24 | 25 | fn rpi(a) = bbs.reverse_post_order.find_index(a) 26 | 27 | fn intersect(a, b): 28 | x = a 29 | y = b 30 | while x != y: 31 | while rpi(x) < rpi(y): 32 | x = doms[x] 33 | while rpi(y) < rpi(x: 34 | y = doms[y] 35 | return x 36 | ``` 37 | - how the heck does it work? 38 | - 1) nodes higher in the dominance tree have higher postorder indices. 39 | - root is entry; entry postorder index is max. 40 | - for other nodes: something like "lower index node can only have edge to higher index node, if there's a loop. and then, the lower index node can't dominate." 41 | - 2) dom is union of idoms. 42 | - `∀ b, dom(b) = { b, idom(b), idom(idom(b)), ..., bb0 }` 43 | - similar logic: if there's another dom, that's not in the chain, there must be another path from the root to the node. so it's not a dom. 44 | - 3) the dom equation: 45 | - `dom[a] = {a} ∪ ∩(dom[p] for p in preds(a))`. 46 | - `dom[a] = {a} ∪ ∩({p, idom(p), ...} for p in preds(a))`. 47 | - intersection is assoc/comm. 48 | - intersection looks like: `{p, idom(p), ...} ∩ {q, idom(q), ...}`. 49 | - so this will be some sequence of idoms. 50 | - meaning, once we hit the first common idom, all following ones will be the same. 51 | - why does the postorder index comparison work? 52 | - all indices in a sub-tree are less. 53 | - but doms isn't idom! why don't we miss anything? 54 | - we start at the node itself. 55 | - and do that in each iteration. 56 | - other observations: 57 | - we always start with a predecessor, then move up. 58 | - this seems to guarantee the immediacy. 59 | - in particular: if indeg(bb) < 2, idom(bb) = bb.pred. 60 | - for indeg(bb) >= 2, we use intersection to find the first common ancestor. 61 | - when does the algo require multiple iters? 62 | - looks like that happens for irreducible graphs. 63 | - eg: initial iter may find one loop entry as the idom. in next iter, other preds are processed & intersection will go higher up. 64 | 65 | 66 | - compute df: 67 | ``` 68 | df = [ [] for bb in bbs ] 69 | 70 | for bb in bbs: 71 | for p in bb.preds: 72 | at = p 73 | while at ≠ idom(bb): 74 | df[at].add(bb) 75 | at = idom(at) 76 | ``` 77 | 78 | - convert to ssa: 79 | ``` 80 | def insert_phis(): 81 | for var in vars: 82 | for d in defs(var): 83 | for bb in df(d.bb): 84 | add once to bb: `var = phi [(p, var) for p in bb.preds]` 85 | and add this new def to defs(var) 86 | 87 | def rename_vars(): 88 | def visit(bb, new_names): 89 | -- update var names. 90 | for stmt in bb: 91 | replace args with new_names[arg] 92 | replace dst with new_dst 93 | -- immutable update. 94 | new_names[dst].push(new_dst) 95 | 96 | -- propagate to successors (dominance frontier) 97 | for s in bb.succ: 98 | update phi reads 99 | 100 | -- propagate to dominated nodes. 101 | -- these don't have phi nodes & can read directly from new_names. 102 | for d in idom_by(bb): 103 | rename(d, new_names) 104 | 105 | visit(bb0, [ [var] for var in vars ]) 106 | ``` 107 | -------------------------------------------------------------------------------- /examples/basic.kb: -------------------------------------------------------------------------------- 1 | println("hello, weirdo") 2 | 3 | 4 | 5 | let a = 6 6 | let b = 9 7 | if a < b: 8 | println(10*a + b) 9 | end 10 | 11 | 12 | 13 | do: 14 | fn abc(a, b, c): 15 | print("a: ") 16 | print(a) 17 | print(", c: ") 18 | print(c) 19 | println("") 20 | end 21 | abc(1, 2, 3) 22 | end 23 | 24 | 25 | 26 | var foo = 5 27 | foo += 3 28 | print("foo: ") 29 | println(foo) 30 | 31 | 32 | 33 | fn fib(n): 34 | var a = 0 35 | var b = 1 36 | var i = 0 37 | while i < n: 38 | (a, b) = (b, a+b) 39 | i += 1 40 | end 41 | return a 42 | end 43 | 44 | var i = 0 45 | while i < 15: 46 | print("fib(") 47 | print(i) 48 | print(") = ") 49 | println(fib(i)) 50 | i += 1 51 | end 52 | 53 | 54 | 55 | let list = [1, 5, 4, "hi", nil] 56 | list[5] := list[1] + list[2] -- `list[5] = ...` would error. 57 | 58 | println(list[0]) 59 | println(list[1]) 60 | println(list[2]) 61 | println(list[3]) 62 | println(list[4]) 63 | println(list[5]) 64 | 65 | 66 | 67 | fn cycle(): 68 | var a = 1 69 | var b = 2 70 | var c = 3 71 | var n = 0 72 | while a < 3: 73 | let d = a 74 | a = b 75 | b = c 76 | c = d 77 | n += 1 78 | end 79 | print(a) 80 | print(b) 81 | print(c) 82 | print(" ") 83 | println(n) 84 | end 85 | cycle() 86 | 87 | 88 | fn whiler(): 89 | print("whiler: ") 90 | while true: 91 | break 92 | undefined() 93 | end 94 | print("1, ") 95 | 96 | var running = true 97 | 'yee while running: 98 | print("x, ") 99 | while true: 100 | print("y, ") 101 | break 102 | end 103 | print("z, ") 104 | running = false 105 | while true: 106 | continue 'yee 107 | undefined() 108 | end 109 | undefined() 110 | end 111 | println("kk.") 112 | end 113 | whiler() 114 | 115 | fn doer(): 116 | print("doer: ") 117 | do: 118 | break 119 | undefined() 120 | end 121 | print("1, ") 122 | 123 | let a = do: 124 | print("x, ") 125 | break break 42 126 | undefined() 127 | end 128 | print(a) 129 | print(", ") 130 | 131 | let b = do: 132 | if a > 0: 133 | do: 134 | print("4, ") 135 | break 136 | undefined() 137 | end 138 | break 69 139 | end 140 | undefined() 141 | end 142 | print(b) 143 | print(", ") 144 | 145 | let c = 146 | if b > 0 do: 147 | print("c, ") 148 | break 7 149 | undefined() 150 | else: 151 | 49 152 | end 153 | print(c) 154 | print(", ") 155 | 156 | let d = 'd do: 157 | while true: 158 | do: 159 | break 'd 8 160 | break 161 | continue 162 | end 163 | undefined() 164 | end 165 | end 166 | print(d) 167 | print(", ") 168 | 169 | println("kk.") 170 | end 171 | doer() 172 | 173 | 174 | -------------------------------------------------------------------------------- /examples/explorer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "explorer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | kibi = { path = "../.." } 8 | minifb = "0.24.0" 9 | raqote = { version = "0.8.2", default-features = false } 10 | fontdue = "0.7.2" 11 | lru = "0.10.0" 12 | ordered-float = "3.4.0" 13 | 14 | 15 | [target.'cfg(windows)'.dependencies] 16 | windows-sys = { version = "0.45.0", features = ["Win32_Media"] } 17 | 18 | -------------------------------------------------------------------------------- /examples/explorer/res/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /examples/explorer/res/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leddoo/kibi/e27e8d8c071d490a47f6442bf465561afdf40ab7/examples/explorer/res/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /examples/explorer/src/renderer.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use core::cell::RefCell; 3 | use raqote::DrawTarget; 4 | use fontdue::Font; 5 | use lru::LruCache; 6 | use ordered_float::NotNan; 7 | 8 | 9 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 10 | struct ClipRect { 11 | x0: i32, 12 | y0: i32, 13 | x1: i32, 14 | y1: i32, 15 | } 16 | 17 | impl ClipRect { 18 | #[inline(always)] 19 | fn new(x0: i32, y0: i32, x1: i32, y1: i32) -> ClipRect { ClipRect { x0, y0, x1, y1 } } 20 | } 21 | 22 | 23 | pub struct Renderer { 24 | target: DrawTarget, 25 | fonts: FontCtx, 26 | 27 | clip: ClipRect, 28 | clip_stack: Vec, 29 | } 30 | 31 | impl Renderer { 32 | pub fn new(fonts: &FontCtx) -> Renderer { 33 | Renderer { 34 | target: DrawTarget::new(1, 1), 35 | fonts: fonts.clone(), 36 | clip: ClipRect { x0: 0, y0: 0, x1: 1, y1: 1 }, 37 | clip_stack: vec![], 38 | } 39 | } 40 | 41 | #[allow(dead_code)] // @temp 42 | #[inline(always)] 43 | pub fn fonts(&self) -> &FontCtx { &self.fonts } 44 | 45 | 46 | #[inline(always)] 47 | pub fn data(&self) -> &[u32] { self.target.get_data() } 48 | 49 | #[inline(always)] 50 | pub fn width(&self) -> i32 { self.target.width() } 51 | 52 | #[inline(always)] 53 | pub fn height(&self) -> i32 { self.target.height() } 54 | 55 | 56 | pub fn has_clip(&self) -> bool { 57 | !self.clip_stack.is_empty() 58 | || self.clip != ClipRect::new(0, 0, self.width(), self.height()) 59 | } 60 | 61 | pub fn set_size(&mut self, w: u32, h: u32) { 62 | let w = w as i32; 63 | let h = h as i32; 64 | if w != self.target.width() || h != self.target.height() { 65 | assert!(self.has_clip() == false); 66 | self.target = DrawTarget::new(w, h); 67 | self.clip = ClipRect::new(0, 0, w, h); 68 | } 69 | } 70 | 71 | 72 | pub fn push_clip_rect(&mut self, x0: i32, y0: i32, x1: i32, y1: i32) { 73 | self.clip_stack.push(self.clip); 74 | self.clip = ClipRect::new( 75 | x0.clamp(self.clip.x0, self.clip.x1), 76 | y0.clamp(self.clip.y0, self.clip.y1), 77 | x1.clamp(self.clip.x0, self.clip.x1), 78 | y1.clamp(self.clip.y0, self.clip.y1)); 79 | } 80 | 81 | pub fn pop_clip_rect(&mut self) { 82 | self.clip = self.clip_stack.pop().unwrap(); 83 | } 84 | 85 | 86 | pub fn fill_rect_abs_opaque(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: u32) { 87 | let tw = self.target.width(); 88 | 89 | let x0 = x0.clamp(self.clip.x0, self.clip.x1) as usize; 90 | let y0 = y0.clamp(self.clip.y0, self.clip.y1) as usize; 91 | let x1 = x1.clamp(self.clip.x0, self.clip.x1) as usize; 92 | let y1 = y1.clamp(self.clip.y0, self.clip.y1) as usize; 93 | 94 | if x0 == x1 || y0 == y1 { return } 95 | 96 | let buf = self.target.get_data_mut(); 97 | 98 | let mut offset = y0 * tw as usize; 99 | for _ in y0..y1 { 100 | let mut x = x0; 101 | 102 | let offset_ptr = unsafe { buf.as_mut_ptr().add(offset) }; 103 | 104 | let x1_8 = x0 + ((x1 - x0) & !(8 - 1)); 105 | while x < x1_8 { unsafe { 106 | // this is for fast drawing in debug mode. 107 | // deal with it. 108 | offset_ptr.add(x + 0).write(color); 109 | offset_ptr.add(x + 1).write(color); 110 | offset_ptr.add(x + 2).write(color); 111 | offset_ptr.add(x + 3).write(color); 112 | offset_ptr.add(x + 4).write(color); 113 | offset_ptr.add(x + 5).write(color); 114 | offset_ptr.add(x + 6).write(color); 115 | offset_ptr.add(x + 7).write(color); 116 | x += 8; 117 | }} 118 | while x < x1 { 119 | buf[offset + x] = color; 120 | x += 1; 121 | } 122 | 123 | offset += tw as usize; 124 | } 125 | } 126 | 127 | #[allow(dead_code)] // @temp 128 | pub fn fill_rect_abs(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: u32) { 129 | if color_unpack(color).3 == 255 { 130 | self.fill_rect_abs_opaque(x0, y0, x1, y1, color); 131 | return; 132 | } 133 | 134 | let tw = self.target.width(); 135 | 136 | let x0 = x0.clamp(self.clip.x0, self.clip.x1) as usize; 137 | let y0 = y0.clamp(self.clip.y0, self.clip.y1) as usize; 138 | let x1 = x1.clamp(self.clip.x0, self.clip.x1) as usize; 139 | let y1 = y1.clamp(self.clip.y0, self.clip.y1) as usize; 140 | 141 | if x0 == x1 || y0 == y1 { return } 142 | 143 | let buf = self.target.get_data_mut(); 144 | 145 | let mut offset = y0 * tw as usize; 146 | for _ in y0..y1 { 147 | for x in x0..x1 { 148 | let dst = &mut buf[offset + x]; 149 | *dst = over(color, *dst); 150 | } 151 | 152 | offset += tw as usize; 153 | } 154 | } 155 | 156 | pub fn clear(&mut self, r: u8, g: u8, b: u8) { 157 | self.fill_rect_abs_opaque(0, 0, self.width(), self.height(), color_pack_u8(r, g, b, 255)); 158 | } 159 | 160 | 161 | pub fn draw_mask_abs(&mut self, x: i32, y: i32, mask: &[u8], mask_w: u32, mask_h: u32, color: u32) { 162 | let x0 = x; 163 | let y0 = y; 164 | let x1 = x0 + mask_w as i32; 165 | let y1 = y0 + mask_h as i32; 166 | 167 | let tw = self.target.width(); 168 | let buf = self.target.get_data_mut(); 169 | 170 | let x0c = x0.clamp(self.clip.x0, self.clip.x1); 171 | let y0c = y0.clamp(self.clip.y0, self.clip.y1); 172 | let x1c = x1.clamp(self.clip.x0, self.clip.x1); 173 | let y1c = y1.clamp(self.clip.y0, self.clip.y1); 174 | 175 | let (r, g, b, a) = color_unpack(color); 176 | 177 | for y in y0c..y1c { 178 | for x in x0c..x1c { 179 | let cx = (x - x0) as usize; 180 | let cy = (y - y0) as usize; 181 | let c = mask[cy * mask_w as usize + cx] as u32; 182 | 183 | let src = color_pack(( 184 | c*r >> 8, 185 | c*g >> 8, 186 | c*b >> 8, 187 | c*a >> 8)); 188 | 189 | let value = &mut buf[(y*tw + x) as usize]; 190 | *value = over(src, *value); 191 | } 192 | } 193 | } 194 | 195 | pub fn draw_text_layout_abs(&mut self, x0: i32, y0: i32, layout: &TextLayout) { 196 | let fonts = self.fonts.clone(); 197 | let mut fonts = fonts.inner.borrow_mut(); 198 | 199 | let mut y0 = y0 as f32; 200 | for line in &layout.lines { 201 | let baseline = (y0 + line.max_ascent) as i32; 202 | 203 | let mut x0 = x0 as f32; 204 | for span in &layout.spans[line.span_range()] { 205 | for glyph in &layout.glyphs[span.glyph_range()] { 206 | let (metrics, mask) = fonts.glyph_mask(span.face_id, span.font_size, glyph.index); 207 | self.draw_mask_abs( 208 | x0 as i32 + glyph.dx as i32, 209 | baseline + glyph.dy as i32, 210 | &mask, metrics.width as u32, metrics.height as u32, 211 | span.color, 212 | ); 213 | x0 += glyph.advance; 214 | } 215 | } 216 | 217 | y0 += line.height; 218 | } 219 | } 220 | } 221 | 222 | 223 | 224 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 225 | pub struct FaceId(u32); 226 | 227 | impl FaceId { 228 | pub const DEFAULT: FaceId = FaceId(0); 229 | 230 | #[inline(always)] 231 | pub fn usize(self) -> usize { self.0 as usize } 232 | } 233 | 234 | impl Default for FaceId { #[inline(always)] fn default() -> FaceId { FaceId::DEFAULT } } 235 | 236 | 237 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 238 | pub struct Bold(pub bool); 239 | 240 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 241 | pub struct Italic(pub bool); 242 | 243 | 244 | #[derive(Clone)] 245 | pub struct FontCtx { 246 | inner: Rc>, 247 | } 248 | 249 | struct FontCtxInner { 250 | families: Vec, 251 | faces: Vec, 252 | 253 | glyph_cache: LruCache<(FaceId, NotNan, u16), (fontdue::Metrics, Vec)>, 254 | } 255 | 256 | struct Family { 257 | name: String, 258 | faces: Vec, 259 | } 260 | 261 | #[derive(Clone, Copy, Debug)] 262 | struct FaceInfo { 263 | id: FaceId, 264 | bold: Bold, 265 | italic: Italic, 266 | } 267 | 268 | struct Face { 269 | font: Font, 270 | #[allow(dead_code)] // @temp 271 | info: FaceInfo, 272 | } 273 | 274 | impl FontCtx { 275 | pub fn new() -> FontCtx { 276 | FontCtx { 277 | inner: Rc::new(RefCell::new(FontCtxInner { 278 | families: vec![], 279 | faces: vec![], 280 | 281 | glyph_cache: LruCache::new(128.try_into().unwrap()), 282 | })), 283 | } 284 | } 285 | 286 | #[allow(dead_code)] // @temp 287 | pub fn find_face(&self, family: &str, bold: Bold, italic: Italic) -> FaceId { 288 | let this = self.inner.borrow(); 289 | 290 | let family = this.families.iter().find(|f| f.name == family); 291 | if let Some(family) = family { 292 | return family.find_face_exact(bold, italic) 293 | .unwrap_or_else(|| family.faces[0].id); 294 | } 295 | FaceId::DEFAULT 296 | } 297 | 298 | pub fn add_face(&self, family: &str, bold: Bold, italic: Italic, data: &[u8]) -> FaceId { 299 | let mut this = self.inner.borrow_mut(); 300 | 301 | // create face. 302 | let id = FaceId(this.faces.len() as u32); 303 | let info = FaceInfo { id, bold, italic }; 304 | let font = Font::from_bytes(data, Default::default()).unwrap(); 305 | this.faces.push(Face { info, font }); 306 | 307 | // add to family. 308 | let fam = this.families.iter_mut().find(|f| f.name == family); 309 | if let Some(family) = fam { 310 | assert!(family.find_face_exact(bold, italic).is_none()); 311 | family.faces.push(info); 312 | } 313 | else { 314 | this.families.push(Family { name: family.into(), faces: vec![info] }); 315 | } 316 | 317 | id 318 | } 319 | } 320 | 321 | impl FontCtxInner { 322 | fn glyph_mask(&mut self, face: FaceId, size: NotNan, glyph: u16) -> &(fontdue::Metrics, Vec) { 323 | self.glyph_cache.get_or_insert((face, size, glyph), || { 324 | self.faces[face.usize()].font.rasterize_indexed(glyph, *size) 325 | }) 326 | } 327 | } 328 | 329 | impl Family { 330 | fn find_face_exact(&self, bold: Bold, italic: Italic) -> Option { 331 | for info in &self.faces { 332 | if info.bold == bold && info.italic == italic { 333 | return Some(info.id); 334 | } 335 | } 336 | None 337 | } 338 | } 339 | 340 | 341 | pub struct TextLayout { 342 | fonts: FontCtx, 343 | text: String, 344 | lines: Vec, 345 | spans: Vec, 346 | glyphs: Vec, 347 | } 348 | 349 | #[derive(Clone, Copy, Debug)] 350 | struct Glyph { 351 | index: u16, 352 | advance: f32, 353 | dx: f32, 354 | dy: f32, 355 | } 356 | 357 | struct Line { 358 | text_begin: u32, 359 | text_end: u32, 360 | span_begin: u32, 361 | span_end: u32, 362 | 363 | width: f32, 364 | max_ascent: f32, 365 | max_descent: f32, 366 | max_gap: f32, 367 | height: f32, 368 | } 369 | 370 | struct Span { 371 | text_begin: u32, 372 | text_end: u32, 373 | glyph_begin: u32, 374 | glyph_end: u32, 375 | 376 | width: f32, 377 | 378 | face_id: FaceId, 379 | font_size: NotNan, 380 | color: u32, 381 | source: u32, 382 | } 383 | 384 | 385 | impl Line { 386 | #[inline(always)] 387 | fn new(text_begin: u32, span_begin: u32) -> Line { 388 | Line { 389 | text_begin, text_end: text_begin, 390 | span_begin, span_end: span_begin, 391 | width: 0.0, 392 | max_ascent: 0.0, 393 | max_descent: 0.0, 394 | max_gap: 0.0, 395 | height: 0.0, 396 | } 397 | } 398 | 399 | #[inline(always)] 400 | fn span_range(&self) -> core::ops::Range { 401 | self.span_begin as usize .. self.span_end as usize 402 | } 403 | } 404 | 405 | impl Span { 406 | #[inline(always)] 407 | fn glyph_range(&self) -> core::ops::Range { 408 | self.glyph_begin as usize .. self.glyph_end as usize 409 | } 410 | } 411 | 412 | 413 | impl TextLayout { 414 | pub fn new(fonts: &FontCtx) -> Self { 415 | TextLayout { 416 | fonts: fonts.clone(), 417 | text: "".into(), 418 | lines: vec![Line::new(0, 0)], 419 | spans: vec![], 420 | glyphs: vec![], 421 | } 422 | } 423 | 424 | #[inline(always)] 425 | pub fn text(&self) -> &str { &self.text } 426 | 427 | pub fn clear(&mut self) { 428 | self.text.clear(); 429 | self.lines.clear(); 430 | self.spans.clear(); 431 | self.glyphs.clear(); 432 | self.lines.push(Line::new(0, 0)); 433 | } 434 | 435 | pub fn append(&mut self, text: &str, face_id: FaceId, font_size: f32, color: u32, source: u32) { 436 | let font_size = NotNan::new(font_size).unwrap(); 437 | 438 | let mut current_line = self.lines.last_mut().unwrap(); 439 | let mut pos_cursor = current_line.width; 440 | 441 | let mut text_cursor = self.text.len() as u32; 442 | self.text.push_str(text); 443 | 444 | let fonts = self.fonts.clone(); 445 | let fonts = fonts.inner.borrow(); 446 | let face = &fonts.faces[face_id.usize()]; 447 | 448 | let font_metrics = face.font.horizontal_line_metrics(*font_size).unwrap(); 449 | 450 | let mut text = text; 451 | while text.len() > 0 { 452 | let (segment_end, is_line) = 453 | text.find('\n').map(|index| (index, true)) 454 | .unwrap_or((text.len(), false)); 455 | 456 | // update line metrics. 457 | current_line.max_ascent = current_line.max_ascent.max(font_metrics.ascent); 458 | current_line.max_descent = current_line.max_descent.max(-font_metrics.descent); 459 | current_line.max_gap = current_line.max_gap.max(font_metrics.line_gap); 460 | current_line.height = current_line.max_ascent + current_line.max_descent + current_line.max_gap; 461 | 462 | let pos_begin = pos_cursor; 463 | 464 | // add glyphs. 465 | // assume one glyph per byte. 466 | let glyph_begin = self.glyphs.len() as u32; 467 | for c in text[..segment_end].chars() { 468 | // @todo: kern. 469 | let glyph_index = face.font.lookup_glyph_index(c); 470 | let metrics = face.font.metrics_indexed(glyph_index, *font_size); 471 | 472 | // @todo-speed: we don't actually need to compute dx/dy here. 473 | // (cause we're not doing advanced shaping.) 474 | // the renderer also has access to the glyph's `Metrics`. 475 | // so we only need the advance here. sadly we can only get that from 476 | // the glyph's metrics (which are somewhat expensive to compute). 477 | self.glyphs.push(Glyph { 478 | index: glyph_index, 479 | advance: metrics.advance_width, 480 | dx: metrics.bounds.xmin, 481 | dy: -metrics.bounds.height - metrics.bounds.ymin, 482 | }); 483 | 484 | pos_cursor += metrics.advance_width; 485 | } 486 | assert_eq!(self.glyphs.len(), glyph_begin as usize + segment_end); 487 | let glyph_end = self.glyphs.len() as u32; 488 | 489 | // add span. 490 | self.spans.push(Span { 491 | text_begin: text_cursor, 492 | text_end: text_cursor + segment_end as u32, 493 | glyph_begin, glyph_end, 494 | width: pos_cursor - pos_begin, 495 | face_id, font_size, 496 | color, 497 | source, 498 | }); 499 | 500 | 501 | // update line. 502 | let line_end = text_cursor + segment_end as u32; 503 | current_line.text_end = line_end; 504 | current_line.span_end += 1; 505 | current_line.width = pos_cursor; 506 | 507 | if is_line { 508 | // line ranges are weird. they don't include the `\n`. 509 | // so the next line starts after the current line's `\n`. 510 | let text_begin = line_end + 1; 511 | let span_begin = self.spans.len() as u32; 512 | 513 | self.lines.push(Line { 514 | text_begin, text_end: text_begin, 515 | span_begin, span_end: span_begin, 516 | width: 0.0, 517 | max_ascent: 0.0, 518 | max_descent: 0.0, 519 | max_gap: 0.0, 520 | height: 0.0, 521 | }); 522 | 523 | current_line = self.lines.last_mut().unwrap(); 524 | pos_cursor = 0.0; 525 | } 526 | 527 | let text_advance = segment_end + is_line as usize; 528 | text_cursor += text_advance as u32; 529 | text = &text[text_advance..]; 530 | } 531 | } 532 | 533 | pub fn width(&self) -> f32 { 534 | let mut width = 0f32; 535 | for line in &self.lines { 536 | width = width.max(line.width); 537 | } 538 | width 539 | } 540 | 541 | pub fn height(&self) -> f32 { 542 | let mut height = 0.0; 543 | for line in &self.lines { 544 | height += line.height; 545 | } 546 | height 547 | } 548 | 549 | pub fn size(&self) -> [f32; 2] { 550 | [self.width(), self.height()] 551 | } 552 | } 553 | 554 | 555 | #[derive(Clone, Copy, Debug)] 556 | pub struct RangeMetrics { 557 | pub x0: f32, 558 | pub x1: f32, 559 | pub y: f32, 560 | // @todo: baseline. 561 | pub line_height: f32, 562 | pub line_index: u32, 563 | pub source: Option, 564 | } 565 | 566 | impl TextLayout { 567 | #[allow(dead_code)] // @temp 568 | pub fn hit_test_text_pos(&self, pos: u32) -> RangeMetrics { 569 | let pos = pos.min(self.text.len() as u32); 570 | 571 | let mut y = 0.0; 572 | for (line_index, line) in self.lines.iter().enumerate() { 573 | let line_index = line_index as u32; 574 | let line_height = line.height; 575 | 576 | let mut x = 0.0; 577 | if pos >= line.text_begin && pos <= line.text_end { 578 | for span in &self.spans[line.span_range()] { 579 | // in current span. 580 | if pos >= span.text_begin 581 | && pos < span.text_end { 582 | let offset = (pos - span.text_begin) as usize; 583 | let glyph_begin = span.glyph_begin as usize; 584 | 585 | for i in 0..offset { 586 | x += self.glyphs[glyph_begin + i].advance; 587 | } 588 | 589 | let advance = self.glyphs[glyph_begin + offset].advance; 590 | return RangeMetrics { 591 | x0: x, 592 | x1: x + advance, 593 | y, 594 | line_height, line_index, 595 | source: Some(span.source), 596 | }; 597 | } 598 | 599 | x += span.width; 600 | } 601 | 602 | // end of line. 603 | let x = line.width; 604 | return RangeMetrics { 605 | x0: x, 606 | x1: x, 607 | y, 608 | line_height, line_index, 609 | source: None, 610 | }; 611 | } 612 | 613 | y += line_height; 614 | } 615 | 616 | assert_eq!(self.text.len(), 0); 617 | return RangeMetrics { x0: 0.0, x1: 0.0, y: 0.0, line_height: 0.0, line_index: 0, source: None }; 618 | } 619 | } 620 | 621 | 622 | impl TextLayout { 623 | pub fn hit_test_text_ranges(&self, begin: u32, end: u32, mut f: F) { 624 | let begin = begin.min(self.text.len() as u32); 625 | let end = end.min(self.text.len() as u32); 626 | if begin >= end { return } 627 | 628 | let mut target = begin; 629 | let mut in_range = false; 630 | 631 | let mut y = 0.0; 632 | 633 | for (line_index, line) in self.lines.iter().enumerate() { 634 | let line_index = line_index as u32; 635 | let line_height = line.height; 636 | 637 | let f = &mut f; 638 | let mut f = |x0: f32, x1: f32, source: Option| { 639 | if x0 < x1 { 640 | f(&RangeMetrics { 641 | x0, x1, y, 642 | line_height, line_index, 643 | source, 644 | }); 645 | } 646 | }; 647 | 648 | let mut x = 0.0; 649 | if target >= line.text_begin && target <= line.text_end { 650 | for span in &self.spans[line.span_range()] { 651 | // in current span. 652 | if target >= span.text_begin 653 | && target < span.text_end { 654 | let offset = (target - span.text_begin) as usize; 655 | let glyph_begin = span.glyph_begin as usize; 656 | 657 | let span_x = x; 658 | let mut target_x = x; 659 | for i in 0..offset { 660 | target_x += self.glyphs[glyph_begin + i].advance; 661 | } 662 | 663 | if in_range { 664 | f(span_x, target_x, Some(span.source)); 665 | return; 666 | } 667 | else { 668 | in_range = true; 669 | target = end; 670 | 671 | if target < span.text_end { 672 | let mut end_x = target_x; 673 | let end_offset = (target - span.text_begin) as usize; 674 | for i in offset..end_offset { 675 | end_x += self.glyphs[glyph_begin + i].advance; 676 | } 677 | 678 | f(target_x, end_x, Some(span.source)); 679 | return; 680 | } 681 | else { 682 | f(target_x, x + span.width, Some(span.source)); 683 | } 684 | } 685 | } 686 | else if in_range { 687 | f(x, x + span.width, Some(span.source)); 688 | } 689 | 690 | x += span.width; 691 | } 692 | 693 | if target == line.text_end { 694 | if in_range { 695 | return; 696 | } 697 | else { 698 | in_range = true; 699 | target = end; 700 | } 701 | } 702 | } 703 | else if in_range { 704 | f(0., line.width, None); 705 | } 706 | 707 | y += line_height; 708 | } 709 | 710 | unreachable!() 711 | } 712 | } 713 | 714 | 715 | #[derive(Clone, Copy, Debug)] 716 | pub struct HitMetrics { 717 | pub text_pos_left: u32, 718 | pub text_pos_right: u32, 719 | 720 | pub fraction: f32, 721 | pub out_of_bounds: [bool; 2], 722 | 723 | pub source: Option, 724 | } 725 | 726 | impl TextLayout { 727 | pub fn hit_test_line(&self, line_index: u32, x: f32) -> HitMetrics { 728 | let line = &self.lines[line_index as usize]; 729 | 730 | if x < 0.0 { 731 | return HitMetrics { 732 | text_pos_left: line.text_begin, 733 | text_pos_right: line.text_begin, 734 | fraction: 0.0, 735 | out_of_bounds: [true, false], 736 | source: None, 737 | }; 738 | } 739 | 740 | let mut cursor = 0.0; 741 | for span in &self.spans[line.span_range()] { 742 | for glyph in span.glyph_range() { 743 | let new_cursor = cursor + self.glyphs[glyph].advance; 744 | 745 | if x >= cursor && x < new_cursor { 746 | let fraction = (x - cursor) / (new_cursor - cursor); 747 | 748 | let offset = glyph as u32 - span.glyph_begin; 749 | return HitMetrics { 750 | text_pos_left: span.text_begin + offset, 751 | text_pos_right: span.text_begin + offset + 1, 752 | fraction, 753 | out_of_bounds: [false, false], 754 | source: Some(span.source), 755 | } 756 | } 757 | 758 | cursor = new_cursor; 759 | } 760 | } 761 | 762 | return HitMetrics { 763 | text_pos_left: line.text_end, 764 | text_pos_right: line.text_end, 765 | fraction: 0.0, 766 | out_of_bounds: [true, false], 767 | source: None, 768 | }; 769 | } 770 | 771 | pub fn hit_test_pos(&self, x: f32, y: f32) -> HitMetrics { 772 | if self.lines.len() == 0 { 773 | return HitMetrics { 774 | text_pos_left: 0, 775 | text_pos_right: 0, 776 | fraction: 0.0, 777 | out_of_bounds: [true, true], 778 | source: None, 779 | } 780 | } 781 | 782 | // above. 783 | if y < 0.0 { 784 | let mut result = self.hit_test_line(0, x); 785 | result.out_of_bounds[1] = true; 786 | return result; 787 | } 788 | 789 | let mut line_y = 0.0; 790 | for (line_index, line) in self.lines.iter().enumerate() { 791 | let new_line_y = line_y + line.height; 792 | 793 | if y >= line_y && y < new_line_y { 794 | return self.hit_test_line(line_index as u32, x); 795 | } 796 | 797 | line_y = new_line_y; 798 | } 799 | 800 | // below. 801 | let mut result = self.hit_test_line(self.lines.len() as u32 - 1, x); 802 | result.out_of_bounds[1] = true; 803 | return result; 804 | } 805 | } 806 | 807 | 808 | 809 | #[inline(always)] 810 | pub fn color_from_unmult_rgba((r, g, b, a): (u32, u32, u32, u32)) -> u32 { 811 | color_pack((muldiv255(r, a), muldiv255(g, a), muldiv255(b, a), a)) 812 | } 813 | 814 | #[inline(always)] 815 | pub fn color_pack((r, g, b, a): (u32, u32, u32, u32)) -> u32 { 816 | a << 24 | r << 16 | g << 8 | b 817 | } 818 | 819 | #[inline(always)] 820 | pub fn color_pack_u8(r: u8, g: u8, b: u8, a: u8) -> u32 { 821 | color_pack((r as u32, g as u32, b as u32, a as u32)) 822 | } 823 | 824 | #[inline(always)] 825 | pub fn color_unpack(c: u32) -> (u32, u32, u32, u32) { 826 | ((c >> 16) & 0xff, (c >> 8) & 0xff, (c >> 0) & 0xff, (c >> 24) & 0xff) 827 | } 828 | 829 | #[inline(always)] 830 | pub fn color_pre_multiply(c: u32) -> u32 { 831 | color_from_unmult_rgba(color_unpack(c)) 832 | } 833 | 834 | 835 | // function's down here are taken from swcomposite, 836 | // cause i ain't adding an explicit dependency for one function. 837 | 838 | // this is an approximation of true 'over' that does a division by 256 instead 839 | // of 255. It is the same style of blending that Skia does. It corresponds 840 | // to Skia's SKPMSrcOver 841 | #[inline(always)] 842 | pub fn over(src: u32, dst: u32) -> u32 { 843 | let a = src >> 24; 844 | let a = 256 - a; 845 | let mask = 0xff00ff; 846 | let rb = ((dst & mask) * a) >> 8; 847 | let ag = ((dst >> 8) & mask) * a; 848 | src + ((rb & mask) | (ag & !mask)) 849 | } 850 | 851 | // Calculates floor(a*b/255 + 0.5) 852 | #[inline(always)] 853 | pub fn muldiv255(x: u32, y: u32) -> u32 { 854 | // The deriviation for this formula can be 855 | // found in "Three Wrongs Make a Right" by Jim Blinn. 856 | let tmp = x * y + 128; 857 | (tmp + (tmp >> 8)) >> 8 858 | } 859 | 860 | // end of sw-composite functions. 861 | 862 | -------------------------------------------------------------------------------- /examples/fib.kb: -------------------------------------------------------------------------------- 1 | fn fib(n): 2 | var a = 0 3 | var b = 1 4 | var i = 0 5 | while i < n: 6 | (a, b) = (b, a+b) 7 | i += 1 8 | end 9 | return a 10 | end 11 | 12 | println(fib(10)) 13 | 14 | -------------------------------------------------------------------------------- /examples/life.kb: -------------------------------------------------------------------------------- 1 | -- @todo: use a struct. 2 | fn mk_board(w, h): 3 | let values = [] 4 | var i = 0 5 | while i < w*h: 6 | values[i] := 0 7 | i += 1 8 | end 9 | return values 10 | end 11 | 12 | fn board_print(board, w, h): 13 | var y = 0 14 | while y < h: 15 | var x = 0 16 | while x < w: 17 | print(if board[y*w + x] > 0: "#" else: "." end) 18 | x += 1 19 | end 20 | println("") 21 | 22 | y += 1 23 | end 24 | 25 | println("") 26 | end 27 | 28 | fn board_get(board, w, h, x, y): 29 | if x < 0: x += w 30 | elif x >= w: x -= w end 31 | 32 | if y < 0: y += h 33 | elif y >= h: y -= h end 34 | 35 | return board[y*w + x] 36 | end 37 | 38 | fn board_step(board, w, h): 39 | let new_board = mk_board(w, h) 40 | 41 | var y = 0 42 | while y < h: 43 | var x = 0 44 | while x < w: 45 | let neighbors = 46 | board_get(board, w, h, x - 1, y - 1) 47 | + board_get(board, w, h, x , y - 1) 48 | + board_get(board, w, h, x + 1, y - 1) 49 | + board_get(board, w, h, x - 1, y ) 50 | + board_get(board, w, h, x + 1, y ) 51 | + board_get(board, w, h, x - 1, y + 1) 52 | + board_get(board, w, h, x , y + 1) 53 | + board_get(board, w, h, x + 1, y + 1) 54 | 55 | if neighbors == 3: 56 | new_board[y*w + x] = 1 57 | elif neighbors == 2 and board[y*w + x] == 1: 58 | new_board[y*w + x] = 1 59 | end 60 | 61 | x += 1 62 | end 63 | 64 | y += 1 65 | end 66 | 67 | return new_board 68 | end 69 | 70 | fn sleep(): 71 | var i = 0 72 | while i < 500000: 73 | i += 1 74 | end 75 | end 76 | 77 | 78 | var w = 10 79 | var h = 10 80 | var board = mk_board(w, h) 81 | 82 | board[3*w + 1] = 1 83 | board[3*w + 2] = 1 84 | board[3*w + 3] = 1 85 | board[2*w + 3] = 1 86 | board[1*w + 2] = 1 87 | 88 | var i = 0 89 | while i < 2: 90 | board_print(board, w, h) 91 | board = board_step(board, w, h) 92 | --sleep() 93 | i += 1 94 | end 95 | 96 | -------------------------------------------------------------------------------- /examples/nested.kb: -------------------------------------------------------------------------------- 1 | do: 2 | foo() 3 | 4 | fn foo(): 5 | println("do foo") 6 | end 7 | end 8 | 9 | foo() 10 | im_a_granma() 11 | 12 | fn foo(): 13 | println("root foo") 14 | end 15 | 16 | fn child(): 17 | println("i'm not your child!") 18 | end 19 | 20 | fn im_a_granma(): 21 | println("granny") 22 | mom() 23 | child() 24 | 25 | fn mom(): 26 | println("mommy") 27 | child() 28 | 29 | fn child(): 30 | println("child") 31 | end 32 | end 33 | end 34 | 35 | -------------------------------------------------------------------------------- /examples/repl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "repl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | kibi = { path = "../.." } 9 | ctrlc = "3.2.4" 10 | 11 | -------------------------------------------------------------------------------- /examples/repl/src/main.rs: -------------------------------------------------------------------------------- 1 | use core::sync::atomic::{AtomicBool, Ordering as MemOrder}; 2 | 3 | use kibi::*; 4 | 5 | 6 | mod builtin { 7 | use super::*; 8 | 9 | pub(crate) fn print(vm: &mut Vm) -> VmResult { 10 | vm.generic_print(0); 11 | return Ok(NativeFuncReturn::Unit); 12 | } 13 | pub(crate) const PRINT: FuncDesc = FuncDesc { 14 | code: FuncCode::Native(NativeFuncPtrEx(print)), 15 | constants: vec![], 16 | num_params: 1, 17 | stack_size: 1, 18 | }; 19 | 20 | pub(crate) fn println(vm: &mut Vm) -> VmResult { 21 | vm.generic_print(0); 22 | println!(); 23 | return Ok(NativeFuncReturn::Unit); 24 | } 25 | pub(crate) const PRINTLN: FuncDesc = FuncDesc { 26 | code: FuncCode::Native(NativeFuncPtrEx(println)), 27 | constants: vec![], 28 | num_params: 1, 29 | stack_size: 1, 30 | }; 31 | 32 | } 33 | 34 | 35 | fn main() { 36 | let mut vm = Vm::new(); 37 | 38 | vm.add_func("print", builtin::PRINT); 39 | vm.add_func("println", builtin::PRINTLN); 40 | vm.add_func("quit", FuncDesc { 41 | code: FuncCode::Native(NativeFuncPtrEx(|_| std::process::exit(0))), 42 | constants: vec![], 43 | num_params: 0, 44 | stack_size: 0, 45 | }); 46 | 47 | let args: Vec = std::env::args().collect(); 48 | if args.len() > 1 { 49 | assert_eq!(args.len(), 2); 50 | 51 | let path = &args[1]; 52 | let source = std::fs::read_to_string(path).unwrap(); 53 | 54 | let t0 = std::time::Instant::now(); 55 | let mut module = parser::parse_module(source.as_bytes()).unwrap(); 56 | let dt_parse = t0.elapsed(); 57 | 58 | let t0 = std::time::Instant::now(); 59 | let mut infer = infer::Infer::new(); 60 | infer.assign_ids(&mut module); 61 | infer.infer(&mut module); 62 | let dt_infer = t0.elapsed(); 63 | 64 | let t0 = std::time::Instant::now(); 65 | let mut builder = bbir_builder::Builder::new(); 66 | builder.build(&module); 67 | let (funcs, items, _) = builder.krate.build(); 68 | let dt_compile = t0.elapsed(); 69 | 70 | let t0 = std::time::Instant::now(); 71 | vm.load_crate(0, funcs.inner(), items.inner()); 72 | vm.call(0, 0, &[]).unwrap(); 73 | let dt_run = t0.elapsed(); 74 | 75 | println!("parse: {:?}", dt_parse); 76 | println!("infer: {:?}", dt_infer); 77 | println!("compile: {:?}", dt_compile); 78 | println!("run: {:?}", dt_run); 79 | 80 | return; 81 | } 82 | 83 | 84 | let running = &*Box::leak(Box::new(AtomicBool::new(false))); 85 | 86 | let interrupt_ptr = unsafe { &*(vm.interrupt_ptr() as *const AtomicBool) }; 87 | ctrlc::set_handler(move || { 88 | if running.load(MemOrder::SeqCst) { 89 | interrupt_ptr.store(true, MemOrder::SeqCst); 90 | } 91 | else { 92 | std::process::exit(0); 93 | } 94 | }).unwrap(); 95 | 96 | 97 | let mut buffer = String::new(); 98 | 99 | loop { 100 | if buffer.len() > 0 { 101 | print!(" "); 102 | } 103 | else { 104 | print!(">>> "); 105 | } 106 | use std::io::Write; 107 | std::io::stdout().lock().flush().unwrap(); 108 | std::io::stdin().read_line(&mut buffer).unwrap(); 109 | 110 | let t0 = std::time::Instant::now(); 111 | let ic = vm.instruction_counter(); 112 | 113 | let ast = { 114 | let chunk = buffer.trim(); 115 | if chunk.len() == 0 { 116 | buffer.clear(); 117 | continue; 118 | } 119 | 120 | match parser::parse_single(chunk.as_bytes()) { 121 | Ok(ast) => ast, 122 | Err(e) => { 123 | match e.data { 124 | compiler::ParseErrorData::UnexpectedEof => { 125 | continue; 126 | } 127 | _ => { 128 | println!("parse error at {}:{}-{}:{}: {:?}", 129 | e.source.begin.line, e.source.begin.column, 130 | e.source.end.line, e.source.end.column, 131 | e.data); 132 | buffer.clear(); 133 | continue; 134 | } 135 | } 136 | } 137 | } 138 | }; 139 | 140 | let mut module = item::Module { source: ast.source, block: expr::Block { stmts: vec![ast.to_stmt()] } }; 141 | 142 | let mut infer = infer::Infer::new(); 143 | infer.assign_ids(&mut module); 144 | infer.infer(&mut module); 145 | 146 | let mut builder = bbir_builder::Builder::new(); 147 | builder.build(&module); 148 | let (funcs, items, _) = builder.krate.build(); 149 | buffer.clear(); 150 | 151 | 152 | running.store(true, core::sync::atomic::Ordering::SeqCst); 153 | vm.load_crate(0, funcs.inner(), items.inner()); 154 | let result = vm.call(0, 0, &[]); 155 | running.store(false, core::sync::atomic::Ordering::SeqCst); 156 | 157 | if let Err(_) = result { 158 | println!("runtime error"); 159 | continue; 160 | } 161 | 162 | let dt = t0.elapsed(); 163 | let di = vm.instruction_counter() - ic; 164 | println!("{:?}, {}op/s", t0.elapsed(), (di as f64 / dt.as_secs_f64()) as u64); 165 | } 166 | } 167 | 168 | -------------------------------------------------------------------------------- /examples/tuples.kb: -------------------------------------------------------------------------------- 1 | 2 | let t = (1, 2) 3 | println(t) 4 | 5 | 6 | var a = 1 7 | var b = 2 8 | print("a: "); print(a); print(", b: "); println(b) 9 | 10 | (a, b) = (b, a) 11 | 12 | print("a: "); print(a); print(", b: "); println(b) 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/values.kb: -------------------------------------------------------------------------------- 1 | var foo = [1, 2, 3] 2 | println(foo) 3 | println("") 4 | 5 | var bar = foo 6 | foo[3] := 4 7 | println(foo) 8 | println(bar) 9 | println("") 10 | 11 | bar[0] = foo 12 | bar[0][3] = "four" 13 | println(foo) 14 | println(bar) 15 | println("") 16 | 17 | -------------------------------------------------------------------------------- /ideas.md: -------------------------------------------------------------------------------- 1 | 2 | - meta programming: 3 | - definitely want something for the gui (like jsx, but in user land). 4 | - want to exploit ability to run code in the compiler. 5 | - especially, as there's no "cross compiling". 6 | - and maybe serialization for state caching. 7 | - issue: querying for definitions of symbols. 8 | - 1) cause dynamic. 9 | - definitions could change at runtime. 10 | - would want to use the same info as the optimizer. 11 | - 2) cause unordered. 12 | - can't know what macro will output ~ have to constrain access to output. 13 | - could make macros ordered. 14 | - can only access definitions "above". 15 | - nothing above can access macro output. 16 | - does get a bit more complicated with imports. 17 | - will have to come back to this, once know how regular out-of-order analysis works here. 18 | - code repr: structs analogous to definitions in compiler. 19 | 20 | - dynamic, but not in the usual sense: 21 | - dynamic is about: 22 | - type flexiblity. `Any` & optional duck typing. 23 | - ability to mutate the running program. hot loading, dynamic binding. 24 | - introspection. 25 | - the more powerful dynamic features (beyond type flexibility) aren't limited to traditionally dynamic languages. 26 | - in fact, introspection and hot loading are greatly enhanced by static analysis & types. 27 | - types enable the runtime to find and replace all instances of a struct by running a migration function. 28 | - introspection & rtti can make statically types (and packed values) accessible to dynamic code. this can be transparent (as with `Any` duck typing) or explicit (with an introspection api). 29 | - features like dynamic binding are very powerful, but in (sane) practice, they're not used that much, or only in very specific cases. opt-in dynamic binding, like with `var fn` can offer many of the benefits, without making everything else slow, cause "everything needs to be dynamic". 30 | - no dynamic by-name binding of globals. 31 | - undeclared identifier will be an error. 32 | - it won't prevent code execution, but executing the access will trigger a trap. 33 | - potential use cases: 34 | - global variables: just define one. you can use `Any` as the type. this is not much effort. 35 | - code generation & "decorators": we do this kind of stuff at compile time with macros. adding code at runtime will be possible, but binding will likely occur through traits. we may support adding methods dynamically. those would be accessible through dynamic method calls, as with `Any`. 36 | - dynamic "linking": you know what functions you're expecting, so list them (or generate that list). that way you'll get load time errors. and if you specify types & versions, the runtime can even verify those. 37 | 38 | - static error resilience: 39 | - compile errors should not prevent code from being executed. 40 | - in general, compiler should insert traps that pause execution & let the user provide a value at runtime. 41 | - ir/bytecode has to be type correct. 42 | - reverting to `Any` & dynamic lookups/errors is type correct. 43 | - it's the task of the compiler to always produce a type correct, executable program. 44 | - all of this must support opt-out. 45 | 46 | - crates & modules: 47 | - crates are the smallest unit of shippable code. a "library" or "executable". 48 | - crate dependencies must be acyclic. 49 | - modules enable code organization inside crates. 50 | - module dependencies can be cyclic and modules do not introduce an ordering on the definitions inside a crate (i.e. everything should behave, as if the code was written without modules and used unique names instead (modulo visibility rules)). 51 | 52 | - module state: 53 | - "global variables" are useful and weren't made by the devil. 54 | - they're a powerful tool, that can be very useful when prototyping. 55 | - some systems like logging, tracing, memory allocation, can be very cumbersome when state needs to be passed around explicitly. 56 | - the compiler should offer tools to constrain use of global variables. this can (and probably should) be done through attributes & user defined meta programs. 57 | - general stuff: 58 | - module state is defined using `var` and `let`. compiler should warn on shadowing definitions. 59 | - only functions defined after module variables can access them. this is to make the behavior consistent with closures inside other functions. 60 | - actually, there's no reason to do this. 61 | - closures are different. and local functions can't access the outer function's `var`s at all. 62 | - and in a 2d code editor, there's no longer a linear ordering anyway. so we'd have to force the user to declare an order somewhere, again, for no practical reason. 63 | - the non-item code is still ordered. in 2d, there won't be any code in modules. only independent startup code snippets (which can be ordered arbitrarily), and initializers (which can be implemented as functions, so they can access all other module state). 64 | - i gotta think less in terms of the current tools (shoutout to bret victor). 65 | - this also solves the weird circular re-export thing. 66 | - though: 67 | - it's kinda unintuitive, how at the top level, you can't shadow, and functions can magically see every local. 68 | - better idea: locals & regular code behave as if concated and put into a function. items can't access them. it's just an implicit main function for each module. then introduce `global` (mutable with dynamic initializer) and `const` (immutable with static initializer) items. 69 | - also consider `let mut` for function wide type inference, no implicit `Any`. and `var` always means `Any` and can't take type annotations (syntactically it can, but the compiler ignores it & errors). 70 | - ig `var fn` should also be `global fn` then? 71 | - not all that intuitive/consistent/useful. 72 | - seems better to just `global the_fun = fn...` 73 | - and maybe we should use different syntax for closures, cause that kinda looks like it could capute module main locals, but it can't. 74 | - imports have access to everything visible at the end of the imported module. 75 | - for shadowing definitions: the last definition. 76 | - what about re-export cycles?? (can they give access to `var`s defined below?) 77 | - module state initialization order is evaluation order of `mod` items. 78 | - issue is: module dependencies can be cyclic. however, module state initialization must be acyclic. 79 | - things simply can't be accessed before they're initialized. they could be, but that leads to very hard to debug bugs. 80 | - for dynamic code this is not an issue: simply give items an initialization state. when accessing an item, panic if it is not initialized. 81 | - static code does not make these checks, if possible. the compiler must prove that static code never accesses uninitialized module state. if it can't prove that, it should raise a compile error and fall back to a checked dynamic access. 82 | - hmm, don't really need to use a dynamic access. just check whether the thing is initialized. and that really doesn't cost much (if anything). 83 | - so we should differentiate between static/dynamic binding (by pointer vs by name) and checked/unchecked access (ensuring proper initialization; though again, skipping this check is unlikely to be beneficial, even c and rust do these checks). 84 | 85 | 86 | - random ideas: 87 | - field-like syntax for indices: 88 | - to make `foo["bar"]` more ergonomic. 89 | - eg to work with dynamic json-like data. 90 | - syntax: `foo:index`. 91 | - dynamic field access: 92 | - something like python's `getattr`. 93 | - cause `foo["field"]` is an index access, not a field access. 94 | - syntax: `foo.["field"]`. 95 | - unlike field-like indexing, this one isn't too common, so a built-in could be enough. 96 | - just unwind & free memory on panic, no destructors, no defer. 97 | - simpler, avoids issues with double panics. 98 | - problem with defer: inout may leave values in an unexpected state. 99 | - non-memory resources are cleaned up by finalizers. 100 | - panics in finalizers are ignored but logged. 101 | - finalizers are run on unwind, if ref count drops to zero. 102 | - `Number` type: 103 | - a non-nan & non-inf `F64`. 104 | - default number type in dynamic code. 105 | - function with untyped parameters returns `Any` by default. 106 | - tbd: entire function in "dynamic mode"? 107 | - or can `Number` be inferred based on types? 108 | - so like if `Any` is expected (eg cause assigned to var), then number literals default to `Number`. 109 | - ad-hoc type unions, implemented as (link-time) deduplicated traits. 110 | - named tuples. 111 | - dynamic tuple indexing (returns union of possible types). 112 | - mutable capture: implicit boxing. 113 | - optimizer can stack allocate based on escape analysis. 114 | - no auto-deref on boxes in `Any`. 115 | - confusing. and can put a box into itself -> cycle. 116 | - eval order: function after args. 117 | - cause inout: `foo.m1(foo.m2())`. 118 | - inner call happens first. 119 | - and want the modified `foo` to go into the outer call. 120 | - analogous to the `&mut self` error in rust. 121 | - can in fact only be resolved by changing the evaulation order. 122 | 123 | -------------------------------------------------------------------------------- /main.md: -------------------------------------------------------------------------------- 1 | 2 | - next steps: 3 | - step debugger. 4 | - wasm. 5 | - nominal structs, enums & traits, typeid. 6 | - better runtime errors. 7 | - macros, modules, multithreading. 8 | - error handling: 9 | - try operator. 10 | - pcall. 11 | - `Gc` type, `new`. 12 | - implicit deref/inout with methods. 13 | - but pass without deref. 14 | - so `&*` to inout boxed value? 15 | - compiler could do implicit stuff if has types. 16 | - fix gc. 17 | 18 | 19 | - todo: step debugger. 20 | - scrollable codeview. 21 | - scrollbars working, you know. 22 | - `scroll_pos` clamping. 23 | - render pcs. 24 | - arrows. 25 | - highlighting lines. 26 | - including dormant ones. (show "inverse depth") 27 | - F10. 28 | - partial code view. 29 | - probably through replacements. 30 | - multiple code views. 31 | 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | the new kibi is now at kibi-lang/kibi. 2 | this is an archive from the real early days, when it still was a dynamic scripting language. 3 | -------------------------------------------------------------------------------- /src/compiler/analysis.rs: -------------------------------------------------------------------------------- 1 | use derive_more::{Deref, Display}; 2 | use crate::index_vec::*; 3 | use crate::macros::define_id; 4 | use super::*; 5 | 6 | 7 | #[derive(Deref)] 8 | pub struct Predecessors { 9 | pub preds: IndexVec>, 10 | } 11 | 12 | 13 | #[derive(Deref)] 14 | pub struct PostOrder { 15 | pub blocks: Vec, 16 | } 17 | 18 | define_id!(PostOrderIndex, OptPostOrderIndex); 19 | 20 | pub struct PostOrderIndices { 21 | pub indices: IndexVec, 22 | } 23 | 24 | 25 | // ### dominance ### 26 | 27 | pub struct ImmediateDominators { 28 | pub idom: IndexVec, 29 | } 30 | 31 | #[derive(Deref)] 32 | pub struct DomTree { 33 | pub tree: IndexVec>, 34 | } 35 | 36 | #[derive(Deref)] 37 | pub struct DominanceFrontiers { 38 | pub frontiers: IndexVec>, 39 | } 40 | 41 | 42 | // ### block order ### 43 | 44 | #[derive(Deref)] 45 | pub struct BlockOrder { 46 | pub order: Vec, 47 | } 48 | 49 | #[derive(Clone, Copy, Debug, Display, PartialEq, Eq, PartialOrd, Ord, Hash)] 50 | pub struct InstrIndex { 51 | pub value: u32, 52 | } 53 | 54 | impl InstrIndex { 55 | pub const NONE: InstrIndex = InstrIndex { value: u32::MAX }; 56 | } 57 | 58 | #[derive(Deref)] 59 | pub struct BlockBegins { 60 | pub begins: IndexVec, 61 | } 62 | 63 | #[derive(Deref)] 64 | pub struct InstrIndices { 65 | pub indices: IndexVec, 66 | } 67 | 68 | 69 | // ### liveness ### 70 | 71 | pub struct BlockGenKill { 72 | pub gens: IndexVec>, 73 | pub kills: IndexVec>, 74 | } 75 | 76 | pub struct BlockLiveInOut { 77 | pub live_ins: IndexVec>, 78 | pub live_outs: IndexVec>, 79 | } 80 | 81 | #[derive(Deref)] 82 | pub struct LiveIntervals { 83 | pub intervals: IndexVec>, 84 | } 85 | 86 | 87 | 88 | impl PostOrderIndices { 89 | #[inline(always)] 90 | pub fn get_unck(&self, bb: BlockId) -> PostOrderIndex { 91 | self.indices[bb].unwrap_unck() 92 | } 93 | 94 | #[inline(always)] 95 | pub fn get(&self, bb: BlockId) -> Option { 96 | self.indices[bb].to_option() 97 | } 98 | } 99 | 100 | impl ImmediateDominators { 101 | #[inline] 102 | pub fn get_unck(&self, bb: BlockId) -> BlockId { 103 | self.idom[bb] 104 | } 105 | 106 | #[inline] 107 | pub fn get(&self, bb: BlockId) -> Option { 108 | let idom = self.idom[bb]; 109 | (bb.is_entry() || idom != bb).then_some(idom) 110 | } 111 | 112 | #[inline] 113 | pub fn is_unreachable(&self, bb: BlockId) -> bool { 114 | self.get(bb).is_none() 115 | } 116 | 117 | pub fn is_dominated_by(&self, bb: BlockId, dom: BlockId) -> bool { 118 | let mut at = bb; 119 | loop { 120 | if at == dom { 121 | return true; 122 | } 123 | 124 | let idom = self.idom[at]; 125 | if at == idom { 126 | return false; 127 | } 128 | at = idom; 129 | } 130 | } 131 | } 132 | 133 | 134 | impl Function { 135 | pub fn predecessors(&self) -> Predecessors { 136 | let mut preds = index_vec![vec![]; self.num_blocks()]; 137 | 138 | for bb in self.block_ids() { 139 | self.block_successors(bb, |succ| 140 | preds[succ].push(bb)); 141 | } 142 | 143 | Predecessors { preds } 144 | } 145 | 146 | pub fn post_order(&self) -> PostOrder { 147 | let mut post_order = vec![]; 148 | let mut visited = index_vec![false; self.num_blocks()]; 149 | 150 | fn visit(fun: &Function, bb: BlockId, 151 | post_order: &mut Vec, 152 | visited: &mut IndexVec, 153 | ) { 154 | fun.block_successors(bb, |succ| { 155 | if !visited[succ] { 156 | visited[succ] = true; 157 | visit(fun, succ, post_order, visited); 158 | } 159 | }); 160 | 161 | post_order.push(bb); 162 | } 163 | visit(self, BlockId::ENTRY, &mut post_order, &mut visited); 164 | 165 | PostOrder { blocks: post_order } 166 | } 167 | 168 | pub fn post_order_indices(&self, post_order: &PostOrder) -> PostOrderIndices { 169 | let mut indices = index_vec![OptPostOrderIndex::NONE; self.num_blocks()]; 170 | for (index, bb) in post_order.blocks.iter().enumerate() { 171 | indices[*bb] = PostOrderIndex::new_unck(index as u32).some(); 172 | } 173 | PostOrderIndices { indices } 174 | } 175 | 176 | pub fn immediate_dominators(&self, preds: &Predecessors, post_order: &PostOrder, post_indices: &PostOrderIndices) -> ImmediateDominators { 177 | let mut doms = index_vec![None; self.num_blocks()]; 178 | 179 | let bb0 = post_indices.get_unck(BlockId::ENTRY); 180 | doms[bb0] = Some(bb0); 181 | 182 | let mut changed = true; 183 | while changed { 184 | changed = false; 185 | 186 | for bb_id in post_order.iter().rev().copied() { 187 | if bb_id.is_entry() { continue } 188 | 189 | let preds = &preds[bb_id]; 190 | let bb = post_indices.get_unck(bb_id); 191 | 192 | let mut new_dom = post_indices.get_unck(preds[0]); 193 | 194 | for pred_id in preds.iter().skip(1).copied() { 195 | let Some(pred) = post_indices.get(pred_id) else { continue }; 196 | 197 | // intersect. 198 | if doms[pred].is_some() { 199 | let mut x = new_dom; 200 | let mut y = pred; 201 | 202 | while x != y { 203 | while x < y { 204 | x = doms[x].unwrap(); 205 | } 206 | while y < x { 207 | y = doms[y].unwrap(); 208 | } 209 | } 210 | 211 | new_dom = x; 212 | } 213 | } 214 | 215 | if doms[bb] != Some(new_dom) { 216 | doms[bb] = Some(new_dom); 217 | changed = true; 218 | } 219 | } 220 | } 221 | 222 | let idom = self.block_ids() 223 | .map(|bb| { 224 | if let Some(post_index) = post_indices.get(bb) { 225 | let idom_post_index = doms[post_index].unwrap(); 226 | let idom = post_order[idom_post_index.usize()]; 227 | idom 228 | } 229 | else { bb } // unreachable. 230 | }).collect(); 231 | ImmediateDominators { idom } 232 | } 233 | 234 | pub fn dominator_tree(&self, idoms: &ImmediateDominators) -> DomTree { 235 | let mut tree = index_vec![vec![]; self.num_blocks()]; 236 | 237 | for bb in self.block_ids().skip(1) { 238 | if let Some(idom) = idoms.get(bb) { 239 | tree[idom].push(bb); 240 | } 241 | } 242 | 243 | DomTree { tree } 244 | } 245 | 246 | pub fn dominance_frontiers(&self, preds: &Predecessors, idoms: &ImmediateDominators) -> DominanceFrontiers { 247 | let mut frontiers = index_vec![vec![]; self.num_blocks()]; 248 | 249 | for bb in self.block_ids() { 250 | let preds = &preds[bb]; 251 | if preds.len() < 2 { continue } 252 | 253 | let Some(idom) = idoms.get(bb) else { continue }; 254 | 255 | for pred in preds.iter().copied() { 256 | if idoms.is_unreachable(pred) { continue } 257 | 258 | let mut at = pred; 259 | while at != idom { 260 | let df = &mut frontiers[at]; 261 | if !df.contains(&bb) { 262 | df.push(bb); 263 | } 264 | at = idoms.get_unck(at); 265 | } 266 | } 267 | } 268 | 269 | DominanceFrontiers { frontiers } 270 | } 271 | 272 | 273 | pub fn block_order_dominators_first(&self, idoms: &ImmediateDominators, dom_tree: &DomTree) -> BlockOrder { 274 | fn visit(bb: BlockId, order: &mut Vec, visited: &mut IndexVec, 275 | fun: &Function, idom: &ImmediateDominators, dom_tree: &DomTree, 276 | ) { 277 | assert!(!visited[bb]); 278 | visited[bb] = true; 279 | order.push(bb); 280 | 281 | fun.block_successors(bb, |succ| { 282 | if !visited[succ] && idom.get_unck(succ) == bb { 283 | visit(succ, order, visited, fun, idom, dom_tree); 284 | } 285 | }); 286 | 287 | for child in &dom_tree[bb] { 288 | if !visited[*child] { 289 | visit(*child, order, visited, fun, idom, dom_tree); 290 | } 291 | } 292 | } 293 | 294 | let mut order = vec![]; 295 | let mut visited = index_vec![false; self.num_blocks()]; 296 | visit(BlockId::ENTRY, &mut order, &mut visited, self, idoms, dom_tree); 297 | BlockOrder { order } 298 | } 299 | 300 | 301 | pub fn block_gen_kill(&self) -> BlockGenKill { 302 | let mut gens = IndexVec::with_capacity(self.num_blocks()); 303 | let mut kills = IndexVec::with_capacity(self.num_blocks()); 304 | 305 | for bb in self.block_ids() { 306 | let mut gen = index_vec![false; self.num_instrs()]; 307 | let mut kill = index_vec![false; self.num_instrs()]; 308 | 309 | // instructions in reverse. 310 | self.block_instr_rev(bb, |instr| { 311 | if instr.has_value() { 312 | kill[instr.id()] = true; 313 | gen [instr.id()] = false; 314 | } 315 | 316 | if !instr.is_phi() { 317 | instr.args(self, |arg| { 318 | gen[arg] = true; 319 | }); 320 | } 321 | }); 322 | 323 | gens.push(gen); 324 | kills.push(kill); 325 | } 326 | 327 | BlockGenKill { gens, kills } 328 | } 329 | 330 | pub fn block_live_in_out(&self, post_order: &PostOrder, gen_kill: &BlockGenKill) -> BlockLiveInOut { 331 | let BlockGenKill { gens, kills } = gen_kill; 332 | 333 | let mut live_ins = index_vec![index_vec![false; self.num_instrs()]; self.num_blocks()]; 334 | let mut live_outs = index_vec![index_vec![false; self.num_instrs()]; self.num_blocks()]; 335 | let mut changed = true; 336 | while changed { 337 | changed = false; 338 | 339 | for bb in post_order.iter().copied() { 340 | let gen = &gens[bb]; 341 | let kill = &kills[bb]; 342 | 343 | let mut new_live_out = index_vec![false; self.num_instrs()]; 344 | self.block_successors(bb, |succ| { 345 | // live_in. 346 | for (i, live) in live_ins[succ].iter().enumerate() { 347 | if *live { 348 | new_live_out[InstrId::from_usize(i)] = true; 349 | } 350 | } 351 | 352 | // phis: gen bb's args. 353 | self.block_instrs_ex(succ, |instr| { 354 | // @todo: try_phi Instr variant? 355 | if let Some(map) = self.try_phi(instr.id()) { 356 | let src = map.get(bb).unwrap(); 357 | new_live_out[src] = true; 358 | return true; 359 | } 360 | false 361 | }); 362 | }); 363 | 364 | let mut new_live_in = new_live_out.clone(); 365 | for (i, kill) in kill.iter().enumerate() { 366 | if *kill { 367 | new_live_in[InstrId::from_usize(i)] = false; 368 | } 369 | } 370 | for (i, gen) in gen.iter().enumerate() { 371 | if *gen { 372 | new_live_in[InstrId::from_usize(i)] = true; 373 | } 374 | } 375 | 376 | if new_live_in != live_ins[bb] { 377 | changed = true; 378 | live_ins[bb] = new_live_in; 379 | } 380 | if new_live_out != live_outs[bb] { 381 | changed = true; 382 | live_outs[bb] = new_live_out; 383 | } 384 | } 385 | } 386 | 387 | BlockLiveInOut { live_ins, live_outs } 388 | } 389 | 390 | pub fn live_intervals_ex(&self, 391 | block_order: &BlockOrder, block_begins: &BlockBegins, 392 | instr_indices: &InstrIndices, 393 | live_sets: &BlockLiveInOut, 394 | no_empty_intervals: bool, 395 | ) -> LiveIntervals { 396 | let BlockLiveInOut { live_ins: _, live_outs } = live_sets; 397 | 398 | let mut intervals = index_vec![vec![]; self.num_instrs()]; 399 | for bb in block_order.iter().copied() { 400 | let live_out = &live_outs[bb]; 401 | 402 | let num_instrs = self.num_block_instrs(bb); 403 | 404 | let block_begin = block_begins[bb]; 405 | let block_end = InstrIndex { value: block_begin.value + num_instrs as u32 }; 406 | 407 | let mut live = live_out.iter().map(|live| { 408 | live.then(|| block_end) 409 | }).collect::>(); 410 | 411 | #[inline] 412 | fn gen(var: InstrId, stop: InstrIndex, live: &mut IndexVec>) { 413 | if live[var].is_none() { 414 | live[var] = Some(stop); 415 | } 416 | } 417 | 418 | fn kill(var: InstrId, start: InstrIndex, live: &mut IndexVec>, intervals: &mut IndexVec>) { 419 | if let Some(stop) = live[var] { 420 | live[var] = None; 421 | 422 | let interval = &mut intervals[var]; 423 | // try to extend. 424 | if let Some((_, old_stop)) = interval.last_mut() { 425 | if *old_stop == start { 426 | *old_stop = stop; 427 | return; 428 | } 429 | } 430 | // need new range. 431 | interval.push((start, stop)); 432 | return; 433 | } 434 | } 435 | 436 | // instructions. 437 | let mut parallel_copy_pos = None; 438 | let mut phi_pos = None; 439 | self.block_instr_rev(bb, |instr| { 440 | let instr_pos = instr_indices[instr.id()]; 441 | 442 | let instr_pos = 443 | if instr.is_phi() { 444 | if let Some(phi_pos) = phi_pos { 445 | phi_pos 446 | } 447 | else { 448 | phi_pos = Some(instr_pos); 449 | instr_pos 450 | } 451 | } 452 | else if let InstrData::ParallelCopy { src: _, copy_id: id } = instr.data { 453 | if let Some((current_id, current_pos)) = parallel_copy_pos { 454 | if current_id == id { 455 | current_pos 456 | } 457 | else { 458 | parallel_copy_pos = Some((id, instr_pos)); 459 | instr_pos 460 | } 461 | } 462 | else { 463 | parallel_copy_pos = Some((id, instr_pos)); 464 | instr_pos 465 | } 466 | } 467 | else { 468 | instr_pos 469 | }; 470 | 471 | if instr.has_value() { 472 | let start = instr_pos; 473 | if no_empty_intervals { 474 | let mut stop = start; 475 | if instr.is_parallel_copy() { 476 | // parallel copies have the same start position. 477 | // if one is unused (start == stop), its register may be 478 | // released and made available for other copies in the 479 | // same parallel group, which is no good. 480 | stop = InstrIndex { value: start.value + 1 }; 481 | } 482 | gen(instr.id(), stop, &mut live); 483 | } 484 | kill(instr.id(), start, &mut live, &mut intervals) 485 | } 486 | 487 | if !instr.is_phi() { 488 | instr.args(self, |arg| { 489 | let stop = instr_pos; 490 | gen(arg, stop, &mut live); 491 | }); 492 | } 493 | }); 494 | 495 | for id in self.instr_ids() { 496 | let start = block_begin; 497 | kill(id, start, &mut live, &mut intervals); 498 | } 499 | } 500 | 501 | LiveIntervals { intervals } 502 | } 503 | 504 | pub fn live_intervals(&self, post_order: &PostOrder, block_order: &BlockOrder, block_begins: &BlockBegins, instr_indices: &InstrIndices, no_empty_intervals: bool) -> LiveIntervals { 505 | let gen_kill = self.block_gen_kill(); 506 | let live_sets = self.block_live_in_out(&post_order, &gen_kill); 507 | self.live_intervals_ex(block_order, block_begins, instr_indices, &live_sets, no_empty_intervals) 508 | } 509 | } 510 | 511 | 512 | impl BlockOrder { 513 | pub fn block_begins_and_instr_indices(&self, fun: &Function) -> (BlockBegins, InstrIndices) { 514 | let mut block_begins = index_vec![InstrIndex::NONE; fun.num_blocks()]; 515 | let mut instr_indices = index_vec![InstrIndex::NONE; fun.num_instrs()]; 516 | 517 | let mut cursor = InstrIndex { value: 0 }; 518 | for bb in self.order.iter().copied() { 519 | block_begins[bb] = cursor; 520 | 521 | fun.block_instrs(bb, |instr| { 522 | instr_indices[instr.id()] = cursor; 523 | cursor.value += 1; 524 | }); 525 | } 526 | block_begins.push(cursor); 527 | 528 | (BlockBegins { begins: block_begins }, InstrIndices { indices: instr_indices }) 529 | } 530 | } 531 | 532 | 533 | impl crate::index_vec::Key for InstrIndex { 534 | #[inline(always)] 535 | fn from_usize(value: usize) -> Self { 536 | debug_assert_eq!(value as u32 as usize, value); 537 | Self { value: value as u32 } 538 | } 539 | #[inline(always)] 540 | fn usize(self) -> usize { self.value as usize } 541 | } 542 | 543 | 544 | -------------------------------------------------------------------------------- /src/compiler/ast.rs: -------------------------------------------------------------------------------- 1 | use derive_more::Deref; 2 | use crate::macros::define_id; 3 | use crate::infer::LocalId; 4 | 5 | 6 | 7 | #[derive(Clone, Copy, Debug, PartialEq)] 8 | pub struct SourcePos { 9 | pub line: u32, 10 | pub column: u32, 11 | } 12 | 13 | #[derive(Clone, Copy, Debug, PartialEq)] 14 | pub struct SourceRange { 15 | pub begin: SourcePos, 16 | pub end: SourcePos, 17 | } 18 | 19 | 20 | impl core::fmt::Display for SourcePos { 21 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 22 | write!(f, "{}:{}", self.line, self.column) 23 | } 24 | } 25 | impl core::fmt::Display for SourceRange { 26 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 27 | write!(f, "{}-{}", self.begin, self.end) 28 | } 29 | } 30 | 31 | 32 | 33 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 34 | pub enum Op1 { 35 | Not, 36 | Negate, // the real negate. 37 | } 38 | 39 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 40 | pub enum Op2 { 41 | Add, 42 | Sub, 43 | Mul, 44 | Div, 45 | FloorDiv, 46 | Rem, 47 | And, 48 | Or, 49 | OrElse, 50 | CmpEq, 51 | CmpNe, 52 | CmpLe, 53 | CmpLt, 54 | CmpGe, 55 | CmpGt, 56 | } 57 | 58 | 59 | 60 | impl SourcePos { 61 | #[inline(always)] 62 | pub const fn to_range(self) -> SourceRange { 63 | SourceRange { begin: self, end: self } 64 | } 65 | 66 | #[inline(always)] 67 | pub const fn null() -> SourcePos { 68 | SourcePos { line: 0, column: 0 } 69 | } 70 | } 71 | 72 | 73 | 74 | 75 | impl SourceRange { 76 | #[inline(always)] 77 | pub fn is_collapsed(&self) -> bool { 78 | self.begin == self.end 79 | } 80 | 81 | #[inline(always)] 82 | pub const fn null() -> SourceRange { 83 | SourcePos::null().to_range() 84 | } 85 | } 86 | 87 | 88 | impl Op1 { 89 | #[inline] 90 | pub fn str(self) -> &'static str { 91 | use self::Op1::*; 92 | match self { 93 | Not => { "not" } 94 | Negate => { "negate" } 95 | } 96 | } 97 | } 98 | 99 | 100 | impl Op2 { 101 | #[inline] 102 | pub fn str(self) -> &'static str { 103 | use Op2::*; 104 | match self { 105 | Add => { "add" } 106 | Sub => { "sub" } 107 | Mul => { "mul" } 108 | Div => { "div" } 109 | FloorDiv => { "floor_div" } 110 | Rem => { "rem" } 111 | And => { "and" } 112 | Or => { "or" } 113 | OrElse => { "or_else" } 114 | CmpEq => { "cmp_eq" } 115 | CmpNe => { "cmp_ne" } 116 | CmpLe => { "cmp_le" } 117 | CmpLt => { "cmp_lt" } 118 | CmpGe => { "cmp_ge" } 119 | CmpGt => { "cmp_gt" } 120 | } 121 | } 122 | 123 | #[inline] 124 | pub fn is_cancelling(self) -> bool { 125 | use Op2::*; 126 | match self { 127 | And | Or | OrElse => true, 128 | 129 | Add | Sub | Mul | Div | FloorDiv | Rem | 130 | CmpEq | CmpNe | CmpLe | CmpLt | CmpGe | CmpGt => false, 131 | } 132 | } 133 | } 134 | 135 | 136 | define_id!(NodeId, OptNodeId); 137 | 138 | // @todo-cleanup: put these into the macro? 139 | impl NodeId { 140 | pub const ZERO: NodeId = NodeId(0); 141 | 142 | pub fn inc(&mut self) -> NodeId { 143 | assert!(self.0 < u32::MAX - 1); 144 | self.0 += 1; 145 | *self 146 | } 147 | } 148 | 149 | 150 | 151 | define_id!(ItemId, OptItemId); 152 | 153 | impl ItemId { 154 | pub const ZERO: ItemId = ItemId(0); 155 | 156 | pub fn inc(&mut self) -> ItemId { 157 | assert!(self.0 < u32::MAX - 1); 158 | self.0 += 1; 159 | *self 160 | } 161 | } 162 | 163 | 164 | #[derive(Clone, Debug, Deref)] 165 | pub struct Item<'a> { 166 | #[deref] 167 | pub data: ItemData<'a>, 168 | pub source: SourceRange, 169 | pub id: ItemId, // computed by `Infer::assign_ids_*`. 170 | pub num_nodes: u32, // computed by `Infer::assign_ids_*`. 171 | } 172 | 173 | #[derive(Clone, Debug)] 174 | pub enum ItemData<'a> { 175 | Module (item::Module<'a>), 176 | Func (item::Func<'a>), 177 | } 178 | 179 | impl<'a> Item<'a> { 180 | #[inline(always)] 181 | pub fn new(source: SourceRange, data: ItemData<'a>) -> Self { 182 | Item { source, data, id: ItemId::ZERO, num_nodes: 0 } 183 | } 184 | } 185 | 186 | 187 | 188 | #[derive(Clone, Debug, Deref)] 189 | pub struct Stmt<'a> { 190 | #[deref] 191 | pub data: StmtData<'a>, 192 | pub source: SourceRange, 193 | pub id: NodeId, // computed by `Infer::assign_ids_*`. 194 | } 195 | 196 | #[derive(Clone, Debug)] 197 | pub enum StmtData<'a> { 198 | Item (Item<'a>), 199 | Local (expr::Local<'a>), 200 | Expr (Expr<'a>), 201 | Empty, 202 | } 203 | 204 | impl<'a> Stmt<'a> { 205 | #[inline(always)] 206 | pub fn new(source: SourceRange, data: StmtData<'a>) -> Self { 207 | Stmt { source, data, id: NodeId::ZERO } 208 | } 209 | } 210 | 211 | 212 | 213 | #[derive(Clone, Debug, Deref)] 214 | pub struct Expr<'a> { 215 | #[deref] 216 | pub data: ExprData<'a>, 217 | pub source: SourceRange, 218 | pub id: NodeId, // computed by `Infer::assign_ids_*`. 219 | pub ty: Option, 220 | } 221 | 222 | #[derive(Clone, Debug)] 223 | pub enum ExprData<'a> { 224 | Nil, 225 | Bool (bool), 226 | Number (&'a str), 227 | QuotedString (&'a str), 228 | Ident (expr::Ident<'a>), 229 | Tuple (Box>), 230 | List (Box>), 231 | Do (Box>), 232 | SubExpr (Box>), 233 | Op1 (Box>), 234 | Op2 (Box>), 235 | Field (Box>), 236 | Index (Box>), 237 | Call (Box>), 238 | If (Box>), 239 | While (Box>), 240 | Break (Box>), 241 | Continue (Box>), 242 | Return (expr::Return<'a>), 243 | Env, 244 | } 245 | 246 | impl<'a> Expr<'a> { 247 | #[inline(always)] 248 | pub fn new(source: SourceRange, data: ExprData<'a>) -> Self { 249 | Expr { source, data, id: NodeId::ZERO, ty: None } 250 | } 251 | 252 | #[inline(always)] 253 | pub fn to_stmt(self) -> Stmt<'a> { 254 | Stmt::new(self.source, StmtData::Expr(self)) 255 | } 256 | } 257 | 258 | 259 | 260 | pub mod item { 261 | use super::*; 262 | 263 | #[derive(Clone, Debug)] 264 | pub struct Func<'a> { 265 | pub name: Option<&'a str>, 266 | pub params: Vec>, 267 | pub body: Vec>, 268 | } 269 | 270 | #[derive(Clone, Copy, Debug)] 271 | pub struct FuncParam<'a> { 272 | pub name: &'a str, 273 | } 274 | 275 | 276 | #[derive(Clone, Debug)] 277 | pub struct Module<'a> { 278 | pub source: SourceRange, 279 | pub block: expr::Block<'a>, 280 | } 281 | } 282 | 283 | 284 | pub mod expr { 285 | use super::*; 286 | 287 | #[derive(Clone, Copy, Debug)] 288 | pub struct Ident<'a> { 289 | pub name: &'a str, 290 | pub info: Option, 291 | } 292 | 293 | #[derive(Clone, Copy, Debug)] 294 | pub enum IdentTarget { 295 | Item (ItemId), 296 | Local { node: NodeId, local: LocalId }, 297 | Dynamic, 298 | } 299 | 300 | impl IdentTarget { 301 | #[inline(always)] 302 | pub fn is_item(self) -> bool { if let IdentTarget::Item(_) = self { true } else { false } } 303 | } 304 | 305 | #[derive(Clone, Copy, Debug)] 306 | pub struct IdentInfo { 307 | pub target: IdentTarget, 308 | } 309 | 310 | 311 | #[derive(Clone, Debug)] 312 | pub struct Local<'a> { 313 | pub name: &'a str, 314 | pub value: Option>, 315 | pub kind: LocalKind, 316 | pub info: Option, 317 | } 318 | 319 | #[derive(Clone, Debug)] 320 | pub enum LocalKind { 321 | Let, 322 | Var, 323 | } 324 | 325 | #[derive(Clone, Copy, Debug)] 326 | pub struct LocalInfo { 327 | pub id: crate::infer::LocalId, 328 | } 329 | 330 | 331 | 332 | #[derive(Clone, Debug)] 333 | pub struct Tuple<'a> { 334 | pub values: Vec>, 335 | } 336 | 337 | #[derive(Clone, Debug)] 338 | pub struct List<'a> { 339 | pub values: Vec>, 340 | } 341 | 342 | #[derive(Clone, Debug)] 343 | pub struct Map<'a> { 344 | pub values: Vec<(Expr<'a>, Expr<'a>)>, 345 | } 346 | 347 | 348 | 349 | 350 | #[derive(Clone, Debug)] 351 | pub struct Op1<'a> { 352 | pub kind: Op1Kind, 353 | pub child: Expr<'a>, 354 | } 355 | 356 | #[derive(Clone, Copy, Debug, PartialEq)] 357 | pub struct Op1Kind (pub super::Op1); 358 | 359 | 360 | #[derive(Clone, Debug)] 361 | pub struct Op2<'a> { 362 | pub kind: Op2Kind, 363 | pub children: [Expr<'a>; 2], 364 | } 365 | 366 | #[derive(Clone, Copy, Debug, PartialEq)] 367 | pub enum Op2Kind { 368 | Op2 (super::Op2), 369 | Op2Assign (super::Op2), 370 | Assign, 371 | Define, 372 | } 373 | 374 | impl Op2Kind { 375 | #[inline] 376 | pub fn lprec(self) -> u32 { 377 | use Op2Kind::*; 378 | use super::Op2::*; 379 | match self { 380 | Assign => 100, 381 | Define => 100, 382 | Op2Assign(_) => 100, 383 | Op2(op) => match op { 384 | OrElse => 150, 385 | Or => 200, 386 | And => 300, 387 | CmpEq => 400, 388 | CmpNe => 400, 389 | CmpLe => 400, 390 | CmpLt => 400, 391 | CmpGe => 400, 392 | CmpGt => 400, 393 | Add => 600, 394 | Sub => 600, 395 | Mul => 800, 396 | Div => 800, 397 | FloorDiv => 800, 398 | Rem => 800, 399 | } 400 | } 401 | } 402 | 403 | #[inline] 404 | pub fn rprec(self) -> u32 { 405 | use Op2Kind::*; 406 | use super::Op2::*; 407 | match self { 408 | Assign => 100, 409 | Define => 100, 410 | Op2Assign(_) => 100, 411 | Op2(op) => match op { 412 | OrElse => 151, 413 | Or => 201, 414 | And => 301, 415 | CmpEq => 401, 416 | CmpNe => 401, 417 | CmpLe => 401, 418 | CmpLt => 401, 419 | CmpGe => 401, 420 | CmpGt => 401, 421 | Add => 601, 422 | Sub => 601, 423 | Mul => 801, 424 | Div => 801, 425 | FloorDiv => 801, 426 | Rem => 801, 427 | } 428 | } 429 | } 430 | } 431 | 432 | pub const PREC_PREFIX: u32 = 900; 433 | pub const PREC_POSTFIX: u32 = 1000; 434 | 435 | 436 | #[derive(Clone, Debug)] 437 | pub struct Field<'a> { 438 | pub base: Expr<'a>, 439 | pub name: &'a str, 440 | } 441 | 442 | #[derive(Clone, Debug)] 443 | pub struct Index<'a> { 444 | pub base: Expr<'a>, 445 | pub index: Expr<'a>, 446 | } 447 | 448 | #[derive(Clone, Debug)] 449 | pub struct Call<'a> { 450 | pub func: Expr<'a>, 451 | pub args: Vec>, 452 | } 453 | 454 | 455 | 456 | #[derive(Clone, Debug)] 457 | pub struct Block<'a> { 458 | pub stmts: Vec>, 459 | } 460 | 461 | #[derive(Clone, Debug)] 462 | pub struct Do<'a> { 463 | pub label: Option<&'a str>, 464 | pub stmts: Vec>, 465 | } 466 | 467 | 468 | #[derive(Clone, Debug)] 469 | pub struct IfBlock<'a> { 470 | pub stmts: Vec>, 471 | pub is_do: bool, 472 | } 473 | 474 | #[derive(Clone, Debug)] 475 | pub struct If<'a> { 476 | pub condition: Expr<'a>, 477 | pub on_true: IfBlock<'a>, 478 | pub on_false: Option>, 479 | } 480 | 481 | #[derive(Clone, Debug)] 482 | pub struct While<'a> { 483 | pub label: Option<&'a str>, 484 | pub condition: Expr<'a>, 485 | pub body: Vec>, 486 | } 487 | 488 | 489 | #[derive(Clone, Debug)] 490 | pub struct Break<'a> { 491 | pub label: Option<&'a str>, 492 | pub value: Option>>, 493 | pub info: Option>, 494 | } 495 | 496 | #[derive(Clone, Debug)] 497 | pub struct Continue<'a> { 498 | pub label: Option<&'a str>, 499 | pub info: Option>, 500 | } 501 | 502 | #[derive(Clone, Copy, Debug)] 503 | pub struct BreakInfo { 504 | pub node: NodeId, 505 | pub scope_index: u32, 506 | } 507 | 508 | 509 | #[derive(Clone, Debug)] 510 | pub struct Return<'a> { 511 | pub value: Option>>, 512 | } 513 | } 514 | 515 | -------------------------------------------------------------------------------- /src/compiler/bbir_builder.rs: -------------------------------------------------------------------------------- 1 | use crate::index_vec::*; 2 | use crate::ast::*; 3 | use crate::infer; 4 | use crate::bbir::{*, self}; 5 | use crate::ast::ItemData; 6 | 7 | 8 | pub struct Builder { 9 | pub krate: bbir::Crate, 10 | } 11 | 12 | impl Builder { 13 | pub fn new() -> Self { 14 | Builder { 15 | krate: bbir::Crate::new(), 16 | } 17 | } 18 | 19 | pub fn build(&mut self, module: &item::Module) { 20 | self.build_module(NodeId::new_unck(1), module); 21 | } 22 | 23 | fn build_module(&mut self, module_id: NodeId, module: &item::Module) { 24 | let mut fun = self.krate.new_function(); 25 | let mut ctx = Ctx::new(&mut fun, module_id, &[]); 26 | 27 | let stmts = &module.block.stmts; 28 | 29 | let as_value_block = false; 30 | let value = 31 | if as_value_block { 32 | self.build_value_block(&mut ctx, module_id, stmts, true).unwrap() 33 | } 34 | else { 35 | self.build_block(&mut ctx, stmts); 36 | ctx.fun.instr_load_unit((None.into(), None.into())) 37 | }; 38 | ctx.fun.instr_return(None.into(), value); 39 | } 40 | 41 | fn build_stmt(&mut self, ctx: &mut Ctx, stmt: &Stmt) { 42 | match &stmt.data { 43 | StmtData::Item(item) => { 44 | match &item.data { 45 | ItemData::Module(module) => { 46 | let _ = module; 47 | unimplemented!() 48 | } 49 | 50 | ItemData::Func(func) => { 51 | let func_id = self.build_func(ctx, stmt.id, func); 52 | self.krate.def_item(item.id, bbir::Item { 53 | data: bbir::ItemData::Func(func_id) 54 | }); 55 | } 56 | } 57 | } 58 | 59 | StmtData::Local(local) => { 60 | let v = 61 | if let Some(value) = &local.value { 62 | self.build_expr(ctx, value, true).unwrap() 63 | } 64 | else { 65 | ctx.fun.instr_load_unit((stmt.id.some(), None.into())) 66 | }; 67 | 68 | let lid = ctx.add_local_decl(stmt.id, local.name, 69 | stmt.id, local.info.unwrap().id); 70 | ctx.fun.instr_set_local((stmt.id.some(), stmt.id.some()), lid, v); 71 | } 72 | 73 | StmtData::Expr (expr) => { 74 | self.build_expr(ctx, expr, false); 75 | } 76 | 77 | StmtData::Empty => (), 78 | } 79 | } 80 | 81 | fn build_expr(&mut self, ctx: &mut Ctx, expr: &Expr, need_value: bool) -> Option { 82 | match &expr.data { 83 | ExprData::Nil => { 84 | Some(ctx.fun.instr_load_nil((expr.id.some(), expr.id.some()))) 85 | } 86 | 87 | ExprData::Bool (value) => { 88 | Some(ctx.fun.instr_load_bool((expr.id.some(), expr.id.some()), *value)) 89 | } 90 | 91 | ExprData::Number (value) => { 92 | let value = value.parse().unwrap(); 93 | Some(ctx.fun.instr_load_int((expr.id.some(), expr.id.some()), value)) 94 | } 95 | 96 | ExprData::QuotedString (value) => { 97 | let string = ctx.fun.add_string(value); 98 | Some(ctx.fun.instr_load_string((expr.id.some(), expr.id.some()), string)) 99 | } 100 | 101 | ExprData::Ident (ident) => { 102 | let info = ident.info.unwrap(); 103 | 104 | match info.target { 105 | expr::IdentTarget::Item(item) => { 106 | let index = ctx.fun.instr_load_int((expr.id.some(), None.into()), item.value() as i64); 107 | Some(ctx.fun.instr_read_path((expr.id.some(), expr.id.some()), PathBase::Items, &[PathKey::Index(index)])) 108 | } 109 | 110 | expr::IdentTarget::Local { node, local } => { 111 | let local = ctx.get_local_id(node, local); 112 | Some(ctx.fun.instr_get_local((expr.id.some(), expr.id.some()), local)) 113 | } 114 | 115 | expr::IdentTarget::Dynamic => { 116 | let name = ctx.fun.add_string(ident.name); 117 | Some(ctx.fun.instr_read_path((expr.id.some(), expr.id.some()), PathBase::Env, &[PathKey::Field(name)])) 118 | } 119 | } 120 | } 121 | 122 | ExprData::Tuple (tuple) => { 123 | let mut values = vec![]; 124 | for v in &tuple.values { 125 | values.push(self.build_expr(ctx, v, true).unwrap()); 126 | } 127 | Some(ctx.fun.instr_tuple_new((expr.id.some(), expr.id.some()), &values)) 128 | } 129 | 130 | ExprData::List (list) => { 131 | let mut values = Vec::with_capacity(list.values.len()); 132 | for v in &list.values { 133 | values.push(self.build_expr(ctx, v, true).unwrap()); 134 | } 135 | Some(ctx.fun.instr_list_new((expr.id.some(), expr.id.some()), &values)) 136 | } 137 | 138 | ExprData::Do (doo) => { 139 | self.build_do_block(ctx, expr.id, &doo.stmts, need_value) 140 | } 141 | 142 | ExprData::SubExpr (sub_expr) => { 143 | self.build_expr(ctx, sub_expr, need_value) 144 | } 145 | 146 | ExprData::Op1 (op1) => { 147 | let src = self.build_expr(ctx, &op1.child, true).unwrap(); 148 | let op = op1.kind.0; 149 | Some(ctx.fun.instr_op1((expr.id.some(), expr.id.some()), op, src)) 150 | } 151 | 152 | ExprData::Op2 (op2) => { 153 | match op2.kind { 154 | expr::Op2Kind::Assign | expr::Op2Kind::Define => { 155 | if let ExprData::Tuple(lhs) = &op2.children[0].data { 156 | if op2.kind != expr::Op2Kind::Assign { 157 | // todo: error. 158 | unimplemented!() 159 | } 160 | 161 | let lhs = &lhs.values; 162 | 163 | if let ExprData::Tuple(rhs) = &op2.children[1].data { 164 | let rhs = &rhs.values; 165 | if lhs.len() != rhs.len() { 166 | // todo: error. 167 | unimplemented!() 168 | } 169 | 170 | let mut values = Vec::with_capacity(rhs.len()); 171 | for v in rhs { 172 | values.push(self.build_expr(ctx, v, true).unwrap()); 173 | } 174 | 175 | for i in 0..lhs.len() { 176 | self.build_assign(ctx, &lhs[i], values[i], false); 177 | } 178 | } 179 | else { 180 | unimplemented!() 181 | } 182 | } 183 | else { 184 | let value = self.build_expr(ctx, &op2.children[1], true).unwrap(); 185 | let is_define = op2.kind == expr::Op2Kind::Define; 186 | self.build_assign(ctx, &op2.children[0], value, is_define); 187 | } 188 | need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), expr.id.some()))) 189 | } 190 | 191 | expr::Op2Kind::Op2Assign(op) => { 192 | if op.is_cancelling() { 193 | unimplemented!() 194 | } 195 | 196 | let src1 = self.build_expr(ctx, &op2.children[0], true).unwrap(); 197 | let src2 = self.build_expr(ctx, &op2.children[1], true).unwrap(); 198 | 199 | let value = ctx.fun.instr_op2((expr.id.some(), None.into()), op, src1, src2); 200 | let is_def = false; 201 | self.build_assign(ctx, &op2.children[0], value, is_def); 202 | 203 | need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), expr.id.some()))) 204 | } 205 | 206 | expr::Op2Kind::Op2(op) => { 207 | if op.is_cancelling() { 208 | let bb_2 = ctx.fun.new_block(); 209 | let bb_after = ctx.fun.new_block(); 210 | 211 | // first value + cancel. 212 | let src1 = self.build_expr(ctx, &op2.children[0], true).unwrap(); 213 | match op { 214 | Op2::And => { ctx.fun.instr_switch_bool(expr.id.some(), src1, bb_2, bb_after); } 215 | Op2::Or => { ctx.fun.instr_switch_bool(expr.id.some(), src1, bb_after, bb_2); } 216 | Op2::OrElse => { ctx.fun.instr_switch_nil(expr.id.some(), src1, bb_2, bb_after); } 217 | 218 | _ => unreachable!() 219 | } 220 | let bb_1 = ctx.fun.get_current_block(); 221 | 222 | // second value. 223 | ctx.fun.set_current_block(bb_2); 224 | let src2 = self.build_expr(ctx, &op2.children[1], true).unwrap(); 225 | ctx.fun.instr_jump(expr.id.some(), bb_after); 226 | let bb_2 = ctx.fun.get_current_block(); 227 | 228 | // join. 229 | ctx.fun.set_current_block(bb_after); 230 | Some(ctx.fun.instr_phi((expr.id.some(), expr.id.some()), &[(bb_1, src1), (bb_2, src2)])) 231 | } 232 | else { 233 | let src1 = self.build_expr(ctx, &op2.children[0], true).unwrap(); 234 | let src2 = self.build_expr(ctx, &op2.children[1], true).unwrap(); 235 | Some(ctx.fun.instr_op2((expr.id.some(), expr.id.some()), op, src1, src2)) 236 | } 237 | } 238 | } 239 | } 240 | 241 | ExprData::Field (_) => { 242 | self.build_read_path(ctx, expr) 243 | } 244 | 245 | ExprData::Index (_) => { 246 | self.build_read_path(ctx, expr) 247 | } 248 | 249 | ExprData::Call (call) => { 250 | let func = self.build_expr(ctx, &call.func, true).unwrap(); 251 | let mut args = vec![]; 252 | for arg in &call.args { 253 | args.push(self.build_expr(ctx, arg, true).unwrap()); 254 | } 255 | Some(ctx.fun.instr_call((expr.id.some(), expr.id.some()), func, &args)) 256 | } 257 | 258 | ExprData::If (iff) => { 259 | let bb_true = ctx.fun.new_block(); 260 | let bb_false = ctx.fun.new_block(); 261 | let after_if = ctx.fun.new_block(); 262 | 263 | // condition. 264 | let cond = self.build_expr(ctx, &iff.condition, true).unwrap(); 265 | ctx.fun.instr_switch_bool(expr.id.some(), cond, bb_true, bb_false); 266 | 267 | 268 | // on_true 269 | ctx.fun.set_current_block(bb_true); 270 | let value_true = self.build_if_block(ctx, expr.id, &iff.on_true, need_value); 271 | let on_true_src = None.into(); // @todo-dbg-info 272 | ctx.fun.instr_jump(on_true_src, after_if); 273 | let bb_true_last = ctx.fun.get_current_block(); 274 | 275 | 276 | // on_false 277 | ctx.fun.set_current_block(bb_false); 278 | let (value_false, on_false_src) = 279 | if let Some(on_false) = &iff.on_false { 280 | let v = self.build_if_block(ctx, expr.id, on_false, need_value); 281 | (v, None.into()) // @todo-dbg-info 282 | } 283 | else { 284 | let v = need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), None.into()))); 285 | (v, expr.id.some()) 286 | }; 287 | ctx.fun.instr_jump(on_false_src, after_if); 288 | let bb_false_last = ctx.fun.get_current_block(); 289 | 290 | 291 | ctx.fun.set_current_block(after_if); 292 | need_value.then(|| 293 | ctx.fun.instr_phi((expr.id.some(), expr.id.some()), &[ 294 | (bb_true_last, value_true.unwrap()), 295 | (bb_false_last, value_false.unwrap()), 296 | ])) 297 | } 298 | 299 | ExprData::While (whilee) => { 300 | let bb_head = ctx.fun.new_block(); 301 | let bb_body = ctx.fun.new_block(); 302 | let bb_after = ctx.fun.new_block(); 303 | 304 | ctx.fun.instr_jump(expr.id.some(), bb_head); 305 | 306 | 307 | // head. 308 | ctx.fun.set_current_block(bb_head); 309 | let cond = self.build_expr(ctx, &whilee.condition, true).unwrap(); 310 | ctx.fun.instr_switch_bool(expr.id.some(), cond, bb_body, bb_after); 311 | 312 | 313 | let bs = ctx.begin_break_scope(expr.id, bb_after, bb_head.some(), false); 314 | 315 | // body. 316 | ctx.fun.set_current_block(bb_body); 317 | self.build_block(ctx, &whilee.body); 318 | ctx.fun.instr_jump(expr.id.some(), bb_head); 319 | 320 | ctx.end_break_scope(bs); 321 | 322 | 323 | ctx.fun.set_current_block(bb_after); 324 | need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), expr.id.some()))) 325 | } 326 | 327 | ExprData::Break(brk) => { 328 | let value = 329 | if let Some(v) = &brk.value { 330 | Some(self.build_expr(ctx, v, true).unwrap()) 331 | } else { None }; 332 | 333 | let info = brk.info.unwrap(); 334 | 335 | if let Some(info) = info { 336 | let scope = &mut ctx.break_scopes[info.scope_index as usize]; 337 | assert_eq!(scope.node, info.node); 338 | let bb_break = scope.bb_break; 339 | 340 | if let Some(values) = &mut scope.values { 341 | let value = value.unwrap_or_else(|| 342 | ctx.fun.instr_load_unit((expr.id.some(), None.into()))); 343 | values.push((ctx.fun.get_current_block(), value)); 344 | } 345 | else if value.is_some() { 346 | println!("info: ignoring error BreakTargetTakesNoValue."); 347 | } 348 | 349 | ctx.fun.instr_jump(expr.id.some(), bb_break); 350 | 351 | let bb_unreach = ctx.fun.new_block(); 352 | ctx.fun.set_current_block(bb_unreach); 353 | } 354 | 355 | need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), expr.id.some()))) 356 | } 357 | 358 | ExprData::Continue(cont) => { 359 | let info = cont.info.unwrap(); 360 | 361 | if let Some(info) = info { 362 | let scope = &mut ctx.break_scopes[info.scope_index as usize]; 363 | assert_eq!(scope.node, info.node); 364 | let bb_continue = scope.bb_continue.unwrap(); 365 | 366 | ctx.fun.instr_jump(expr.id.some(), bb_continue); 367 | 368 | let bb_unreach = ctx.fun.new_block(); 369 | ctx.fun.set_current_block(bb_unreach); 370 | } 371 | 372 | need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), expr.id.some()))) 373 | } 374 | 375 | ExprData::Return (returnn) => { 376 | let value = 377 | if let Some(value) = &returnn.value { 378 | self.build_expr(ctx, value, true).unwrap() 379 | } 380 | else { 381 | ctx.fun.instr_load_unit((expr.id.some(), None.into())) 382 | }; 383 | ctx.fun.instr_return(expr.id.some(), value); 384 | 385 | let new_block = ctx.fun.new_block(); 386 | ctx.fun.set_current_block(new_block); 387 | 388 | need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), expr.id.some()))) 389 | } 390 | 391 | ExprData::Env => { 392 | // @temp-no-env-access. 393 | //Some(ctx.fun.instr_load_env(expr.source)) 394 | println!("ignoring error: no env access"); 395 | need_value.then(|| ctx.fun.instr_load_unit((expr.id.some(), None.into()))) 396 | } 397 | } 398 | } 399 | 400 | fn build_block(&mut self, ctx: &mut Ctx, block: &[Stmt]) { 401 | for stmt in block { 402 | self.build_stmt(ctx, stmt); 403 | } 404 | } 405 | 406 | fn build_do_block(&mut self, ctx: &mut Ctx, node: NodeId, block: &[Stmt], need_value: bool) -> Option { 407 | let bb_after = ctx.fun.new_block(); 408 | 409 | let bs = ctx.begin_break_scope(node, bb_after, None.into(), need_value); 410 | self.build_block(ctx, block); 411 | let values = ctx.end_break_scope(bs); 412 | 413 | let default_block = ctx.fun.get_current_block(); 414 | let default_value = need_value.then(|| 415 | ctx.fun.instr_load_unit((None.into(), None.into()))); 416 | ctx.fun.instr_jump(None.into(), bb_after); 417 | ctx.fun.set_current_block(bb_after); 418 | 419 | need_value.then(|| { 420 | let mut values = values.unwrap(); 421 | values.push((default_block, default_value.unwrap())); 422 | ctx.fun.instr_phi((None.into(), node.some()), &values) 423 | }) 424 | } 425 | 426 | fn build_value_block(&mut self, ctx: &mut Ctx, node: NodeId, block: &[Stmt], need_value: bool) -> Option { 427 | if block.len() == 1 { 428 | if let StmtData::Expr(expr) = &block[0].data { 429 | return self.build_expr(ctx, expr, need_value); 430 | } 431 | } 432 | 433 | self.build_block(ctx, block); 434 | need_value.then(|| ctx.fun.instr_load_unit((None.into(), node.some()))) 435 | } 436 | 437 | fn build_if_block(&mut self, ctx: &mut Ctx, node: NodeId, block: &expr::IfBlock, need_value: bool) -> Option { 438 | if block.is_do { 439 | self.build_do_block(ctx, node, &block.stmts, need_value) 440 | } 441 | else { 442 | self.build_value_block(ctx, node, &block.stmts, need_value) 443 | } 444 | } 445 | 446 | fn build_path(&mut self, ctx: &mut Ctx, expr: &Expr) -> Option<(PathBase, bbir::OptLocalId, Vec)> { 447 | fn rec(this: &mut Builder, ctx: &mut Ctx, expr: &Expr, keys: &mut Vec) -> Option<(PathBase, bbir::OptLocalId)> { 448 | match &expr.data { 449 | ExprData::Field(field) => { 450 | let result = rec(this, ctx, &field.base, keys)?; 451 | keys.push(PathKey::Field( 452 | ctx.fun.add_string(field.name))); 453 | Some(result) 454 | } 455 | 456 | ExprData::Index(index) => { 457 | let result = rec(this, ctx, &index.base, keys)?; 458 | keys.push(PathKey::Index( 459 | this.build_expr(ctx, &index.index, true).unwrap())); 460 | Some(result) 461 | } 462 | 463 | ExprData::Ident(ident) => { 464 | let info = ident.info.unwrap(); 465 | 466 | match info.target { 467 | expr::IdentTarget::Item(item) => { 468 | let _ = item; 469 | unimplemented!() 470 | } 471 | 472 | expr::IdentTarget::Local { node, local } => { 473 | let local = ctx.get_local_id(node, local); 474 | Some((PathBase::Instr(ctx.fun.instr_get_local((expr.id.some(), expr.id.some()), local)), local.some())) 475 | } 476 | 477 | expr::IdentTarget::Dynamic => { 478 | keys.push(PathKey::Field(ctx.fun.add_string(ident.name))); 479 | Some((PathBase::Env, None.into())) 480 | } 481 | } 482 | } 483 | 484 | ExprData::Env => { 485 | Some((PathBase::Env, None.into())) 486 | } 487 | 488 | _ => { 489 | println!("ignoring error: invalid path base"); 490 | None 491 | } 492 | } 493 | } 494 | 495 | let mut keys = vec![]; 496 | let (base, lid) = rec(self, ctx, expr, &mut keys)?; 497 | Some((base, lid, keys)) 498 | } 499 | 500 | fn build_read_path(&mut self, ctx: &mut Ctx, expr: &Expr) -> Option { 501 | if let Some((base, _, keys)) = self.build_path(ctx, expr) { 502 | return Some(ctx.fun.instr_read_path((expr.id.some(), expr.id.some()), base, &keys)) 503 | } 504 | // ignore error. 505 | Some(ctx.fun.instr_load_unit((expr.id.some(), expr.id.some()))) 506 | } 507 | 508 | fn build_assign(&mut self, ctx: &mut Ctx, lhs: &Expr, rhs: InstrId, is_def: bool) { 509 | if let ExprData::Ident(ident) = lhs.data { 510 | let info = ident.info.unwrap(); 511 | 512 | match info.target { 513 | expr::IdentTarget::Item(item) => { 514 | let index = ctx.fun.instr_load_int((lhs.id.some(), None.into()), item.value() as i64); 515 | ctx.fun.instr_write_path((lhs.id.some(), None.into()), PathBase::Items, &[PathKey::Index(index)], rhs, is_def); 516 | } 517 | 518 | expr::IdentTarget::Local { node, local } => { 519 | let local = ctx.get_local_id(node, local); 520 | ctx.fun.instr_set_local((lhs.id.some(), lhs.id.some()), local, rhs); 521 | } 522 | 523 | expr::IdentTarget::Dynamic => { 524 | let name = ctx.fun.add_string(ident.name); 525 | ctx.fun.instr_write_path((lhs.id.some(), None.into()), PathBase::Env, &[PathKey::Field(name)], rhs, is_def); 526 | } 527 | } 528 | } 529 | else if let ExprData::Env = lhs.data { 530 | println!("ignored error: tried to assign to ENV"); 531 | } 532 | else if let ExprData::Field(_) | ExprData::Index(_) = lhs.data { 533 | if let Some((base, lid, keys)) = self.build_path(ctx, lhs) { 534 | let new_value = ctx.fun.instr_write_path((lhs.id.some(), None.into()), base, &keys, rhs, is_def); 535 | if let Some(lid) = lid.to_option() { 536 | // todo: this is scuffed. 537 | ctx.fun.instr_set_local((lhs.id.some(), None.into()), lid, new_value); 538 | } 539 | } 540 | } 541 | else { 542 | println!("ignoring error: invalid assign target"); 543 | } 544 | } 545 | 546 | fn build_func(&mut self, ctx: &mut Ctx, node: NodeId, func: &item::Func) -> FunctionId { 547 | let _ = ctx; 548 | 549 | let mut inner_fun = self.krate.new_function(); 550 | let mut inner_ctx = Ctx::new(&mut inner_fun, node, &func.params); 551 | 552 | let value = self.build_value_block(&mut inner_ctx, node, &func.body, true).unwrap(); 553 | inner_ctx.fun.instr_return(node.some(), value); 554 | 555 | inner_ctx.fun.id() 556 | } 557 | } 558 | 559 | 560 | 561 | struct BreakScope { 562 | node: NodeId, 563 | bb_break: BlockId, 564 | bb_continue: OptBlockId, 565 | values: Option>, 566 | } 567 | 568 | 569 | 570 | struct Ctx<'a> { 571 | fun: &'a mut Function, 572 | locals: IndexVec, 573 | break_scopes: Vec, 574 | } 575 | 576 | impl<'a> Ctx<'a> { 577 | #[inline(always)] 578 | pub fn new(fun: &'a mut Function, node: NodeId, params: &[item::FuncParam]) -> Self { 579 | let mut locals = index_vec![]; 580 | for param in params { 581 | let lid = fun.new_param(param.name, node); 582 | locals.push((node, lid)); 583 | } 584 | 585 | Ctx { fun, locals, break_scopes: vec![] } 586 | } 587 | 588 | pub fn add_local_decl(&mut self, source: NodeId, name: &str, node: NodeId, local: infer::LocalId) -> bbir::LocalId { 589 | assert_eq!(local.value(), self.locals.len() as u32); 590 | let lid = self.fun.new_local(name, source); 591 | self.locals.push((node, lid)); 592 | lid 593 | } 594 | 595 | pub fn get_local_id(&mut self, node: NodeId, local: infer::LocalId) -> bbir::LocalId { 596 | let (entry_node, lid) = self.locals[local]; 597 | assert_eq!(node, entry_node); 598 | lid 599 | } 600 | 601 | pub fn begin_break_scope(&mut self, node: NodeId, bb_break: BlockId, bb_continue: OptBlockId, has_value: bool) -> u32 { 602 | let index = self.break_scopes.len() as u32; 603 | self.break_scopes.push(BreakScope { 604 | node, bb_break, bb_continue, 605 | values: has_value.then(|| vec![]), 606 | }); 607 | index 608 | } 609 | 610 | pub fn end_break_scope(&mut self, scope: u32) -> Option> { 611 | assert_eq!(scope + 1, self.break_scopes.len() as u32); 612 | self.break_scopes.pop().unwrap().values 613 | } 614 | } 615 | 616 | -------------------------------------------------------------------------------- /src/compiler/infer.rs: -------------------------------------------------------------------------------- 1 | use crate::macros::define_id; 2 | use super::ast::*; 3 | 4 | 5 | #[derive(Clone, Debug)] 6 | pub enum Type { 7 | None, 8 | Error, 9 | 10 | Any, 11 | Nil, 12 | Bool, 13 | Number, 14 | String, 15 | Unit, 16 | Tuple (Box<[Type]>), // this is actually 16 bytes, oops. 17 | List (Box), 18 | Map (Box<(Type, Type)>), 19 | Func (Box<(Box<[Type]>, Type)>), 20 | } 21 | 22 | 23 | 24 | pub struct Infer { 25 | prev_item_id: ItemId, 26 | prev_node_id: NodeId, 27 | } 28 | 29 | impl Infer { 30 | pub fn new() -> Self { 31 | Infer { 32 | prev_item_id: ItemId::ZERO, 33 | prev_node_id: NodeId::ZERO, 34 | } 35 | } 36 | 37 | #[inline(always)] 38 | fn next_item_id(&mut self) -> ItemId { self.prev_item_id.inc() } 39 | 40 | #[inline(always)] 41 | fn next_node_id(&mut self) -> NodeId { self.prev_node_id.inc() } 42 | 43 | pub fn assign_ids(&mut self, module: &mut item::Module) { 44 | self.assign_ids_block(&mut module.block.stmts); 45 | } 46 | 47 | fn assign_ids_stmt(&mut self, stmt: &mut Stmt) { 48 | stmt.id = self.next_node_id(); 49 | 50 | match &mut stmt.data { 51 | StmtData::Item (item) => { 52 | item.id = self.next_item_id(); 53 | 54 | let id0 = self.prev_node_id; 55 | match &mut item.data { 56 | ItemData::Module(module) => { 57 | let _ = module; 58 | unimplemented!() 59 | } 60 | 61 | ItemData::Func(func) => { 62 | self.assign_ids_block(&mut func.body); 63 | } 64 | } 65 | let id1 = self.prev_node_id; 66 | item.num_nodes = id1.value() - id0.value(); 67 | } 68 | 69 | StmtData::Local (local) => { 70 | if let Some(value) = &mut local.value { 71 | self.assign_ids_expr(value); 72 | } 73 | } 74 | 75 | StmtData::Expr (expr) => { self.assign_ids_expr(expr); } 76 | 77 | StmtData::Empty => (), 78 | } 79 | } 80 | 81 | fn assign_ids_expr(&mut self, expr: &mut Expr) { 82 | expr.id = self.next_node_id(); 83 | 84 | match &mut expr.data { 85 | ExprData::Nil | 86 | ExprData::Bool (_) | 87 | ExprData::Number (_) | 88 | ExprData::QuotedString (_) | 89 | ExprData::Ident (_) => {} 90 | 91 | 92 | ExprData::Tuple (tuple) => { 93 | for value in &mut tuple.values { 94 | self.assign_ids_expr(value); 95 | } 96 | } 97 | 98 | ExprData::List (list) => { 99 | for value in &mut list.values { 100 | self.assign_ids_expr(value); 101 | } 102 | } 103 | 104 | ExprData::Do (doo) => { 105 | self.assign_ids_block(&mut doo.stmts); 106 | } 107 | 108 | ExprData::SubExpr (sub_expr) => { 109 | self.assign_ids_expr(sub_expr); 110 | } 111 | 112 | ExprData::Op1 (op1) => { 113 | self.assign_ids_expr(&mut op1.child); 114 | } 115 | 116 | ExprData::Op2 (op2) => { 117 | self.assign_ids_expr(&mut op2.children[0]); 118 | self.assign_ids_expr(&mut op2.children[1]); 119 | } 120 | 121 | ExprData::Field (field) => { 122 | self.assign_ids_expr(&mut field.base); 123 | } 124 | 125 | ExprData::Index (index) => { 126 | self.assign_ids_expr(&mut index.base); 127 | self.assign_ids_expr(&mut index.index); 128 | } 129 | 130 | ExprData::Call (call) => { 131 | self.assign_ids_expr(&mut call.func); 132 | for arg in &mut call.args { 133 | self.assign_ids_expr(arg); 134 | } 135 | } 136 | 137 | ExprData::If (iff) => { 138 | self.assign_ids_expr(&mut iff.condition); 139 | self.assign_ids_block(&mut iff.on_true.stmts); 140 | if let Some(on_false) = &mut iff.on_false { 141 | self.assign_ids_block(&mut on_false.stmts); 142 | } 143 | } 144 | 145 | ExprData::While (whilee) => { 146 | self.assign_ids_expr(&mut whilee.condition); 147 | self.assign_ids_block(&mut whilee.body); 148 | } 149 | 150 | ExprData::Break (brk) => { 151 | if let Some(value) = &mut brk.value { 152 | self.assign_ids_expr(value); 153 | } 154 | } 155 | 156 | ExprData::Continue (_) => {} 157 | 158 | ExprData::Return (ret) => { 159 | if let Some(value) = &mut ret.value { 160 | self.assign_ids_expr(value); 161 | } 162 | } 163 | 164 | ExprData::Env => {} 165 | } 166 | } 167 | 168 | fn assign_ids_block(&mut self, block: &mut [Stmt]) { 169 | for stmt in block.iter_mut() { 170 | self.assign_ids_stmt(stmt); 171 | } 172 | } 173 | 174 | 175 | pub fn infer(&mut self, module: &mut item::Module) { 176 | self.infer_module(module); 177 | } 178 | 179 | fn infer_module(&mut self, module: &mut item::Module) { 180 | let mut ctx = InferCtx::new(None); 181 | self.infer_block(&mut ctx, &mut module.block.stmts); 182 | } 183 | 184 | fn infer_stmt(&mut self, ctx: &mut InferCtx, stmt: &mut Stmt) { 185 | match &mut stmt.data { 186 | StmtData::Item (item) => { 187 | match &mut item.data { 188 | ItemData::Module(module) => { 189 | let _ = module; 190 | unimplemented!() 191 | } 192 | 193 | ItemData::Func(func) => { 194 | let mut fctx = InferCtx::new(Some(ctx)); 195 | 196 | for param in &func.params { 197 | fctx.add_local_decl(stmt.id, param.name); 198 | } 199 | 200 | self.infer_value_block(&mut fctx, &mut func.body, None); 201 | } 202 | } 203 | } 204 | 205 | StmtData::Local (local) => { 206 | if let Some(value) = &mut local.value { 207 | self.infer_expr(ctx, value, None); 208 | } 209 | let lid = ctx.add_local_decl(stmt.id, local.name); 210 | local.info = Some(expr::LocalInfo { id: lid }); 211 | } 212 | 213 | StmtData::Expr (expr) => { self.infer_expr(ctx, expr, None); } 214 | 215 | StmtData::Empty => (), 216 | } 217 | } 218 | 219 | fn infer_expr(&mut self, ctx: &mut InferCtx, expr: &mut Expr, expected_ty: Option<&Type>) -> Type { 220 | let ty = match &mut expr.data { 221 | ExprData::Nil => { 222 | Type::Any 223 | } 224 | 225 | ExprData::Bool (_) => { 226 | Type::Bool 227 | } 228 | 229 | ExprData::Number (_) => { 230 | Type::Number 231 | } 232 | 233 | ExprData::QuotedString (_) => { 234 | Type::String 235 | } 236 | 237 | ExprData::Ident (ident) => { 238 | if let Some(decl) = ctx.find_decl(ident.name) { 239 | ident.info = Some(expr::IdentInfo { target: decl.target }); 240 | } 241 | else { 242 | ident.info = Some(expr::IdentInfo { target: expr::IdentTarget::Dynamic }); 243 | } 244 | Type::Any 245 | } 246 | 247 | 248 | ExprData::Tuple (tuple) => { 249 | let mut types = Vec::with_capacity(tuple.values.len()); 250 | for value in &mut tuple.values { 251 | types.push(self.infer_expr(ctx, value, None)); 252 | } 253 | Type::Tuple(types.into_boxed_slice()) 254 | } 255 | 256 | ExprData::List (list) => { 257 | for value in &mut list.values { 258 | self.infer_expr(ctx, value, None); 259 | } 260 | Type::List(Box::new(Type::Any)) 261 | } 262 | 263 | ExprData::Do (doo) => { 264 | self.infer_do_block(ctx, expr.id, doo.label, &mut doo.stmts, expected_ty) 265 | } 266 | 267 | ExprData::SubExpr (sub_expr) => { 268 | self.infer_expr(ctx, sub_expr, expected_ty) 269 | } 270 | 271 | ExprData::Op1 (op1) => { 272 | self.infer_expr(ctx, &mut op1.child, None); 273 | Type::Any 274 | } 275 | 276 | ExprData::Op2 (op2) => { 277 | match op2.kind { 278 | expr::Op2Kind::Assign | expr::Op2Kind::Define => { 279 | let [op_lhs, op_rhs] = &mut op2.children; 280 | 281 | if let ExprData::Tuple(lhs) = &mut op_lhs.data { 282 | if op2.kind != expr::Op2Kind::Assign { 283 | println!("error: tried to define tuple"); 284 | } 285 | 286 | let lhs = &mut lhs.values; 287 | 288 | if let ExprData::Tuple(rhs) = &mut op_rhs.data { 289 | let rhs = &mut rhs.values; 290 | if lhs.len() != rhs.len() { 291 | println!("error: tuple assign length mismatch"); 292 | } 293 | 294 | for i in 0..lhs.len() { 295 | if let Some(rhs) = rhs.get_mut(i) { 296 | let rhs_ty = self.infer_expr(ctx, rhs, None); 297 | self.infer_assign(ctx, &mut lhs[i], &rhs_ty, false); 298 | } 299 | } 300 | 301 | op_rhs.ty = Some(Type::Any); 302 | } 303 | else { 304 | unimplemented!() 305 | } 306 | 307 | op_lhs.ty = Some(Type::Any); 308 | } 309 | else { 310 | let is_def = op2.kind == expr::Op2Kind::Define; 311 | let rhs = self.infer_expr(ctx, &mut op2.children[1], None); 312 | self.infer_assign(ctx, &mut op2.children[0], &rhs, is_def); 313 | } 314 | Type::Any 315 | } 316 | 317 | expr::Op2Kind::Op2Assign(op) => { 318 | let [src1, src2] = &mut op2.children; 319 | let src1 = self.infer_expr(ctx, src1, None); 320 | let src2 = self.infer_expr(ctx, src2, None); 321 | 322 | // todo: do op analysis w/ src1,2. 323 | let _ = (op, src1, src2); 324 | let value = Type::Any; 325 | 326 | let is_define = false; 327 | self.infer_assign(ctx, &mut op2.children[0], &value, is_define); 328 | 329 | Type::Unit 330 | } 331 | 332 | expr::Op2Kind::Op2(op) => { 333 | let [src1, src2] = &mut op2.children; 334 | let src1 = self.infer_expr(ctx, src1, None); 335 | let src2 = self.infer_expr(ctx, src2, None); 336 | 337 | // todo: do op analysis w/ src1,2. 338 | let _ = (op, src1, src2); 339 | let result = Type::Any; 340 | 341 | result 342 | } 343 | } 344 | } 345 | 346 | ExprData::Field (_) | 347 | ExprData::Index (_) => { 348 | self.infer_path(ctx, expr, expected_ty) 349 | } 350 | 351 | ExprData::Call (call) => { 352 | self.infer_expr(ctx, &mut call.func, None); 353 | for arg in &mut call.args { 354 | self.infer_expr(ctx, arg, None); 355 | } 356 | Type::Any 357 | } 358 | 359 | ExprData::If (iff) => { 360 | self.infer_expr(ctx, &mut iff.condition, Some(&Type::Bool)); 361 | self.infer_if_block(ctx, expr.id, &mut iff.on_true, None); 362 | if let Some(on_false) = &mut iff.on_false { 363 | self.infer_if_block(ctx, expr.id, on_false, None); 364 | } 365 | Type::Any 366 | } 367 | 368 | ExprData::While (whilee) => { 369 | self.infer_expr(ctx, &mut whilee.condition, Some(&Type::Bool)); 370 | 371 | let bs = ctx.begin_break_scope(expr.id, whilee.label, true, Type::None); 372 | self.infer_block(ctx, &mut whilee.body); 373 | ctx.end_break_scope(bs); 374 | 375 | Type::Unit 376 | } 377 | 378 | ExprData::Break (brk) => { 379 | let target = ctx.current_break_target(expr.source, brk.label); 380 | brk.info = Some(target.map(|bs| expr::BreakInfo { 381 | node: bs.node, scope_index: bs.index })); 382 | 383 | if let Some(value) = &mut brk.value { 384 | let ty = target.map(|bs| bs.ty.clone()).unwrap_or(Type::Any); 385 | self.infer_expr(ctx, value, Some(&ty)); 386 | } 387 | 388 | Type::Unit 389 | } 390 | 391 | ExprData::Continue (cont) => { 392 | let target = ctx.current_continue_target(expr.source, cont.label); 393 | cont.info = Some(target.map(|bs| expr::BreakInfo { 394 | node: bs.node, scope_index: bs.index })); 395 | 396 | Type::Unit 397 | } 398 | 399 | ExprData::Return (ret) => { 400 | if let Some(value) = &mut ret.value { 401 | self.infer_expr(ctx, value, None); 402 | } 403 | Type::Unit 404 | } 405 | 406 | ExprData::Env => { 407 | // @temp-no-env-access. 408 | println!("error {}: can't read ENV.", expr.source); 409 | Type::Any 410 | } 411 | }; 412 | expr.ty = Some(ty.clone()); 413 | ty 414 | } 415 | 416 | fn infer_path(&mut self, ctx: &mut InferCtx, expr: &mut Expr, expected_ty: Option<&Type>) -> Type { 417 | // @todo: use. 418 | let _ = expected_ty; 419 | 420 | match &mut expr.data { 421 | ExprData::Field (field) => { 422 | let _ = self.infer_path(ctx, &mut field.base, None); 423 | Type::Any 424 | } 425 | 426 | ExprData::Index (index) => { 427 | let _ = self.infer_path(ctx, &mut index.base, None); 428 | self.infer_expr(ctx, &mut index.index, None); 429 | Type::Any 430 | } 431 | 432 | ExprData::Ident(ident) => { 433 | if let Some(decl) = ctx.find_decl(ident.name) { 434 | ident.info = Some(expr::IdentInfo { target: decl.target }); 435 | } 436 | else { 437 | ident.info = Some(expr::IdentInfo { target: expr::IdentTarget::Dynamic }); 438 | } 439 | Type::Any 440 | } 441 | 442 | ExprData::Env => { 443 | Type::Any 444 | } 445 | 446 | _ => { 447 | println!("error {}: invalid path base", expr.source); 448 | Type::Error 449 | } 450 | } 451 | } 452 | 453 | fn infer_assign(&mut self, ctx: &mut InferCtx, lhs: &mut Expr, rhs: &Type, is_def: bool) { 454 | if let ExprData::Ident(ident) = &mut lhs.data { 455 | if let Some(decl) = ctx.find_decl(ident.name) { 456 | ident.info = Some(expr::IdentInfo { target: decl.target }); 457 | } 458 | else { 459 | if is_def != false { 460 | println!("error: tried to define global"); 461 | } 462 | ident.info = Some(expr::IdentInfo { target: expr::IdentTarget::Dynamic }); 463 | } 464 | lhs.ty = Some(Type::Any); 465 | } 466 | else if let ExprData::Env = lhs.data { 467 | println!("error: tried to assign to ENV"); 468 | lhs.ty = Some(Type::Error); 469 | } 470 | else if let ExprData::Field(_) | ExprData::Index(_) = lhs.data { 471 | lhs.ty = Some(self.infer_path(ctx, lhs, Some(rhs))); 472 | } 473 | else { 474 | println!("error {}: invalid assign target", lhs.source); 475 | lhs.ty = Some(Type::Error); 476 | } 477 | } 478 | 479 | fn infer_block(&mut self, ctx: &mut InferCtx, block: &mut [Stmt]) { 480 | // collect items. 481 | let item_scope = ctx.begin_scope(); 482 | for stmt in block.iter() { 483 | if let StmtData::Item(item) = &stmt.data { 484 | match &item.data { 485 | ItemData::Module(module) => { 486 | let _ = module; 487 | unimplemented!() 488 | } 489 | 490 | ItemData::Func(func) => { 491 | if let Some(name) = func.name { 492 | ctx.add_item_decl(name, item.id); 493 | } 494 | } 495 | } 496 | } 497 | } 498 | 499 | // infer stmts. 500 | let block_scope = ctx.begin_scope(); 501 | for stmt in block.iter_mut() { 502 | self.infer_stmt(ctx, stmt); 503 | } 504 | 505 | ctx.end_scope(block_scope); 506 | ctx.end_scope(item_scope); 507 | } 508 | 509 | fn infer_do_block(&mut self, ctx: &mut InferCtx, node: NodeId, label: Option<&str>, block: &mut [Stmt], expected_ty: Option<&Type>) -> Type { 510 | // @todo: use. 511 | let _ = expected_ty; 512 | 513 | let bs = ctx.begin_break_scope(node, label, false, Type::Any); 514 | self.infer_block(ctx, block); 515 | ctx.end_break_scope(bs); 516 | 517 | Type::Any 518 | } 519 | 520 | fn infer_value_block(&mut self, ctx: &mut InferCtx, block: &mut [Stmt], expected_ty: Option<&Type>) -> Type { 521 | // @todo: use. 522 | let _ = expected_ty; 523 | 524 | self.infer_block(ctx, block); 525 | Type::Any 526 | } 527 | 528 | fn infer_if_block(&mut self, ctx: &mut InferCtx, node: NodeId, block: &mut expr::IfBlock, expected_ty: Option<&Type>) -> Type { 529 | if block.is_do { 530 | self.infer_do_block(ctx, node, None, &mut block.stmts, expected_ty) 531 | } 532 | else { 533 | self.infer_value_block(ctx, &mut block.stmts, expected_ty) 534 | } 535 | } 536 | } 537 | 538 | 539 | define_id!(LocalId); 540 | 541 | #[derive(Clone)] 542 | struct Decl { 543 | name: String, 544 | scope: u32, 545 | target: expr::IdentTarget, 546 | } 547 | 548 | struct BreakScope { 549 | index: u32, 550 | node: NodeId, 551 | ty: Type, 552 | label: Option, 553 | can_continue: bool, 554 | } 555 | 556 | struct InferCtx { 557 | scope: u32, 558 | decls: Vec, 559 | locals: Vec<()>, 560 | break_scopes: Vec, 561 | } 562 | 563 | impl InferCtx { 564 | pub fn new(parent: Option<&InferCtx>) -> InferCtx { 565 | let mut decls = vec![]; 566 | let mut scope = 0; 567 | 568 | if let Some(parent) = parent { 569 | decls = parent.decls.iter() 570 | .filter(|decl| { decl.target.is_item() }) 571 | .cloned() 572 | .collect(); 573 | scope = parent.scope + 1; 574 | } 575 | 576 | InferCtx { 577 | scope, 578 | decls, 579 | locals: vec![], 580 | break_scopes: vec![], 581 | } 582 | } 583 | 584 | fn add_local_decl(&mut self, node: NodeId, name: &str) -> LocalId { 585 | let id = LocalId(self.locals.len() as u32); 586 | self.locals.push(()); 587 | self.decls.push(Decl { 588 | name: name.to_string(), 589 | scope: self.scope, 590 | target: expr::IdentTarget::Local { node, local: id }, 591 | }); 592 | id 593 | } 594 | 595 | fn add_item_decl(&mut self, name: &str, id: ItemId) { 596 | if let Some(decl) = self.find_decl(name) { 597 | if decl.scope == self.scope { 598 | println!("duplicate definition of {name:?}"); 599 | } 600 | } 601 | self.decls.push(Decl { 602 | name: name.to_string(), 603 | scope: self.scope, 604 | target: expr::IdentTarget::Item(id) 605 | }); 606 | } 607 | 608 | fn find_decl(&self, name: &str) -> Option<&Decl> { 609 | self.decls.iter().rev().find(|decl| decl.name == name) 610 | } 611 | 612 | fn begin_scope(&mut self) -> u32 { 613 | self.scope += 1; 614 | self.scope 615 | } 616 | 617 | fn end_scope(&mut self, scope: u32) { 618 | assert_eq!(self.scope, scope); 619 | self.decls.retain(|decl| decl.scope < self.scope); 620 | self.scope -= 1; 621 | } 622 | 623 | 624 | fn begin_break_scope(&mut self, node: NodeId, label: Option<&str>, can_continue: bool, ty: Type) -> u32 { 625 | let index = self.break_scopes.len() as u32; 626 | self.break_scopes.push(BreakScope { 627 | index, node, ty, 628 | label: label.map(str::to_string), 629 | can_continue, 630 | }); 631 | index 632 | } 633 | 634 | fn end_break_scope(&mut self, scope: u32) { 635 | assert_eq!(scope + 1, self.break_scopes.len() as u32); 636 | self.break_scopes.pop(); 637 | } 638 | 639 | fn current_break_target(&self, source: SourceRange, label: Option<&str>) -> Option<&BreakScope> { 640 | if let Some(label) = label { 641 | for scope in self.break_scopes.iter().rev() { 642 | if scope.label.as_deref() == Some(label) { 643 | return Some(scope); 644 | } 645 | } 646 | println!("error {source}: no break target with label {label}"); 647 | return None; 648 | } 649 | else { 650 | if let Some(scope) = self.break_scopes.last() { 651 | return Some(scope); 652 | } 653 | println!("error {source}: no break target"); 654 | return None; 655 | } 656 | } 657 | 658 | fn current_continue_target(&self, source: SourceRange, label: Option<&str>) -> Option<&BreakScope> { 659 | if let Some(label) = label { 660 | for scope in self.break_scopes.iter().rev() { 661 | if scope.label.as_deref() == Some(label) { 662 | if !scope.can_continue { 663 | println!("error {source}: {label} is not a continue target"); 664 | return None; 665 | } 666 | else { 667 | return Some(scope); 668 | } 669 | } 670 | } 671 | println!("error {source}: no continue target with label {label}"); 672 | return None; 673 | } 674 | else { 675 | for scope in self.break_scopes.iter().rev() { 676 | if scope.can_continue { 677 | return Some(scope); 678 | } 679 | } 680 | println!("error {source}: no continue target"); 681 | return None; 682 | } 683 | } 684 | } 685 | 686 | -------------------------------------------------------------------------------- /src/compiler/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ast; 2 | pub mod parser; 3 | pub mod infer; 4 | pub mod bbir; 5 | pub mod bbir_builder; 6 | pub mod analysis; 7 | pub mod opt; 8 | pub mod transform; 9 | pub mod codegen; 10 | 11 | pub use ast::*; 12 | pub use parser::*; 13 | pub use bbir::*; 14 | pub use analysis::*; 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/compiler/opt.rs: -------------------------------------------------------------------------------- 1 | use crate::index_vec::*; 2 | use super::*; 3 | 4 | 5 | pub fn local2reg_ex(fun: &mut Function, preds: &Predecessors, dom_tree: &DomTree, dom_frontiers: &DominanceFrontiers) { 6 | // find phis 7 | let mut phis = { 8 | let mut visited = index_vec![index_vec![false; fun.num_locals()]; fun.num_blocks()]; 9 | 10 | // collect defs. 11 | let mut stack = vec![]; 12 | for bb in fun.block_ids() { 13 | fun.block_instrs(bb, |instr| { 14 | let InstrData::SetLocal { dst: lid, src: _ } = instr.data else { return }; 15 | 16 | let key = (bb, lid); 17 | if !visited[bb][lid] { 18 | visited[bb][lid] = true; 19 | stack.push(key); 20 | } 21 | }); 22 | } 23 | 24 | // phis for each bb. 25 | let mut phis: IndexVec, InstrId)>> 26 | = index_vec![vec![]; fun.num_blocks()]; 27 | 28 | // for each def, create phis in dom frontier. 29 | while let Some((from_bb, lid)) = stack.pop() { 30 | for to_bb in dom_frontiers[from_bb].iter().copied() { 31 | let to_phis = &mut phis[to_bb]; 32 | 33 | let local_was_new = to_phis.iter().find(|(l, _, _)| *l == lid).is_none(); 34 | if local_was_new { 35 | let preds = &preds[to_bb]; 36 | 37 | // init phi. 38 | let map = preds.iter().map(|p| (*p, None.into())).collect(); 39 | let instr = fun.new_phi((None.into(), None.into()), &[]); 40 | to_phis.push((lid, map, instr)); 41 | 42 | // to_bb now defines the local. 43 | let key = (to_bb, lid); 44 | if !visited[to_bb][lid] { 45 | visited[to_bb][lid] = true; 46 | stack.push(key); 47 | } 48 | } 49 | } 50 | } 51 | 52 | phis 53 | }; 54 | 55 | // rename vars. 56 | { 57 | fn visit(bb: BlockId, mut new_names: IndexVec, 58 | phis: &mut IndexVec, InstrId)>>, 59 | fun: &mut Function, idom_tree: &DomTree, 60 | ) { 61 | // update var names. 62 | for (lid, _map, instr) in &phis[bb] { 63 | new_names[*lid] = instr.some(); 64 | } 65 | fun.block_instr_mut(bb, |instr| { match instr.data { 66 | InstrData::Param { id } | 67 | InstrData::Local { id } => { 68 | new_names[id] = instr.id().some(); 69 | } 70 | 71 | InstrData::GetLocal { src } => { 72 | let new_name = new_names[src].unwrap(); 73 | instr.data = InstrData::Copy { src: new_name }; 74 | new_names[src] = instr.id().some(); 75 | } 76 | 77 | InstrData::SetLocal { dst, src } => { 78 | instr.data = InstrData::Copy { src }; 79 | new_names[dst] = instr.id().some(); 80 | } 81 | 82 | _ => (), 83 | }}); 84 | 85 | // propagate to successors. 86 | fun.block_successors(bb, |succ| { 87 | for (l, map, _) in &mut phis[succ] { 88 | let entry = map.iter_mut().find(|(from, _)| *from == bb).unwrap(); 89 | assert!(entry.1.is_none()); 90 | 91 | entry.1 = new_names[*l].unwrap().some(); 92 | } 93 | }); 94 | 95 | // propagate to dominated blocks. 96 | for d in idom_tree[bb].iter() { 97 | visit(*d, new_names.clone(), phis, fun, idom_tree); 98 | } 99 | } 100 | 101 | let new_names = index_vec![None.into(); fun.num_locals()]; 102 | visit(BlockId::ENTRY, new_names, &mut phis, fun, &dom_tree); 103 | } 104 | 105 | // insert phis. 106 | { 107 | for (bb_index, phis) in phis.into_iter().enumerate() { 108 | let block = BlockId::from_usize(bb_index); 109 | let mut at = None.into(); 110 | 111 | for (_, map, instr_id) in phis { 112 | let map: Vec = map.into_iter().map(|(bb, src)| { (bb, src.unwrap()) }).collect(); 113 | fun.set_phi(instr_id, &map); 114 | 115 | fun.insert_after(block, at, instr_id); 116 | at = instr_id.some(); 117 | } 118 | } 119 | } 120 | fun.slow_integrity_check(); 121 | } 122 | 123 | 124 | pub fn copy_propagation_ex(fun: &mut Function, dom_tree: &DomTree) { 125 | fn visit(bb: BlockId, fun: &mut Function, dom_tree: &DomTree) { 126 | // inline copies. 127 | fun.block_replace_args(bb, |fun, arg| { 128 | if let InstrData::Copy { src } = arg.get(fun).data { 129 | let mut new_arg = src; 130 | while let InstrData::Copy { src } = new_arg.get(fun).data { 131 | new_arg = src; 132 | } 133 | *arg = new_arg; 134 | } 135 | }); 136 | 137 | // propagate to dominated blocks. 138 | for d in dom_tree[bb].iter() { 139 | visit(*d, fun, dom_tree); 140 | } 141 | } 142 | 143 | visit(BlockId::ENTRY, fun, &dom_tree); 144 | fun.slow_integrity_check(); 145 | } 146 | 147 | 148 | pub fn dead_copy_elim(fun: &mut Function) { 149 | let mut visited = index_vec![false; fun.num_instrs()]; 150 | fun.all_args(|arg| visited[arg] = true); 151 | 152 | for bb in fun.block_ids() { 153 | let mut current = bb.get(fun).first(); 154 | 155 | while let Some(at) = current.to_option() { 156 | let instr = at.get_mut(fun); 157 | let next = instr.next(); 158 | if !visited[at] { 159 | if let InstrData::Copy { src } = instr.data { 160 | let source_values = core::mem::take(&mut instr.source.values); 161 | 162 | // put value info on copy src. 163 | let src = src.get_mut(fun); 164 | src.source.values.extend(source_values); 165 | 166 | // remove. 167 | fun.remove_instr(at); 168 | } 169 | } 170 | 171 | current = next; 172 | } 173 | } 174 | 175 | fun.slow_integrity_check(); 176 | } 177 | 178 | -------------------------------------------------------------------------------- /src/compiler/transform.rs: -------------------------------------------------------------------------------- 1 | use crate::index_vec::*; 2 | use super::*; 3 | 4 | 5 | // - conventional ssa: phis & all their arguments don't interfere (live intervals don't overlap & can be joined). 6 | // - assumes there are no critical edges. 7 | // - inserts parallel copies for phi arguments in predecessor blocks. 8 | // - inserts parallel copies for phi outputs after phis. 9 | // - inserts copies before in-place mutating instructions (like set_index). 10 | // - obviously not idempotent. 11 | pub fn convert_to_cssa_naive(fun: &mut Function, preds: &Predecessors) { 12 | // reused across iterations. cleared at end of iter. 13 | let mut pred_copy_ids = index_vec![None; fun.num_blocks()]; 14 | 15 | for bb in fun.block_ids() { 16 | if let Some(first_phi) = fun.block_first_phi(bb).to_option() { 17 | let phis_copy_id = fun.new_parallel_copy_id(); 18 | 19 | for pred in preds[bb].iter() { 20 | pred_copy_ids[*pred] = Some(fun.new_parallel_copy_id()); 21 | } 22 | 23 | let mut new_phi_cursor = OptInstrId::NONE; 24 | let mut old_phi_cursor = first_phi.some(); 25 | while let Some(at) = old_phi_cursor.to_option() { 26 | let Some(phi_map) = fun.try_phi(at) else { break }; 27 | 28 | // parallel copies for predecessors. 29 | // update phi map. 30 | let mut phi_map = phi_map.to_vec(); 31 | for (pred, src) in &mut phi_map { 32 | let source = src.get(fun).source.clone(); 33 | let copy_id = pred_copy_ids[*pred].unwrap(); 34 | let copy = fun.new_instr_ex(source, 35 | InstrData::ParallelCopy { src: *src, copy_id }); 36 | 37 | fun.insert_before_terminator(*pred, copy); 38 | *src = copy; 39 | } 40 | fun.set_phi(at, &phi_map); 41 | 42 | // make parallel copies for own phis. 43 | // the phis themselves become the parallel copies, 44 | // so uses don't have to be updated. 45 | // new phis are inserted at the start of the block. 46 | let phi = at.get(fun); 47 | let new_phi = fun.new_instr_ex(phi.source.clone(), phi.data); 48 | 49 | let phi = at.get_mut(fun); 50 | phi.data = InstrData::ParallelCopy { src: new_phi, copy_id: phis_copy_id }; 51 | old_phi_cursor = phi.next(); 52 | 53 | fun.insert_after(bb, new_phi_cursor, new_phi); 54 | new_phi_cursor = new_phi.some(); 55 | } 56 | 57 | // clear copy ids. 58 | for copy_id in &mut pred_copy_ids { 59 | *copy_id = None; 60 | } 61 | } 62 | 63 | // insert copies before in-place mutating instructions. 64 | let mut cursor = bb.get(fun).first(); 65 | while let Some(at) = cursor.to_option() { 66 | let instr = at.get(fun); 67 | let next = instr.next(); 68 | 69 | if let InstrData::WritePath { path_id, value: _, is_def: _ } = instr.data { 70 | if let PathBase::Instr(base) = path_id.get(fun).base { 71 | let copy = fun.new_instr_ex(instr.source.clone(), InstrData::Copy { src: base }); 72 | fun.insert_before(bb, at.some(), copy); 73 | 74 | path_id.set_base(fun, PathBase::Instr(copy)); 75 | } 76 | } 77 | 78 | cursor = next; 79 | } 80 | } 81 | 82 | fun.slow_integrity_check(); 83 | } 84 | -------------------------------------------------------------------------------- /src/index_vec.rs: -------------------------------------------------------------------------------- 1 | use core::marker::PhantomData; 2 | 3 | pub trait Key: Copy { 4 | fn from_usize(value: usize) -> Self; 5 | fn usize(self) -> usize; 6 | } 7 | 8 | 9 | pub struct IndexVec { 10 | values: Vec, 11 | unused: PhantomData, 12 | } 13 | 14 | impl IndexVec { 15 | #[inline(always)] 16 | pub fn new() -> Self { 17 | Self { values: vec![], unused: PhantomData } 18 | } 19 | 20 | #[inline(always)] 21 | pub fn with_capacity(cap: usize) -> Self { 22 | Self { values: Vec::with_capacity(cap), unused: PhantomData } 23 | } 24 | 25 | #[inline(always)] 26 | pub fn len(&self) -> usize { 27 | self.values.len() 28 | } 29 | 30 | #[inline(always)] 31 | pub fn push(&mut self, value: V) { 32 | self.values.push(value); 33 | } 34 | 35 | #[inline(always)] 36 | pub fn get(&self, key: K) -> Option<&V> { 37 | self.values.get(key.usize()) 38 | } 39 | 40 | #[inline(always)] 41 | pub fn get_mut(&mut self, key: K) -> Option<&mut V> { 42 | self.values.get_mut(key.usize()) 43 | } 44 | 45 | #[inline(always)] 46 | pub fn retain bool>(&mut self, f: F) { 47 | self.values.retain(f) 48 | } 49 | 50 | #[inline(always)] 51 | pub fn retain_mut bool>(&mut self, f: F) { 52 | self.values.retain_mut(f) 53 | } 54 | 55 | #[inline(always)] 56 | pub fn iter(&self) -> core::slice::Iter { self.values.iter() } 57 | 58 | #[inline(always)] 59 | pub fn iter_mut(&mut self) -> core::slice::IterMut { self.values.iter_mut() } 60 | 61 | #[inline(always)] 62 | pub fn inner(&self) -> &Vec { &self.values } 63 | 64 | #[inline(always)] 65 | pub fn inner_mut(&mut self) -> &mut Vec { &mut self.values } 66 | 67 | #[inline(always)] 68 | pub fn into_inner(self) -> Vec { self.values } 69 | } 70 | 71 | 72 | impl core::ops::Index for IndexVec { 73 | type Output = V; 74 | 75 | #[inline(always)] 76 | fn index(&self, index: K) -> &Self::Output { 77 | &self.values[index.usize()] 78 | } 79 | } 80 | 81 | impl core::ops::IndexMut for IndexVec { 82 | #[inline(always)] 83 | fn index_mut(&mut self, index: K) -> &mut Self::Output { 84 | &mut self.values[index.usize()] 85 | } 86 | } 87 | 88 | 89 | impl Clone for IndexVec { 90 | #[inline(always)] 91 | fn clone(&self) -> Self { 92 | Self { values: self.values.clone(), unused: PhantomData } 93 | } 94 | } 95 | 96 | impl core::fmt::Debug for IndexVec { 97 | #[inline(always)] 98 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 99 | self.values.fmt(f) 100 | } 101 | } 102 | 103 | impl PartialEq for IndexVec { 104 | #[inline(always)] 105 | fn eq(&self, other: &Self) -> bool { 106 | self.values.eq(&other.values) 107 | } 108 | } 109 | 110 | 111 | impl From> for IndexVec { 112 | #[inline(always)] 113 | fn from(values: Vec) -> Self { 114 | Self { values, unused: PhantomData } 115 | } 116 | } 117 | 118 | 119 | impl<'a, K: Key, V> IntoIterator for &'a IndexVec { 120 | type Item = &'a V; 121 | type IntoIter = core::slice::Iter<'a, V>; 122 | #[inline(always)] 123 | fn into_iter(self) -> Self::IntoIter { 124 | self.values.iter() 125 | } 126 | } 127 | 128 | impl<'a, K: Key, V> IntoIterator for &'a mut IndexVec { 129 | type Item = &'a mut V; 130 | type IntoIter = core::slice::IterMut<'a, V>; 131 | #[inline(always)] 132 | fn into_iter(self) -> Self::IntoIter { 133 | self.values.iter_mut() 134 | } 135 | } 136 | 137 | impl IntoIterator for IndexVec { 138 | type Item = V; 139 | type IntoIter = as IntoIterator>::IntoIter; 140 | #[inline(always)] 141 | fn into_iter(self) -> Self::IntoIter { 142 | self.values.into_iter() 143 | } 144 | } 145 | 146 | impl FromIterator for IndexVec { 147 | #[inline(always)] 148 | fn from_iter>(iter: T) -> Self { 149 | Self { values: Vec::from_iter(iter), unused: PhantomData } 150 | } 151 | } 152 | 153 | 154 | macro_rules! index_vec { 155 | () => ( 156 | IndexVec::from(vec![]) 157 | ); 158 | ($elem:expr; $n:expr) => ( 159 | IndexVec::from(vec![$elem; $n]) 160 | ); 161 | ($($x:expr),+ $(,)?) => ( 162 | IndexVec::from(vec![$($x),+]) 163 | ); 164 | } 165 | pub(crate) use index_vec; 166 | 167 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod packed_option; 2 | pub mod index_vec; 3 | pub mod compiler; 4 | pub mod bytecode; 5 | pub mod value; 6 | pub mod vm; 7 | 8 | pub use compiler::*; 9 | pub use bytecode::*; 10 | pub use value::*; 11 | pub use vm::*; 12 | 13 | 14 | 15 | mod macros { 16 | macro_rules! define_id_basic { 17 | ($name: ident) => { 18 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 19 | #[repr(transparent)] 20 | pub struct $name(u32); 21 | 22 | impl $name { 23 | #[inline(always)] 24 | pub fn new_unck(value: u32) -> Self { Self(value) } 25 | 26 | #[inline(always)] 27 | pub const fn value(self) -> u32 { self.0 } 28 | 29 | #[inline(always)] 30 | pub fn from_usize(value: usize) -> Self { 31 | debug_assert!(value < u32::MAX as usize / 2); 32 | $name(value as u32) 33 | } 34 | 35 | #[inline(always)] 36 | pub fn usize(self) -> usize { 37 | self.0 as usize 38 | } 39 | 40 | #[inline(always)] 41 | pub fn some(self) -> crate::packed_option::PackedOption { 42 | crate::packed_option::Reserved::some(self) 43 | } 44 | } 45 | 46 | impl crate::packed_option::Reserved for $name { 47 | const RESERVED: Self = Self(u32::MAX); 48 | } 49 | 50 | impl crate::index_vec::Key for $name { 51 | #[inline(always)] 52 | fn from_usize(value: usize) -> Self { 53 | Self::from_usize(value) 54 | } 55 | 56 | #[inline(always)] 57 | fn usize(self) -> usize { 58 | self.0 as usize 59 | } 60 | } 61 | }; 62 | } 63 | pub(crate) use define_id_basic; 64 | 65 | macro_rules! define_id_opt { 66 | ($name: ident, $opt_name: ident) => { 67 | pub type $opt_name = crate::packed_option::PackedOption<$name>; 68 | }; 69 | } 70 | pub(crate) use define_id_opt; 71 | 72 | 73 | macro_rules! define_id_display { 74 | ($name: ident, $fmt: expr) => { 75 | impl core::fmt::Display for $name { 76 | #[inline] 77 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 78 | write!(f, $fmt, self.0) 79 | } 80 | } 81 | }; 82 | } 83 | pub(crate) use define_id_display; 84 | 85 | 86 | macro_rules! define_id { 87 | ($name: ident) => { 88 | crate::macros::define_id_basic!($name); 89 | }; 90 | 91 | ($name: ident, $opt_name: ident) => { 92 | crate::macros::define_id_basic!($name); 93 | crate::macros::define_id_opt!($name, $opt_name); 94 | }; 95 | 96 | ($name: ident, $fmt: expr) => { 97 | crate::macros::define_id_basic!($name); 98 | crate::macros::define_id_display!($name, $fmt); 99 | }; 100 | 101 | ($name: ident, $opt_name: ident, $fmt: expr) => { 102 | crate::macros::define_id_basic!($name); 103 | crate::macros::define_id_opt!($name, $opt_name); 104 | crate::macros::define_id_display!($name, $fmt); 105 | }; 106 | } 107 | pub(crate) use define_id; 108 | } 109 | 110 | -------------------------------------------------------------------------------- /src/packed_option.rs: -------------------------------------------------------------------------------- 1 | 2 | pub trait Reserved: PartialEq + Sized + Copy + PartialEq + core::fmt::Debug { 3 | const RESERVED: Self; 4 | 5 | #[inline(always)] 6 | fn some(self) -> PackedOption { 7 | debug_assert_ne!(self, Self::RESERVED); 8 | PackedOption { value: self } 9 | } 10 | } 11 | 12 | #[derive(Clone, Copy, PartialEq)] 13 | #[repr(transparent)] 14 | pub struct PackedOption { 15 | value: T 16 | } 17 | 18 | impl PackedOption { 19 | pub const NONE: PackedOption = PackedOption { value: Reserved::RESERVED }; 20 | 21 | #[inline(always)] 22 | pub fn is_none(&self) -> bool { 23 | self.value == T::RESERVED 24 | } 25 | 26 | #[inline(always)] 27 | pub fn is_some(&self) -> bool { 28 | self.value != T::RESERVED 29 | } 30 | 31 | #[inline(always)] 32 | pub fn to_option(self) -> Option { 33 | self.is_some().then_some(self.value) 34 | } 35 | 36 | #[inline(always)] 37 | pub fn unwrap(self) -> T { 38 | self.to_option().unwrap() 39 | } 40 | 41 | #[inline(always)] 42 | pub fn unwrap_unck(self) -> T { 43 | self.value 44 | } 45 | } 46 | 47 | 48 | impl From> for PackedOption { 49 | #[inline(always)] 50 | fn from(value: Option) -> Self { 51 | if let Some(value) = value { 52 | PackedOption { value } 53 | } 54 | else { 55 | PackedOption { value: T::RESERVED } 56 | } 57 | } 58 | } 59 | 60 | impl Into> for PackedOption { 61 | #[inline(always)] 62 | fn into(self) -> Option { 63 | self.to_option() 64 | } 65 | } 66 | 67 | 68 | impl core::fmt::Debug for PackedOption { 69 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 70 | if self.is_some() { 71 | write!(f, "Some({:?})", self.value) 72 | } 73 | else { write!(f, "None") } 74 | } 75 | } 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use crate::vm::*; 3 | use crate::bytecode::*; 4 | 5 | 6 | #[derive(Clone, Debug)] 7 | pub(crate) enum Value { 8 | Nil, 9 | Bool { value: bool }, 10 | Number { value: f64 }, 11 | String { value: Rc, }, 12 | Unit, 13 | Tuple { values: Rc>, }, 14 | List { values: Rc>, }, 15 | Map { values: Rc> }, 16 | Func { proto: usize }, 17 | } 18 | 19 | impl From for Value { #[inline(always)] fn from(value: bool) -> Self { Value::Bool { value } } } 20 | impl From for Value { #[inline(always)] fn from(value: f64) -> Self { Value::Number { value } } } 21 | 22 | impl Value { 23 | #[inline(always)] 24 | pub fn is_nil(&self) -> bool { 25 | if let Value::Nil = self { true } else { false } 26 | } 27 | } 28 | 29 | 30 | 31 | #[derive(Debug)] 32 | pub(crate) struct GcObject { 33 | pub marked: bool, 34 | pub data: GcObjectData, 35 | } 36 | 37 | #[derive(Debug)] 38 | pub(crate) enum GcObjectData { 39 | Nil, 40 | Free { next: Option }, 41 | } 42 | 43 | 44 | 45 | pub enum NativeFuncReturn { 46 | Unit, 47 | Reg (u32), 48 | } 49 | 50 | pub type NativeFuncPtr = fn(&mut Vm) -> VmResult; 51 | 52 | #[derive(Clone)] 53 | pub struct NativeFuncPtrEx(pub NativeFuncPtr); 54 | impl core::fmt::Debug for NativeFuncPtrEx { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { (self.0 as *const u8).fmt(f) } } 55 | 56 | 57 | crate::macros::define_id!(CrateId, OptCrateId); 58 | 59 | 60 | #[derive(Debug)] 61 | pub(crate) struct FuncProto { 62 | pub krate: OptCrateId, 63 | pub func_idx: u32, 64 | pub code: FuncCode, 65 | pub constants: Vec, 66 | pub num_params: u32, 67 | pub stack_size: u32, 68 | } 69 | 70 | #[derive(Clone, Debug)] 71 | pub enum FuncCode { 72 | ByteCode (Vec), 73 | Native (NativeFuncPtrEx), 74 | } 75 | 76 | impl FuncCode { 77 | #[inline(always)] 78 | pub fn is_native(&self) -> bool { 79 | match self { 80 | FuncCode::ByteCode(_) => false, 81 | FuncCode::Native(_) => true, 82 | } 83 | } 84 | } 85 | 86 | 87 | 88 | // @temp: move to kbtf. 89 | 90 | #[derive(Clone, Debug)] 91 | pub enum Constant { 92 | Nil, 93 | Bool { value: bool }, 94 | Number { value: f64 }, 95 | String { value: String }, 96 | } 97 | 98 | #[derive(Clone, Debug)] 99 | pub struct FuncDesc { 100 | pub code: FuncCode, 101 | pub constants: Vec, 102 | pub num_params: u32, 103 | pub stack_size: u32, 104 | } 105 | 106 | --------------------------------------------------------------------------------