From bd11d756e47bfda8107171bbd8dfb84e86709311 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Sat, 17 Feb 2024 15:16:32 -0800 Subject: [PATCH] Sets and gets and stuff --- src/main.rs | 384 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 309 insertions(+), 75 deletions(-) diff --git a/src/main.rs b/src/main.rs index b227c84..1f40171 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,72 +1,158 @@ -struct VM<'p> { program: &'p Program, block: u32, ip: u32, stack: Vec, status: Status } - -enum Status { - Running, - AwaitSyscall(Term), - Crash(&'static str), - Exit(Term), +struct VM<'p> { + program: &'p Program, + block: u32, ip: u32, + heap: Heap<'p>, + locals: Term, + stack: Vec, + status: Status } -#[derive(Clone, Debug)] -struct Term { atom: u32, args: Vec, } -struct Program { blocks: Vec } -struct Block { instructions: Vec } +#[derive(Clone, Copy)] +enum Status { + Running, + AwaitSyscall(u32), + Crash(&'static str), + Exit, +} +#[derive(Clone, Copy, Debug)] +enum StackItem { + Term(Term), + Integer(u32), +} +impl StackItem { + fn term(self) -> Option { + if let StackItem::Term(t) = self { return Some(t) } + return None + } + + fn integer(self) -> Option { + if let StackItem::Integer(u) = self { return Some(u) } + return None + } +} + +#[derive(Clone, Copy, Debug)] +struct Term { index: u32 } +struct Program { + blocks: Vec, + types: Vec, +} +struct BlockData { instructions: Vec } + +struct Block { index: u32 } + +#[derive(Debug)] enum Instruction { - // push Atom to stack, optionally with the current `stack` appended to it - Push { atom: u32, cc: bool }, - // do a binary operation between the top two stack items - BinOp { op: fn(Term, Term) -> Result }, - // expand the top stack item to the stack, failing if its len is not `len` - Expand { len: u32 }, - // jump to the given block. its stack is the top stack item - Call { block: u32 }, - // system call. pass the top stack item. get a stack item back - Syscall { syscall: u32 }, + // push NULL to stack + PushNull, + // push an integer to the stack + PushInteger(u32), + + // push a new term to the stack with the given type + PushNew(Type), + + // get the locals register + PushLocals, + // pop the locals register + PopLocals, + + Op11(Op11), // [t1] -> [t2] + Op21(Op21), // [t1, t2] -> [t3] + + // assert that the stack has n items and jump to the given block + Call { block: u32, n: u32, }, + // assert that the stack has n items, clear it, and execute a syscall + Syscall { syscall: u32, n: u32 }, + // kill the program Crash(&'static str), // end the program, popping the top stack item Exit } + +#[derive(Debug)] +enum Op11 { Get(u32) } + +#[derive(Debug)] +enum Op21 { Add, Set(u32) } + +const TY_NULL: Type = Type { id: 0x0 }; +const NULL: Term = Term { index: 0 }; + const SYSCALL_DBG: u32 = 0x1000; fn main() { - host_program(&Program { - blocks: vec![ - Block { instructions: vec![ - Instruction::Expand { len: 0 }, - Instruction::Push { atom: SYSCALL_DBG, cc: false }, - Instruction::Push { atom: 0xdead0000, cc: false }, - Instruction::Push { atom: 0x0000ba75, cc: false }, - Instruction::BinOp { op: binop_add }, - Instruction::BinOp { op: binop_append }, - Instruction::Syscall { syscall: SYSCALL_DBG }, - Instruction::Push { atom: 0x0, cc: false }, + let mut program = Program::new(); + let ty_locals = program.type_create(3); + program.block_create( + BlockData { + instructions: vec![ + Instruction::PushNew(ty_locals), Instruction::PopLocals, + + Instruction::PushLocals, + Instruction::PushInteger(0xdead0000), Instruction::Op21(Op21::Set(0)), + Instruction::PushInteger(0x0000ba75), Instruction::Op21(Op21::Set(1)), + // stack: [locals] + + Instruction::Call { block: 1, n: 1 }, + ] + } + ); + + program.block_create( + BlockData { + instructions: vec![ + Instruction::PopLocals, + + Instruction::PushLocals, + Instruction::PushLocals, Instruction::Op11(Op11::Get(0)), + Instruction::PushLocals, Instruction::Op11(Op11::Get(1)), + Instruction::Op21(Op21::Add), + Instruction::Op21(Op21::Set(2)), + + Instruction::Call { block: 2, n: 1 } + ] + } + ); + + program.block_create( + BlockData { + instructions: vec![ + Instruction::PopLocals, + + Instruction::PushLocals, + Instruction::Op11(Op11::Get(0)), + Instruction::Syscall { syscall: SYSCALL_DBG, n: 1 }, + Instruction::Exit, - ]} - ] - }).unwrap(); + ] + } + ); + + host_program(&program).unwrap() } -fn host_program(p: &Program) -> Result { +fn host_program(p: &Program) -> Result<(), String> { let mut vm = VM::start(p); loop { let status = vm.step(); match status { Status::Running => {} - Status::AwaitSyscall(sc) => { - match sc.atom { + Status::AwaitSyscall(ix) => { + match ix { SYSCALL_DBG => { - println!("debug print: {:08x?}", sc.args[0].atom); - vm.complete_syscall(None) + let top = vm.stack.pop().ok_or("should be an item on the stack")?; + println!("debug print: {:08x?}", top); + vm.complete_syscall() } - _ => { return Err(format!("unknown syscall: {}", sc.atom)) } + _ => { return Err(format!("unknown syscall: {}", ix)) } } } Status::Crash(err) => { return Err(err.to_string()); } - Status::Exit(t) => { return Ok(t.clone()); } + Status::Exit => { return Ok(()); } } } } @@ -74,23 +160,26 @@ fn host_program(p: &Program) -> Result { impl<'p> VM<'p> { pub fn start(program: &'p Program) -> VM<'p> { + let mut heap = Heap::new(program); + VM { program, block: 0, ip: 0, - stack: vec![Term { atom: 0, args: vec![] }], + heap, + locals: NULL, + stack: vec![], status: Status::Running, - } } - pub fn step<'a>(&'a mut self) -> &'a Status { + pub fn step(&mut self) -> Status { let error = match self.internal_step() { - Ok(_) => { return &self.status; } + Ok(_) => { return self.status; } Err(e) => e }; self.status = Status::Crash(error); - return &self.status + self.status } fn internal_step(&mut self) -> Result<(), &'static str> { if let Status::Running = self.status { } else { return Ok(()); } @@ -100,52 +189,71 @@ impl<'p> VM<'p> { .blocks.get(self.block as usize).ok_or("block must exist")? .instructions.get(self.ip as usize).ok_or("instruction must exist")?; self.ip += 1; + println!("instruction: {:?}", instruction); match instruction { - &Instruction::Push { atom, cc } => { - let mut term = Term { atom, args: vec![] }; - if cc { term.args.extend(self.stack.drain(..)) } - self.stack.push(term) + &Instruction::PushNull => { + self.stack.push(StackItem::Term(NULL)) } - Instruction::BinOp { op } => { + + &Instruction::PushInteger(int) => { + self.stack.push(StackItem::Integer(int)) + } + + &Instruction::PushNew(ty) => { + let t = self.heap.term_create(ty); + self.stack.push(StackItem::Term(t)) + } + + Instruction::PushLocals => { + self.stack.push(StackItem::Term(self.locals)) + } + + Instruction::PopLocals => { + let t0 = self.stack.pop().ok_or("t0 must be present")?.term().ok_or("t0 must be a term")?; + self.locals = t0; + } + + Instruction::Op11(op) => { + let t0 = self.stack.pop().ok_or("t0 must be present")?; + self.stack.push(op.perform(&mut self.heap, t0)?); + } + + Instruction::Op21(op) => { let t1 = self.stack.pop().ok_or("t1 must be present")?; let t0 = self.stack.pop().ok_or("t0 must be present")?; - self.stack.push(op(t0, t1)?); + self.stack.push(op.perform(&mut self.heap, t0, t1)?); } - &Instruction::Expand { len } => { - let term = self.stack.pop().ok_or("term must be present")?; - if term.args.len() != len as usize { panic!("term must have {} args", len) } - } - &Instruction::Call { block } => { - let invocation = self.stack.pop().ok_or("invocation must be present")?; - if invocation.atom != block { panic!("invocation doesn't match block"); } - self.stack.clear(); - self.stack.push(invocation); + + &Instruction::Call { block, n } => { + if self.stack.len() != n as usize { + return Err("stack has wrong number of elements"); + } + self.locals = NULL; self.block = block; self.ip = 0; } - &Instruction::Syscall { syscall } => { - let invocation = self.stack.pop().ok_or("invocation must be present")?; - if invocation.atom != syscall { panic!("invocation doesn't match syscall"); } - self.status = Status::AwaitSyscall(invocation) + &Instruction::Syscall { syscall, n } => { + if self.stack.len() != n as usize { + return Err("stack has wrong number of elements"); + } + self.status = Status::AwaitSyscall(syscall) } Instruction::Crash(c) => { panic!("crashed: {}", c) } Instruction::Exit => { - let exit = self.stack.pop().ok_or("exit arg must be present")?; - self.status = Status::Exit(exit) + self.status = Status::Exit } } Ok(()) } - pub fn complete_syscall(&mut self, result: Option) { + pub fn complete_syscall(&mut self) { match &self.status { Status::AwaitSyscall(_) => { - if let Some(t) = result { self.stack.push(t); } self.status = Status::Running; }, // continue _ => { @@ -155,12 +263,138 @@ impl<'p> VM<'p> { } } +impl Op11 { + fn perform(&self, heap: &mut Heap, v0: StackItem) -> Result { + match self { + &Op11::Get(ix) => { + let t0 = v0.term().ok_or("must be a term")?; + Ok(heap.term_get_arg(t0, ix)) + } + } + } -fn binop_append(mut t0: Term, mut t1: Term) -> Result { - t0.args.push(t1); Ok(t0) } -fn binop_add(mut t0: Term, mut t1: Term) -> Result { - t0.atom += t1.atom; - Ok(t0) +impl Op21 { + fn perform(&self, heap: &mut Heap, v0: StackItem, v1: StackItem) -> Result { + match self { + Op21::Add => { + let i0 = v0.integer().ok_or("must be an integer")?; + let i1 = v1.integer().ok_or("must be an integer")?; + Ok(StackItem::Integer(i0.wrapping_add(i1))) + } + &Op21::Set(ix) => { + let t0 = v0.term().ok_or("must be a term")?; + heap.term_set_arg(t0, ix, v1); + Ok(v0) + } + } + } +} + + +impl Program { + fn new() -> Program { + let mut p = Self { + blocks: vec![], + types: vec![], + }; + let ty_null = p.type_create(0); + assert_eq!(TY_NULL, ty_null); + return p + } + + fn type_create(&mut self, len: u32) -> Type { + let index = self.types.len(); + self.types.push(TypeData { len }); + Type { id: index as u32 } + } + + fn type_get(&self, ty: Type) -> &TypeData { + return self.types.get(ty.id as usize).expect("type should have existed") + } + + fn block_create(&mut self, block: BlockData) -> Block { + let index = self.blocks.len(); + self.blocks.push(block); + Block { index: index as u32 } + } +} + +struct Heap<'p> { + program: &'p Program, + data: Vec +} + +#[derive(Debug)] +enum HeapItem { + Type(Type), + StackItem(StackItem), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Type { + id: u32 +} + +struct TypeData { + len: u32 +} + +impl<'p> Heap<'p> { + fn new(p: &'p Program) -> Self { + let mut h = Heap { + program: p, + data: vec![], + }; + let null = h.term_create(TY_NULL); + assert_eq!(NULL.index, null.index); + h + } + + fn term_create(&mut self, ty: Type) -> Term { + let td = self.program.type_get(ty); + let index = self.data.len() as u32; + self.data.push(HeapItem::Type(ty)); + for _ in 0..td.len { + self.data.push(HeapItem::StackItem(StackItem::Term(NULL))); + } + Term { index } + } + + fn term_get_type(&self, term: Term) -> Type { + let header = self.data.get(term.index as usize).expect("term should have been valid"); + if let &HeapItem::Type(ty) = header { + ty + } else { + panic!("header is invalid: {:?}", header) + } + } + + fn term_get_arg(&self, term: Term, ix: u32) -> StackItem { + let ty = self.term_get_type(term); + let td = self.program.type_get(ty); + + if ix < td.len { + let value = self.data.get((term.index + ix + 1) as usize).expect("should have been present"); + match value { + &HeapItem::StackItem(si) => si, + _ => panic!("should have been a StackItem") + } + } else { + panic!("invalid ix: {}", ix) + } + } + + fn term_set_arg(&mut self, term: Term, ix: u32, arg: StackItem) { + let ty = self.term_get_type(term); + let td = self.program.type_get(ty); + + if ix < td.len { + let slot = self.data.get_mut((term.index + ix + 1) as usize).expect("should have been present"); + *slot = HeapItem::StackItem(arg) + } else { + panic!("invalid ix: {}", ix) + } + } } \ No newline at end of file