diff --git a/src/main.rs b/src/main.rs index d49a190..6f3a637 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,8 @@ +use std::collections::HashMap; + struct VM<'p> { program: &'p Program, - block: u32, ip: u32, + block: Block, ip: u32, heap: Heap<'p>, locals: Object, stack: Vec, @@ -35,11 +37,12 @@ impl Value { #[derive(Clone, Copy, Debug)] struct Object { index: u32 } struct Program { - blocks: Vec, - types: Vec, + blocks: Interner, + types: Interner, } struct BlockData { instructions: Vec } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] struct Block { index: u32 } #[derive(Debug)] @@ -61,7 +64,7 @@ enum Instruction { Op21(Op21), // [t1, t2] -> [t3] // assert that the stack has n items and jump to the given block - Call { block: u32, n: u32, }, + Call { block: Block, n: u32, }, // assert that the stack has n items, clear it, and execute a syscall Syscall { syscall: u32, n: u32 }, @@ -78,6 +81,7 @@ enum Op11 { Get(u32) } #[derive(Debug)] enum Op21 { Add, Set(u32) } +const BL_MAIN: Block = Block { index: 0x0 }; const TY_NULL: Type = Type { id: 0x0 }; const NULL: Object = Object { index: 0 }; @@ -85,9 +89,19 @@ const SYSCALL_DBG: u32 = 0x1000; fn main() { + _main().unwrap() +} +fn _main() -> Result<(), String> { let mut program = Program::new(); - let ty_locals = program.type_create(3); - program.block_create( + let ty_locals = program.type_prototype("main.locals"); + program.type_instantiate(ty_locals, 3)?; + + let bl_main_0 = program.block_prototype("main"); + let bl_main_1 = program.block_prototype("main.1"); + let bl_main_2 = program.block_prototype("main.2"); + + program.block_instantiate( + bl_main_0, BlockData { instructions: vec![ Instruction::PushNew(ty_locals), Instruction::PopLocals, @@ -97,12 +111,13 @@ fn main() { Instruction::PushInteger(0x0000ba75), Instruction::Op21(Op21::Set(1)), // stack: [locals] - Instruction::Call { block: 1, n: 1 }, + Instruction::Call { block: bl_main_1, n: 1 }, ] } - ); + )?; - program.block_create( + program.block_instantiate( + bl_main_1, BlockData { instructions: vec![ Instruction::PopLocals, @@ -113,26 +128,27 @@ fn main() { Instruction::Op21(Op21::Add), Instruction::Op21(Op21::Set(2)), - Instruction::Call { block: 2, n: 1 } + Instruction::Call { block: bl_main_2, n: 1 } ] } - ); + )?; - program.block_create( + program.block_instantiate( + bl_main_2, BlockData { instructions: vec![ Instruction::PopLocals, Instruction::PushLocals, - Instruction::Op11(Op11::Get(0)), + Instruction::Op11(Op11::Get(2)), Instruction::Syscall { syscall: SYSCALL_DBG, n: 1 }, Instruction::Exit, ] } - ); + )?; - host_program(&program).unwrap() + host_program(&program) } fn host_program(p: &Program) -> Result<(), String> { @@ -164,7 +180,7 @@ impl<'p> VM<'p> { VM { program, - block: 0, + block: BL_MAIN, ip: 0, heap, locals: NULL, @@ -186,7 +202,7 @@ impl<'p> VM<'p> { let instruction = self.program - .blocks.get(self.block as usize).ok_or("block must exist")? + .block_get(self.block)? .instructions.get(self.ip as usize).ok_or("instruction must exist")?; self.ip += 1; println!("instruction: {:?}", instruction); @@ -201,7 +217,7 @@ impl<'p> VM<'p> { } &Instruction::PushNew(ty) => { - let t = self.heap.object_create(ty); + let t = self.heap.object_create(ty)?; self.stack.push(Value::Object(t)) } @@ -268,7 +284,7 @@ impl Op11 { match self { &Op11::Get(ix) => { let t0 = v0.object().ok_or("must be a object")?; - Ok(heap.object_get_arg(t0, ix)) + heap.object_get_arg(t0, ix) } } } @@ -285,7 +301,7 @@ impl Op21 { } &Op21::Set(ix) => { let t0 = v0.object().ok_or("must be a object")?; - heap.object_set_arg(t0, ix, v1); + heap.object_set_arg(t0, ix, v1)?; Ok(v0) } } @@ -296,28 +312,43 @@ impl Op21 { impl Program { fn new() -> Program { let mut p = Self { - blocks: vec![], - types: vec![], + blocks: Interner::::new(), + types: Interner::::new(), }; - let ty_null = p.type_create(0); + + let ty_null = p.type_prototype("null"); assert_eq!(TY_NULL, ty_null); + p.type_instantiate(ty_null, 0).expect("cannot fail to create null instance"); + + let bl_main = p.block_prototype("main"); + assert_eq!(BL_MAIN, bl_main); + + 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_prototype(&mut self, name: &str) -> Type { + Type { id: self.types.prototype(name) } } - fn type_get(&self, ty: Type) -> &TypeData { - return self.types.get(ty.id as usize).expect("type should have existed") + fn type_instantiate(&mut self, ty: Type, len: u32) -> Result<(), &'static str> { + self.types.instantiate(ty.id, TypeData { len }) } - fn block_create(&mut self, block: BlockData) -> Block { - let index = self.blocks.len(); - self.blocks.push(block); - Block { index: index as u32 } + fn type_get(&self, ty: Type) -> Result<&TypeData, &'static str> { + self.types.get(ty.id) + } + + fn block_prototype(&mut self, name: &str) -> Block { + Block { index: self.blocks.prototype(name) } + } + + fn block_instantiate(&mut self, bl: Block, data: BlockData) -> Result<(), &'static str> { + self.blocks.instantiate(bl.index, data) + } + + fn block_get(&self, bl: Block) -> Result<&BlockData, &'static str> { + self.blocks.get(bl.index) } } @@ -347,54 +378,108 @@ impl<'p> Heap<'p> { program: p, data: vec![], }; - let null = h.object_create(TY_NULL); + let null = h.object_create(TY_NULL).expect("shouldn't fail during Heap construction"); assert_eq!(NULL.index, null.index); h } - fn object_create(&mut self, ty: Type) -> Object { - let td = self.program.type_get(ty); + fn object_create(&mut self, ty: Type) -> Result { + 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::Value(Value::Object(NULL))); } - Object { index } + Ok(Object { index }) } - fn object_get_type(&self, object: Object) -> Type { - let header = self.data.get(object.index as usize).expect("object should have been valid"); + fn object_get_type(&self, object: Object) -> Result { + let header = self.data.get(object.index as usize).ok_or("object should have been valid")?; if let &HeapItem::Type(ty) = header { - ty + Ok(ty) } else { - panic!("header is invalid: {:?}", header) + Err("header is invalid") } } - fn object_get_arg(&self, object: Object, ix: u32) -> Value { - let ty = self.object_get_type(object); - let td = self.program.type_get(ty); + fn object_get_arg(&self, object: Object, ix: u32) -> Result { + let ty = self.object_get_type(object)?; + let td = self.program.type_get(ty)?; if ix < td.len { let value = self.data.get((object.index + ix + 1) as usize).expect("should have been present"); match value { - &HeapItem::Value(si) => si, - _ => panic!("should have been a Value") + &HeapItem::Value(si) => Ok(si), + _ => return Err("should have been a Value"), } } else { - panic!("invalid ix: {}", ix) + return Err("invalid field index") } } - fn object_set_arg(&mut self, object: Object, ix: u32, arg: Value) { - let ty = self.object_get_type(object); - let td = self.program.type_get(ty); + fn object_set_arg(&mut self, object: Object, ix: u32, arg: Value) -> Result<(), &'static str> { + let ty = self.object_get_type(object)?; + let td = self.program.type_get(ty)?; if ix < td.len { let slot = self.data.get_mut((object.index + ix + 1) as usize).expect("should have been present"); - *slot = HeapItem::Value(arg) + *slot = HeapItem::Value(arg); + Ok(()) } else { - panic!("invalid ix: {}", ix) + return Err("invalid field index") } } -} \ No newline at end of file +} + +struct Interner { + by_name: HashMap, + names: Vec, + values: Vec> +} + +impl Interner { + pub fn new() -> Self { + Self { + by_name: HashMap::new(), + names: vec![], + values: vec![], + } + } + + pub fn prototype(&mut self, name: &str) -> u32 { + if let Some(v) = self.by_name.get(name) { return *v; } + + let ix = self.values.len() as u32; + self.by_name.insert(name.to_string(), ix); + self.names.push(name.to_string()); + self.values.push(None); + ix + } + + pub fn instantiate(&mut self, handle: u32, value: T) -> Result<(), &'static str> { + let slot = self.values + .get_mut(handle as usize) + .ok_or("handle was not prototyped on this interner")?; + if slot.is_some() { + return Err("can't instantiate handle twice") + } + *slot = Some(value); + Ok(()) + } + + pub fn get(&self, handle: u32) -> Result<&T, &'static str> { + match self.values.get(handle as usize) { + Some(Some(h)) => Ok(h), + Some(_) => Err("handle was prototyped but not instantiated"), + _ => Err("handle was not prototyped on this interner"), + } + } + + pub fn get_mut(&mut self, handle: u32) -> Result<&mut T, &'static str> { + match self.values.get_mut(handle as usize) { + Some(Some(h)) => Ok(h), + Some(_) => Err("handle was prototyped but not instantiated"), + _ => Err("handle was not prototyped on this interner"), + } + } +}