Most basic programming language features
This commit is contained in:
commit
4bfea02287
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "crocolisk"
|
||||
version = "0.1.0"
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "crocolisk"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
166
src/main.rs
Normal file
166
src/main.rs
Normal file
@ -0,0 +1,166 @@
|
||||
struct VM<'p> { program: &'p Program, block: u32, ip: u32, stack: Vec<Term>, status: Status }
|
||||
|
||||
enum Status {
|
||||
Running,
|
||||
AwaitSyscall(Term),
|
||||
Crash(&'static str),
|
||||
Exit(Term),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Term { atom: u32, args: Vec<Term>, }
|
||||
struct Program { blocks: Vec<Block> }
|
||||
struct Block { instructions: Vec<Instruction> }
|
||||
|
||||
enum Instruction {
|
||||
// push Atom to stack, optionally with the current `stack` appended to it
|
||||
Push { atom: u32, cc: bool },
|
||||
// do a binary operation between the top two stack items
|
||||
BinOp { op: fn(Term, Term) -> Result<Term, &'static str> },
|
||||
// expand the top stack item to the stack, failing if its len is not `len`
|
||||
Expand { len: u32 },
|
||||
// jump to the given block. its stack is the top stack item
|
||||
Call { block: u32 },
|
||||
// system call. pass the top stack item. get a stack item back
|
||||
Syscall { syscall: u32 },
|
||||
// kill the program
|
||||
Crash(&'static str),
|
||||
// end the program, popping the top stack item
|
||||
Exit
|
||||
}
|
||||
|
||||
const SYSCALL_DBG: u32 = 0x1000;
|
||||
|
||||
|
||||
fn main() {
|
||||
host_program(&Program {
|
||||
blocks: vec![
|
||||
Block { instructions: vec![
|
||||
Instruction::Expand { len: 0 },
|
||||
Instruction::Push { atom: SYSCALL_DBG, cc: false },
|
||||
Instruction::Push { atom: 0xdead0000, cc: false },
|
||||
Instruction::Push { atom: 0x0000ba75, cc: false },
|
||||
Instruction::BinOp { op: binop_add },
|
||||
Instruction::BinOp { op: binop_append },
|
||||
Instruction::Syscall { syscall: SYSCALL_DBG },
|
||||
Instruction::Push { atom: 0x0, cc: false },
|
||||
Instruction::Exit,
|
||||
]}
|
||||
]
|
||||
}).unwrap();
|
||||
}
|
||||
|
||||
fn host_program(p: &Program) -> Result<Term, String> {
|
||||
let mut vm = VM::start(p);
|
||||
loop {
|
||||
let status = vm.step();
|
||||
match status {
|
||||
Status::Running => {}
|
||||
Status::AwaitSyscall(sc) => {
|
||||
match sc.atom {
|
||||
SYSCALL_DBG => {
|
||||
println!("debug print: {:08x?}", sc.args[0].atom);
|
||||
vm.complete_syscall(None)
|
||||
}
|
||||
_ => { return Err(format!("unknown syscall: {}", sc.atom)) }
|
||||
}
|
||||
}
|
||||
Status::Crash(err) => { return Err(err.to_string()); }
|
||||
Status::Exit(t) => { return Ok(t.clone()); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'p> VM<'p> {
|
||||
pub fn start(program: &'p Program) -> VM<'p> {
|
||||
VM {
|
||||
program,
|
||||
block: 0,
|
||||
ip: 0,
|
||||
stack: vec![Term { atom: 0, args: vec![] }],
|
||||
status: Status::Running,
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step<'a>(&'a mut self) -> &'a Status {
|
||||
let error = match self.internal_step() {
|
||||
Ok(_) => { return &self.status; }
|
||||
Err(e) => e
|
||||
};
|
||||
self.status = Status::Crash(error);
|
||||
return &self.status
|
||||
}
|
||||
fn internal_step(&mut self) -> Result<(), &'static str> {
|
||||
if let Status::Running = self.status { } else { return Ok(()); }
|
||||
|
||||
let instruction =
|
||||
self.program
|
||||
.blocks.get(self.block as usize).ok_or("block must exist")?
|
||||
.instructions.get(self.ip as usize).ok_or("instruction must exist")?;
|
||||
self.ip += 1;
|
||||
|
||||
match instruction {
|
||||
&Instruction::Push { atom, cc } => {
|
||||
let mut term = Term { atom, args: vec![] };
|
||||
if cc { term.args.extend(self.stack.drain(..)) }
|
||||
self.stack.push(term)
|
||||
}
|
||||
Instruction::BinOp { 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(t0, t1)?);
|
||||
}
|
||||
&Instruction::Expand { len } => {
|
||||
let term = self.stack.pop().ok_or("term must be present")?;
|
||||
if term.args.len() != len as usize { panic!("term must have {} args", len) }
|
||||
}
|
||||
&Instruction::Call { block } => {
|
||||
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.ip = 0;
|
||||
|
||||
}
|
||||
&Instruction::Syscall { syscall } => {
|
||||
let invocation = self.stack.pop().ok_or("invocation must be present")?;
|
||||
if invocation.atom != syscall { panic!("invocation doesn't match syscall"); }
|
||||
self.status = Status::AwaitSyscall(invocation)
|
||||
}
|
||||
Instruction::Crash(c) => {
|
||||
panic!("crashed: {}", c)
|
||||
}
|
||||
Instruction::Exit => {
|
||||
let exit = self.stack.pop().ok_or("exit arg must be present")?;
|
||||
self.status = Status::Exit(exit)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn complete_syscall(&mut self, result: Option<Term>) {
|
||||
match &self.status {
|
||||
Status::AwaitSyscall(_) => {
|
||||
if let Some(t) = result { self.stack.push(t); }
|
||||
self.status = Status::Running;
|
||||
}, // continue
|
||||
_ => {
|
||||
self.status = Status::Crash("tried to complete a syscall while not awaiting one")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn binop_append(mut t0: Term, mut t1: Term) -> Result<Term, &'static str> {
|
||||
t0.args.push(t1); Ok(t0)
|
||||
}
|
||||
|
||||
fn binop_add(mut t0: Term, mut t1: Term) -> Result<Term, &'static str> {
|
||||
t0.atom += t1.atom;
|
||||
Ok(t0)
|
||||
}
|
Loading…
Reference in New Issue
Block a user