Add working macro system

This commit is contained in:
Dominic Grimm 2022-09-02 11:33:46 +02:00
parent 6adb943754
commit 1ff9a6c44b
No known key found for this signature in database
GPG key ID: A6C051C716D2CE65
19 changed files with 1258 additions and 571 deletions

2
.gitignore vendored
View file

@ -1 +1,3 @@
/target
wapm_packages

205
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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 | |

View file

@ -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
View 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
View 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
View 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
View 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
View 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

View file

@ -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 {}

View file

@ -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());
}
}

View file

@ -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)
}
}

View file

@ -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)),
}
}

View file

@ -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(())
}
}

View file

@ -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;

View file

@ -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) => {

View file

@ -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(", ")
)
}
}
}