Cleanup, interner
This commit is contained in:
parent
c1834805dd
commit
a2a89ccaf2
191
src/main.rs
191
src/main.rs
@ -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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user