frustration/frustration.rs

536 lines
14 KiB
Rust
Raw Normal View History

2022-03-22 04:45:42 +01:00
use std::io;
use std::convert::TryInto;
2022-03-22 04:45:42 +01:00
2022-03-26 09:01:56 +01:00
#[derive(Debug, Eq, PartialEq)]
2022-03-24 08:01:51 +01:00
enum State {
Compiling,
Interpreting
}
2022-03-27 08:49:25 +02:00
#[derive(Debug)]
enum Post {
Nothing,
EatWord,
2022-03-27 10:05:23 +02:00
WarmReset,
2022-03-27 08:49:25 +02:00
}
2022-03-27 22:56:39 +02:00
const ADDRESS_SPACE: usize = 65535;
const STACK_WORDS: usize = 16;
const RAM_BYTES: usize = ADDRESS_SPACE - 2*2*STACK_WORDS;
2022-03-22 04:45:42 +01:00
#[derive(Debug)]
struct Core {
2022-03-27 22:56:39 +02:00
ram: [u8; RAM_BYTES],
2022-03-24 08:01:51 +01:00
ip: u16,
dp: u16, // newest link field, or 0
here: u16, // first unused byte
state: State,
2022-03-27 08:49:25 +02:00
next_token: Option<String>,
post: Post,
2022-03-27 22:56:39 +02:00
dstack: [u16; STACK_WORDS],
2022-03-24 08:01:51 +01:00
tds: usize, // post-incremented; exceeds top by one
2022-03-27 22:56:39 +02:00
rstack: [u16; STACK_WORDS],
2022-03-24 08:01:51 +01:00
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: *b" ",
length: name_bytes.len() as u8 };
let n = std::cmp::min(3, out.length) as usize;
out.bytes[0..n].copy_from_slice(&name_bytes[0..n]);
2022-03-24 08:01:51 +01:00
return out;
2022-03-22 04:45:42 +01:00
}
2022-03-24 08:01:51 +01:00
struct TableEntry {
f: Primitive,
2022-03-26 23:20:52 +01:00
name: Option<ShortName>,
2022-03-26 09:01:56 +01:00
immediate: bool
2022-03-24 08:01:51 +01:00
}
2022-03-28 06:17:01 +02:00
const PRIMITIVES: [TableEntry; 31] = [
2022-03-26 23:20:52 +01:00
TableEntry {f: ret , name: None, immediate: false},
TableEntry {f: lit , name: None, immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: add , name: Some(ShortName {bytes: *b"+ ", length: 1}), immediate: false},
2022-03-26 23:20:52 +01:00
TableEntry {f: call , name: Some(ShortName {bytes: *b"cal", length: 4}), immediate: false},
TableEntry {f: comma_d , name: Some(ShortName {bytes: *b", ", length: 1}), immediate: false},
TableEntry {f: create_d, name: Some(ShortName {bytes: *b"cre", length: 6}), immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: div , name: Some(ShortName {bytes: *b"/ ", length: 1}), immediate: false},
2022-03-26 23:20:52 +01:00
TableEntry {f: dot , name: Some(ShortName {bytes: *b". ", length: 1}), immediate: false},
2022-03-28 06:17:01 +02:00
TableEntry {f: dots , name: Some(ShortName {bytes: *b".s ", length: 2}), immediate: false},
2022-03-27 09:15:39 +02:00
TableEntry {f: drop , name: Some(ShortName {bytes: *b"dro", length: 4}), immediate: false},
TableEntry {f: dup , name: Some(ShortName {bytes: *b"dup", length: 3}), immediate: false},
TableEntry {f: dump , name: Some(ShortName {bytes: *b"dum", length: 4}), immediate: false},
2022-03-28 06:17:01 +02:00
TableEntry {f: forget , name: Some(ShortName {bytes: *b"for", length: 6}), immediate: false},
TableEntry {f: from_r_d, name: Some(ShortName {bytes: *b"r> ", length: 2}), immediate: false},
2022-03-28 06:17:01 +02:00
TableEntry {f: here , name: Some(ShortName {bytes: *b"her", length: 4}), immediate: false},
2022-03-27 22:22:35 +02:00
TableEntry {f: if_skip ,name: Some(ShortName {bytes: *b"? ", length: 1}), immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: immediate,name: Some(ShortName {bytes: *b"imm", length: 9}), immediate: false},
2022-03-28 06:17:01 +02:00
TableEntry {f: latest , name: Some(ShortName {bytes: *b"lat", length: 6}), immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: lbracket, name: Some(ShortName {bytes: *b"[ ", length: 1}), immediate: true},
2022-03-26 23:20:52 +01:00
TableEntry {f: load , name: Some(ShortName {bytes: *b"@ ", length: 1}), immediate: false},
TableEntry {f: mul , name: Some(ShortName {bytes: *b"* ", length: 1}), immediate: false},
TableEntry {f: ret_d , name: Some(ShortName {bytes: *b"ret", length: 3}), immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: rbracket, name: Some(ShortName {bytes: *b"] ", length: 1}), immediate: false},
2022-03-27 22:22:35 +02:00
TableEntry {f: smudge , name: Some(ShortName {bytes: *b"smu", length: 6}), immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: store , name: Some(ShortName {bytes: *b"! ", length: 1}), immediate: false},
TableEntry {f: sub , name: Some(ShortName {bytes: *b"- ", length: 1}), immediate: false},
2022-03-27 09:15:39 +02:00
TableEntry {f: swap , name: Some(ShortName {bytes: *b"swa", length: 4}), immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: tick , name: Some(ShortName {bytes: *b"' ", length: 1}), immediate: false},
TableEntry {f: to_r_d , name: Some(ShortName {bytes: *b">r ", length: 2}), immediate: false},
2022-03-27 22:22:35 +02:00
TableEntry {f: unsmudge, name: Some(ShortName {bytes: *b"uns", length: 8}), immediate: false},
2022-03-27 09:08:28 +02:00
TableEntry {f: word , name: Some(ShortName {bytes: *b"wor", length: 4}), immediate: false}
2022-03-24 08:01:51 +01:00
];
2022-03-22 04:45:42 +01:00
fn new_core() -> Core {
2022-03-24 08:01:51 +01:00
let mut c = Core {
2022-03-27 22:56:39 +02:00
ram: [0; RAM_BYTES], ip: 0, dp: 0, here: 2, state: State::Interpreting,
2022-03-27 08:49:25 +02:00
next_token: None,
post: Post::Nothing,
2022-03-27 22:56:39 +02:00
dstack: [0; STACK_WORDS], tds: 0,
rstack: [0; STACK_WORDS], trs: 0 };
2022-03-24 08:01:51 +01:00
init_dictionary(&mut c);
2022-03-27 22:56:39 +02:00
let autoexec = [
2022-03-28 06:17:01 +02:00
"create : ] create smudge ] [ 65535 ,",
"create ; ] unsmudge 65535 , [ ' [ , 65535 , immediate",
": recursive unsmudge ; immediate",
": literal 65534 , , ; immediate",
": constant create [ ' literal , ] [ ' ret ] literal , ;",
": variable create here 6 + [ ' literal , ] [ ' ret ] literal , 0 , ;"
2022-03-27 22:56:39 +02:00
];
for s in autoexec {
outer(&mut c, s);
}
2022-03-24 08:01:51 +01:00
return c;
}
// --- Dictionary management ---
2022-03-27 22:56:39 +02:00
fn init_dictionary(c: &mut Core) {
let mut opcode = 65535;
for p in PRIMITIVES {
match p.name {
Some(name) => {
create(c, name);
if p.immediate {
immediate(c);
}
comma(c, opcode);
comma(c, 65535); // ret
}
None => {}
}
opcode -= 1;
}
}
2022-03-24 08:01:51 +01:00
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;
2022-03-26 09:01:56 +01:00
c.ram[addr+2] = name.length & 0x7f;
2022-03-24 08:01:51 +01:00
c.ram[addr+3..=addr+5].copy_from_slice(&name.bytes);
c.here = (addr+6) as u16;
}
fn create_d(c: &mut Core) {
2022-03-27 08:49:25 +02:00
match &c.next_token {
Some(t) => {
let short_name = truncate_name(t);
create(c, short_name);
c.post = Post::EatWord;
}
_ => {
println!(" create needs an argument");
c.post = Post::WarmReset;
}
2022-03-27 08:49:25 +02:00
}
}
fn find(c: &mut Core, name: ShortName) -> Option<u16> {
let mut addr = c.dp as usize;
while addr != 0 {
2022-03-26 09:01:56 +01:00
if (c.ram[addr+2] & 0x7f) == 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;
}
2022-03-26 22:59:25 +01:00
fn smudge(c: &mut Core) {
2022-03-27 22:22:35 +02:00
c.ram[(c.dp as usize) + 2] |= 0x40;
}
fn unsmudge(c: &mut Core) {
c.ram[(c.dp as usize) + 2] &= 0xbf;
2022-03-26 22:59:25 +01:00
}
fn immediate(c: &mut Core) {
c.ram[(c.dp as usize) + 2] ^= 0x80;
}
fn is_immediate(c: &mut Core, addr: u16) -> bool {
return (c.ram[(addr as usize) - 4] & 0x80) != 0;
}
2022-03-27 08:49:25 +02:00
fn tick(c: &mut Core) {
match &c.next_token {
Some(t) => {
let name = t.to_string();
let addr = find(c, truncate_name(&name));
match addr {
Some(xt) => {
push(c, xt);
c.post = Post::EatWord;
}
None => {
println!(" ' cannot find {}", name);
2022-03-27 10:05:23 +02:00
c.post = Post::WarmReset;
2022-03-27 08:49:25 +02:00
}
}
}
_ => {
println!(" ' needs an argument");
2022-03-27 10:05:23 +02:00
c.post = Post::WarmReset;
2022-03-27 08:49:25 +02:00
}
}
}
2022-03-26 22:59:25 +01:00
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 comma_d(c: &mut Core) {
2022-03-26 22:59:25 +01:00
let val = pop(c);
comma(c, val);
}
// --- Memory management ---
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()));
}
2022-03-28 06:17:01 +02:00
fn forget(c: &mut Core) {
let xt = pop(c);
c.here = xt - 6;
let i = c.here as usize;
c.dp = u16::from_le_bytes(c.ram[i..=i+1].try_into().unwrap());
}
2022-03-26 22:59:25 +01:00
// --- Stack management ---
2022-03-22 04:45:42 +01:00
fn push(c: &mut Core, val: u16) {
c.dstack[c.tds] = val;
c.tds += 1;
}
fn pop(c: &mut Core) -> u16 {
2022-03-27 10:05:23 +02:00
if c.tds == 0 {
println!(" stack underflow");
c.post = Post::WarmReset; // note: could get overwritten later :(
return 0; // half-assed, should really return straight to interpreter
} else {
c.tds -= 1;
return c.dstack[c.tds];
}
2022-03-22 04:45:42 +01:00
}
2022-03-27 09:15:39 +02:00
fn dup(c: &mut Core) {
let val = pop(c);
push(c, val);
push(c, val);
}
fn swap(c: &mut Core) {
let val1 = pop(c);
let val2 = pop(c);
push(c, val1);
push(c, val2);
}
fn drop(c: &mut Core) {
let _ = pop(c);
}
2022-03-22 04:45:42 +01:00
fn to_r(c: &mut Core, val: u16) {
c.rstack[c.trs] = val;
c.trs += 1;
}
fn to_r_d(c: &mut Core) {
let r1 = from_r(c);
let r2 = pop(c);
to_r(c, r2);
to_r(c, r1);
}
2022-03-22 04:45:42 +01:00
fn from_r(c: &mut Core) -> u16 {
c.trs -= 1;
return c.rstack[c.trs];
}
fn from_r_d(c: &mut Core) {
let r1 = from_r(c);
let r2 = from_r(c);
to_r(c, r1);
push(c, r2);
}
2022-03-26 23:05:00 +01:00
fn call(c: &mut Core) {
to_r(c, c.ip);
c.ip = pop(c);
}
2022-03-26 22:59:25 +01:00
// note: this is an inline primitive, not a dict entry
fn ret(c: &mut Core) {
if c.trs == 0 {
std::process::exit(0);
}
2022-03-26 22:59:25 +01:00
c.ip = from_r(c);
}
fn ret_d(c: &mut Core) {
_ = from_r(c);
ret(c);
}
2022-03-27 22:22:35 +02:00
// --- Control flow ---
fn if_skip(c: &mut Core) {
let truthy = pop(c);
let retaddr = from_r(c);
to_r(c, retaddr + if truthy == 0 { 2 } else { 0 });
}
2022-03-27 22:22:35 +02:00
// --- I/O ---
2022-03-24 08:01:51 +01:00
fn dot(c: &mut Core) {
print!("{} ", pop(c));
}
2022-03-28 06:17:01 +02:00
fn dots(c: &mut Core) {
for i in &c.dstack[0..c.tds] {
print!("{} ", i);
}
}
fn dump(c: &mut Core) {
println!("{:?}", c);
}
2022-03-27 08:49:25 +02:00
fn word(c: &mut Core) {
match &c.next_token {
Some(t) => {
println!("{}", t);
c.post = Post::EatWord;
}
_ => {}
}
}
2022-03-27 22:22:35 +02:00
// --- Math and logic ---
2022-03-26 22:21:02 +01:00
// note: this is an inline primitive, not a dict entry
2022-03-26 10:15:00 +01:00
fn lit(c: &mut Core) {
2022-03-26 22:21:02 +01:00
let ip = c.ip as usize;
push(c, u16::from_le_bytes(c.ram[ip..=ip+1].try_into().unwrap()));
c.ip += 2;
2022-03-26 10:15:00 +01:00
}
2022-03-22 04:45:42 +01:00
fn add(c: &mut Core) {
let v1 = pop(c);
let v2 = pop(c);
2022-03-27 10:05:23 +02:00
push(c, v1.wrapping_add(v2));
2022-03-22 04:45:42 +01:00
}
fn sub(c: &mut Core) {
let v1 = pop(c);
let v2 = pop(c);
2022-03-27 10:05:23 +02:00
push(c, v2.wrapping_sub(v1));
2022-03-22 04:45:42 +01:00
}
fn mul(c: &mut Core) {
let v1 = pop(c);
let v2 = pop(c);
2022-03-27 10:05:23 +02:00
push(c, v1.saturating_mul(v2));
2022-03-22 04:45:42 +01:00
}
fn div(c: &mut Core) {
let v1 = pop(c);
let v2 = pop(c);
2022-03-27 10:05:23 +02:00
push(c, v2.saturating_div(v1));
2022-03-22 04:45:42 +01:00
}
2022-03-27 22:22:35 +02:00
// --- Inner interpreter ---
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) {
loop {
step(c);
//println!("ip={} trs={}", c.ip, c.trs);
if c.trs == 0 {
break;
}
}
}
2022-03-26 22:59:25 +01:00
// --- Outer interpreter ---
2022-03-26 23:20:52 +01:00
fn lbracket(c: &mut Core) {
c.state = State::Interpreting;
}
fn rbracket(c: &mut Core) {
c.state = State::Compiling;
}
2022-03-28 06:17:01 +02:00
fn latest(c: &mut Core) {
push(c, c.dp);
}
fn here(c: &mut Core) {
push(c, c.here);
}
2022-03-23 05:13:17 +01:00
fn outer(c: &mut Core, s: &str) {
2022-03-22 04:45:42 +01:00
let ss = s.trim();
2022-03-27 08:49:25 +02:00
let mut tokens = ss.split(" ").peekable();
loop {
c.post = Post::Nothing;
match tokens.next() {
Some(t) => {
c.next_token = match tokens.peek() {
Some(t) => { Some(t.to_string()) }
None => { None }
};
match find(c, truncate_name(t)) {
Some(addr) => {
if c.state == State::Interpreting || is_immediate(c, addr) {
to_r(c, c.ip);
c.ip = addr;
inner(c);
} else {
comma(c, addr);
}
}
None => {
let val = t.parse::<u16>();
match val {
Ok(n) => {
match c.state {
State::Interpreting => { push(c, n) }
State::Compiling => {
comma(c, 65534); // lit
comma(c, n);
}
}
}
2022-03-27 10:05:23 +02:00
Err(_) => {
if t != "" {
println!("{}?", t);
c.post = Post::WarmReset;
}
}
}
}
2022-03-22 04:45:42 +01:00
}
}
2022-03-27 08:49:25 +02:00
None => { break ; }
2022-03-22 04:45:42 +01:00
}
2022-03-27 08:49:25 +02:00
match c.post {
Post::EatWord => { _ = tokens.next(); }
2022-03-27 10:05:23 +02:00
Post::WarmReset => {
c.tds = 0;
c.trs = 0;
c.state = State::Interpreting;
2022-03-27 10:05:23 +02:00
break; // discard rest of input line
}
2022-03-27 08:49:25 +02:00
Post::Nothing => { }
};
2022-03-22 04:45:42 +01:00
}
}
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);
match c.state {
State::Interpreting => {println!(" ok")}
State::Compiling => {}
};
}
2022-03-23 05:13:17 +01:00
Err(_) => { break; }
2022-03-22 04:45:42 +01:00
}
}
}
2022-03-27 08:49:25 +02:00
2022-03-27 22:22:35 +02:00
/*
2022-03-27 22:56:39 +02:00
: dog recursive r> drop dup . 1 - dup ? dog ;
2022-03-27 22:22:35 +02:00
: dog dog ;
100 dog
*/
2022-04-02 08:37:17 +02:00
/* TODO LIST
* 0=
* allot, cell, cells
* base
* c@ and c!
* comments ( )
* comments \
* emit
* forth-style line parsing, instead of pre-baked str::split(" ").
* key
* recursive with more creature comforts
* see
* startup message "XXXXX bytes free"
* strings ." this", s" that", etc.
* words
*/