Sets and gets and stuff

This commit is contained in:
Pyrex 2024-02-17 15:16:32 -08:00
parent 4bfea02287
commit bd11d756e4

View File

@ -1,72 +1,158 @@
struct VM<'p> { program: &'p Program, block: u32, ip: u32, stack: Vec<Term>, status: Status } struct VM<'p> {
program: &'p Program,
enum Status { block: u32, ip: u32,
Running, heap: Heap<'p>,
AwaitSyscall(Term), locals: Term,
Crash(&'static str), stack: Vec<StackItem>,
Exit(Term), status: Status
} }
#[derive(Clone, Debug)] #[derive(Clone, Copy)]
struct Term { atom: u32, args: Vec<Term>, } enum Status {
struct Program { blocks: Vec<Block> } Running,
struct Block { instructions: Vec<Instruction> } AwaitSyscall(u32),
Crash(&'static str),
Exit,
}
#[derive(Clone, Copy, Debug)]
enum StackItem {
Term(Term),
Integer(u32),
}
impl StackItem {
fn term(self) -> Option<Term> {
if let StackItem::Term(t) = self { return Some(t) }
return None
}
fn integer(self) -> Option<u32> {
if let StackItem::Integer(u) = self { return Some(u) }
return None
}
}
#[derive(Clone, Copy, Debug)]
struct Term { index: u32 }
struct Program {
blocks: Vec<BlockData>,
types: Vec<TypeData>,
}
struct BlockData { instructions: Vec<Instruction> }
struct Block { index: u32 }
#[derive(Debug)]
enum Instruction { enum Instruction {
// push Atom to stack, optionally with the current `stack` appended to it // push NULL to stack
Push { atom: u32, cc: bool }, PushNull,
// do a binary operation between the top two stack items // push an integer to the stack
BinOp { op: fn(Term, Term) -> Result<Term, &'static str> }, PushInteger(u32),
// expand the top stack item to the stack, failing if its len is not `len`
Expand { len: u32 }, // push a new term to the stack with the given type
// jump to the given block. its stack is the top stack item PushNew(Type),
Call { block: u32 },
// system call. pass the top stack item. get a stack item back // get the locals register
Syscall { syscall: u32 }, 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 // kill the program
Crash(&'static str), Crash(&'static str),
// end the program, popping the top stack item // end the program, popping the top stack item
Exit 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; const SYSCALL_DBG: u32 = 0x1000;
fn main() { fn main() {
host_program(&Program { let mut program = Program::new();
blocks: vec![ let ty_locals = program.type_create(3);
Block { instructions: vec![ program.block_create(
Instruction::Expand { len: 0 }, BlockData {
Instruction::Push { atom: SYSCALL_DBG, cc: false }, instructions: vec![
Instruction::Push { atom: 0xdead0000, cc: false }, Instruction::PushNew(ty_locals), Instruction::PopLocals,
Instruction::Push { atom: 0x0000ba75, cc: false },
Instruction::BinOp { op: binop_add }, Instruction::PushLocals,
Instruction::BinOp { op: binop_append }, Instruction::PushInteger(0xdead0000), Instruction::Op21(Op21::Set(0)),
Instruction::Syscall { syscall: SYSCALL_DBG }, Instruction::PushInteger(0x0000ba75), Instruction::Op21(Op21::Set(1)),
Instruction::Push { atom: 0x0, cc: false }, // stack: [locals]
Instruction::Exit,
]} Instruction::Call { block: 1, n: 1 },
] ]
}).unwrap(); }
);
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,
]
}
);
host_program(&program).unwrap()
} }
fn host_program(p: &Program) -> Result<Term, String> { fn host_program(p: &Program) -> Result<(), String> {
let mut vm = VM::start(p); let mut vm = VM::start(p);
loop { loop {
let status = vm.step(); let status = vm.step();
match status { match status {
Status::Running => {} Status::Running => {}
Status::AwaitSyscall(sc) => { Status::AwaitSyscall(ix) => {
match sc.atom { match ix {
SYSCALL_DBG => { SYSCALL_DBG => {
println!("debug print: {:08x?}", sc.args[0].atom); let top = vm.stack.pop().ok_or("should be an item on the stack")?;
vm.complete_syscall(None) 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::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<Term, String> {
impl<'p> VM<'p> { impl<'p> VM<'p> {
pub fn start(program: &'p Program) -> VM<'p> { pub fn start(program: &'p Program) -> VM<'p> {
let mut heap = Heap::new(program);
VM { VM {
program, program,
block: 0, block: 0,
ip: 0, ip: 0,
stack: vec![Term { atom: 0, args: vec![] }], heap,
locals: NULL,
stack: vec![],
status: Status::Running, status: Status::Running,
} }
} }
pub fn step<'a>(&'a mut self) -> &'a Status { pub fn step(&mut self) -> Status {
let error = match self.internal_step() { let error = match self.internal_step() {
Ok(_) => { return &self.status; } Ok(_) => { return self.status; }
Err(e) => e Err(e) => e
}; };
self.status = Status::Crash(error); self.status = Status::Crash(error);
return &self.status self.status
} }
fn internal_step(&mut self) -> Result<(), &'static str> { fn internal_step(&mut self) -> Result<(), &'static str> {
if let Status::Running = self.status { } else { return Ok(()); } 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")? .blocks.get(self.block as usize).ok_or("block must exist")?
.instructions.get(self.ip as usize).ok_or("instruction must exist")?; .instructions.get(self.ip as usize).ok_or("instruction must exist")?;
self.ip += 1; self.ip += 1;
println!("instruction: {:?}", instruction);
match instruction { match instruction {
&Instruction::Push { atom, cc } => { &Instruction::PushNull => {
let mut term = Term { atom, args: vec![] }; self.stack.push(StackItem::Term(NULL))
if cc { term.args.extend(self.stack.drain(..)) }
self.stack.push(term)
} }
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 t1 = self.stack.pop().ok_or("t1 must be present")?;
let t0 = self.stack.pop().ok_or("t0 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")?; &Instruction::Call { block, n } => {
if term.args.len() != len as usize { panic!("term must have {} args", len) } if self.stack.len() != n as usize {
return Err("stack has wrong number of elements");
} }
&Instruction::Call { block } => { self.locals = NULL;
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);
self.block = block; self.block = block;
self.ip = 0; self.ip = 0;
} }
&Instruction::Syscall { syscall } => { &Instruction::Syscall { syscall, n } => {
let invocation = self.stack.pop().ok_or("invocation must be present")?; if self.stack.len() != n as usize {
if invocation.atom != syscall { panic!("invocation doesn't match syscall"); } return Err("stack has wrong number of elements");
self.status = Status::AwaitSyscall(invocation) }
self.status = Status::AwaitSyscall(syscall)
} }
Instruction::Crash(c) => { Instruction::Crash(c) => {
panic!("crashed: {}", c) panic!("crashed: {}", c)
} }
Instruction::Exit => { Instruction::Exit => {
let exit = self.stack.pop().ok_or("exit arg must be present")?; self.status = Status::Exit
self.status = Status::Exit(exit)
} }
} }
Ok(()) Ok(())
} }
pub fn complete_syscall(&mut self, result: Option<Term>) { pub fn complete_syscall(&mut self) {
match &self.status { match &self.status {
Status::AwaitSyscall(_) => { Status::AwaitSyscall(_) => {
if let Some(t) = result { self.stack.push(t); }
self.status = Status::Running; self.status = Status::Running;
}, // continue }, // continue
_ => { _ => {
@ -155,12 +263,138 @@ impl<'p> VM<'p> {
} }
} }
impl Op11 {
fn binop_append(mut t0: Term, mut t1: Term) -> Result<Term, &'static str> { fn perform(&self, heap: &mut Heap, v0: StackItem) -> Result<StackItem, &'static str> {
t0.args.push(t1); Ok(t0) match self {
&Op11::Get(ix) => {
let t0 = v0.term().ok_or("must be a term")?;
Ok(heap.term_get_arg(t0, ix))
}
}
} }
fn binop_add(mut t0: Term, mut t1: Term) -> Result<Term, &'static str> { }
t0.atom += t1.atom;
Ok(t0) impl Op21 {
fn perform(&self, heap: &mut Heap, v0: StackItem, v1: StackItem) -> Result<StackItem, &'static str> {
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<HeapItem>
}
#[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)
}
}
} }