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
|
||||
|
||||
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"
|
||||
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]]
|
||||
name = "cc"
|
||||
version = "1.0.73"
|
||||
|
@ -129,6 +144,24 @@ dependencies = [
|
|||
"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]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
|
@ -141,6 +174,16 @@ version = "0.3.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "getrandom"
|
||||
version = "0.2.7"
|
||||
|
@ -148,8 +191,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -177,11 +222,14 @@ dependencies = [
|
|||
"anyhow",
|
||||
"clap",
|
||||
"console",
|
||||
"getrandom",
|
||||
"itertools",
|
||||
"num-parse",
|
||||
"radix_fmt",
|
||||
"rand",
|
||||
"rhexdump",
|
||||
"rust-embed",
|
||||
"unescape",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -212,12 +260,30 @@ dependencies = [
|
|||
"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]]
|
||||
name = "libc"
|
||||
version = "0.2.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
@ -333,6 +399,12 @@ version = "1.13.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
|
@ -429,12 +501,68 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
|
@ -477,6 +605,18 @@ version = "0.15.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
|
@ -495,12 +635,77 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
name = "hence"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = ["Dominic Grimm <dominic@dergrimm.net>"]
|
||||
repository = "https://git.dergrimm.net/dergrimm/hence.git"
|
||||
|
||||
[lib]
|
||||
name = "hence"
|
||||
|
@ -19,7 +21,9 @@ num-parse = "0.1.2"
|
|||
clap = { version = "3.2.16", features = ["derive"] }
|
||||
rhexdump = "0.1.1"
|
||||
radix_fmt = "1"
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
rand = "0.8.5"
|
||||
console = "0.15.1"
|
||||
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 | |
|
||||
| `0x0c` | `get` | Sets `tmp` to memory at address in `tmp` | |
|
||||
| `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 std::fs;
|
||||
use std::fs::File;
|
||||
|
@ -41,54 +42,67 @@ enum Commands {
|
|||
},
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<()> {
|
||||
let args = Cli::parse();
|
||||
match args.commands {
|
||||
Commands::Lex { src } => {
|
||||
let assembly = fs::read_to_string(src).unwrap();
|
||||
let tokens = lexer::lex(assembly).unwrap();
|
||||
let assembly = fs::read_to_string(src)?;
|
||||
let tokens = lexer::lex(assembly)?;
|
||||
dbg!(tokens);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Commands::Parse { src } => {
|
||||
let assembly = fs::read_to_string(src).unwrap();
|
||||
let tokens = lexer::lex(assembly).unwrap();
|
||||
let ast = parser::parse(tokens).unwrap();
|
||||
let assembly = fs::read_to_string(src)?;
|
||||
let tokens = lexer::lex(assembly)?;
|
||||
let ast = parser::parse(tokens)?;
|
||||
dbg!(ast);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Commands::Assemble { src, bin, dump } => {
|
||||
let assembly = fs::read_to_string(&src).unwrap();
|
||||
let tokens = lexer::lex(assembly).unwrap();
|
||||
let ast = parser::parse(tokens).unwrap();
|
||||
let assembly = fs::read_to_string(&src)?;
|
||||
let tokens = lexer::lex(assembly)?;
|
||||
let ast = parser::parse(tokens)?;
|
||||
|
||||
let mut data = assembler::Data::new(
|
||||
match Path::new(&src).parent().unwrap().to_str() {
|
||||
Some(x) => x.to_string(),
|
||||
_ => panic!("Could not get directory in which source code resides"),
|
||||
match match Path::new(&src).parent() {
|
||||
Some(x) => x.to_str().map(|y| y.to_string()),
|
||||
None => None,
|
||||
} {
|
||||
Some(s) => s,
|
||||
None => bail!("Could not get directory in which source code resides"),
|
||||
},
|
||||
ast,
|
||||
);
|
||||
// assembler::assemble(&mut data).unwrap();
|
||||
data.assemble().unwrap();
|
||||
data.assemble()?;
|
||||
|
||||
if let Some(x) = bin {
|
||||
File::create(x).unwrap().write_all(&data.program).unwrap();
|
||||
File::create(x)?.write_all(&data.program)?;
|
||||
}
|
||||
if dump {
|
||||
println!("{}", rhexdump::hexdump(&data.program));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Commands::Run { bin } => {
|
||||
let mut program_buf: Vec<u8> = Vec::new();
|
||||
let file = File::open(bin).unwrap();
|
||||
BufReader::new(file).read_to_end(&mut program_buf).unwrap();
|
||||
let mut program_buf: Vec<u8> = vec![];
|
||||
let file = File::open(bin)?;
|
||||
BufReader::new(file).read_to_end(&mut program_buf)?;
|
||||
if program_buf.len() < (32 * 1024) {
|
||||
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);
|
||||
|
||||
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 rand;
|
||||
use std::cmp::Ordering;
|
||||
use unescape::unescape;
|
||||
|
||||
use crate::arg;
|
||||
use crate::assembler;
|
||||
use crate::lexer;
|
||||
|
||||
pub fn unescape_string(string: &str) -> String {
|
||||
match unescape(string) {
|
||||
Some(x) => x,
|
||||
None => "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Arg {
|
||||
Char(char),
|
||||
String(String),
|
||||
Number(u16),
|
||||
Variable(String),
|
||||
|
@ -19,15 +30,16 @@ pub enum Arg {
|
|||
impl assembler::ToAssembly for Arg {
|
||||
fn to_assembly(&self) -> String {
|
||||
match self {
|
||||
Arg::String(x) => format!("\"{x}\""),
|
||||
Arg::Char(x) => format!("'{}'", x),
|
||||
Arg::String(x) => format!("\"{}\"", x),
|
||||
Arg::Number(x) => x.to_string(),
|
||||
Arg::Variable(x) => x.clone(),
|
||||
Arg::BinaryExpression { left, right, op } => {
|
||||
format!(
|
||||
"({left} {op} {right})",
|
||||
left = left.to_assembly(),
|
||||
op = op.to_assembly(),
|
||||
right = right.to_assembly()
|
||||
"({} {} {})",
|
||||
left.to_assembly(),
|
||||
op.to_assembly(),
|
||||
right.to_assembly()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -35,11 +47,11 @@ impl assembler::ToAssembly 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 {
|
||||
Arg::Char(x) => Ok(*x as u16),
|
||||
Arg::String(x) => {
|
||||
let y = x.replace("\\n", "\n");
|
||||
|
||||
let y = unescape_string(x);
|
||||
if y.len() == 1 {
|
||||
Ok(y.as_bytes()[0] as u16)
|
||||
} else {
|
||||
|
@ -58,36 +70,59 @@ impl assembler::ByteResolvable<assembler::Data> for Arg {
|
|||
|
||||
match arg {
|
||||
Some(a) => {
|
||||
// arg = Some(a.clone());
|
||||
match a {
|
||||
arg::Arg::Variable(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 } => {
|
||||
let left_val = left.resolve_number(data).unwrap();
|
||||
let right_val = right.resolve_number(data).unwrap();
|
||||
let left_val = left.resolve_number(data)?;
|
||||
let right_val = right.resolve_number(data)?;
|
||||
|
||||
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::Sub => left_val.wrapping_sub(right_val),
|
||||
BinaryExpressionOperator::Mul => left_val.wrapping_mul(right_val),
|
||||
BinaryExpressionOperator::Div => left_val.wrapping_div(right_val),
|
||||
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 {
|
||||
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::Variable(x) => {
|
||||
let mut name = x.clone();
|
||||
|
@ -98,15 +133,14 @@ impl assembler::ByteResolvable<assembler::Data> for Arg {
|
|||
|
||||
match arg {
|
||||
Some(a) => {
|
||||
// arg = Some(a.clone());
|
||||
match a {
|
||||
arg::Arg::Variable(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: _,
|
||||
op: _,
|
||||
} => {
|
||||
let x = self.resolve_number(data).unwrap();
|
||||
let x = self.resolve_number(data)?;
|
||||
|
||||
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)]
|
||||
pub enum BinaryExpressionOperator {
|
||||
Not,
|
||||
And,
|
||||
Nand,
|
||||
Or,
|
||||
Nor,
|
||||
Xor,
|
||||
Xnor,
|
||||
Lsh,
|
||||
Rsh,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Pow,
|
||||
Cmp,
|
||||
Eq,
|
||||
Neq,
|
||||
Lt,
|
||||
Gt,
|
||||
Leq,
|
||||
Geq,
|
||||
Bol,
|
||||
Inv,
|
||||
Rnd,
|
||||
}
|
||||
|
||||
impl assembler::ToAssembly for BinaryExpressionOperator {
|
||||
fn to_assembly(&self) -> String {
|
||||
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::Sub => "-".to_string(),
|
||||
BinaryExpressionOperator::Mul => "*".to_string(),
|
||||
BinaryExpressionOperator::Div => "/".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> {
|
||||
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::Sub => Ok(BinaryExpressionOperator::Sub),
|
||||
lexer::Token::Mul => Ok(BinaryExpressionOperator::Mul),
|
||||
lexer::Token::Div => Ok(BinaryExpressionOperator::Div),
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
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 {
|
||||
left: Box::new((&parse_args(vec![args[0]]).unwrap()[0]).clone()),
|
||||
right: Box::new((&parse_args(vec![args[2]]).unwrap()[0]).clone()),
|
||||
op: parse_binary_operation(args[1]).unwrap(),
|
||||
left: Box::new((&parse_args(vec![args[0]])?[0]).clone()),
|
||||
right: Box::new((&parse_args(vec![args[2]])?[0]).clone()),
|
||||
op: parse_binary_operation(args[1])?,
|
||||
};
|
||||
|
||||
while !tokens.is_empty() {
|
||||
args = tokens.drain(..2).collect();
|
||||
bin_expr = Arg::BinaryExpression {
|
||||
left: Box::new(bin_expr),
|
||||
right: Box::new((&parse_args(vec![args[1]]).unwrap()[0]).clone()),
|
||||
op: parse_binary_operation(args[0]).unwrap(),
|
||||
right: Box::new((&parse_args(vec![args[1]])?[0]).clone()),
|
||||
op: parse_binary_operation(args[0])?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(bin_expr)
|
||||
}
|
||||
|
||||
pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
||||
let mut iter = tokens.iter();
|
||||
let mut args: Vec<Arg> = Vec::new();
|
||||
pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>> {
|
||||
let mut iter = tokens.into_iter();
|
||||
let mut args: Vec<Arg> = vec![];
|
||||
let mut last_was_comma = true;
|
||||
|
||||
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 {
|
||||
return Err("Invalid argument separation");
|
||||
bail!("Invalid argument separation");
|
||||
}
|
||||
|
||||
match token {
|
||||
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) => {
|
||||
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) => {
|
||||
args.push(Arg::Number(match num_parse::parse_uint(x) {
|
||||
Some(y) => y,
|
||||
_ => return Err("Error parsing number"),
|
||||
None => bail!("Error parsing number"),
|
||||
}));
|
||||
}
|
||||
lexer::Token::LParen => {
|
||||
let mut depth: usize = 1;
|
||||
|
||||
args.push(
|
||||
parse_binary_expression_arg(
|
||||
args.push(parse_binary_expression_arg(
|
||||
&mut iter
|
||||
.by_ref()
|
||||
.take_while(|t| match t {
|
||||
|
@ -228,11 +342,9 @@ pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
|||
_ => true,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
)?);
|
||||
}
|
||||
_ => return Err("Unexpected token for argument"),
|
||||
_ => bail!("Unexpected token for argument"),
|
||||
}
|
||||
|
||||
last_was_comma = false;
|
||||
|
@ -242,3 +354,14 @@ pub fn parse_args(tokens: Vec<&lexer::Token>) -> Result<Vec<Arg>, &str> {
|
|||
|
||||
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 radix_fmt::radix;
|
||||
// use std::collections::HashMap;
|
||||
//
|
||||
// use crate::arg;
|
||||
// use crate::parser;
|
||||
//
|
||||
// pub trait ToCode {
|
||||
// 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 anyhow::{bail, Result};
|
||||
use itertools::Itertools;
|
||||
use radix_fmt::radix;
|
||||
use rust_embed::RustEmbed;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::vec;
|
||||
|
||||
use crate::arg;
|
||||
use crate::lexer;
|
||||
use crate::parser;
|
||||
|
||||
pub trait ToAssembly {
|
||||
|
@ -279,27 +16,28 @@ pub trait ToAssembly {
|
|||
}
|
||||
|
||||
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)]
|
||||
pub enum ResolveResult {
|
||||
Resolved(Vec<u8>),
|
||||
Partial(u16),
|
||||
Unresolved,
|
||||
}
|
||||
|
||||
impl ResolveResult {
|
||||
pub fn empty() -> Self {
|
||||
ResolveResult::Resolved(vec![])
|
||||
}
|
||||
fn resolve_string(&self, data: &mut T) -> Result<String>;
|
||||
}
|
||||
|
||||
pub const PROGRAM_SIZE: u16 = 32 * 1024;
|
||||
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 dir: String,
|
||||
pub ast: parser::ast::AST,
|
||||
|
@ -307,6 +45,11 @@ pub struct Data {
|
|||
pub offset: u16,
|
||||
pub body_stack: Vec<parser::ast::Node>,
|
||||
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 {
|
||||
|
@ -318,37 +61,342 @@ impl Data {
|
|||
program: [0; PROGRAM_SIZE as usize],
|
||||
offset: 0,
|
||||
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 {
|
||||
parser::ast::Node::Comment(_) => ResolveResult::empty(),
|
||||
parser::ast::Node::Comment(_) => Ok(None),
|
||||
parser::ast::Node::Label(x) => {
|
||||
self.constants.insert(x, arg::Arg::Number(self.offset));
|
||||
|
||||
ResolveResult::empty()
|
||||
Ok(None)
|
||||
}
|
||||
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> {
|
||||
while let Some(node) = self.body_stack.pop() {
|
||||
let res = self.resolve_node(node);
|
||||
dbg!(res);
|
||||
let offset_name = "OFFSET".to_string();
|
||||
while !self.body_stack.is_empty() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use anyhow::{bail, Result};
|
||||
use itertools::Itertools;
|
||||
use radix_fmt::radix;
|
||||
use std::cmp::Ordering;
|
||||
|
@ -6,9 +7,7 @@ use std::io::{self, Write};
|
|||
#[derive(Debug)]
|
||||
pub struct Data {
|
||||
program: [u8; 32 * 1024],
|
||||
|
||||
pub tmp: u16,
|
||||
|
||||
pub reg_pc: u16,
|
||||
pub reg_opc: u8,
|
||||
pub reg_arg: u16,
|
||||
|
@ -18,10 +17,8 @@ pub struct Data {
|
|||
pub reg_b: u16,
|
||||
pub reg_c: u16,
|
||||
pub reg_d: u16,
|
||||
|
||||
stack: [u16; 8 * 1024],
|
||||
memory: [u16; 16 * 1024],
|
||||
|
||||
term: console::Term,
|
||||
}
|
||||
|
||||
|
@ -29,9 +26,7 @@ impl Data {
|
|||
pub fn new(program: [u8; 32 * 1024]) -> Self {
|
||||
Self {
|
||||
program,
|
||||
|
||||
tmp: 0,
|
||||
|
||||
reg_pc: 0,
|
||||
reg_opc: 0,
|
||||
reg_arg: 0,
|
||||
|
@ -41,44 +36,43 @@ impl Data {
|
|||
reg_b: 0,
|
||||
reg_c: 0,
|
||||
reg_d: 0,
|
||||
|
||||
stack: [0; 8 * 1024],
|
||||
memory: [0; 16 * 1024],
|
||||
|
||||
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) {
|
||||
match self.program.get(address as usize) {
|
||||
Some(val) => *val as u16,
|
||||
None => 0,
|
||||
Some(val) => Ok(*val as u16),
|
||||
None => Ok(0),
|
||||
}
|
||||
} 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) {
|
||||
self.memory[(address - (40 * 1024)) as usize]
|
||||
Ok(self.memory[(address - (40 * 1024)) as usize])
|
||||
} else if address == (56 * 1024 + 2) {
|
||||
self.term.read_char().unwrap() as u16
|
||||
Ok(self.term.read_char()? as u16)
|
||||
} else {
|
||||
0
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_memory(&mut self, address: u16, value: u16) {
|
||||
// if address >= (32 * 1024) && address < (40 * 1024) {
|
||||
pub fn set_memory(&mut self, address: u16, value: u16) -> Result<()> {
|
||||
if ((32 * 1024)..(40 * 1024)).contains(&address) {
|
||||
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;
|
||||
} else if address == (56 * 1024) {
|
||||
print!("{value}");
|
||||
io::stdout().flush().unwrap();
|
||||
io::stdout().flush()?;
|
||||
} else if address == (56 * 1024 + 1) {
|
||||
print!("{}", char::from(value as u8));
|
||||
io::stdout().flush().unwrap();
|
||||
io::stdout().flush()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_register(&self, register: u8) -> u16 {
|
||||
|
@ -100,54 +94,45 @@ impl Data {
|
|||
match register {
|
||||
0x0 => {
|
||||
self.reg_pc = value;
|
||||
|
||||
value
|
||||
}
|
||||
0x1 => {
|
||||
self.reg_opc = value as u8;
|
||||
|
||||
value & 0xff
|
||||
}
|
||||
0x2 => {
|
||||
self.reg_arg = value;
|
||||
|
||||
value
|
||||
}
|
||||
0x3 => {
|
||||
self.reg_s = value as u8;
|
||||
|
||||
value & 0xff
|
||||
}
|
||||
0x4 => {
|
||||
self.reg_sp = value;
|
||||
|
||||
value
|
||||
}
|
||||
0x5 => {
|
||||
self.reg_a = value;
|
||||
|
||||
value
|
||||
}
|
||||
0x6 => {
|
||||
self.reg_b = value;
|
||||
|
||||
value
|
||||
}
|
||||
0x7 => {
|
||||
self.reg_c = value;
|
||||
|
||||
value
|
||||
}
|
||||
0x8 => {
|
||||
self.reg_d = value;
|
||||
|
||||
value
|
||||
}
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn alu(&mut self, operation: u8) -> Result<(), String> {
|
||||
fn alu(&mut self, operation: u8) -> Result<()> {
|
||||
match operation {
|
||||
0x00 => {
|
||||
self.tmp = !self.reg_a;
|
||||
|
@ -203,57 +188,49 @@ impl Data {
|
|||
}
|
||||
}
|
||||
0x0b => {
|
||||
self.tmp = bool_to_num(self.reg_a == self.reg_b);
|
||||
self.tmp = (self.reg_a == self.reg_b) as u16;
|
||||
}
|
||||
0x0c => {
|
||||
self.tmp = bool_to_num(self.reg_a < self.reg_b);
|
||||
self.tmp = (self.reg_a < self.reg_b) as u16;
|
||||
}
|
||||
0x0d => {
|
||||
self.tmp = bool_to_num(self.reg_a > self.reg_b);
|
||||
self.tmp = (self.reg_a > self.reg_b) as u16;
|
||||
}
|
||||
0x0e => {
|
||||
self.tmp = bool_to_num(self.reg_a <= self.reg_b);
|
||||
self.tmp = (self.reg_a <= self.reg_b) as u16;
|
||||
}
|
||||
0x0f => {
|
||||
self.tmp = bool_to_num(self.reg_a >= self.reg_b);
|
||||
self.tmp = (self.reg_a >= self.reg_b) as u16;
|
||||
}
|
||||
0x10 => {
|
||||
self.tmp = bool_to_num(self.reg_a == 1);
|
||||
self.tmp = (self.reg_a & 1 == 1) as u16;
|
||||
}
|
||||
0x11 => {
|
||||
self.tmp = bool_to_num(self.reg_a != 1);
|
||||
self.tmp = (self.reg_a & 1 != 1) as u16;
|
||||
}
|
||||
0x12 => {
|
||||
self.tmp = rand::random();
|
||||
}
|
||||
_ => return Err(format!("Invalid ALU operation: 0x{}", radix(operation, 16))),
|
||||
_ => bail!("Invalid ALU operation: 0x{}", radix(operation, 16)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bool_to_num(x: bool) -> u16 {
|
||||
if x {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emulate(data: &mut Data) -> Result<(), String> {
|
||||
pub fn emulate(data: &mut Data) -> Result<()> {
|
||||
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);
|
||||
|
||||
if data.reg_opc >> 7 == 1 {
|
||||
data.reg_opc &= 0b0111111;
|
||||
data.reg_arg = 0;
|
||||
} 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_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);
|
||||
}
|
||||
|
||||
|
@ -299,19 +276,19 @@ pub fn emulate(data: &mut Data) -> Result<(), String> {
|
|||
data.stack.iter().take(data.reg_sp as usize).join(", ")
|
||||
);
|
||||
print!("Press enter to continue execution...",);
|
||||
io::stdout().flush().unwrap();
|
||||
data.term.read_line().unwrap();
|
||||
io::stdout().flush()?;
|
||||
data.term.read_line()?;
|
||||
}
|
||||
0x0b => {
|
||||
data.alu(data.tmp as u8).unwrap();
|
||||
data.alu(data.tmp as u8)?;
|
||||
}
|
||||
0x0c => {
|
||||
data.tmp = data.get_memory(data.tmp);
|
||||
data.tmp = data.get_memory(data.tmp)?;
|
||||
}
|
||||
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 itertools::{Itertools, PeekingNext};
|
||||
use anyhow::{bail, Result};
|
||||
use itertools::Itertools;
|
||||
|
||||
#[derive(Debug)]
|
||||
use crate::assembler;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Token {
|
||||
Comment(String),
|
||||
|
||||
CharLiteral(String),
|
||||
StringLiteral(String),
|
||||
MacroLiteral(String),
|
||||
Literal(String),
|
||||
|
@ -15,13 +18,30 @@ pub enum Token {
|
|||
LParen,
|
||||
RParen,
|
||||
|
||||
Assign,
|
||||
|
||||
Not,
|
||||
And,
|
||||
Nand,
|
||||
Or,
|
||||
Nor,
|
||||
Xor,
|
||||
Xnor,
|
||||
Lsh,
|
||||
Rsh,
|
||||
Add,
|
||||
Sub,
|
||||
Mul,
|
||||
Div,
|
||||
Pow,
|
||||
Cmp,
|
||||
Eq,
|
||||
Neq,
|
||||
Lt,
|
||||
Gt,
|
||||
Leq,
|
||||
Geq,
|
||||
Bol,
|
||||
Inv,
|
||||
Rnd,
|
||||
|
||||
Newline(String),
|
||||
Whitespace(String),
|
||||
|
@ -30,8 +50,9 @@ pub enum Token {
|
|||
impl assembler::ToAssembly for Token {
|
||||
fn to_assembly(&self) -> String {
|
||||
match self {
|
||||
Token::Comment(x) => format!(";{x}"),
|
||||
Token::StringLiteral(x) => format!("\"{x}\""),
|
||||
Token::Comment(x) => format!(";{}", x),
|
||||
Token::CharLiteral(x) => format!("'{}'", x),
|
||||
Token::StringLiteral(x) => format!("\"{}\"", x),
|
||||
Token::MacroLiteral(x) => x.clone(),
|
||||
Token::Literal(x) => x.clone(),
|
||||
Token::Number(x) => x.clone(),
|
||||
|
@ -39,127 +60,290 @@ impl assembler::ToAssembly for Token {
|
|||
Token::Colon => ":".to_string(),
|
||||
Token::LParen => "(".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::Sub => "-".to_string(),
|
||||
Token::Mul => "*".to_string(),
|
||||
Token::Div => "/".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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 tokens = Vec::<Token>::new();
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
|
||||
while let Some(&ch) = chars.peek() {
|
||||
match ch {
|
||||
// ';' => {
|
||||
// chars.next();
|
||||
// chars.next_if(|c| *c == ';');
|
||||
|
||||
// tokens.push(Token::Comment(
|
||||
// chars.peeking_take_while(|c| *c != '\n').collect::<String>(),
|
||||
// ));
|
||||
// }
|
||||
';' | '@' => {
|
||||
tokens.push(match ch {
|
||||
';' => {
|
||||
chars.next();
|
||||
chars.next_if(|c| *c == ';' || *c == '@');
|
||||
|
||||
tokens.push(Token::Comment(
|
||||
chars.peeking_take_while(|c| *c != '\n').collect::<String>(),
|
||||
));
|
||||
chars.next_if(|c| *c == ';');
|
||||
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::Comment(chars.peeking_take_while(|c| *c != '\n').collect())
|
||||
}
|
||||
'\'' => {
|
||||
chars.next();
|
||||
Token::CharLiteral(chars.by_ref().take_while(|c| *c != '\'').collect())
|
||||
}
|
||||
'"' => {
|
||||
chars.next();
|
||||
tokens.push(Token::StringLiteral(
|
||||
chars.by_ref().take_while(|c| *c != '"').collect::<String>(),
|
||||
));
|
||||
Token::StringLiteral(chars.by_ref().take_while(|c| *c != '"').collect())
|
||||
}
|
||||
'.' => {
|
||||
chars.next();
|
||||
tokens.push(Token::MacroLiteral(format!(
|
||||
Token::MacroLiteral(format!(
|
||||
".{}",
|
||||
chars
|
||||
.peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_')
|
||||
.collect::<String>()
|
||||
)));
|
||||
))
|
||||
}
|
||||
ch if ch.is_alphabetic() => {
|
||||
let name: String = chars
|
||||
.peeking_take_while(|c| c.is_alphabetic() || c.is_numeric() || *c == '_')
|
||||
.collect();
|
||||
|
||||
tokens.push(Token::Literal(name));
|
||||
Token::Literal(name)
|
||||
}
|
||||
ch if ch.is_numeric() => {
|
||||
tokens.push(Token::Number(
|
||||
ch if ch.is_numeric() => Token::Number(
|
||||
chars
|
||||
.peeking_take_while(|c| c.is_alphanumeric() || *c == '_')
|
||||
.collect::<String>(),
|
||||
));
|
||||
}
|
||||
.collect(),
|
||||
),
|
||||
',' => {
|
||||
tokens.push(Token::Comma);
|
||||
chars.next();
|
||||
Token::Comma
|
||||
}
|
||||
':' => {
|
||||
tokens.push(Token::Colon);
|
||||
chars.next();
|
||||
Token::Colon
|
||||
}
|
||||
'(' => {
|
||||
tokens.push(Token::LParen);
|
||||
chars.next();
|
||||
Token::LParen
|
||||
}
|
||||
')' => {
|
||||
tokens.push(Token::RParen);
|
||||
chars.next();
|
||||
Token::RParen
|
||||
}
|
||||
'=' => {
|
||||
tokens.push(Token::Assign);
|
||||
'~' => {
|
||||
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();
|
||||
Token::Add
|
||||
}
|
||||
'-' => {
|
||||
tokens.push(Token::Sub);
|
||||
chars.next();
|
||||
Token::Sub
|
||||
}
|
||||
'*' => {
|
||||
chars.next();
|
||||
tokens.push(if chars.peeking_next(|c| *c == '*').is_some() {
|
||||
if let Some('*') = chars.peek() {
|
||||
chars.next();
|
||||
Token::Pow
|
||||
} else {
|
||||
Token::Mul
|
||||
});
|
||||
}
|
||||
}
|
||||
'/' => {
|
||||
tokens.push(Token::Div);
|
||||
chars.next();
|
||||
Token::Div
|
||||
}
|
||||
'=' => {
|
||||
chars.next();
|
||||
if let Some('=') = chars.peek() {
|
||||
chars.next();
|
||||
}
|
||||
'\n' => {
|
||||
tokens.push(Token::Newline(
|
||||
chars.peeking_take_while(|c| *c == '\n').collect::<String>(),
|
||||
));
|
||||
Token::Eq
|
||||
}
|
||||
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
|
||||
.peeking_take_while(|c| c.is_whitespace() && *c != '\n')
|
||||
.collect::<String>(),
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
// tokens.push(Token::Error(ch.to_string()));
|
||||
// chars.next();
|
||||
return Err(format!("Unexpected token: '{ch}'"));
|
||||
}
|
||||
}
|
||||
.collect(),
|
||||
),
|
||||
_ => bail!("Unexpected token: {}", ch),
|
||||
});
|
||||
}
|
||||
|
||||
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 assembler;
|
||||
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(_)))
|
||||
.filter(|t| !matches!(t, lexer::Token::Whitespace(_)))
|
||||
.collect(),
|
||||
)
|
||||
.unwrap(),
|
||||
)?,
|
||||
});
|
||||
}
|
||||
lexer::Token::Literal(x) => {
|
||||
|
|
|
@ -27,7 +27,10 @@ impl assembler::ToAssembly for Node {
|
|||
if args.is_empty() {
|
||||
format!(".{name}")
|
||||
} 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