use std::cmp; use std::io; use std::process; use std::convert::TryInto; //const CORE_SIZE: usize = 65280 const CORE_SIZE: usize = 128; #[derive(Debug)] enum State { Compiling, Interpreting } #[derive(Debug)] struct Core { ram: [u8; CORE_SIZE], ip: u16, dp: u16, // newest link field, or 0 here: u16, // first unused byte state: State, dstack: [u16; 64], tds: usize, // post-incremented; exceeds top by one rstack: [u16; 64], trs: usize, // post-incremented; exceeds top by one } type Primitive = fn(&mut Core); struct ShortName { bytes: [u8; 3], length: u8 } fn truncate_name(name: &str) -> ShortName { let name_bytes = name.as_bytes(); let mut out = ShortName { bytes: [32, 32, 32], length: std::cmp::min(3, name_bytes.len()) as u8 }; out.bytes[0..out.length as usize].copy_from_slice(&name_bytes[0..out.length as usize]); return out; } struct TableEntry { f: Primitive, name: ShortName } // NOTE: lazy_static or SyncLazy would allow nicer init const PRIMITIVES: [TableEntry; 8] = [ TableEntry {f: ret , name: ShortName {bytes: ['r' as u8, 'e' as u8, 't' as u8], length: 3}}, TableEntry {f: dot , name: ShortName {bytes: ['.' as u8, 32, 32], length: 1}}, TableEntry {f: store, name: ShortName {bytes: ['!' as u8, 32, 32], length: 1}}, TableEntry {f: load , name: ShortName {bytes: ['@' as u8, 32, 32], length: 1}}, TableEntry {f: add , name: ShortName {bytes: ['+' as u8, 32, 32], length: 1}}, TableEntry {f: sub , name: ShortName {bytes: ['-' as u8, 32, 32], length: 1}}, TableEntry {f: mul , name: ShortName {bytes: ['*' as u8, 32, 32], length: 1}}, TableEntry {f: div , name: ShortName {bytes: ['/' as u8, 32, 32], length: 1}} ]; fn new_core() -> Core { let mut c = Core { ram: [0; CORE_SIZE], ip: 0, dp: 0, here: 2, state: State::Interpreting, dstack: [0; 64], tds: 0, rstack: [0; 64], trs: 0 }; init_dictionary(&mut c); return c; } fn create(c: &mut Core, name: ShortName) { let addr: usize = c.here as usize; c.ram[addr+0..=addr+1].copy_from_slice(&c.dp.to_le_bytes()); c.dp = addr as u16; c.ram[addr+2] = name.length; c.ram[addr+3..=addr+5].copy_from_slice(&name.bytes); c.here = (addr+6) as u16; } fn find(c: &mut Core, name: ShortName) -> Option { let mut addr = c.dp as usize; while addr != 0 { if c.ram[addr+2] == name.length { if c.ram[addr+3..=addr+5] == name.bytes { return Some((addr+6) as u16); } } addr = u16::from_le_bytes(c.ram[addr..=addr+1].try_into().unwrap()) as usize; } return None; } fn init_dictionary(c: &mut Core) { let mut opcode = 65535; for p in PRIMITIVES { create(c, p.name); comma(c, opcode); opcode -= 1; } } fn push(c: &mut Core, val: u16) { c.dstack[c.tds] = val; c.tds += 1; } fn pop(c: &mut Core) -> u16 { c.tds -= 1; return c.dstack[c.tds]; } fn to_r(c: &mut Core, val: u16) { c.rstack[c.trs] = val; c.trs += 1; } fn from_r(c: &mut Core) -> u16 { c.trs -= 1; return c.rstack[c.trs]; } fn fetch(c: &mut Core) -> u16 { let ip = c.ip as usize; let opcode = u16::from_le_bytes(c.ram[ip..=ip+1].try_into().unwrap()); c.ip += 2; return opcode; } fn execute(c: &mut Core, opcode: u16) { let primitive_index = (65535 - opcode) as usize; if primitive_index < PRIMITIVES.len() { (PRIMITIVES[primitive_index].f)(c); } else { // call to_r(c, c.ip); c.ip = opcode; } } fn step(c: &mut Core) { let opcode = fetch(c); execute(c, opcode); } fn inner(c: &mut Core) { while c.ip != 2 { step(c); } } fn ret(c: &mut Core) { if c.trs == 0 { std::process::exit(0); } c.ip = from_r(c); } fn dot(c: &mut Core) { print!("{} ", pop(c)); } fn comma(c: &mut Core, val: u16) { let addr = c.here as usize; c.ram[addr..=addr+1].copy_from_slice(&val.to_le_bytes()); c.here += 2; } fn store(c: &mut Core) { let addr = pop(c) as usize; let val = pop(c); c.ram[addr..=addr+1].copy_from_slice(&val.to_le_bytes()); } fn load(c: &mut Core) { let addr = pop(c) as usize; push(c, u16::from_le_bytes(c.ram[addr..=addr+1].try_into().unwrap())); } fn add(c: &mut Core) { let v1 = pop(c); let v2 = pop(c); push(c, v1 + v2); } fn sub(c: &mut Core) { let v1 = pop(c); let v2 = pop(c); push(c, v2 - v1); } fn mul(c: &mut Core) { let v1 = pop(c); let v2 = pop(c); push(c, v1 * v2); } fn div(c: &mut Core) { let v1 = pop(c); let v2 = pop(c); push(c, v2 / v1); } fn outer(c: &mut Core, s: &str) { let ss = s.trim(); let tokens = ss.split(" "); for t in tokens { match t { "s" => { step(c); }, "g" => { c.ip = 0; inner(c); }, "dmp" => { println!("{:?}", c); }, _ => { match find(c, truncate_name(t)) { Some(addr) => { c.ip = addr; inner(c); } None => { let val = t.parse::(); match val { Ok(n) => { push(c, n) } Err(_) => { if t != "" { println!("{}?", t) }} } } } } } } } fn main() { let mut c = new_core(); loop { let mut buf = String::new(); match io::stdin().read_line(&mut buf) { Ok(_) => { outer(&mut c, &buf); println!(" ok");} Err(_) => { break; } } } //push(&mut c, 56); push(&mut c, 9); mul(&mut c); dot(&mut c); //let v = pop(&mut c); call(&mut c, v); //println!("{:?}", c); }