Cleanup, interner

This commit is contained in:
Pyrex 2024-02-17 15:54:36 -08:00
parent c1834805dd
commit a2a89ccaf2

View File

@ -1,6 +1,8 @@
use std::collections::HashMap;
struct VM<'p> { struct VM<'p> {
program: &'p Program, program: &'p Program,
block: u32, ip: u32, block: Block, ip: u32,
heap: Heap<'p>, heap: Heap<'p>,
locals: Object, locals: Object,
stack: Vec<Value>, stack: Vec<Value>,
@ -35,11 +37,12 @@ impl Value {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
struct Object { index: u32 } struct Object { index: u32 }
struct Program { struct Program {
blocks: Vec<BlockData>, blocks: Interner<BlockData>,
types: Vec<TypeData>, types: Interner<TypeData>,
} }
struct BlockData { instructions: Vec<Instruction> } struct BlockData { instructions: Vec<Instruction> }
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
struct Block { index: u32 } struct Block { index: u32 }
#[derive(Debug)] #[derive(Debug)]
@ -61,7 +64,7 @@ enum Instruction {
Op21(Op21), // [t1, t2] -> [t3] Op21(Op21), // [t1, t2] -> [t3]
// assert that the stack has n items and jump to the given block // 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 // assert that the stack has n items, clear it, and execute a syscall
Syscall { syscall: u32, n: u32 }, Syscall { syscall: u32, n: u32 },
@ -78,6 +81,7 @@ enum Op11 { Get(u32) }
#[derive(Debug)] #[derive(Debug)]
enum Op21 { Add, Set(u32) } enum Op21 { Add, Set(u32) }
const BL_MAIN: Block = Block { index: 0x0 };
const TY_NULL: Type = Type { id: 0x0 }; const TY_NULL: Type = Type { id: 0x0 };
const NULL: Object = Object { index: 0 }; const NULL: Object = Object { index: 0 };
@ -85,9 +89,19 @@ const SYSCALL_DBG: u32 = 0x1000;
fn main() { fn main() {
_main().unwrap()
}
fn _main() -> Result<(), String> {
let mut program = Program::new(); let mut program = Program::new();
let ty_locals = program.type_create(3); let ty_locals = program.type_prototype("main.locals");
program.block_create( 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 { BlockData {
instructions: vec![ instructions: vec![
Instruction::PushNew(ty_locals), Instruction::PopLocals, Instruction::PushNew(ty_locals), Instruction::PopLocals,
@ -97,12 +111,13 @@ fn main() {
Instruction::PushInteger(0x0000ba75), Instruction::Op21(Op21::Set(1)), Instruction::PushInteger(0x0000ba75), Instruction::Op21(Op21::Set(1)),
// stack: [locals] // 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 { BlockData {
instructions: vec![ instructions: vec![
Instruction::PopLocals, Instruction::PopLocals,
@ -113,26 +128,27 @@ fn main() {
Instruction::Op21(Op21::Add), Instruction::Op21(Op21::Add),
Instruction::Op21(Op21::Set(2)), 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 { BlockData {
instructions: vec![ instructions: vec![
Instruction::PopLocals, Instruction::PopLocals,
Instruction::PushLocals, Instruction::PushLocals,
Instruction::Op11(Op11::Get(0)), Instruction::Op11(Op11::Get(2)),
Instruction::Syscall { syscall: SYSCALL_DBG, n: 1 }, Instruction::Syscall { syscall: SYSCALL_DBG, n: 1 },
Instruction::Exit, Instruction::Exit,
] ]
} }
); )?;
host_program(&program).unwrap() host_program(&program)
} }
fn host_program(p: &Program) -> Result<(), String> { fn host_program(p: &Program) -> Result<(), String> {
@ -164,7 +180,7 @@ impl<'p> VM<'p> {
VM { VM {
program, program,
block: 0, block: BL_MAIN,
ip: 0, ip: 0,
heap, heap,
locals: NULL, locals: NULL,
@ -186,7 +202,7 @@ impl<'p> VM<'p> {
let instruction = let instruction =
self.program 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")?; .instructions.get(self.ip as usize).ok_or("instruction must exist")?;
self.ip += 1; self.ip += 1;
println!("instruction: {:?}", instruction); println!("instruction: {:?}", instruction);
@ -201,7 +217,7 @@ impl<'p> VM<'p> {
} }
&Instruction::PushNew(ty) => { &Instruction::PushNew(ty) => {
let t = self.heap.object_create(ty); let t = self.heap.object_create(ty)?;
self.stack.push(Value::Object(t)) self.stack.push(Value::Object(t))
} }
@ -268,7 +284,7 @@ impl Op11 {
match self { match self {
&Op11::Get(ix) => { &Op11::Get(ix) => {
let t0 = v0.object().ok_or("must be a object")?; 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) => { &Op21::Set(ix) => {
let t0 = v0.object().ok_or("must be a object")?; 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) Ok(v0)
} }
} }
@ -296,28 +312,43 @@ impl Op21 {
impl Program { impl Program {
fn new() -> Program { fn new() -> Program {
let mut p = Self { let mut p = Self {
blocks: vec![], blocks: Interner::<BlockData>::new(),
types: vec![], types: Interner::<TypeData>::new(),
}; };
let ty_null = p.type_create(0);
let ty_null = p.type_prototype("null");
assert_eq!(TY_NULL, ty_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 return p
} }
fn type_create(&mut self, len: u32) -> Type { fn type_prototype(&mut self, name: &str) -> Type {
let index = self.types.len(); Type { id: self.types.prototype(name) }
self.types.push(TypeData { len });
Type { id: index as u32 }
} }
fn type_get(&self, ty: Type) -> &TypeData { fn type_instantiate(&mut self, ty: Type, len: u32) -> Result<(), &'static str> {
return self.types.get(ty.id as usize).expect("type should have existed") self.types.instantiate(ty.id, TypeData { len })
} }
fn block_create(&mut self, block: BlockData) -> Block { fn type_get(&self, ty: Type) -> Result<&TypeData, &'static str> {
let index = self.blocks.len(); self.types.get(ty.id)
self.blocks.push(block); }
Block { index: index as u32 }
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, program: p,
data: vec![], 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); assert_eq!(NULL.index, null.index);
h h
} }
fn object_create(&mut self, ty: Type) -> Object { fn object_create(&mut self, ty: Type) -> Result<Object, &'static str> {
let td = self.program.type_get(ty); let td = self.program.type_get(ty)?;
let index = self.data.len() as u32; let index = self.data.len() as u32;
self.data.push(HeapItem::Type(ty)); self.data.push(HeapItem::Type(ty));
for _ in 0..td.len { for _ in 0..td.len {
self.data.push(HeapItem::Value(Value::Object(NULL))); self.data.push(HeapItem::Value(Value::Object(NULL)));
} }
Object { index } Ok(Object { index })
} }
fn object_get_type(&self, object: Object) -> Type { fn object_get_type(&self, object: Object) -> Result<Type, &'static str> {
let header = self.data.get(object.index as usize).expect("object should have been valid"); let header = self.data.get(object.index as usize).ok_or("object should have been valid")?;
if let &HeapItem::Type(ty) = header { if let &HeapItem::Type(ty) = header {
ty Ok(ty)
} else { } else {
panic!("header is invalid: {:?}", header) Err("header is invalid")
} }
} }
fn object_get_arg(&self, object: Object, ix: u32) -> Value { fn object_get_arg(&self, object: Object, ix: u32) -> Result<Value, &'static str> {
let ty = self.object_get_type(object); let ty = self.object_get_type(object)?;
let td = self.program.type_get(ty); let td = self.program.type_get(ty)?;
if ix < td.len { if ix < td.len {
let value = self.data.get((object.index + ix + 1) as usize).expect("should have been present"); let value = self.data.get((object.index + ix + 1) as usize).expect("should have been present");
match value { match value {
&HeapItem::Value(si) => si, &HeapItem::Value(si) => Ok(si),
_ => panic!("should have been a Value") _ => return Err("should have been a Value"),
} }
} else { } else {
panic!("invalid ix: {}", ix) return Err("invalid field index")
} }
} }
fn object_set_arg(&mut self, object: Object, ix: u32, arg: Value) { fn object_set_arg(&mut self, object: Object, ix: u32, arg: Value) -> Result<(), &'static str> {
let ty = self.object_get_type(object); let ty = self.object_get_type(object)?;
let td = self.program.type_get(ty); let td = self.program.type_get(ty)?;
if ix < td.len { if ix < td.len {
let slot = self.data.get_mut((object.index + ix + 1) as usize).expect("should have been present"); 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 { } else {
panic!("invalid ix: {}", ix) return Err("invalid field index")
} }
} }
} }
struct Interner<T> {
by_name: HashMap<String, u32>,
names: Vec<String>,
values: Vec<Option<T>>
}
impl<T> Interner<T> {
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"),
}
}
}