Add working macro system
This commit is contained in:
parent
6adb943754
commit
1ff9a6c44b
19 changed files with 1258 additions and 571 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1,3 @@
|
||||||
/target
|
/target
|
||||||
|
|
||||||
|
wapm_packages
|
205
Cargo.lock
generated
205
Cargo.lock
generated
|
@ -64,6 +64,21 @@ version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.73"
|
version = "1.0.73"
|
||||||
|
@ -129,6 +144,24 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
@ -141,6 +174,16 @@ version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
|
@ -148,8 +191,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
"js-sys",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -177,11 +222,14 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"console",
|
"console",
|
||||||
|
"getrandom",
|
||||||
"itertools",
|
"itertools",
|
||||||
"num-parse",
|
"num-parse",
|
||||||
"radix_fmt",
|
"radix_fmt",
|
||||||
"rand",
|
"rand",
|
||||||
"rhexdump",
|
"rhexdump",
|
||||||
|
"rust-embed",
|
||||||
|
"unescape",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -212,12 +260,30 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.59"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.132"
|
version = "0.2.132"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -333,6 +399,12 @@ version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "os_str_bytes"
|
name = "os_str_bytes"
|
||||||
version = "6.3.0"
|
version = "6.3.0"
|
||||||
|
@ -429,12 +501,68 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847"
|
checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed"
|
||||||
|
version = "6.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a17e5ac65b318f397182ae94e532da0ba56b88dd1200b774715d36c4943b1c3"
|
||||||
|
dependencies = [
|
||||||
|
"rust-embed-impl",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-impl"
|
||||||
|
version = "6.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rust-embed-utils",
|
||||||
|
"syn",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rust-embed-utils"
|
||||||
|
version = "7.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029"
|
||||||
|
dependencies = [
|
||||||
|
"sha2",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.21"
|
version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -477,6 +605,18 @@ version = "0.15.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unescape"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
|
@ -495,12 +635,77 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.82"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
name = "hence"
|
name = "hence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = ["Dominic Grimm <dominic@dergrimm.net>"]
|
||||||
|
repository = "https://git.dergrimm.net/dergrimm/hence.git"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "hence"
|
name = "hence"
|
||||||
|
@ -19,7 +21,9 @@ num-parse = "0.1.2"
|
||||||
clap = { version = "3.2.16", features = ["derive"] }
|
clap = { version = "3.2.16", features = ["derive"] }
|
||||||
rhexdump = "0.1.1"
|
rhexdump = "0.1.1"
|
||||||
radix_fmt = "1"
|
radix_fmt = "1"
|
||||||
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
console = "0.15.1"
|
console = "0.15.1"
|
||||||
anyhow = { version = "1.0.62", features = ["backtrace"] }
|
anyhow = { version = "1.0.62", features = ["backtrace"] }
|
||||||
|
rust-embed = "6.4.0"
|
||||||
|
unescape = "0.1.0"
|
||||||
|
|
|
@ -26,4 +26,3 @@
|
||||||
| `0x0b` | `alu` | Runs ALU with `tmp`'s value as operator | |
|
| `0x0b` | `alu` | Runs ALU with `tmp`'s value as operator | |
|
||||||
| `0x0c` | `get` | Sets `tmp` to memory at address in `tmp` | |
|
| `0x0c` | `get` | Sets `tmp` to memory at address in `tmp` | |
|
||||||
| `0x0d` | `set` | Sets memory to value at specific address | |
|
| `0x0d` | `set` | Sets memory to value at specific address | |
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
; hence core lib
|
|
||||||
core:
|
|
||||||
core_mem:
|
|
||||||
.define CORE_MEM_PRG, (0 * 1024)
|
|
||||||
.define CORE_MEM_ST, (32 * 1024)
|
|
||||||
.define CORE_MEM_MEM, (40 * 1024)
|
|
||||||
.define CORE_MEM_OUT, (56 * 1024)
|
|
||||||
.define CORE_MEM_CHR, (56 * 1024 + 1)
|
|
||||||
.define CORE_MEM_KEY, (56 * 1024 + 2)
|
|
||||||
|
|
||||||
core_reg:
|
|
||||||
.define CORE_REG_PC, 0x0
|
|
||||||
.define CORE_REG_OPC, 0x1
|
|
||||||
.define CORE_REG_ARG, 0x2
|
|
||||||
.define CORE_REG_S, 0x3
|
|
||||||
.define CORE_REG_SP, 0x4
|
|
||||||
.define CORE_REG_A, 0x5
|
|
||||||
.define CORE_REG_B, 0x6
|
|
||||||
.define CORE_REG_C, 0x7
|
|
||||||
.define CORE_REG_D, 0x8
|
|
||||||
|
|
||||||
core_alu:
|
|
||||||
.define CORE_ALU_NOT, 0x00
|
|
||||||
.define CORE_ALU_AND, 0x01
|
|
||||||
.define CORE_ALU_OR, 0x02
|
|
||||||
.define CORE_ALU_XOR, 0x03
|
|
||||||
.define CORE_ALU_LSH, 0x04
|
|
||||||
.define CORE_ALU_RSH, 0x05
|
|
||||||
.define CORE_ALU_ADD, 0x06
|
|
||||||
.define CORE_ALU_SUB, 0x07
|
|
||||||
.define CORE_ALU_MUL, 0x08
|
|
||||||
.define CORE_ALU_DIV, 0x09
|
|
||||||
.define CORE_ALU_CMP, 0x0a
|
|
||||||
.define CORE_ALU_EQ, 0x0b
|
|
||||||
.define CORE_ALU_LT, 0x0c
|
|
||||||
.define CORE_ALU_GT, 0x0d
|
|
||||||
.define CORE_ALU_LEQ, 0x0e
|
|
||||||
.define CORE_ALU_GEQ, 0x0f
|
|
||||||
.define CORE_ALU_BOL, 0x10
|
|
||||||
.define CORE_ALU_INV, 0x11
|
|
||||||
.define CORE_ALU_RND, 0x12
|
|
||||||
|
|
||||||
; hence standard lib
|
|
||||||
STD:
|
|
||||||
.define STD_U8_MAX, 0xff
|
|
||||||
.define STD_U16_MAX, 0xffff
|
|
||||||
|
|
||||||
.macro std_stop
|
|
||||||
ts 0xffff
|
|
||||||
tlr CORE_REG_PC
|
|
||||||
.macroend
|
|
||||||
|
|
||||||
forth:
|
|
||||||
.define FORTH_MEM_INPUT_SIZE, 16
|
|
||||||
.define FORTH_MEM_INPUT_DYN_SIZE, CORE_MEM_MEM
|
|
||||||
.define FORTH_MEM_INPUT_START, (FORTH_MEM_INPUT_DYN_SIZE + 1)
|
|
||||||
.define FORTH_MEM_INPUT_END, (FORTH_MEM_INPUT_START + FORTH_MEM_INPUT_SIZE)
|
|
||||||
|
|
||||||
.define jump_main, (CORE_MEM_ST - 3 - 1)
|
|
||||||
|
|
||||||
ts jump_main
|
|
||||||
tlr CORE_REG_PC
|
|
||||||
|
|
||||||
main:
|
|
||||||
dbg
|
|
||||||
|
|
||||||
; set PC to maximum for u16 and therefore stops program execution
|
|
||||||
ts 0xffff ; load 0xffff into TMP
|
|
||||||
tlr CORE_REG_PC ; store value of TMP into register PC
|
|
||||||
|
|
||||||
.org jump_main
|
|
||||||
ts main
|
|
||||||
tlr CORE_REG_PC
|
|
||||||
|
|
Binary file not shown.
6
forth/constants.asm
Normal file
6
forth/constants.asm
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.requires "$lib/core.asm"
|
||||||
|
|
||||||
|
.define MEM_INPUT_SIZE, 16
|
||||||
|
.define MEM_INPUT_DYN_END, CORE_MEM_MEM
|
||||||
|
.define MEM_INPUT_START, (MEM_INPUT_DYN_END + 1)
|
||||||
|
.define MEM_INPUT_END, (MEM_INPUT_START + MEM_INPUT_SIZE)
|
64
forth/main.asm
Normal file
64
forth/main.asm
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
.include "$lib/core.asm"
|
||||||
|
.include "$lib/std.asm"
|
||||||
|
.include "$lib/main.asm"
|
||||||
|
|
||||||
|
.include "constants.asm"
|
||||||
|
|
||||||
|
.jump_main
|
||||||
|
|
||||||
|
data:
|
||||||
|
|
||||||
|
.main
|
||||||
|
.std_rset CORE_REG_C, MEM_INPUT_START
|
||||||
|
get_input_loop:
|
||||||
|
.std_get CORE_MEM_KEY
|
||||||
|
tlr CORE_REG_A
|
||||||
|
@ tlr CORE_REG_D
|
||||||
|
.std_set CORE_MEM_CHR
|
||||||
|
tsr CORE_REG_C
|
||||||
|
set
|
||||||
|
|
||||||
|
@ .std_rset CORE_REG_B, ' '
|
||||||
|
@ .std_alu CORE_ALU_EQ
|
||||||
|
@ tlr CORE_REG_A
|
||||||
|
@ .std_cond_jump get_input_loop
|
||||||
|
@ .std_cp CORE_REG_D, CORE_REG_A
|
||||||
|
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_rset CORE_REG_B, 1
|
||||||
|
.std_alu CORE_ALU_ADD
|
||||||
|
tlr CORE_REG_A
|
||||||
|
tlr CORE_REG_C
|
||||||
|
|
||||||
|
.std_rset CORE_REG_B, MEM_INPUT_END
|
||||||
|
.std_alu CORE_ALU_LT
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_cond_jump get_input_loop
|
||||||
|
|
||||||
|
.std_cp CORE_REG_C, CORE_REG_A
|
||||||
|
.std_set MEM_INPUT_DYN_END
|
||||||
|
|
||||||
|
.std_rset CORE_REG_A, '\n'
|
||||||
|
.std_set CORE_MEM_CHR
|
||||||
|
|
||||||
|
.std_rset CORE_REG_B, MEM_INPUT_START
|
||||||
|
.std_get MEM_INPUT_DYN_END
|
||||||
|
tlr CORE_REG_D
|
||||||
|
print_loop:
|
||||||
|
tsr CORE_REG_B
|
||||||
|
get
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_set CORE_MEM_CHR
|
||||||
|
|
||||||
|
.std_rset CORE_REG_A, 1
|
||||||
|
.std_alu CORE_ALU_ADD
|
||||||
|
tlr CORE_REG_B
|
||||||
|
|
||||||
|
.std_cp CORE_REG_D, CORE_REG_A
|
||||||
|
.std_alu CORE_ALU_GT
|
||||||
|
tlr CORE_REG_A
|
||||||
|
.std_cond_jump print_loop
|
||||||
|
|
||||||
|
.std_rset CORE_REG_A, '\n'
|
||||||
|
.std_set CORE_MEM_CHR
|
||||||
|
.std_stop
|
52
lib/core.asm
Normal file
52
lib/core.asm
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
; hence core lib
|
||||||
|
core:
|
||||||
|
.define NULL, 0x0000
|
||||||
|
.define VOID, NULL
|
||||||
|
|
||||||
|
.define FALSE, NULL
|
||||||
|
.define TRUE, 0x0001
|
||||||
|
|
||||||
|
.define CORE_U8_MAX, 0x00ff
|
||||||
|
.define CORE_U16_MAX, 0xffff
|
||||||
|
|
||||||
|
.define CORE_KB, 1024
|
||||||
|
|
||||||
|
core_mem:
|
||||||
|
.define CORE_MEM_PRG, (0 * CORE_KB)
|
||||||
|
.define CORE_MEM_ST, (32 * CORE_KB)
|
||||||
|
.define CORE_MEM_MEM, (40 * CORE_KB)
|
||||||
|
.define CORE_MEM_OUT, (56 * CORE_KB)
|
||||||
|
.define CORE_MEM_CHR, (56 * CORE_KB + 1)
|
||||||
|
.define CORE_MEM_KEY, (56 * CORE_KB + 2)
|
||||||
|
|
||||||
|
core_reg:
|
||||||
|
.define CORE_REG_PC, 0x0
|
||||||
|
.define CORE_REG_OPC, 0x1
|
||||||
|
.define CORE_REG_ARG, 0x2
|
||||||
|
.define CORE_REG_S, 0x3
|
||||||
|
.define CORE_REG_SP, 0x4
|
||||||
|
.define CORE_REG_A, 0x5
|
||||||
|
.define CORE_REG_B, 0x6
|
||||||
|
.define CORE_REG_C, 0x7
|
||||||
|
.define CORE_REG_D, 0x8
|
||||||
|
|
||||||
|
core_alu:
|
||||||
|
.define CORE_ALU_NOT, 0x00
|
||||||
|
.define CORE_ALU_AND, 0x01
|
||||||
|
.define CORE_ALU_OR, 0x02
|
||||||
|
.define CORE_ALU_XOR, 0x03
|
||||||
|
.define CORE_ALU_LSH, 0x04
|
||||||
|
.define CORE_ALU_RSH, 0x05
|
||||||
|
.define CORE_ALU_ADD, 0x06
|
||||||
|
.define CORE_ALU_SUB, 0x07
|
||||||
|
.define CORE_ALU_MUL, 0x08
|
||||||
|
.define CORE_ALU_DIV, 0x09
|
||||||
|
.define CORE_ALU_CMP, 0x0a
|
||||||
|
.define CORE_ALU_EQ, 0x0b
|
||||||
|
.define CORE_ALU_LT, 0x0c
|
||||||
|
.define CORE_ALU_GT, 0x0d
|
||||||
|
.define CORE_ALU_LEQ, 0x0e
|
||||||
|
.define CORE_ALU_GEQ, 0x0f
|
||||||
|
.define CORE_ALU_BOL, 0x10
|
||||||
|
.define CORE_ALU_INV, 0x11
|
||||||
|
.define CORE_ALU_RND, 0x12
|
19
lib/main.asm
Normal file
19
lib/main.asm
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
; "main function" like definition macro
|
||||||
|
|
||||||
|
.requires "$lib/core.asm"
|
||||||
|
.requires "$lib/std.asm"
|
||||||
|
|
||||||
|
.define main_local_jump_main, (CORE_MEM_ST - 3 - 1)
|
||||||
|
|
||||||
|
.macro jump_main
|
||||||
|
.std_jump main_local_jump_main
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro main
|
||||||
|
main:
|
||||||
|
|
||||||
|
.org main_local_jump_main
|
||||||
|
ts main
|
||||||
|
tlr CORE_REG_PC
|
||||||
|
.org main
|
||||||
|
.endmacro
|
62
lib/std.asm
Normal file
62
lib/std.asm
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
; hence standard lib
|
||||||
|
|
||||||
|
.requires "$lib/core.asm"
|
||||||
|
|
||||||
|
std:
|
||||||
|
.macro std_tclr
|
||||||
|
ts NULL
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_rclr, std_rclr_arg_0_reg
|
||||||
|
ts NULL
|
||||||
|
tlr std_rclr_arg_0_reg
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_alu, std_alu_arg_0_op
|
||||||
|
ts std_alu_arg_0_op
|
||||||
|
alu
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_get, std_get_arg_0_addr
|
||||||
|
ts std_get_arg_0_addr
|
||||||
|
get
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_set, std_set_arg_0_addr
|
||||||
|
ts std_set_arg_0_addr
|
||||||
|
set
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_cp, std_cp_arg_0_from, std_cp_arg_1_to
|
||||||
|
tsr std_cp_arg_0_from
|
||||||
|
tlr std_cp_arg_1_to
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_mv, std_mv_arg_0_from, std_cp_arg_1_to
|
||||||
|
tsr std_cp_arg_0_from
|
||||||
|
tlr std_cp_arg_1_to
|
||||||
|
.std_rclr std_cp_arg_1_to
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_rset, std_init_arg_0_reg, std_init_arg_1_val
|
||||||
|
ts std_init_arg_1_val
|
||||||
|
tlr std_init_arg_0_reg
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_jump, std_jump_arg_0_label
|
||||||
|
.std_rset CORE_REG_PC, std_jump_arg_0_label
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_cond_jump, std_cond_jump_arg_0_label
|
||||||
|
ts std_cond_jump_arg_0_label
|
||||||
|
tlrc CORE_REG_PC
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_stop
|
||||||
|
.std_rset CORE_REG_PC, 0xffff
|
||||||
|
.endmacro
|
||||||
|
|
||||||
|
.macro std_inc, std_inc_arg_0_reg
|
||||||
|
.std_rset std_inc_arg_0_reg, 1
|
||||||
|
.std_alu CORE_ALU_ADD
|
||||||
|
.endmacro
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -41,54 +42,67 @@ enum Commands {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<()> {
|
||||||
let args = Cli::parse();
|
let args = Cli::parse();
|
||||||
match args.commands {
|
match args.commands {
|
||||||
Commands::Lex { src } => {
|
Commands::Lex { src } => {
|
||||||
let assembly = fs::read_to_string(src).unwrap();
|
let assembly = fs::read_to_string(src)?;
|
||||||
let tokens = lexer::lex(assembly).unwrap();
|
let tokens = lexer::lex(assembly)?;
|
||||||
dbg!(tokens);
|
dbg!(tokens);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Commands::Parse { src } => {
|
Commands::Parse { src } => {
|
||||||
let assembly = fs::read_to_string(src).unwrap();
|
let assembly = fs::read_to_string(src)?;
|
||||||
let tokens = lexer::lex(assembly).unwrap();
|
let tokens = lexer::lex(assembly)?;
|
||||||
let ast = parser::parse(tokens).unwrap();
|
let ast = parser::parse(tokens)?;
|
||||||
dbg!(ast);
|
dbg!(ast);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Commands::Assemble { src, bin, dump } => {
|
Commands::Assemble { src, bin, dump } => {
|
||||||
let assembly = fs::read_to_string(&src).unwrap();
|
let assembly = fs::read_to_string(&src)?;
|
||||||
let tokens = lexer::lex(assembly).unwrap();
|
let tokens = lexer::lex(assembly)?;
|
||||||
let ast = parser::parse(tokens).unwrap();
|
let ast = parser::parse(tokens)?;
|
||||||
|
|
||||||
let mut data = assembler::Data::new(
|
let mut data = assembler::Data::new(
|
||||||
match Path::new(&src).parent().unwrap().to_str() {
|
match match Path::new(&src).parent() {
|
||||||
Some(x) => x.to_string(),
|
Some(x) => x.to_str().map(|y| y.to_string()),
|
||||||
_ => panic!("Could not get directory in which source code resides"),
|
None => None,
|
||||||
|
} {
|
||||||
|
Some(s) => s,
|
||||||
|
None => bail!("Could not get directory in which source code resides"),
|
||||||
},
|
},
|
||||||
ast,
|
ast,
|
||||||
);
|
);
|
||||||
// assembler::assemble(&mut data).unwrap();
|
data.assemble()?;
|
||||||
data.assemble().unwrap();
|
|
||||||
|
|
||||||
if let Some(x) = bin {
|
if let Some(x) = bin {
|
||||||
File::create(x).unwrap().write_all(&data.program).unwrap();
|
File::create(x)?.write_all(&data.program)?;
|
||||||
}
|
}
|
||||||
if dump {
|
if dump {
|
||||||
println!("{}", rhexdump::hexdump(&data.program));
|
println!("{}", rhexdump::hexdump(&data.program));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Commands::Run { bin } => {
|
Commands::Run { bin } => {
|
||||||
let mut program_buf: Vec<u8> = Vec::new();
|
let mut program_buf: Vec<u8> = vec![];
|
||||||
let file = File::open(bin).unwrap();
|
let file = File::open(bin)?;
|
||||||
BufReader::new(file).read_to_end(&mut program_buf).unwrap();
|
BufReader::new(file).read_to_end(&mut program_buf)?;
|
||||||
if program_buf.len() < (32 * 1024) {
|
if program_buf.len() < (32 * 1024) {
|
||||||
program_buf.append(&mut vec![0; 32 * 1024 - program_buf.len()]);
|
program_buf.append(&mut vec![0; 32 * 1024 - program_buf.len()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let program: [u8; 32 * 1024] = (&program_buf[0..(32 * 1024)]).try_into().unwrap();
|
let program: [u8; 32 * 1024] = (&program_buf[0..(32 * 1024)]).try_into()?;
|
||||||
let mut data = emulator::Data::new(program);
|
let mut data = emulator::Data::new(program);
|
||||||
|
|
||||||
emulator::emulate(&mut data).unwrap();
|
emulator::emulate(&mut data)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {}
|
||||||
|
|
197
src/lib/arg.rs
197
src/lib/arg.rs
|
@ -1,11 +1,22 @@
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
|
use rand;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use unescape::unescape;
|
||||||
|
|
||||||
use crate::arg;
|
use crate::arg;
|
||||||
use crate::assembler;
|
use crate::assembler;
|
||||||
use crate::lexer;
|
use crate::lexer;
|
||||||
|
|
||||||
|
pub fn unescape_string(string: &str) -> String {
|
||||||
|
match unescape(string) {
|
||||||
|
Some(x) => x,
|
||||||
|
None => "".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Arg {
|
pub enum Arg {
|
||||||
|
Char(char),
|
||||||
String(String),
|
String(String),
|
||||||
Number(u16),
|
Number(u16),
|
||||||
Variable(String),
|
Variable(String),
|
||||||
|
@ -19,15 +30,16 @@ pub enum Arg {
|
||||||
impl assembler::ToAssembly for Arg {
|
impl assembler::ToAssembly for Arg {
|
||||||
fn to_assembly(&self) -> String {
|
fn to_assembly(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Arg::String(x) => format!("\"{x}\""),
|
Arg::Char(x) => format!("'{}'", x),
|
||||||
|
Arg::String(x) => format!("\"{}\"", x),
|
||||||
Arg::Number(x) => x.to_string(),
|
Arg::Number(x) => x.to_string(),
|
||||||
Arg::Variable(x) => x.clone(),
|
Arg::Variable(x) => x.clone(),
|
||||||
Arg::BinaryExpression { left, right, op } => {
|
Arg::BinaryExpression { left, right, op } => {
|
||||||
format!(
|
format!(
|
||||||
"({left} {op} {right})",
|
"({} {} {})",
|
||||||
left = left.to_assembly(),
|
left.to_assembly(),
|
||||||
op = op.to_assembly(),
|
op.to_assembly(),
|
||||||
right = right.to_assembly()
|
right.to_assembly()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,11 +47,11 @@ impl assembler::ToAssembly for Arg {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl assembler::ByteResolvable<assembler::Data> for Arg {
|
impl assembler::ByteResolvable<assembler::Data> for Arg {
|
||||||
fn resolve_number(&self, data: &mut assembler::Data) -> Result<u16, String> {
|
fn resolve_number(&self, data: &mut assembler::Data) -> Result<u16> {
|
||||||
match self {
|
match self {
|
||||||
|
Arg::Char(x) => Ok(*x as u16),
|
||||||
Arg::String(x) => {
|
Arg::String(x) => {
|
||||||
let y = x.replace("\\n", "\n");
|
let y = unescape_string(x);
|
||||||
|
|
||||||
if y.len() == 1 {
|
if y.len() == 1 {
|
||||||
Ok(y.as_bytes()[0] as u16)
|
Ok(y.as_bytes()[0] as u16)
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,36 +70,59 @@ impl assembler::ByteResolvable<assembler::Data> for Arg {
|
||||||
|
|
||||||
match arg {
|
match arg {
|
||||||
Some(a) => {
|
Some(a) => {
|
||||||
// arg = Some(a.clone());
|
|
||||||
match a {
|
match a {
|
||||||
arg::Arg::Variable(n) => {
|
arg::Arg::Variable(n) => {
|
||||||
name = n;
|
name = n;
|
||||||
}
|
}
|
||||||
_ => break Ok(a.resolve_number(data).unwrap()),
|
_ => break Ok(a.resolve_number(data)?),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Variable does not exist: '{name}'")),
|
_ => bail!("Variable does not exist: {}", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Arg::BinaryExpression { left, right, op } => {
|
Arg::BinaryExpression { left, right, op } => {
|
||||||
let left_val = left.resolve_number(data).unwrap();
|
let left_val = left.resolve_number(data)?;
|
||||||
let right_val = right.resolve_number(data).unwrap();
|
let right_val = right.resolve_number(data)?;
|
||||||
|
|
||||||
Ok(match op {
|
Ok(match op {
|
||||||
|
BinaryExpressionOperator::Not => !left_val,
|
||||||
|
BinaryExpressionOperator::And => left_val & right_val,
|
||||||
|
BinaryExpressionOperator::Nand => !(left_val & right_val),
|
||||||
|
BinaryExpressionOperator::Or => left_val | right_val,
|
||||||
|
BinaryExpressionOperator::Nor => !(left_val | right_val),
|
||||||
|
BinaryExpressionOperator::Xor => left_val ^ right_val,
|
||||||
|
BinaryExpressionOperator::Xnor => !(left_val ^ right_val),
|
||||||
|
BinaryExpressionOperator::Lsh => left_val << right_val,
|
||||||
|
BinaryExpressionOperator::Rsh => left_val >> right_val,
|
||||||
BinaryExpressionOperator::Add => left_val.wrapping_add(right_val),
|
BinaryExpressionOperator::Add => left_val.wrapping_add(right_val),
|
||||||
BinaryExpressionOperator::Sub => left_val.wrapping_sub(right_val),
|
BinaryExpressionOperator::Sub => left_val.wrapping_sub(right_val),
|
||||||
BinaryExpressionOperator::Mul => left_val.wrapping_mul(right_val),
|
BinaryExpressionOperator::Mul => left_val.wrapping_mul(right_val),
|
||||||
BinaryExpressionOperator::Div => left_val.wrapping_div(right_val),
|
BinaryExpressionOperator::Div => left_val.wrapping_div(right_val),
|
||||||
BinaryExpressionOperator::Pow => left_val.wrapping_pow(right_val as u32),
|
BinaryExpressionOperator::Pow => left_val.wrapping_pow(right_val as u32),
|
||||||
|
BinaryExpressionOperator::Cmp => match left_val.cmp(&right_val) {
|
||||||
|
Ordering::Equal => 0,
|
||||||
|
Ordering::Less => 1,
|
||||||
|
Ordering::Greater => 2,
|
||||||
|
},
|
||||||
|
BinaryExpressionOperator::Eq => (left_val == right_val) as u16,
|
||||||
|
BinaryExpressionOperator::Neq => (left_val != right_val) as u16,
|
||||||
|
BinaryExpressionOperator::Lt => (left_val < right_val) as u16,
|
||||||
|
BinaryExpressionOperator::Gt => (left_val > right_val) as u16,
|
||||||
|
BinaryExpressionOperator::Leq => (left_val <= right_val) as u16,
|
||||||
|
BinaryExpressionOperator::Geq => (left_val >= right_val) as u16,
|
||||||
|
BinaryExpressionOperator::Bol => (left_val & 1 == 1) as u16,
|
||||||
|
BinaryExpressionOperator::Inv => (left_val & 1 != 1) as u16,
|
||||||
|
BinaryExpressionOperator::Rnd => rand::random(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_bytes(&self, data: &mut assembler::Data) -> Result<Vec<u8>, String> {
|
fn resolve_bytes(&self, data: &mut assembler::Data) -> Result<Vec<u8>> {
|
||||||
match self {
|
match self {
|
||||||
Arg::String(x) => Ok(x.replace("\\n", "\n").bytes().collect()),
|
Arg::Char(x) => Ok(vec![*x as u8]),
|
||||||
|
Arg::String(x) => Ok(unescape_string(x).bytes().collect()),
|
||||||
Arg::Number(x) => Ok(vec![(*x >> 8) as u8, *x as u8]),
|
Arg::Number(x) => Ok(vec![(*x >> 8) as u8, *x as u8]),
|
||||||
Arg::Variable(x) => {
|
Arg::Variable(x) => {
|
||||||
let mut name = x.clone();
|
let mut name = x.clone();
|
||||||
|
@ -98,15 +133,14 @@ impl assembler::ByteResolvable<assembler::Data> for Arg {
|
||||||
|
|
||||||
match arg {
|
match arg {
|
||||||
Some(a) => {
|
Some(a) => {
|
||||||
// arg = Some(a.clone());
|
|
||||||
match a {
|
match a {
|
||||||
arg::Arg::Variable(n) => {
|
arg::Arg::Variable(n) => {
|
||||||
name = n;
|
name = n;
|
||||||
}
|
}
|
||||||
_ => break Ok(a.resolve_bytes(data).unwrap()),
|
_ => break Ok(a.resolve_bytes(data)?),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Variable does not exist: '{name}'")),
|
_ => bail!("Variable could not be resolved: {}", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,74 +149,145 @@ impl assembler::ByteResolvable<assembler::Data> for Arg {
|
||||||
right: _,
|
right: _,
|
||||||
op: _,
|
op: _,
|
||||||
} => {
|
} => {
|
||||||
let x = self.resolve_number(data).unwrap();
|
let x = self.resolve_number(data)?;
|
||||||
|
|
||||||
Ok(vec![(x >> 8) as u8, x as u8])
|
Ok(vec![(x >> 8) as u8, x as u8])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_string(&self, data: &mut assembler::Data) -> Result<String> {
|
||||||
|
match self {
|
||||||
|
Arg::Char(x) => Ok(x.to_string()),
|
||||||
|
Arg::String(x) => Ok(x.clone()),
|
||||||
|
Arg::Number(_) => bail!("Number cannot be resolved as string"),
|
||||||
|
Arg::Variable(_) => self.resolve_string(data),
|
||||||
|
Arg::BinaryExpression {
|
||||||
|
left: _,
|
||||||
|
right: _,
|
||||||
|
op: _,
|
||||||
|
} => bail!("Binary expression cannot be resolved as string"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum BinaryExpressionOperator {
|
pub enum BinaryExpressionOperator {
|
||||||
|
Not,
|
||||||
|
And,
|
||||||
|
Nand,
|
||||||
|
Or,
|
||||||
|
Nor,
|
||||||
|
Xor,
|
||||||
|
Xnor,
|
||||||
|
Lsh,
|
||||||
|
Rsh,
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
Pow,
|
Pow,
|
||||||
|
Cmp,
|
||||||
|
Eq,
|
||||||
|
Neq,
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
Leq,
|
||||||
|
Geq,
|
||||||
|
Bol,
|
||||||
|
Inv,
|
||||||
|
Rnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl assembler::ToAssembly for BinaryExpressionOperator {
|
impl assembler::ToAssembly for BinaryExpressionOperator {
|
||||||
fn to_assembly(&self) -> String {
|
fn to_assembly(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
BinaryExpressionOperator::Not => "~".to_string(),
|
||||||
|
BinaryExpressionOperator::And => "&".to_string(),
|
||||||
|
BinaryExpressionOperator::Nand => "~&".to_string(),
|
||||||
|
BinaryExpressionOperator::Or => "|".to_string(),
|
||||||
|
BinaryExpressionOperator::Nor => "~|".to_string(),
|
||||||
|
BinaryExpressionOperator::Xor => "^".to_string(),
|
||||||
|
BinaryExpressionOperator::Xnor => "~^".to_string(),
|
||||||
|
BinaryExpressionOperator::Lsh => "<<".to_string(),
|
||||||
|
BinaryExpressionOperator::Rsh => ">>".to_string(),
|
||||||
BinaryExpressionOperator::Add => "+".to_string(),
|
BinaryExpressionOperator::Add => "+".to_string(),
|
||||||
BinaryExpressionOperator::Sub => "-".to_string(),
|
BinaryExpressionOperator::Sub => "-".to_string(),
|
||||||
BinaryExpressionOperator::Mul => "*".to_string(),
|
BinaryExpressionOperator::Mul => "*".to_string(),
|
||||||
BinaryExpressionOperator::Div => "/".to_string(),
|
BinaryExpressionOperator::Div => "/".to_string(),
|
||||||
BinaryExpressionOperator::Pow => "**".to_string(),
|
BinaryExpressionOperator::Pow => "**".to_string(),
|
||||||
|
BinaryExpressionOperator::Cmp => "<=>".to_string(),
|
||||||
|
BinaryExpressionOperator::Eq => "==".to_string(),
|
||||||
|
BinaryExpressionOperator::Neq => "!=".to_string(),
|
||||||
|
BinaryExpressionOperator::Lt => "<".to_string(),
|
||||||
|
BinaryExpressionOperator::Gt => ">".to_string(),
|
||||||
|
BinaryExpressionOperator::Leq => "<=".to_string(),
|
||||||
|
BinaryExpressionOperator::Geq => ">=".to_string(),
|
||||||
|
BinaryExpressionOperator::Bol => "!!".to_string(),
|
||||||
|
BinaryExpressionOperator::Inv => "!".to_string(),
|
||||||
|
BinaryExpressionOperator::Rnd => "?".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_binary_operation(token: &lexer::Token) -> Result<BinaryExpressionOperator> {
|
pub fn parse_binary_operation(token: &lexer::Token) -> Result<BinaryExpressionOperator> {
|
||||||
match token {
|
match token {
|
||||||
|
lexer::Token::Not => Ok(BinaryExpressionOperator::Not),
|
||||||
|
lexer::Token::And => Ok(BinaryExpressionOperator::And),
|
||||||
|
lexer::Token::Nand => Ok(BinaryExpressionOperator::Nand),
|
||||||
|
lexer::Token::Or => Ok(BinaryExpressionOperator::Or),
|
||||||
|
lexer::Token::Nor => Ok(BinaryExpressionOperator::Nor),
|
||||||
|
lexer::Token::Xor => Ok(BinaryExpressionOperator::Xor),
|
||||||
|
lexer::Token::Xnor => Ok(BinaryExpressionOperator::Xnor),
|
||||||
|
lexer::Token::Lsh => Ok(BinaryExpressionOperator::Lsh),
|
||||||
|
lexer::Token::Rsh => Ok(BinaryExpressionOperator::Rsh),
|
||||||
lexer::Token::Add => Ok(BinaryExpressionOperator::Add),
|
lexer::Token::Add => Ok(BinaryExpressionOperator::Add),
|
||||||
lexer::Token::Sub => Ok(BinaryExpressionOperator::Sub),
|
lexer::Token::Sub => Ok(BinaryExpressionOperator::Sub),
|
||||||
lexer::Token::Mul => Ok(BinaryExpressionOperator::Mul),
|
lexer::Token::Mul => Ok(BinaryExpressionOperator::Mul),
|
||||||
lexer::Token::Div => Ok(BinaryExpressionOperator::Div),
|
lexer::Token::Div => Ok(BinaryExpressionOperator::Div),
|
||||||
lexer::Token::Pow => Ok(BinaryExpressionOperator::Pow),
|
lexer::Token::Pow => Ok(BinaryExpressionOperator::Pow),
|
||||||
|
lexer::Token::Cmp => Ok(BinaryExpressionOperator::Cmp),
|
||||||
|
lexer::Token::Eq => Ok(BinaryExpressionOperator::Eq),
|
||||||
|
lexer::Token::Neq => Ok(BinaryExpressionOperator::Neq),
|
||||||
|
lexer::Token::Lt => Ok(BinaryExpressionOperator::Lt),
|
||||||
|
lexer::Token::Gt => Ok(BinaryExpressionOperator::Gt),
|
||||||
|
lexer::Token::Leq => Ok(BinaryExpressionOperator::Leq),
|
||||||
|
lexer::Token::Geq => Ok(BinaryExpressionOperator::Geq),
|
||||||
|
lexer::Token::Bol => Ok(BinaryExpressionOperator::Bol),
|
||||||
|
lexer::Token::Inv => Ok(BinaryExpressionOperator::Inv),
|
||||||
|
lexer::Token::Rnd => Ok(BinaryExpressionOperator::Rnd),
|
||||||
_ => bail!("Invalid binary expression operator"),
|
_ => bail!("Invalid binary expression operator"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_binary_expression_arg(tokens: &mut Vec<&&lexer::Token>) -> Result<Arg> {
|
pub fn parse_binary_expression_arg(tokens: &mut Vec<&lexer::Token>) -> Result<Arg> {
|
||||||
if tokens.is_empty() {
|
if tokens.is_empty() {
|
||||||
bail!("Malformed binary expression");
|
bail!("Malformed binary expression");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut args: Vec<&&lexer::Token> = tokens.drain(..3).collect();
|
let mut args: Vec<_> = tokens.drain(..3).collect();
|
||||||
|
|
||||||
let mut bin_expr = Arg::BinaryExpression {
|
let mut bin_expr = Arg::BinaryExpression {
|
||||||
left: Box::new((&parse_args(vec![args[0]]).unwrap()[0]).clone()),
|
left: Box::new((&parse_args(vec![args[0]])?[0]).clone()),
|
||||||
right: Box::new((&parse_args(vec![args[2]]).unwrap()[0]).clone()),
|
right: Box::new((&parse_args(vec![args[2]])?[0]).clone()),
|
||||||
op: parse_binary_operation(args[1]).unwrap(),
|
op: parse_binary_operation(args[1])?,
|
||||||
};
|
};
|
||||||
|
|
||||||
while !tokens.is_empty() {
|
while !tokens.is_empty() {
|
||||||
args = tokens.drain(..2).collect();
|
args = tokens.drain(..2).collect();
|
||||||
bin_expr = Arg::BinaryExpression {
|
bin_expr = Arg::BinaryExpression {
|
||||||
left: Box::new(bin_expr),
|
left: Box::new(bin_expr),
|
||||||
right: Box::new((&parse_args(vec![args[1]]).unwrap()[0]).clone()),
|
right: Box::new((&parse_args(vec![args[1]])?[0]).clone()),
|
||||||
op: parse_binary_operation(args[0]).unwrap(),
|
op: parse_binary_operation(args[0])?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(bin_expr)
|
Ok(bin_expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>> {
|
||||||
let mut iter = tokens.iter();
|
let mut iter = tokens.into_iter();
|
||||||
let mut args: Vec<Arg> = Vec::new();
|
let mut args: Vec<Arg> = vec![];
|
||||||
let mut last_was_comma = true;
|
let mut last_was_comma = true;
|
||||||
|
|
||||||
while let Some(token) = iter.next() {
|
while let Some(token) = iter.next() {
|
||||||
|
@ -192,11 +297,21 @@ pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if !last_was_comma {
|
if !last_was_comma {
|
||||||
return Err("Invalid argument separation");
|
bail!("Invalid argument separation");
|
||||||
}
|
}
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
lexer::Token::Comment(_) => {}
|
lexer::Token::Comment(_) => {}
|
||||||
|
lexer::Token::CharLiteral(x) => {
|
||||||
|
let y = unescape_string(x);
|
||||||
|
if y.len() > 1 {
|
||||||
|
bail!("Char cannot hold string")
|
||||||
|
} else if let Some(c) = y.chars().next() {
|
||||||
|
args.push(Arg::Char(c));
|
||||||
|
} else {
|
||||||
|
bail!("Char cannot be empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
lexer::Token::StringLiteral(x) => {
|
lexer::Token::StringLiteral(x) => {
|
||||||
args.push(Arg::String(x.clone()));
|
args.push(Arg::String(x.clone()));
|
||||||
}
|
}
|
||||||
|
@ -206,14 +321,13 @@ pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
||||||
lexer::Token::Number(x) => {
|
lexer::Token::Number(x) => {
|
||||||
args.push(Arg::Number(match num_parse::parse_uint(x) {
|
args.push(Arg::Number(match num_parse::parse_uint(x) {
|
||||||
Some(y) => y,
|
Some(y) => y,
|
||||||
_ => return Err("Error parsing number"),
|
None => bail!("Error parsing number"),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
lexer::Token::LParen => {
|
lexer::Token::LParen => {
|
||||||
let mut depth: usize = 1;
|
let mut depth: usize = 1;
|
||||||
|
|
||||||
args.push(
|
args.push(parse_binary_expression_arg(
|
||||||
parse_binary_expression_arg(
|
|
||||||
&mut iter
|
&mut iter
|
||||||
.by_ref()
|
.by_ref()
|
||||||
.take_while(|t| match t {
|
.take_while(|t| match t {
|
||||||
|
@ -228,11 +342,9 @@ pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
||||||
_ => true,
|
_ => true,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)?);
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => return Err("Unexpected token for argument"),
|
_ => bail!("Unexpected token for argument"),
|
||||||
}
|
}
|
||||||
|
|
||||||
last_was_comma = false;
|
last_was_comma = false;
|
||||||
|
@ -242,3 +354,14 @@ pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
||||||
|
|
||||||
Ok(args)
|
Ok(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unescape_string() {
|
||||||
|
assert_eq!(unescape_string("\\n test"), "\n test".to_string());
|
||||||
|
assert_eq!(unescape_string(""), "".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,277 +1,14 @@
|
||||||
// use itertools::Itertools;
|
use anyhow::{bail, Result};
|
||||||
// use radix_fmt::radix;
|
use itertools::Itertools;
|
||||||
// use std::collections::HashMap;
|
use radix_fmt::radix;
|
||||||
//
|
use rust_embed::RustEmbed;
|
||||||
// use crate::arg;
|
use std::collections::{HashMap, HashSet};
|
||||||
// use crate::parser;
|
use std::fs;
|
||||||
//
|
use std::path::Path;
|
||||||
// pub trait ToCode {
|
use std::vec;
|
||||||
// fn to_code(&self) -> String;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub trait ByteResolvable<T> {
|
|
||||||
// fn resolve_number(&self, data: &mut T) -> Result<u16, String>;
|
|
||||||
//
|
|
||||||
// fn resolve_bytes(&self, data: &mut T) -> Result<Vec<u8>, String>;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// pub struct Macro {
|
|
||||||
// pub args: Vec<String>,
|
|
||||||
// pub body: Vec<parser::ast::Node>,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// pub enum State {
|
|
||||||
// Default,
|
|
||||||
// Macro { name: String, depth: usize },
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// #[derive(Debug)]
|
|
||||||
// pub struct Data {
|
|
||||||
// pub dir: String,
|
|
||||||
// pub body: Vec<parser::ast::Node>,
|
|
||||||
// pub program: [u8; 32 * 1024],
|
|
||||||
// pub offset: u16,
|
|
||||||
// pub contants: HashMap<String, arg::Arg>,
|
|
||||||
// pub macros: HashMap<String, Macro>,
|
|
||||||
// pub state: State,
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// impl Data {
|
|
||||||
// pub fn new(dir: String, body: Vec<parser::ast::Node>) -> Self {
|
|
||||||
// Self {
|
|
||||||
// dir,
|
|
||||||
// body,
|
|
||||||
// program: [0; 32 * 1024],
|
|
||||||
// offset: 0,
|
|
||||||
// contants: HashMap::new(),
|
|
||||||
// macros: HashMap::new(),
|
|
||||||
// state: State::Default,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub fn assemble(data: &mut Data) -> Result<(), String> {
|
|
||||||
// for node in data.body.clone() {
|
|
||||||
// data.contants
|
|
||||||
// .insert("OFFSET".to_string(), arg::Arg::Number(data.offset));
|
|
||||||
//
|
|
||||||
// match &data.state {
|
|
||||||
// State::Default => {
|
|
||||||
// match node {
|
|
||||||
// parser::ast::Node::Comment(_) => {}
|
|
||||||
// parser::ast::Node::Label(x) => {
|
|
||||||
// if data.contants.contains_key(&x) {
|
|
||||||
// return Err(format!("Label already exists: '{x}'"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// data.contants
|
|
||||||
// .insert(x.to_string(), arg::Arg::Number(data.offset));
|
|
||||||
// }
|
|
||||||
// parser::ast::Node::Call { name, arg } => {
|
|
||||||
// let arg_num = match arg {
|
|
||||||
// Some(x) => x.resolve_number(data).unwrap(),
|
|
||||||
// _ => 0,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// data.program[data.offset as usize] = match name.as_str() {
|
|
||||||
// "nop" => 0x00,
|
|
||||||
// "push" => 0x01,
|
|
||||||
// "pop" => 0x02,
|
|
||||||
// "ts" => 0x03,
|
|
||||||
// "tsr" => 0x04,
|
|
||||||
// "tss" => 0x05,
|
|
||||||
// "tlr" => 0x06,
|
|
||||||
// "tlrc" => 0x07,
|
|
||||||
// "tls" => 0x08,
|
|
||||||
// "ld" => 0x09,
|
|
||||||
// "dbg" => 0x0a,
|
|
||||||
// "alu" => 0x0b,
|
|
||||||
// "get" => 0x0c,
|
|
||||||
// "set" => 0x0d,
|
|
||||||
// _ => return Err(format!("Unknown opcode: '{name}'")),
|
|
||||||
// };
|
|
||||||
// if arg_num == 0 {
|
|
||||||
// data.program[data.offset as usize] |= 0b10000000;
|
|
||||||
// }
|
|
||||||
// data.offset += 1;
|
|
||||||
//
|
|
||||||
// if arg_num != 0 {
|
|
||||||
// data.program[data.offset as usize] = (arg_num >> 8) as u8;
|
|
||||||
// data.offset += 1;
|
|
||||||
//
|
|
||||||
// data.program[data.offset as usize] = arg_num as u8;
|
|
||||||
// data.offset += 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// parser::ast::Node::MacroCall { name, args } => {
|
|
||||||
// match name.as_str() {
|
|
||||||
// "debug" => {
|
|
||||||
// for arg in args {
|
|
||||||
// let bytes = arg.resolve_bytes(data).unwrap();
|
|
||||||
//
|
|
||||||
// println!("{}", arg.to_code().replace('\n', "\\n"));
|
|
||||||
// println!(" => {}", arg.resolve_number(data).unwrap());
|
|
||||||
// println!(
|
|
||||||
// " => [{}]",
|
|
||||||
// bytes
|
|
||||||
// .iter()
|
|
||||||
// .map(|n| {
|
|
||||||
// let num = radix(*n, 16).to_string();
|
|
||||||
// format!(
|
|
||||||
// "0x{}{}",
|
|
||||||
// "00".chars()
|
|
||||||
// .take(2 - num.len())
|
|
||||||
// .collect::<String>(),
|
|
||||||
// num
|
|
||||||
// )
|
|
||||||
// })
|
|
||||||
// .join(", ")
|
|
||||||
// );
|
|
||||||
// println!(
|
|
||||||
// " => \"{}\"",
|
|
||||||
// String::from_utf8(bytes).unwrap().replace('\n', "\\n")
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// println!("==========");
|
|
||||||
// }
|
|
||||||
// "define" => {
|
|
||||||
// let name = match &args[0] {
|
|
||||||
// arg::Arg::Variable(x) | arg::Arg::String(x) => x,
|
|
||||||
// _ => return Err(
|
|
||||||
// "First argument of define macro needs to be a literal-like"
|
|
||||||
// .to_string(),
|
|
||||||
// ),
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// data.contants.insert(name.to_string(), (&args[1]).clone());
|
|
||||||
// }
|
|
||||||
// "macro" => {
|
|
||||||
// let name = match &args[0] {
|
|
||||||
// arg::Arg::Variable(x) | arg::Arg::String(x) => x,
|
|
||||||
// _ => return Err(
|
|
||||||
// "First argument of define macro needs to be a literal-like"
|
|
||||||
// .to_string(),
|
|
||||||
// ),
|
|
||||||
// };
|
|
||||||
// let args = match (&args[1..])
|
|
||||||
// .iter()
|
|
||||||
// // .map(|a| match a {
|
|
||||||
// // arg::Arg::Variable(x) => Ok(x.clone()),
|
|
||||||
// // __ => {
|
|
||||||
// // Err("Macro arguments need to be variables".to_string())
|
|
||||||
// // }
|
|
||||||
// // })
|
|
||||||
// .map(|a| {
|
|
||||||
// if let arg::Arg::Variable(x) = a {
|
|
||||||
// Ok(x.clone())
|
|
||||||
// } else {
|
|
||||||
// Err("Macro arguments need to be variables".to_string())
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .collect::<Result<Vec<_>, _>>()
|
|
||||||
// {
|
|
||||||
// Ok(x) => x,
|
|
||||||
// Err(x) => return Err(x),
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// data.macros.insert(
|
|
||||||
// name.clone(),
|
|
||||||
// Macro {
|
|
||||||
// args,
|
|
||||||
// body: Vec::new(),
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
// data.state = State::Macro {
|
|
||||||
// name: name.clone(),
|
|
||||||
// depth: 1,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// "macroend" => return Err("Unexpected macro end".to_string()),
|
|
||||||
// "org" => {
|
|
||||||
// data.offset = args[0].resolve_number(data).unwrap();
|
|
||||||
// }
|
|
||||||
// "org_add" => {
|
|
||||||
// data.offset += args[0].resolve_number(data).unwrap();
|
|
||||||
// }
|
|
||||||
// "org_sub" => {
|
|
||||||
// data.offset -= args[0].resolve_number(data).unwrap();
|
|
||||||
// }
|
|
||||||
// "bytes" => {
|
|
||||||
// for arg in args {
|
|
||||||
// for n in arg.resolve_bytes(data).unwrap() {
|
|
||||||
// data.program[data.offset as usize] = n;
|
|
||||||
// data.offset += 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "bw" => {
|
|
||||||
// let string_arg = args[0].resolve_bytes(data).unwrap();
|
|
||||||
// let string =
|
|
||||||
// String::from_utf8(string_arg).unwrap().replace("\\n", "\n");
|
|
||||||
//
|
|
||||||
// for n in string.bytes() {
|
|
||||||
// data.program[data.offset as usize] = n;
|
|
||||||
// data.offset += 1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// _ => match data.macros.get(&name) {
|
|
||||||
// Some(m) => {
|
|
||||||
// dbg!(name, m);
|
|
||||||
// }
|
|
||||||
// None => return Err(format!("Unknown macro: '{name}'")),
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// State::Macro { name, depth } => match &node {
|
|
||||||
// parser::ast::Node::MacroCall {
|
|
||||||
// name: node_name,
|
|
||||||
// args: _,
|
|
||||||
// } => match node_name.as_str() {
|
|
||||||
// "macro" => {
|
|
||||||
// data.state = State::Macro {
|
|
||||||
// name: name.clone(),
|
|
||||||
// depth: depth + 1,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// "macroend" => {
|
|
||||||
// if *depth - 1 == 0 {
|
|
||||||
// data.state = State::Default;
|
|
||||||
// } else {
|
|
||||||
// data.state = State::Macro {
|
|
||||||
// name: name.clone(),
|
|
||||||
// depth: depth - 1,
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// _ => {
|
|
||||||
// data.macros.get_mut(name).unwrap().body.push(node.clone());
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// _ => {
|
|
||||||
// data.macros.get_mut(name).unwrap().body.push(node.clone());
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if data.offset > (32 * 1024) {
|
|
||||||
// return Err(format!(
|
|
||||||
// "Offset out of bounds: 0x{} > 0x8000",
|
|
||||||
// radix(data.offset, 16),
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use crate::arg;
|
use crate::arg;
|
||||||
|
use crate::lexer;
|
||||||
use crate::parser;
|
use crate::parser;
|
||||||
|
|
||||||
pub trait ToAssembly {
|
pub trait ToAssembly {
|
||||||
|
@ -279,27 +16,28 @@ pub trait ToAssembly {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ByteResolvable<T> {
|
pub trait ByteResolvable<T> {
|
||||||
fn resolve_number(&self, data: &mut T) -> Result<u16, String>;
|
fn resolve_number(&self, data: &mut T) -> Result<u16>;
|
||||||
|
|
||||||
fn resolve_bytes(&self, data: &mut T) -> Result<Vec<u8>, String>;
|
fn resolve_bytes(&self, data: &mut T) -> Result<Vec<u8>>;
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn resolve_string(&self, data: &mut T) -> Result<String>;
|
||||||
pub enum ResolveResult {
|
|
||||||
Resolved(Vec<u8>),
|
|
||||||
Partial(u16),
|
|
||||||
Unresolved,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResolveResult {
|
|
||||||
pub fn empty() -> Self {
|
|
||||||
ResolveResult::Resolved(vec![])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const PROGRAM_SIZE: u16 = 32 * 1024;
|
pub const PROGRAM_SIZE: u16 = 32 * 1024;
|
||||||
pub type Program = [u8; PROGRAM_SIZE as usize];
|
pub type Program = [u8; PROGRAM_SIZE as usize];
|
||||||
|
|
||||||
|
#[derive(RustEmbed)]
|
||||||
|
#[folder = "lib/"]
|
||||||
|
#[prefix = "$lib/"]
|
||||||
|
pub struct Lib;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Macro {
|
||||||
|
pub args: Vec<String>,
|
||||||
|
pub body: parser::ast::Body,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
pub dir: String,
|
pub dir: String,
|
||||||
pub ast: parser::ast::AST,
|
pub ast: parser::ast::AST,
|
||||||
|
@ -307,6 +45,11 @@ pub struct Data {
|
||||||
pub offset: u16,
|
pub offset: u16,
|
||||||
pub body_stack: Vec<parser::ast::Node>,
|
pub body_stack: Vec<parser::ast::Node>,
|
||||||
pub constants: HashMap<String, arg::Arg>,
|
pub constants: HashMap<String, arg::Arg>,
|
||||||
|
pub macros: HashMap<String, Macro>,
|
||||||
|
pub includes: HashSet<String>,
|
||||||
|
pub tmp: Vec<u8>,
|
||||||
|
pub tmp_offset: u16,
|
||||||
|
pub tmp_enabled: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
|
@ -318,37 +61,342 @@ impl Data {
|
||||||
program: [0; PROGRAM_SIZE as usize],
|
program: [0; PROGRAM_SIZE as usize],
|
||||||
offset: 0,
|
offset: 0,
|
||||||
constants: HashMap::new(),
|
constants: HashMap::new(),
|
||||||
|
macros: HashMap::new(),
|
||||||
|
includes: HashSet::new(),
|
||||||
|
tmp: vec![],
|
||||||
|
tmp_offset: 0,
|
||||||
|
tmp_enabled: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_node(&mut self, node: parser::ast::Node) -> ResolveResult {
|
pub fn resolve_node(&mut self) -> Result<Option<Vec<u8>>> {
|
||||||
|
let node = match self.body_stack.pop() {
|
||||||
|
Some(x) => x,
|
||||||
|
None => bail!("Body stack empty. No more nodes to resolve"),
|
||||||
|
};
|
||||||
|
|
||||||
match node {
|
match node {
|
||||||
parser::ast::Node::Comment(_) => ResolveResult::empty(),
|
parser::ast::Node::Comment(_) => Ok(None),
|
||||||
parser::ast::Node::Label(x) => {
|
parser::ast::Node::Label(x) => {
|
||||||
self.constants.insert(x, arg::Arg::Number(self.offset));
|
self.constants.insert(x, arg::Arg::Number(self.offset));
|
||||||
|
|
||||||
ResolveResult::empty()
|
Ok(None)
|
||||||
}
|
}
|
||||||
parser::ast::Node::Call { name, arg } => {
|
parser::ast::Node::Call { name, arg } => {
|
||||||
dbg!(name, arg);
|
let opcode: u8 = match name.as_str() {
|
||||||
|
"nop" => 0x00,
|
||||||
|
"push" => 0x01,
|
||||||
|
"pop" => 0x02,
|
||||||
|
"ts" => 0x03,
|
||||||
|
"tsr" => 0x04,
|
||||||
|
"tss" => 0x05,
|
||||||
|
"tlr" => 0x06,
|
||||||
|
"tlrc" => 0x07,
|
||||||
|
"tls" => 0x08,
|
||||||
|
"ld" => 0x09,
|
||||||
|
"dbg" => 0x0a,
|
||||||
|
"alu" => 0x0b,
|
||||||
|
"get" => 0x0c,
|
||||||
|
"set" => 0x0d,
|
||||||
|
_ => bail!("Unknown opcode: {}", name),
|
||||||
|
};
|
||||||
|
let a = match arg {
|
||||||
|
Some(x) => x.resolve_number(self)?,
|
||||||
|
None => 0,
|
||||||
|
};
|
||||||
|
|
||||||
ResolveResult::empty()
|
if a == 0 {
|
||||||
|
Ok(Some(vec![opcode | 0b10000000]))
|
||||||
|
} else {
|
||||||
|
Ok(Some(vec![opcode, (a >> 8) as u8, a as u8]))
|
||||||
}
|
}
|
||||||
parser::ast::Node::MacroCall { name, args } => {
|
}
|
||||||
dbg!(name, args);
|
parser::ast::Node::MacroCall { name, args } => match name.as_str() {
|
||||||
|
"debug" => {
|
||||||
|
for arg in args {
|
||||||
|
let assembly = arg.to_assembly().replace('\n', "\\n");
|
||||||
|
let num = arg.resolve_number(self)?;
|
||||||
|
let bytes = arg.resolve_bytes(self)?;
|
||||||
|
|
||||||
ResolveResult::empty()
|
println!("{}", assembly);
|
||||||
|
let bin_num = radix(num, 2).to_string();
|
||||||
|
println!(
|
||||||
|
" => 0b{}{}",
|
||||||
|
"0000000000000000"[..16 - bin_num.len()].to_owned(),
|
||||||
|
bin_num
|
||||||
|
);
|
||||||
|
println!(" => {}", num);
|
||||||
|
let hex_num = radix(num, 16).to_string();
|
||||||
|
println!(
|
||||||
|
" => 0x{}{}",
|
||||||
|
"0000"[..4 - hex_num.len()].to_owned(),
|
||||||
|
hex_num
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
" => [{}]",
|
||||||
|
bytes
|
||||||
|
.iter()
|
||||||
|
.map(|n| {
|
||||||
|
let num = radix(*n, 16).to_string();
|
||||||
|
|
||||||
|
format!(
|
||||||
|
"0x{}{}",
|
||||||
|
"00".chars().take(2 - num.len()).collect::<String>(),
|
||||||
|
num
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.join(", ")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"define" => {
|
||||||
|
let name = match &args[0] {
|
||||||
|
arg::Arg::Variable(x) | arg::Arg::String(x) => x,
|
||||||
|
_ => {
|
||||||
|
bail!("First argument of define macro needs to be a variable")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.constants.insert(name.clone(), (&args[1]).clone());
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"define_num_nl" => {
|
||||||
|
let name = match &args[0] {
|
||||||
|
arg::Arg::Variable(x) => x,
|
||||||
|
_ => {
|
||||||
|
bail!("First argument of define macro needs to be a variable")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let a = arg::Arg::Number(args[1].resolve_number(self)?);
|
||||||
|
self.constants.insert(name.clone(), a);
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"macro" => {
|
||||||
|
let name = match &args[0] {
|
||||||
|
arg::Arg::Variable(x) | arg::Arg::String(x) => x,
|
||||||
|
_ => {
|
||||||
|
bail!("First argument of define macro needs to be a literal-like value")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let args = (&args[1..])
|
||||||
|
.iter()
|
||||||
|
.map(|a| match a {
|
||||||
|
arg::Arg::Variable(x) => Ok(x.clone()),
|
||||||
|
_ => bail!("Macro arguments need to be variables"),
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
let mut depth: usize = 1;
|
||||||
|
let body: Vec<_> = self
|
||||||
|
.body_stack
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.take_while(|n| match n {
|
||||||
|
parser::ast::Node::MacroCall { name, args: _ } => match name.as_str() {
|
||||||
|
"macro" => {
|
||||||
|
depth += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
"endmacro" => {
|
||||||
|
depth -= 1;
|
||||||
|
depth != 0
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
self.body_stack
|
||||||
|
.drain((self.body_stack.len() - body.len() - 1)..)
|
||||||
|
.for_each(drop);
|
||||||
|
self.macros.insert(name.clone(), Macro { args, body });
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"if" => {
|
||||||
|
let cond = (args[0].resolve_number(self)? & 1) == 1;
|
||||||
|
dbg!(cond);
|
||||||
|
|
||||||
|
let mut if_depth: usize = 1;
|
||||||
|
let body: Vec<_> = self
|
||||||
|
.body_stack
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.take_while(|n| match n {
|
||||||
|
parser::ast::Node::MacroCall { name, args: _ } => match name.as_str() {
|
||||||
|
"if" => {
|
||||||
|
if_depth += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
"endif" => {
|
||||||
|
if_depth -= 1;
|
||||||
|
if_depth != 0
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
},
|
||||||
|
_ => true,
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
self.body_stack
|
||||||
|
.drain((self.body_stack.len() - body.len() - 1)..)
|
||||||
|
.for_each(drop);
|
||||||
|
dbg!(&body);
|
||||||
|
|
||||||
|
let mut iter = body.iter().enumerate();
|
||||||
|
if let Some(pos) = loop {
|
||||||
|
if let Some(pos) = iter.next() {
|
||||||
|
if let parser::ast::Node::MacroCall { name, args: _ } = pos.1 {
|
||||||
|
match name.as_str() {
|
||||||
|
"if" => {
|
||||||
|
break None;
|
||||||
|
}
|
||||||
|
"else" => {
|
||||||
|
break Some(pos);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break None;
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
self.body_stack.extend(if cond {
|
||||||
|
body[..pos.0].to_vec()
|
||||||
|
} else {
|
||||||
|
body[(pos.0 + 1)..].to_vec()
|
||||||
|
});
|
||||||
|
} else if cond {
|
||||||
|
self.body_stack.extend(body.into_iter().rev());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"org" => {
|
||||||
|
self.offset = args[0].resolve_number(self)?;
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"org_add" => {
|
||||||
|
self.offset = self.offset.wrapping_add(args[0].resolve_number(self)?);
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"org_sub" => {
|
||||||
|
self.offset = self.offset.wrapping_sub(args[0].resolve_number(self)?);
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"bytes" => {
|
||||||
|
let mut data: Vec<u8> = vec![];
|
||||||
|
for arg in args {
|
||||||
|
data.append(&mut arg.resolve_bytes(self)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.is_empty() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"embed" => {
|
||||||
|
let arg = args[0].resolve_string(self)?;
|
||||||
|
let data = if Path::new(&arg).starts_with("$lib") {
|
||||||
|
match Lib::get(arg.as_str()) {
|
||||||
|
Some(x) => x.data.to_vec(),
|
||||||
|
None => bail!("Virtual lib file couldn't be found with path: {}", arg),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let path = Path::new(&self.dir).join(arg);
|
||||||
|
fs::read(path)?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(data))
|
||||||
|
}
|
||||||
|
"requires" => {
|
||||||
|
let arg = args[0].resolve_string(self)?;
|
||||||
|
if !self.includes.contains(&arg) {
|
||||||
|
bail!("File requires include of not included file: {}", arg);
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
"include" => {
|
||||||
|
let arg = args[0].resolve_string(self)?;
|
||||||
|
let source = if Path::new(&arg).starts_with("$lib") {
|
||||||
|
match Lib::get(arg.as_str()) {
|
||||||
|
Some(x) => std::str::from_utf8(&x.data)?.to_string(),
|
||||||
|
None => bail!("Virtual lib file couldn't be found with path: {}", arg),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let path = Path::new(&self.dir).join(&arg);
|
||||||
|
fs::read_to_string(path)?
|
||||||
|
};
|
||||||
|
|
||||||
|
self.includes.insert(arg);
|
||||||
|
let body = parser::parse(lexer::lex(source)?)?.body;
|
||||||
|
self.body_stack
|
||||||
|
.extend(body.into_iter().rev().collect::<Vec<_>>());
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
_ => match self.macros.get(&name) {
|
||||||
|
Some(m) => {
|
||||||
|
if m.args.len() != args.len() {
|
||||||
|
bail!(
|
||||||
|
"Macro call argument signature does not match: (.macro {name}{macro_args}) != (.{name}{call_args})",
|
||||||
|
macro_args = if m.args.is_empty() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!(" {}", m.args.join(", "))
|
||||||
|
},
|
||||||
|
call_args = if args.is_empty() {
|
||||||
|
"".to_string()
|
||||||
|
} else {
|
||||||
|
format!(" {}", args.iter().map(|a| a.to_assembly()).join(", "))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// dbg!(&args);
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
self.constants.insert(m.args[i].clone(), arg.clone());
|
||||||
|
}
|
||||||
|
self.body_stack
|
||||||
|
.append(&mut m.body.iter().rev().cloned().collect());
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
None => bail!("Unknown macro: {}", name),
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assemble(&mut self) -> Result<Program> {
|
pub fn assemble(&mut self) -> Result<Program> {
|
||||||
while let Some(node) = self.body_stack.pop() {
|
let offset_name = "OFFSET".to_string();
|
||||||
let res = self.resolve_node(node);
|
while !self.body_stack.is_empty() {
|
||||||
dbg!(res);
|
self.constants
|
||||||
|
.insert(offset_name.clone(), arg::Arg::Number(self.offset));
|
||||||
|
match self.resolve_node()? {
|
||||||
|
Some(x) => {
|
||||||
|
if self.offset + (x.len() as u16) > 32 * 1024 {
|
||||||
|
bail!(
|
||||||
|
"Offset out of bounds: 0x{} > 0x8000",
|
||||||
|
radix(self.offset, 16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for byte in x {
|
||||||
|
self.program[self.offset as usize] = byte;
|
||||||
|
self.offset += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self.program)
|
Ok(self.program)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::{bail, Result};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use radix_fmt::radix;
|
use radix_fmt::radix;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
@ -6,9 +7,7 @@ use std::io::{self, Write};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
program: [u8; 32 * 1024],
|
program: [u8; 32 * 1024],
|
||||||
|
|
||||||
pub tmp: u16,
|
pub tmp: u16,
|
||||||
|
|
||||||
pub reg_pc: u16,
|
pub reg_pc: u16,
|
||||||
pub reg_opc: u8,
|
pub reg_opc: u8,
|
||||||
pub reg_arg: u16,
|
pub reg_arg: u16,
|
||||||
|
@ -18,10 +17,8 @@ pub struct Data {
|
||||||
pub reg_b: u16,
|
pub reg_b: u16,
|
||||||
pub reg_c: u16,
|
pub reg_c: u16,
|
||||||
pub reg_d: u16,
|
pub reg_d: u16,
|
||||||
|
|
||||||
stack: [u16; 8 * 1024],
|
stack: [u16; 8 * 1024],
|
||||||
memory: [u16; 16 * 1024],
|
memory: [u16; 16 * 1024],
|
||||||
|
|
||||||
term: console::Term,
|
term: console::Term,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,9 +26,7 @@ impl Data {
|
||||||
pub fn new(program: [u8; 32 * 1024]) -> Self {
|
pub fn new(program: [u8; 32 * 1024]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
program,
|
program,
|
||||||
|
|
||||||
tmp: 0,
|
tmp: 0,
|
||||||
|
|
||||||
reg_pc: 0,
|
reg_pc: 0,
|
||||||
reg_opc: 0,
|
reg_opc: 0,
|
||||||
reg_arg: 0,
|
reg_arg: 0,
|
||||||
|
@ -41,44 +36,43 @@ impl Data {
|
||||||
reg_b: 0,
|
reg_b: 0,
|
||||||
reg_c: 0,
|
reg_c: 0,
|
||||||
reg_d: 0,
|
reg_d: 0,
|
||||||
|
|
||||||
stack: [0; 8 * 1024],
|
stack: [0; 8 * 1024],
|
||||||
memory: [0; 16 * 1024],
|
memory: [0; 16 * 1024],
|
||||||
|
|
||||||
term: console::Term::stdout(),
|
term: console::Term::stdout(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_memory(&self, address: u16) -> u16 {
|
pub fn get_memory(&self, address: u16) -> Result<u16> {
|
||||||
if address < (32 * 1024) {
|
if address < (32 * 1024) {
|
||||||
match self.program.get(address as usize) {
|
match self.program.get(address as usize) {
|
||||||
Some(val) => *val as u16,
|
Some(val) => Ok(*val as u16),
|
||||||
None => 0,
|
None => Ok(0),
|
||||||
}
|
}
|
||||||
} else if address < (40 * 1024) {
|
} else if address < (40 * 1024) {
|
||||||
self.stack[(address - (32 * 1024)) as usize]
|
Ok(self.stack[(address - (32 * 1024)) as usize])
|
||||||
} else if address < (56 * 1024) {
|
} else if address < (56 * 1024) {
|
||||||
self.memory[(address - (40 * 1024)) as usize]
|
Ok(self.memory[(address - (40 * 1024)) as usize])
|
||||||
} else if address == (56 * 1024 + 2) {
|
} else if address == (56 * 1024 + 2) {
|
||||||
self.term.read_char().unwrap() as u16
|
Ok(self.term.read_char()? as u16)
|
||||||
} else {
|
} else {
|
||||||
0
|
Ok(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_memory(&mut self, address: u16, value: u16) {
|
pub fn set_memory(&mut self, address: u16, value: u16) -> Result<()> {
|
||||||
// if address >= (32 * 1024) && address < (40 * 1024) {
|
|
||||||
if ((32 * 1024)..(40 * 1024)).contains(&address) {
|
if ((32 * 1024)..(40 * 1024)).contains(&address) {
|
||||||
self.stack[(address - (32 * 1024)) as usize] = value;
|
self.stack[(address - (32 * 1024)) as usize] = value;
|
||||||
} else if address < (40 * 1024) {
|
} else if address < (56 * 1024) {
|
||||||
self.memory[(address - (40 * 1024)) as usize] = value;
|
self.memory[(address - (40 * 1024)) as usize] = value;
|
||||||
} else if address == (56 * 1024) {
|
} else if address == (56 * 1024) {
|
||||||
print!("{value}");
|
print!("{value}");
|
||||||
io::stdout().flush().unwrap();
|
io::stdout().flush()?;
|
||||||
} else if address == (56 * 1024 + 1) {
|
} else if address == (56 * 1024 + 1) {
|
||||||
print!("{}", char::from(value as u8));
|
print!("{}", char::from(value as u8));
|
||||||
io::stdout().flush().unwrap();
|
io::stdout().flush()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register(&self, register: u8) -> u16 {
|
pub fn get_register(&self, register: u8) -> u16 {
|
||||||
|
@ -100,54 +94,45 @@ impl Data {
|
||||||
match register {
|
match register {
|
||||||
0x0 => {
|
0x0 => {
|
||||||
self.reg_pc = value;
|
self.reg_pc = value;
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
0x1 => {
|
0x1 => {
|
||||||
self.reg_opc = value as u8;
|
self.reg_opc = value as u8;
|
||||||
|
|
||||||
value & 0xff
|
value & 0xff
|
||||||
}
|
}
|
||||||
0x2 => {
|
0x2 => {
|
||||||
self.reg_arg = value;
|
self.reg_arg = value;
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
0x3 => {
|
0x3 => {
|
||||||
self.reg_s = value as u8;
|
self.reg_s = value as u8;
|
||||||
|
|
||||||
value & 0xff
|
value & 0xff
|
||||||
}
|
}
|
||||||
0x4 => {
|
0x4 => {
|
||||||
self.reg_sp = value;
|
self.reg_sp = value;
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
0x5 => {
|
0x5 => {
|
||||||
self.reg_a = value;
|
self.reg_a = value;
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
0x6 => {
|
0x6 => {
|
||||||
self.reg_b = value;
|
self.reg_b = value;
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
0x7 => {
|
0x7 => {
|
||||||
self.reg_c = value;
|
self.reg_c = value;
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
0x8 => {
|
0x8 => {
|
||||||
self.reg_d = value;
|
self.reg_d = value;
|
||||||
|
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
_ => 0,
|
_ => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alu(&mut self, operation: u8) -> Result<(), String> {
|
fn alu(&mut self, operation: u8) -> Result<()> {
|
||||||
match operation {
|
match operation {
|
||||||
0x00 => {
|
0x00 => {
|
||||||
self.tmp = !self.reg_a;
|
self.tmp = !self.reg_a;
|
||||||
|
@ -203,57 +188,49 @@ impl Data {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x0b => {
|
0x0b => {
|
||||||
self.tmp = bool_to_num(self.reg_a == self.reg_b);
|
self.tmp = (self.reg_a == self.reg_b) as u16;
|
||||||
}
|
}
|
||||||
0x0c => {
|
0x0c => {
|
||||||
self.tmp = bool_to_num(self.reg_a < self.reg_b);
|
self.tmp = (self.reg_a < self.reg_b) as u16;
|
||||||
}
|
}
|
||||||
0x0d => {
|
0x0d => {
|
||||||
self.tmp = bool_to_num(self.reg_a > self.reg_b);
|
self.tmp = (self.reg_a > self.reg_b) as u16;
|
||||||
}
|
}
|
||||||
0x0e => {
|
0x0e => {
|
||||||
self.tmp = bool_to_num(self.reg_a <= self.reg_b);
|
self.tmp = (self.reg_a <= self.reg_b) as u16;
|
||||||
}
|
}
|
||||||
0x0f => {
|
0x0f => {
|
||||||
self.tmp = bool_to_num(self.reg_a >= self.reg_b);
|
self.tmp = (self.reg_a >= self.reg_b) as u16;
|
||||||
}
|
}
|
||||||
0x10 => {
|
0x10 => {
|
||||||
self.tmp = bool_to_num(self.reg_a == 1);
|
self.tmp = (self.reg_a & 1 == 1) as u16;
|
||||||
}
|
}
|
||||||
0x11 => {
|
0x11 => {
|
||||||
self.tmp = bool_to_num(self.reg_a != 1);
|
self.tmp = (self.reg_a & 1 != 1) as u16;
|
||||||
}
|
}
|
||||||
0x12 => {
|
0x12 => {
|
||||||
self.tmp = rand::random();
|
self.tmp = rand::random();
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Invalid ALU operation: 0x{}", radix(operation, 16))),
|
_ => bail!("Invalid ALU operation: 0x{}", radix(operation, 16)),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bool_to_num(x: bool) -> u16 {
|
pub fn emulate(data: &mut Data) -> Result<()> {
|
||||||
if x {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn emulate(data: &mut Data) -> Result<(), String> {
|
|
||||||
while data.reg_pc != 0xffff {
|
while data.reg_pc != 0xffff {
|
||||||
data.reg_opc = data.get_memory(data.reg_pc) as u8;
|
data.reg_opc = data.get_memory(data.reg_pc)? as u8;
|
||||||
data.reg_pc = data.reg_pc.wrapping_add(1);
|
data.reg_pc = data.reg_pc.wrapping_add(1);
|
||||||
|
|
||||||
if data.reg_opc >> 7 == 1 {
|
if data.reg_opc >> 7 == 1 {
|
||||||
data.reg_opc &= 0b0111111;
|
data.reg_opc &= 0b0111111;
|
||||||
data.reg_arg = 0;
|
data.reg_arg = 0;
|
||||||
} else {
|
} else {
|
||||||
data.reg_arg = data.get_memory(data.reg_pc) << 8;
|
data.reg_arg = data.get_memory(data.reg_pc)? << 8;
|
||||||
data.reg_pc = data.reg_pc.wrapping_add(1);
|
data.reg_pc = data.reg_pc.wrapping_add(1);
|
||||||
|
|
||||||
data.reg_arg |= data.get_memory(data.reg_pc);
|
data.reg_arg |= data.get_memory(data.reg_pc)?;
|
||||||
data.reg_pc = data.reg_pc.wrapping_add(1);
|
data.reg_pc = data.reg_pc.wrapping_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,19 +276,19 @@ pub fn emulate(data: &mut Data) -> Result<(), String> {
|
||||||
data.stack.iter().take(data.reg_sp as usize).join(", ")
|
data.stack.iter().take(data.reg_sp as usize).join(", ")
|
||||||
);
|
);
|
||||||
print!("Press enter to continue execution...",);
|
print!("Press enter to continue execution...",);
|
||||||
io::stdout().flush().unwrap();
|
io::stdout().flush()?;
|
||||||
data.term.read_line().unwrap();
|
data.term.read_line()?;
|
||||||
}
|
}
|
||||||
0x0b => {
|
0x0b => {
|
||||||
data.alu(data.tmp as u8).unwrap();
|
data.alu(data.tmp as u8)?;
|
||||||
}
|
}
|
||||||
0x0c => {
|
0x0c => {
|
||||||
data.tmp = data.get_memory(data.tmp);
|
data.tmp = data.get_memory(data.tmp)?;
|
||||||
}
|
}
|
||||||
0x0d => {
|
0x0d => {
|
||||||
data.set_memory(data.tmp, data.reg_a);
|
data.set_memory(data.tmp, data.reg_a)?;
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Invalid opcode: 0x{}", radix(data.reg_opc, 16))),
|
_ => bail!("Invalid opcode: 0x{}", radix(data.reg_opc, 16)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
308
src/lib/lexer.rs
308
src/lib/lexer.rs
|
@ -1,10 +1,13 @@
|
||||||
use crate::assembler;
|
use anyhow::{bail, Result};
|
||||||
use itertools::{Itertools, PeekingNext};
|
use itertools::Itertools;
|
||||||
|
|
||||||
#[derive(Debug)]
|
use crate::assembler;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum Token {
|
pub enum Token {
|
||||||
Comment(String),
|
Comment(String),
|
||||||
|
|
||||||
|
CharLiteral(String),
|
||||||
StringLiteral(String),
|
StringLiteral(String),
|
||||||
MacroLiteral(String),
|
MacroLiteral(String),
|
||||||
Literal(String),
|
Literal(String),
|
||||||
|
@ -15,13 +18,30 @@ pub enum Token {
|
||||||
LParen,
|
LParen,
|
||||||
RParen,
|
RParen,
|
||||||
|
|
||||||
Assign,
|
Not,
|
||||||
|
And,
|
||||||
|
Nand,
|
||||||
|
Or,
|
||||||
|
Nor,
|
||||||
|
Xor,
|
||||||
|
Xnor,
|
||||||
|
Lsh,
|
||||||
|
Rsh,
|
||||||
Add,
|
Add,
|
||||||
Sub,
|
Sub,
|
||||||
Mul,
|
Mul,
|
||||||
Div,
|
Div,
|
||||||
Pow,
|
Pow,
|
||||||
|
Cmp,
|
||||||
|
Eq,
|
||||||
|
Neq,
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
Leq,
|
||||||
|
Geq,
|
||||||
|
Bol,
|
||||||
|
Inv,
|
||||||
|
Rnd,
|
||||||
|
|
||||||
Newline(String),
|
Newline(String),
|
||||||
Whitespace(String),
|
Whitespace(String),
|
||||||
|
@ -30,8 +50,9 @@ pub enum Token {
|
||||||
impl assembler::ToAssembly for Token {
|
impl assembler::ToAssembly for Token {
|
||||||
fn to_assembly(&self) -> String {
|
fn to_assembly(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Token::Comment(x) => format!(";{x}"),
|
Token::Comment(x) => format!(";{}", x),
|
||||||
Token::StringLiteral(x) => format!("\"{x}\""),
|
Token::CharLiteral(x) => format!("'{}'", x),
|
||||||
|
Token::StringLiteral(x) => format!("\"{}\"", x),
|
||||||
Token::MacroLiteral(x) => x.clone(),
|
Token::MacroLiteral(x) => x.clone(),
|
||||||
Token::Literal(x) => x.clone(),
|
Token::Literal(x) => x.clone(),
|
||||||
Token::Number(x) => x.clone(),
|
Token::Number(x) => x.clone(),
|
||||||
|
@ -39,127 +60,290 @@ impl assembler::ToAssembly for Token {
|
||||||
Token::Colon => ":".to_string(),
|
Token::Colon => ":".to_string(),
|
||||||
Token::LParen => "(".to_string(),
|
Token::LParen => "(".to_string(),
|
||||||
Token::RParen => ")".to_string(),
|
Token::RParen => ")".to_string(),
|
||||||
Token::Assign => "=".to_string(),
|
Token::Not => "~".to_string(),
|
||||||
|
Token::And => "&".to_string(),
|
||||||
|
Token::Nand => "~&".to_string(),
|
||||||
|
Token::Or => "|".to_string(),
|
||||||
|
Token::Nor => "~|".to_string(),
|
||||||
|
Token::Xor => "^".to_string(),
|
||||||
|
Token::Xnor => "~^".to_string(),
|
||||||
|
Token::Lsh => "<<".to_string(),
|
||||||
|
Token::Rsh => ">>".to_string(),
|
||||||
Token::Add => "+".to_string(),
|
Token::Add => "+".to_string(),
|
||||||
Token::Sub => "-".to_string(),
|
Token::Sub => "-".to_string(),
|
||||||
Token::Mul => "*".to_string(),
|
Token::Mul => "*".to_string(),
|
||||||
Token::Div => "/".to_string(),
|
Token::Div => "/".to_string(),
|
||||||
Token::Pow => "**".to_string(),
|
Token::Pow => "**".to_string(),
|
||||||
|
Token::Cmp => "<=>".to_string(),
|
||||||
|
Token::Eq => "==".to_string(),
|
||||||
|
Token::Neq => "!=".to_string(),
|
||||||
|
Token::Lt => "<".to_string(),
|
||||||
|
Token::Gt => ">".to_string(),
|
||||||
|
Token::Leq => "<=".to_string(),
|
||||||
|
Token::Geq => ">=".to_string(),
|
||||||
|
Token::Bol => "!!".to_string(),
|
||||||
|
Token::Inv => "!".to_string(),
|
||||||
|
Token::Rnd => "?".to_string(),
|
||||||
Token::Newline(x) | Token::Whitespace(x) => x.clone(),
|
Token::Newline(x) | Token::Whitespace(x) => x.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lex(source: String) -> Result<Vec<Token>, String> {
|
pub fn lex(source: String) -> Result<Vec<Token>> {
|
||||||
let mut chars = source.chars().peekable();
|
let mut chars = source.chars().peekable();
|
||||||
let mut tokens = Vec::<Token>::new();
|
let mut tokens: Vec<Token> = vec![];
|
||||||
|
|
||||||
while let Some(&ch) = chars.peek() {
|
while let Some(&ch) = chars.peek() {
|
||||||
match ch {
|
tokens.push(match ch {
|
||||||
// ';' => {
|
';' => {
|
||||||
// chars.next();
|
|
||||||
// chars.next_if(|c| *c == ';');
|
|
||||||
|
|
||||||
// tokens.push(Token::Comment(
|
|
||||||
// chars.peeking_take_while(|c| *c != '\n').collect::<String>(),
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
';' | '@' => {
|
|
||||||
chars.next();
|
chars.next();
|
||||||
chars.next_if(|c| *c == ';' || *c == '@');
|
chars.next_if(|c| *c == ';');
|
||||||
|
Token::Comment(chars.peeking_take_while(|c| *c != '\n').collect())
|
||||||
tokens.push(Token::Comment(
|
}
|
||||||
chars.peeking_take_while(|c| *c != '\n').collect::<String>(),
|
'@' => {
|
||||||
));
|
chars.next();
|
||||||
|
Token::Comment(chars.peeking_take_while(|c| *c != '\n').collect())
|
||||||
|
}
|
||||||
|
'#' => {
|
||||||
|
chars.next();
|
||||||
|
Token::Comment(chars.peeking_take_while(|c| *c != '\n').collect())
|
||||||
|
}
|
||||||
|
'\'' => {
|
||||||
|
chars.next();
|
||||||
|
Token::CharLiteral(chars.by_ref().take_while(|c| *c != '\'').collect())
|
||||||
}
|
}
|
||||||
'"' => {
|
'"' => {
|
||||||
chars.next();
|
chars.next();
|
||||||
tokens.push(Token::StringLiteral(
|
Token::StringLiteral(chars.by_ref().take_while(|c| *c != '"').collect())
|
||||||
chars.by_ref().take_while(|c| *c != '"').collect::<String>(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
'.' => {
|
'.' => {
|
||||||
chars.next();
|
chars.next();
|
||||||
tokens.push(Token::MacroLiteral(format!(
|
Token::MacroLiteral(format!(
|
||||||
".{}",
|
".{}",
|
||||||
chars
|
chars
|
||||||
.peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_')
|
.peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_')
|
||||||
.collect::<String>()
|
.collect::<String>()
|
||||||
)));
|
))
|
||||||
}
|
}
|
||||||
ch if ch.is_alphabetic() => {
|
ch if ch.is_alphabetic() => {
|
||||||
let name: String = chars
|
let name: String = chars
|
||||||
.peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_')
|
.peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_')
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
tokens.push(Token::Literal(name));
|
Token::Literal(name)
|
||||||
}
|
}
|
||||||
ch if ch.is_numeric() => {
|
ch if ch.is_numeric() => Token::Number(
|
||||||
tokens.push(Token::Number(
|
|
||||||
chars
|
chars
|
||||||
.peeking_take_while(|c| c.is_alphanumeric() || *c == '_')
|
.peeking_take_while(|c| c.is_alphanumeric() || *c == '_')
|
||||||
.collect::<String>(),
|
.collect(),
|
||||||
));
|
),
|
||||||
}
|
|
||||||
',' => {
|
',' => {
|
||||||
tokens.push(Token::Comma);
|
|
||||||
chars.next();
|
chars.next();
|
||||||
|
Token::Comma
|
||||||
}
|
}
|
||||||
':' => {
|
':' => {
|
||||||
tokens.push(Token::Colon);
|
|
||||||
chars.next();
|
chars.next();
|
||||||
|
Token::Colon
|
||||||
}
|
}
|
||||||
'(' => {
|
'(' => {
|
||||||
tokens.push(Token::LParen);
|
|
||||||
chars.next();
|
chars.next();
|
||||||
|
Token::LParen
|
||||||
}
|
}
|
||||||
')' => {
|
')' => {
|
||||||
tokens.push(Token::RParen);
|
|
||||||
chars.next();
|
chars.next();
|
||||||
|
Token::RParen
|
||||||
}
|
}
|
||||||
'=' => {
|
'~' => {
|
||||||
tokens.push(Token::Assign);
|
|
||||||
chars.next();
|
chars.next();
|
||||||
|
if let Some(c) = chars.peek() {
|
||||||
|
match c {
|
||||||
|
'&' => {
|
||||||
|
chars.next();
|
||||||
|
Token::Nand
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
chars.next();
|
||||||
|
Token::Nor
|
||||||
|
}
|
||||||
|
'^' => {
|
||||||
|
chars.next();
|
||||||
|
Token::Xnor
|
||||||
|
}
|
||||||
|
_ => Token::Not,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Token::Not
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'&' => {
|
||||||
|
chars.next();
|
||||||
|
Token::And
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
chars.next();
|
||||||
|
Token::Or
|
||||||
|
}
|
||||||
|
'^' => {
|
||||||
|
chars.next();
|
||||||
|
Token::Xor
|
||||||
|
}
|
||||||
|
'<' => {
|
||||||
|
chars.next();
|
||||||
|
match chars.peek() {
|
||||||
|
Some('<') => {
|
||||||
|
chars.next();
|
||||||
|
Token::Lsh
|
||||||
|
}
|
||||||
|
Some('=') => {
|
||||||
|
chars.next();
|
||||||
|
match chars.peek() {
|
||||||
|
Some('>') => {
|
||||||
|
chars.next();
|
||||||
|
Token::Cmp
|
||||||
|
}
|
||||||
|
_ => Token::Leq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Token::Lt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'>' => {
|
||||||
|
chars.next();
|
||||||
|
match chars.peek() {
|
||||||
|
Some('>') => {
|
||||||
|
chars.next();
|
||||||
|
Token::Rsh
|
||||||
|
}
|
||||||
|
Some('=') => {
|
||||||
|
chars.next();
|
||||||
|
Token::Geq
|
||||||
|
}
|
||||||
|
_ => Token::Gt,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
'+' => {
|
'+' => {
|
||||||
tokens.push(Token::Add);
|
|
||||||
chars.next();
|
chars.next();
|
||||||
|
Token::Add
|
||||||
}
|
}
|
||||||
'-' => {
|
'-' => {
|
||||||
tokens.push(Token::Sub);
|
|
||||||
chars.next();
|
chars.next();
|
||||||
|
Token::Sub
|
||||||
}
|
}
|
||||||
'*' => {
|
'*' => {
|
||||||
chars.next();
|
chars.next();
|
||||||
tokens.push(if chars.peeking_next(|c| *c == '*').is_some() {
|
if let Some('*') = chars.peek() {
|
||||||
|
chars.next();
|
||||||
Token::Pow
|
Token::Pow
|
||||||
} else {
|
} else {
|
||||||
Token::Mul
|
Token::Mul
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
'/' => {
|
'/' => {
|
||||||
tokens.push(Token::Div);
|
chars.next();
|
||||||
|
Token::Div
|
||||||
|
}
|
||||||
|
'=' => {
|
||||||
|
chars.next();
|
||||||
|
if let Some('=') = chars.peek() {
|
||||||
chars.next();
|
chars.next();
|
||||||
}
|
}
|
||||||
'\n' => {
|
Token::Eq
|
||||||
tokens.push(Token::Newline(
|
|
||||||
chars.peeking_take_while(|c| *c == '\n').collect::<String>(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
ch if ch.is_whitespace() => {
|
'!' => {
|
||||||
tokens.push(Token::Whitespace(
|
chars.next();
|
||||||
|
match chars.peek() {
|
||||||
|
Some('!') => {
|
||||||
|
chars.next();
|
||||||
|
Token::Bol
|
||||||
|
}
|
||||||
|
Some('=') => {
|
||||||
|
chars.next();
|
||||||
|
Token::Neq
|
||||||
|
}
|
||||||
|
_ => Token::Inv,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'?' => {
|
||||||
|
chars.next();
|
||||||
|
Token::Rnd
|
||||||
|
}
|
||||||
|
'\n' => Token::Newline(chars.peeking_take_while(|c| *c == '\n').collect()),
|
||||||
|
ch if ch.is_whitespace() => Token::Whitespace(
|
||||||
chars
|
chars
|
||||||
.peeking_take_while(|c| c.is_whitespace() && *c != '\n')
|
.peeking_take_while(|c| c.is_whitespace() && *c != '\n')
|
||||||
.collect::<String>(),
|
.collect(),
|
||||||
));
|
),
|
||||||
}
|
_ => bail!("Unexpected token: {}", ch),
|
||||||
_ => {
|
});
|
||||||
// tokens.push(Token::Error(ch.to_string()));
|
|
||||||
// chars.next();
|
|
||||||
return Err(format!("Unexpected token: '{ch}'"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(tokens)
|
Ok(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::assembler::ToAssembly;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_token_to_assembly() {
|
||||||
|
assert_eq!(
|
||||||
|
Token::Comment(" \"main function\" like definition macro".to_string()).to_assembly(),
|
||||||
|
"; \"main function\" like definition macro".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Token::CharLiteral("\\n".to_string()).to_assembly(),
|
||||||
|
"'\\n'".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Token::MacroLiteral("xyz".to_string()).to_assembly(),
|
||||||
|
"xyz".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Token::Literal("xkcd".to_string()).to_assembly(),
|
||||||
|
"xkcd".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Token::Newline("\n".to_string()).to_assembly(),
|
||||||
|
"\n".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Token::Whitespace(" ".to_string()).to_assembly(),
|
||||||
|
" ".to_string()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lex() -> Result<()> {
|
||||||
|
assert_eq!(
|
||||||
|
lex(";; test".to_string())?,
|
||||||
|
vec![Token::Comment(" test".to_string())]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lex("@ test".to_string())?,
|
||||||
|
vec![Token::Comment(" test".to_string())]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lex("# test".to_string())?,
|
||||||
|
vec![Token::Comment(" test".to_string())]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lex("'\\n'".to_string())?,
|
||||||
|
vec![Token::CharLiteral("\\n".to_string())]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lex("\"test\"".to_string())?,
|
||||||
|
vec![Token::StringLiteral("test".to_string())]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lex(".debug CORE_REG_PC".to_string())?,
|
||||||
|
vec![
|
||||||
|
Token::MacroLiteral(".debug".to_string()),
|
||||||
|
Token::Whitespace(" ".to_string()),
|
||||||
|
Token::Literal("CORE_REG_PC".to_string())
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
||||||
pub mod arg;
|
pub mod arg;
|
||||||
pub mod assembler;
|
pub mod assembler;
|
||||||
pub mod emulator;
|
pub mod emulator;
|
||||||
pub mod lexer;
|
|
||||||
pub mod parser;
|
|
||||||
|
|
|
@ -26,8 +26,7 @@ pub fn parse(tokens: Vec<lexer::Token>) -> Result<ast::AST> {
|
||||||
.take_while(|t| !matches!(t, lexer::Token::Newline(_)))
|
.take_while(|t| !matches!(t, lexer::Token::Newline(_)))
|
||||||
.filter(|t| !matches!(t, lexer::Token::Whitespace(_)))
|
.filter(|t| !matches!(t, lexer::Token::Whitespace(_)))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)?,
|
||||||
.unwrap(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
lexer::Token::Literal(x) => {
|
lexer::Token::Literal(x) => {
|
||||||
|
|
|
@ -27,7 +27,10 @@ impl assembler::ToAssembly for Node {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
format!(".{name}")
|
format!(".{name}")
|
||||||
} else {
|
} else {
|
||||||
format!(".{name} {}", args.iter().map(|a| a.to_assembly()).join(", "))
|
format!(
|
||||||
|
".{name} {}",
|
||||||
|
args.iter().map(|a| a.to_assembly()).join(", ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue