crocolisk/src/main.rs

486 lines
13 KiB
Rust
Raw Normal View History

2024-02-17 23:54:36 +00:00
use std::collections::HashMap;
2024-02-17 23:16:32 +00:00
struct VM<'p> {
program: &'p Program,
2024-02-17 23:54:36 +00:00
block: Block, ip: u32,
2024-02-17 23:16:32 +00:00
heap: Heap<'p>,
2024-02-17 23:18:29 +00:00
locals: Object,
2024-02-17 23:18:59 +00:00
stack: Vec<Value>,
2024-02-17 23:16:32 +00:00
status: Status
}
2024-02-17 23:16:32 +00:00
#[derive(Clone, Copy)]
enum Status {
Running,
2024-02-17 23:16:32 +00:00
AwaitSyscall(u32),
Crash(&'static str),
2024-02-17 23:16:32 +00:00
Exit,
}
#[derive(Clone, Copy, Debug)]
2024-02-17 23:18:59 +00:00
enum Value {
2024-02-17 23:18:29 +00:00
Object(Object),
2024-02-17 23:16:32 +00:00
Integer(u32),
}
2024-02-17 23:18:59 +00:00
impl Value {
2024-02-17 23:18:29 +00:00
fn object(self) -> Option<Object> {
2024-02-17 23:18:59 +00:00
if let Value::Object(t) = self { return Some(t) }
2024-02-17 23:16:32 +00:00
return None
}
fn integer(self) -> Option<u32> {
2024-02-17 23:18:59 +00:00
if let Value::Integer(u) = self { return Some(u) }
2024-02-17 23:16:32 +00:00
return None
}
}
2024-02-17 23:16:32 +00:00
#[derive(Clone, Copy, Debug)]
2024-02-17 23:18:29 +00:00
struct Object { index: u32 }
2024-02-17 23:16:32 +00:00
struct Program {
2024-02-17 23:54:36 +00:00
blocks: Interner<BlockData>,
types: Interner<TypeData>,
2024-02-17 23:16:32 +00:00
}
struct BlockData { instructions: Vec<Instruction> }
2024-02-17 23:54:36 +00:00
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
2024-02-17 23:16:32 +00:00
struct Block { index: u32 }
2024-02-17 23:16:32 +00:00
#[derive(Debug)]
enum Instruction {
2024-02-17 23:16:32 +00:00
// push NULL to stack
PushNull,
// push an integer to the stack
PushInteger(u32),
2024-02-17 23:18:29 +00:00
// push a new object to the stack with the given type
2024-02-17 23:16:32 +00:00
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
2024-02-17 23:54:36 +00:00
Call { block: Block, n: u32, },
2024-02-17 23:16:32 +00:00
// 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
}
2024-02-17 23:16:32 +00:00
#[derive(Debug)]
enum Op11 { Get(u32) }
#[derive(Debug)]
enum Op21 { Add, Set(u32) }
2024-02-17 23:54:36 +00:00
const BL_MAIN: Block = Block { index: 0x0 };
2024-02-17 23:16:32 +00:00
const TY_NULL: Type = Type { id: 0x0 };
2024-02-17 23:18:29 +00:00
const NULL: Object = Object { index: 0 };
2024-02-17 23:16:32 +00:00
const SYSCALL_DBG: u32 = 0x1000;
fn main() {
2024-02-17 23:54:36 +00:00
_main().unwrap()
}
fn _main() -> Result<(), String> {
2024-02-17 23:16:32 +00:00
let mut program = Program::new();
2024-02-17 23:54:36 +00:00
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,
2024-02-17 23:16:32 +00:00
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]
2024-02-17 23:54:36 +00:00
Instruction::Call { block: bl_main_1, n: 1 },
2024-02-17 23:16:32 +00:00
]
}
2024-02-17 23:54:36 +00:00
)?;
2024-02-17 23:16:32 +00:00
2024-02-17 23:54:36 +00:00
program.block_instantiate(
bl_main_1,
2024-02-17 23:16:32 +00:00
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)),
2024-02-17 23:54:36 +00:00
Instruction::Call { block: bl_main_2, n: 1 }
2024-02-17 23:16:32 +00:00
]
}
2024-02-17 23:54:36 +00:00
)?;
2024-02-17 23:16:32 +00:00
2024-02-17 23:54:36 +00:00
program.block_instantiate(
bl_main_2,
2024-02-17 23:16:32 +00:00
BlockData {
instructions: vec![
Instruction::PopLocals,
Instruction::PushLocals,
2024-02-17 23:54:36 +00:00
Instruction::Op11(Op11::Get(2)),
2024-02-17 23:16:32 +00:00
Instruction::Syscall { syscall: SYSCALL_DBG, n: 1 },
Instruction::Exit,
2024-02-17 23:16:32 +00:00
]
}
2024-02-17 23:54:36 +00:00
)?;
2024-02-17 23:16:32 +00:00
2024-02-17 23:54:36 +00:00
host_program(&program)
}
2024-02-17 23:16:32 +00:00
fn host_program(p: &Program) -> Result<(), String> {
let mut vm = VM::start(p);
loop {
let status = vm.step();
match status {
Status::Running => {}
2024-02-17 23:16:32 +00:00
Status::AwaitSyscall(ix) => {
match ix {
SYSCALL_DBG => {
2024-02-17 23:16:32 +00:00
let top = vm.stack.pop().ok_or("should be an item on the stack")?;
println!("debug print: {:08x?}", top);
vm.complete_syscall()
}
2024-02-17 23:16:32 +00:00
_ => { return Err(format!("unknown syscall: {}", ix)) }
}
}
Status::Crash(err) => { return Err(err.to_string()); }
2024-02-17 23:16:32 +00:00
Status::Exit => { return Ok(()); }
}
}
}
impl<'p> VM<'p> {
pub fn start(program: &'p Program) -> VM<'p> {
2024-02-17 23:18:29 +00:00
let heap = Heap::new(program);
2024-02-17 23:16:32 +00:00
VM {
program,
2024-02-17 23:54:36 +00:00
block: BL_MAIN,
ip: 0,
2024-02-17 23:16:32 +00:00
heap,
locals: NULL,
stack: vec![],
status: Status::Running,
}
}
2024-02-17 23:16:32 +00:00
pub fn step(&mut self) -> Status {
let error = match self.internal_step() {
2024-02-17 23:16:32 +00:00
Ok(_) => { return self.status; }
Err(e) => e
};
self.status = Status::Crash(error);
2024-02-17 23:16:32 +00:00
self.status
}
fn internal_step(&mut self) -> Result<(), &'static str> {
if let Status::Running = self.status { } else { return Ok(()); }
let instruction =
self.program
2024-02-17 23:54:36 +00:00
.block_get(self.block)?
.instructions.get(self.ip as usize).ok_or("instruction must exist")?;
self.ip += 1;
2024-02-17 23:16:32 +00:00
println!("instruction: {:?}", instruction);
match instruction {
2024-02-17 23:16:32 +00:00
&Instruction::PushNull => {
2024-02-17 23:18:59 +00:00
self.stack.push(Value::Object(NULL))
}
2024-02-17 23:16:32 +00:00
&Instruction::PushInteger(int) => {
2024-02-17 23:18:59 +00:00
self.stack.push(Value::Integer(int))
2024-02-17 23:16:32 +00:00
}
&Instruction::PushNew(ty) => {
2024-02-17 23:54:36 +00:00
let t = self.heap.object_create(ty)?;
2024-02-17 23:18:59 +00:00
self.stack.push(Value::Object(t))
2024-02-17 23:16:32 +00:00
}
Instruction::PushLocals => {
2024-02-17 23:18:59 +00:00
self.stack.push(Value::Object(self.locals))
2024-02-17 23:16:32 +00:00
}
Instruction::PopLocals => {
2024-02-17 23:18:29 +00:00
let t0 = self.stack.pop().ok_or("t0 must be present")?.object().ok_or("t0 must be a object")?;
2024-02-17 23:16:32 +00:00
self.locals = t0;
}
Instruction::Op11(op) => {
let t0 = self.stack.pop().ok_or("t0 must be present")?;
2024-02-17 23:16:32 +00:00
self.stack.push(op.perform(&mut self.heap, t0)?);
}
2024-02-17 23:16:32 +00:00
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.perform(&mut self.heap, t0, t1)?);
}
2024-02-17 23:16:32 +00:00
&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;
}
2024-02-17 23:16:32 +00:00
&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 => {
2024-02-17 23:16:32 +00:00
self.status = Status::Exit
}
}
Ok(())
}
2024-02-17 23:16:32 +00:00
pub fn complete_syscall(&mut self) {
match &self.status {
Status::AwaitSyscall(_) => {
self.status = Status::Running;
}, // continue
_ => {
self.status = Status::Crash("tried to complete a syscall while not awaiting one")
}
}
}
}
2024-02-17 23:16:32 +00:00
impl Op11 {
2024-02-17 23:18:59 +00:00
fn perform(&self, heap: &mut Heap, v0: Value) -> Result<Value, &'static str> {
2024-02-17 23:16:32 +00:00
match self {
&Op11::Get(ix) => {
2024-02-17 23:18:29 +00:00
let t0 = v0.object().ok_or("must be a object")?;
2024-02-17 23:54:36 +00:00
heap.object_get_arg(t0, ix)
2024-02-17 23:16:32 +00:00
}
}
}
}
impl Op21 {
2024-02-17 23:18:59 +00:00
fn perform(&self, heap: &mut Heap, v0: Value, v1: Value) -> Result<Value, &'static str> {
2024-02-17 23:16:32 +00:00
match self {
Op21::Add => {
let i0 = v0.integer().ok_or("must be an integer")?;
let i1 = v1.integer().ok_or("must be an integer")?;
2024-02-17 23:18:59 +00:00
Ok(Value::Integer(i0.wrapping_add(i1)))
2024-02-17 23:16:32 +00:00
}
&Op21::Set(ix) => {
2024-02-17 23:18:29 +00:00
let t0 = v0.object().ok_or("must be a object")?;
2024-02-17 23:54:36 +00:00
heap.object_set_arg(t0, ix, v1)?;
2024-02-17 23:16:32 +00:00
Ok(v0)
}
}
}
}
impl Program {
fn new() -> Program {
let mut p = Self {
2024-02-17 23:54:36 +00:00
blocks: Interner::<BlockData>::new(),
types: Interner::<TypeData>::new(),
2024-02-17 23:16:32 +00:00
};
2024-02-17 23:54:36 +00:00
let ty_null = p.type_prototype("null");
2024-02-17 23:16:32 +00:00
assert_eq!(TY_NULL, ty_null);
2024-02-17 23:54:36 +00:00
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);
2024-02-17 23:16:32 +00:00
return p
}
2024-02-17 23:54:36 +00:00
fn type_prototype(&mut self, name: &str) -> Type {
Type { id: self.types.prototype(name) }
2024-02-17 23:16:32 +00:00
}
2024-02-17 23:54:36 +00:00
fn type_instantiate(&mut self, ty: Type, len: u32) -> Result<(), &'static str> {
self.types.instantiate(ty.id, TypeData { len })
2024-02-17 23:16:32 +00:00
}
2024-02-17 23:54:36 +00:00
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)
2024-02-17 23:16:32 +00:00
}
}
struct Heap<'p> {
program: &'p Program,
data: Vec<HeapItem>
}
#[derive(Debug)]
enum HeapItem {
Type(Type),
2024-02-17 23:18:59 +00:00
Value(Value),
2024-02-17 23:16:32 +00:00
}
2024-02-17 23:16:32 +00:00
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Type {
id: u32
}
2024-02-17 23:16:32 +00:00
struct TypeData {
len: u32
}
impl<'p> Heap<'p> {
fn new(p: &'p Program) -> Self {
let mut h = Heap {
program: p,
data: vec![],
};
2024-02-17 23:54:36 +00:00
let null = h.object_create(TY_NULL).expect("shouldn't fail during Heap construction");
2024-02-17 23:16:32 +00:00
assert_eq!(NULL.index, null.index);
h
}
2024-02-17 23:54:36 +00:00
fn object_create(&mut self, ty: Type) -> Result<Object, &'static str> {
let td = self.program.type_get(ty)?;
2024-02-17 23:16:32 +00:00
let index = self.data.len() as u32;
self.data.push(HeapItem::Type(ty));
for _ in 0..td.len {
2024-02-17 23:18:59 +00:00
self.data.push(HeapItem::Value(Value::Object(NULL)));
2024-02-17 23:16:32 +00:00
}
2024-02-17 23:54:36 +00:00
Ok(Object { index })
2024-02-17 23:16:32 +00:00
}
2024-02-17 23:54:36 +00:00
fn object_get_type(&self, object: Object) -> Result<Type, &'static str> {
let header = self.data.get(object.index as usize).ok_or("object should have been valid")?;
2024-02-17 23:16:32 +00:00
if let &HeapItem::Type(ty) = header {
2024-02-17 23:54:36 +00:00
Ok(ty)
2024-02-17 23:16:32 +00:00
} else {
2024-02-17 23:54:36 +00:00
Err("header is invalid")
2024-02-17 23:16:32 +00:00
}
}
2024-02-17 23:54:36 +00:00
fn object_get_arg(&self, object: Object, ix: u32) -> Result<Value, &'static str> {
let ty = self.object_get_type(object)?;
let td = self.program.type_get(ty)?;
2024-02-17 23:16:32 +00:00
if ix < td.len {
2024-02-17 23:18:29 +00:00
let value = self.data.get((object.index + ix + 1) as usize).expect("should have been present");
2024-02-17 23:16:32 +00:00
match value {
2024-02-17 23:54:36 +00:00
&HeapItem::Value(si) => Ok(si),
_ => return Err("should have been a Value"),
2024-02-17 23:16:32 +00:00
}
} else {
2024-02-17 23:54:36 +00:00
return Err("invalid field index")
2024-02-17 23:16:32 +00:00
}
}
2024-02-17 23:54:36 +00:00
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)?;
2024-02-17 23:16:32 +00:00
if ix < td.len {
2024-02-17 23:18:29 +00:00
let slot = self.data.get_mut((object.index + ix + 1) as usize).expect("should have been present");
2024-02-17 23:54:36 +00:00
*slot = HeapItem::Value(arg);
Ok(())
2024-02-17 23:16:32 +00:00
} else {
2024-02-17 23:54:36 +00:00
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![],
2024-02-17 23:16:32 +00:00
}
}
2024-02-17 23:54:36 +00:00
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"),
}
}
}