From 339541a5688c764b7baa4aa9cf0a252e5bc4365e Mon Sep 17 00:00:00 2001 From: jiyinyiyong Date: Sat, 18 Sep 2021 15:58:48 +0800 Subject: [PATCH 1/4] add CalxError --- README.md | 1 - src/bin/calx.rs | 14 ++--- src/lib.rs | 2 +- src/primes.rs | 28 ++++++++-- src/vm.rs | 134 +++++++++++++++++++++++++++++------------------- 5 files changed, 114 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index f58becd..f30b95f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ For binary op, top value puts on right. | Code | Usage | Note | | -------------------- | ------------------------------------------------------- | ----------------- | -| `local` | new local variable | (not ready) | | `local.set $idx` | set value at `$idx` | | | `local.tee $idx` | set value at `$idx`, and also load it | | | `local.get $idx` | get value at `$idx` load on stack | | diff --git a/src/bin/calx.rs b/src/bin/calx.rs index 0db1577..152e10a 100644 --- a/src/bin/calx.rs +++ b/src/bin/calx.rs @@ -5,7 +5,7 @@ use std::time::Instant; use cirru_parser::{parse, Cirru}; use clap::{App, Arg}; -use calx_vm::{parse_function, Calx, CalxFunc, CalxImportsDict, CalxVM}; +use calx_vm::{parse_function, Calx, CalxError, CalxFunc, CalxImportsDict, CalxVM}; fn main() -> Result<(), String> { let matches = App::new("Calx VM") @@ -17,7 +17,7 @@ fn main() -> Result<(), String> { .short('S') .long("show-code") .value_name("show-code") - .about("Sets a custom config file") + .about("display processed instructions of functions") .takes_value(false), ) .arg( @@ -41,12 +41,14 @@ fn main() -> Result<(), String> { let f = parse_function(&ys)?; fns.push(f); } else { - panic!("TODO"); + panic!("expected top level expressions"); } } let mut imports: CalxImportsDict = HashMap::new(); - imports.insert(String::from("log2"), (log2_calx_value, 2)); + imports.insert(String::from("log"), (log_calx_value, 1)); + imports.insert(String::from("log2"), (log_calx_value, 2)); + imports.insert(String::from("log3"), (log_calx_value, 3)); let mut vm = CalxVM::new(fns, vec![], imports); if show_code { @@ -66,7 +68,7 @@ fn main() -> Result<(), String> { Err(e) => { println!("VM state: {:?}", vm.stack); println!("{}", e); - Err(String::from("Failed to run")) + Err(String::from("Failed to run.")) } } } else { @@ -74,7 +76,7 @@ fn main() -> Result<(), String> { } } -fn log2_calx_value(xs: Vec) -> Result { +fn log_calx_value(xs: Vec) -> Result { println!("log: {:?}", xs); Ok(Calx::Nil) } diff --git a/src/lib.rs b/src/lib.rs index 87160f5..e303aba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,5 +3,5 @@ mod primes; mod vm; pub use parser::parse_function; -pub use primes::{Calx, CalxFrame, CalxFunc}; +pub use primes::{Calx, CalxError, CalxFrame, CalxFunc}; pub use vm::{CalxImportsDict, CalxVM}; diff --git a/src/primes.rs b/src/primes.rs index 47e62d5..beefbc0 100644 --- a/src/primes.rs +++ b/src/primes.rs @@ -155,8 +155,28 @@ pub enum CalxInstr { #[derive(Debug, Clone, PartialEq, PartialOrd)] pub struct CalxError { pub message: String, - pub instrs: Vec, - pub recent_stack: Vec, // maybe partial of stack + pub stack: Vec, + pub top_frame: CalxFrame, + pub blocks: Vec, + pub globals: Vec, +} + +impl fmt::Display for CalxError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}\n{:?}\n{}", self.message, self.stack, self.top_frame) + } +} + +impl CalxError { + pub fn new_raw(s: String) -> Self { + CalxError { + message: s, + stack: vec![], + top_frame: CalxFrame::new_empty(), + blocks: vec![], + globals: vec![], + } + } } #[derive(Debug, Clone, PartialEq, PartialOrd)] @@ -194,11 +214,11 @@ impl CalxFrame { impl fmt::Display for CalxFrame { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("CalxFrame ")?; - write!(f, "@{} (", self.initial_stack_size)?; + write!(f, "_{} (", self.initial_stack_size)?; for p in &self.ret_types { write!(f, "{:?} ", p)?; } - f.write_str(")")?; + write!(f, ") @{}", self.pointer)?; for (idx, instr) in self.instrs.iter().enumerate() { write!(f, "\n {:02} {:?}", idx, instr)?; } diff --git a/src/vm.rs b/src/vm.rs index 45a90f0..408f26d 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,9 +1,9 @@ -use crate::primes::{BlockData, Calx, CalxFrame, CalxFunc, CalxInstr}; +use crate::primes::{BlockData, Calx, CalxError, CalxFrame, CalxFunc, CalxInstr}; use std::collections::hash_map::HashMap; use std::fmt; use std::ops::Rem; -pub type CalxImportsDict = HashMap) -> Result, usize)>; +pub type CalxImportsDict = HashMap) -> Result, usize)>; #[derive(Clone)] pub struct CalxVM { @@ -42,7 +42,7 @@ impl CalxVM { } } - pub fn run(&mut self) -> Result<(), String> { + pub fn run(&mut self) -> Result<(), CalxError> { loop { // println!("Stack {:?}", self.stack); // println!("-- op {} {:?}", self.stack.len(), instr); @@ -95,10 +95,10 @@ impl CalxVM { ret_types, } => { if self.stack.len() < params_types.len() { - return Err(format!( + return Err(self.gen_err(format!( "no enough data on stack {:?} for {:?}", self.stack, params_types - )); + ))); } self.top_frame.blocks_track.push(BlockData { looped, @@ -112,24 +112,24 @@ impl CalxVM { CalxInstr::BlockEnd => { let last_block = self.top_frame.blocks_track.pop().unwrap(); if self.stack.len() != last_block.initial_stack_size + last_block.ret_types.len() { - return Err(format!( + return Err(self.gen_err(format!( "block-end {:?} expected initial size {} plus {:?}, got stack size {}, in\n {}", last_block, last_block.initial_stack_size, last_block.ret_types, self.stack.len(), self.top_frame - )); + ))); } } CalxInstr::Local => self.top_frame.locals.push(Calx::Nil), CalxInstr::LocalSet(idx) => { let v = self.stack_pop()?; if idx >= self.top_frame.locals.len() { - return Err(format!( + return Err(self.gen_err(format!( "out of bound in local.set {} for {:?}", idx, self.top_frame.locals - )); + ))); } else { self.top_frame.locals[idx] = v } @@ -137,7 +137,7 @@ impl CalxVM { CalxInstr::LocalTee(idx) => { let v = self.stack_pop()?; if idx >= self.top_frame.locals.len() { - return Err(format!("out of bound in local.tee {}", idx)); + return Err(self.gen_err(format!("out of bound in local.tee {}", idx))); } else { self.top_frame.locals[idx] = v.to_owned() } @@ -147,7 +147,7 @@ impl CalxVM { if idx < self.top_frame.locals.len() { self.stack_push(self.top_frame.locals[idx].to_owned()) } else { - return Err(format!("invalid index for local.get {}", idx)); + return Err(self.gen_err(format!("invalid index for local.get {}", idx))); } } CalxInstr::Return => { @@ -163,7 +163,7 @@ impl CalxVM { CalxInstr::GlobalSet(idx) => { let v = self.stack_pop()?; if self.globals.len() >= idx { - return Err(format!("out of bound in global.set {}", idx)); + return Err(self.gen_err(format!("out of bound in global.set {}", idx))); } else { self.globals[idx] = v } @@ -172,7 +172,7 @@ impl CalxVM { if idx < self.globals.len() { self.stack_push(self.globals[idx].to_owned()) } else { - return Err(format!("invalid index for global.get {}", idx)); + return Err(self.gen_err(format!("invalid index for global.get {}", idx))); } } CalxInstr::GlobalNew => self.globals.push(Calx::Nil), @@ -190,7 +190,9 @@ impl CalxVM { let v1 = self.stack_pop()?; match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64(n1 + n2)), - (_, _) => return Err(format!("expected 2 integers to add, {:?} {:?}", v1, v2)), + (_, _) => { + return Err(self.gen_err(format!("expected 2 integers to add, {:?} {:?}", v1, v2))) + } } } CalxInstr::IntMul => { @@ -200,10 +202,10 @@ impl CalxVM { match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64(n1 * n2)), (_, _) => { - return Err(format!( + return Err(self.gen_err(format!( "expected 2 integers to multiply, {:?} {:?}", v1, v2 - )) + ))) } } } @@ -213,7 +215,9 @@ impl CalxVM { let v1 = self.stack_pop()?; match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64(n1 / n2)), - (_, _) => return Err(format!("expected 2 integers to divide, {:?} {:?}", v1, v2)), + (_, _) => { + return Err(self.gen_err(format!("expected 2 integers to divide, {:?} {:?}", v1, v2))) + } } } CalxInstr::IntRem => { @@ -222,7 +226,9 @@ impl CalxVM { let v1 = self.stack_pop()?; match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64((*n1).rem(n2))), - (_, _) => return Err(format!("expected 2 integers to add, {:?} {:?}", v1, v2)), + (_, _) => { + return Err(self.gen_err(format!("expected 2 integers to add, {:?} {:?}", v1, v2))) + } } } CalxInstr::IntNeg => { @@ -230,7 +236,7 @@ impl CalxVM { if let Calx::I64(n) = v { self.stack_push(Calx::I64(-n)) } else { - return Err(format!("expected int, got {}", v)); + return Err(self.gen_err(format!("expected int, got {}", v))); } } CalxInstr::IntShr => { @@ -240,7 +246,9 @@ impl CalxVM { (Calx::I64(n), Calx::I64(b)) => { self.stack_push(Calx::I64(n.checked_shr(*b as u32).unwrap())) } - (_, _) => return Err(format!("invalid number for SHR, {:?} {:?}", v, bits)), + (_, _) => { + return Err(self.gen_err(format!("invalid number for SHR, {:?} {:?}", v, bits))) + } } } CalxInstr::IntShl => { @@ -250,7 +258,9 @@ impl CalxVM { (Calx::I64(n), Calx::I64(b)) => { self.stack_push(Calx::I64(n.checked_shl(*b as u32).unwrap())) } - (_, _) => return Err(format!("invalid number for SHL, {:?} {:?}", v, bits)), + (_, _) => { + return Err(self.gen_err(format!("invalid number for SHL, {:?} {:?}", v, bits))) + } } } CalxInstr::IntEq => { @@ -260,10 +270,10 @@ impl CalxVM { match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 == n2)), (_, _) => { - return Err(format!( + return Err(self.gen_err(format!( "expected 2 integers to eq compare, {:?} {:?}", v1, v2 - )) + ))) } } } @@ -275,10 +285,10 @@ impl CalxVM { match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 != n2)), (_, _) => { - return Err(format!( + return Err(self.gen_err(format!( "expected 2 integers to ne compare, {:?} {:?}", v1, v2 - )) + ))) } } } @@ -289,10 +299,10 @@ impl CalxVM { match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 < n2)), (_, _) => { - return Err(format!( + return Err(self.gen_err(format!( "expected 2 integers to le compare, {:?} {:?}", v1, v2 - )) + ))) } } } @@ -303,10 +313,10 @@ impl CalxVM { match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 <= n2)), (_, _) => { - return Err(format!( + return Err(self.gen_err(format!( "expected 2 integers to le compare, {:?} {:?}", v1, v2 - )) + ))) } } } @@ -317,10 +327,10 @@ impl CalxVM { match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 > n2)), (_, _) => { - return Err(format!( + return Err(self.gen_err(format!( "expected 2 integers to gt compare, {:?} {:?}", v1, v2 - )) + ))) } } } @@ -331,10 +341,10 @@ impl CalxVM { match (&v1, &v2) { (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 >= n2)), (_, _) => { - return Err(format!( + return Err(self.gen_err(format!( "expected 2 integers to ge compare, {:?} {:?}", v1, v2 - )) + ))) } } } @@ -344,7 +354,9 @@ impl CalxVM { let v1 = self.stack_pop()?; match (&v1, &v2) { (Calx::F64(n1), Calx::F64(n2)) => self.stack_push(Calx::F64(n1 + n2)), - (_, _) => return Err(format!("expected 2 numbers to +, {:?} {:?}", v1, v2)), + (_, _) => { + return Err(self.gen_err(format!("expected 2 numbers to +, {:?} {:?}", v1, v2))) + } } } CalxInstr::Mul => { @@ -353,7 +365,11 @@ impl CalxVM { let v1 = self.stack_pop()?; match (&v1, &v2) { (Calx::F64(n1), Calx::F64(n2)) => self.stack_push(Calx::F64(n1 * n2)), - (_, _) => return Err(format!("expected 2 numbers to multiply, {:?} {:?}", v1, v2)), + (_, _) => { + return Err( + self.gen_err(format!("expected 2 numbers to multiply, {:?} {:?}", v1, v2)), + ) + } } } CalxInstr::Div => { @@ -362,7 +378,9 @@ impl CalxVM { let v1 = self.stack_pop()?; match (&v1, &v2) { (Calx::F64(n1), Calx::F64(n2)) => self.stack_push(Calx::F64(n1 / n2)), - (_, _) => return Err(format!("expected 2 numbers to divide, {:?} {:?}", v1, v2)), + (_, _) => { + return Err(self.gen_err(format!("expected 2 numbers to divide, {:?} {:?}", v1, v2))) + } } } CalxInstr::Neg => { @@ -370,7 +388,7 @@ impl CalxVM { if let Calx::F64(n) = v { self.stack_push(Calx::F64(-n)) } else { - return Err(format!("expected float, got {}", v)); + return Err(self.gen_err(format!("expected float, got {}", v))); } } CalxInstr::NewList => { @@ -415,19 +433,19 @@ impl CalxVM { // start in new frame continue; } - None => return Err(format!("cannot find function named: {}", f_name)), + None => return Err(self.gen_err(format!("cannot find function named: {}", f_name))), } } CalxInstr::CallImport(f_name) => match self.imports.to_owned().get(&f_name) { - None => return Err(format!("missing imported function {}", f_name)), + None => return Err(self.gen_err(format!("missing imported function {}", f_name))), Some((f, size)) => { if self.stack.len() < *size { - return Err(format!( + return Err(self.gen_err(format!( "imported function {} expected {} arguemtns, found {} on stack", f_name, size, self.stack.len() - )); + ))); } let mut args: Vec = vec![]; for _ in 0..*size { @@ -455,25 +473,25 @@ impl CalxVM { } #[inline(always)] - fn check_func_return(&mut self) -> Result<(), String> { + fn check_func_return(&mut self) -> Result<(), CalxError> { if self.stack.len() != self.top_frame.initial_stack_size + self.top_frame.ret_types.len() { - return Err(format!( + return Err(self.gen_err(format!( "stack size {} does not fit initial size {} plus {:?}", self.stack.len(), self.top_frame.initial_stack_size, self.top_frame.ret_types - )); + ))); } Ok(()) } #[inline(always)] - fn stack_pop(&mut self) -> Result { + fn stack_pop(&mut self) -> Result { if self.stack.is_empty() { - Err(String::from("cannot pop from empty stack")) + Err(self.gen_err(String::from("cannot pop from empty stack"))) } else if self.stack.len() <= self.top_frame.initial_stack_size { - Err(String::from("cannot pop from parent stack")) + Err(self.gen_err(String::from("cannot pop from parent stack"))) } else { let v = self.stack.pop().unwrap(); Ok(v) @@ -486,11 +504,11 @@ impl CalxVM { } #[inline(always)] - fn stack_peek(&mut self) -> Result { + fn stack_peek(&mut self) -> Result { if self.stack.is_empty() { - Err(String::from("cannot peek empty stack")) + Err(self.gen_err(String::from("cannot peek empty stack"))) } else if self.stack.len() <= self.top_frame.initial_stack_size { - Err(String::from("cannot peek parent stack")) + Err(self.gen_err(String::from("cannot peek parent stack"))) } else { Ok(self.stack.last().unwrap().to_owned()) } @@ -498,13 +516,13 @@ impl CalxVM { /// assumed that the size already checked #[inline(always)] - fn shrink_blocks_by(&mut self, size: usize) -> Result<(), String> { + fn shrink_blocks_by(&mut self, size: usize) -> Result<(), CalxError> { if self.top_frame.blocks_track.len() <= size { - return Err(format!( + return Err(self.gen_err(format!( "stack size {} eq/smaller than br size {}", self.top_frame.blocks_track.len(), size - )); + ))); } let mut i = size; @@ -515,6 +533,16 @@ impl CalxVM { Ok(()) } + + fn gen_err(&mut self, s: String) -> CalxError { + CalxError { + message: s, + blocks: self.top_frame.blocks_track.to_owned(), + top_frame: self.top_frame.to_owned(), + stack: self.stack.to_owned(), + globals: self.globals.to_owned(), + } + } } pub fn find_func(funcs: &[CalxFunc], name: &str) -> Option { From 98dd77a2ce9f115a6c8b486f044b2d05d0fd4175 Mon Sep 17 00:00:00 2001 From: jiyinyiyong Date: Sat, 18 Sep 2021 17:02:56 +0800 Subject: [PATCH 2/4] new instruction: assert --- .github/workflows/publish.yaml | 6 +++++- .github/workflows/test.yaml | 1 + README.md | 1 + examples/assert.cirru | 8 ++++++++ src/parser.rs | 13 +++++++++++++ src/primes.rs | 13 ++++++++----- src/vm.rs | 8 ++++++++ 7 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 examples/assert.cirru diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index ff3c49c..1c03110 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -14,7 +14,11 @@ jobs: with: toolchain: stable - - run: cargo test + - run: cargo run examples/hello.cirru + - run: cargo run examples/sum.cirru + - run: cargo run examples/assert.cirru + + # - run: cargo test - uses: katyo/publish-crates@v1 with: diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 19d0bf5..23e60a6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -18,3 +18,4 @@ jobs: - run: cargo run examples/hello.cirru - run: cargo run examples/sum.cirru + - run: cargo run examples/assert.cirru diff --git a/README.md b/README.md index f30b95f..f6b9882 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ For binary op, top value puts on right. | `quit $code` | quit program and return exit code `$code` | | | `return` | | TODO | | `fn $types $body` | | Global definition | +| `assert` | `quit(1)` if not `true` | for testing | For `$types`, it can be `($t1 $t2 -> $t3 $t4)`, where supported types are: diff --git a/examples/assert.cirru b/examples/assert.cirru new file mode 100644 index 0000000..e7ae44e --- /dev/null +++ b/examples/assert.cirru @@ -0,0 +1,8 @@ + +fn main () + const 1 + const 1 + i.add + const 2 + i.eq + assert "should be have 2" diff --git a/src/parser.rs b/src/parser.rs index ae9d218..c2bf4ba 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -266,6 +266,19 @@ pub fn parse_instr(ptr_base: usize, node: &Cirru) -> Result, Stri Ok(vec![CalxInstr::Quit(idx)]) } "return" => Ok(vec![CalxInstr::Return]), + + "assert" => { + let message: String; + if xs.len() != 2 { + return Err(format!("assert expected an extra message, {:?}", xs)); + } + match &xs[1] { + Cirru::Leaf(s) => message = s.to_owned(), + Cirru::List(_) => return Err(format!("assert expected a message, got {:?}", xs[1])), + } + + Ok(vec![CalxInstr::Assert(message)]) + } _ => Err(format!("unknown instruction: {}", name)), }, } diff --git a/src/primes.rs b/src/primes.rs index beefbc0..faab8f4 100644 --- a/src/primes.rs +++ b/src/primes.rs @@ -134,22 +134,25 @@ pub enum CalxInstr { Br(usize), BrIf(usize), Block { - // bool oo to indicate loop params_types: Vec, ret_types: Vec, + /// bool to indicate loop looped: bool, from: usize, to: usize, }, BlockEnd, - /// TODO use function name at first - Echo, // pop and println current value - Call(String), // during running, only use index, - CallImport(String), // TODO, + /// pop and println current value + Echo, + /// TODO use function name at first, during running, only use index, + Call(String), + CallImport(String), Unreachable, Nop, Quit(usize), // quit and return value Return, + /// TODO might also be a foreign function instead + Assert(String), } #[derive(Debug, Clone, PartialEq, PartialOrd)] diff --git a/src/vm.rs b/src/vm.rs index 408f26d..a25c51e 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -466,6 +466,14 @@ impl CalxVM { let v = self.stack_pop()?; println!("{}", v); } + CalxInstr::Assert(message) => { + let v = self.stack_pop()?; + if v == Calx::Bool(true) || v == Calx::I64(1) { + // Ok + } else { + return Err(self.gen_err(format!("Failed assertion: {}", message))); + } + } } self.top_frame.pointer += 1; From 011508ab156f5f00fb05d149dc23a3f0a16089f4 Mon Sep 17 00:00:00 2001 From: jiyinyiyong Date: Sat, 18 Sep 2021 20:30:52 +0800 Subject: [PATCH 3/4] implement preprocess phase --- README.md | 112 +++++++++++++---------- examples/sum.cirru | 28 +++++- src/bin/calx.rs | 24 ++++- src/parser.rs | 2 +- src/primes.rs | 11 ++- src/vm.rs | 219 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 336 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index f6b9882..5ae8c42 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,18 @@ fn main () echo ``` +`-S` to show instructions: + +```bash +calx -S hello.cirru +``` + +`-D` to disable preprocess: + +```bash +calx -D hello.cirru +``` + ### Instructions Highly inspired by: @@ -26,55 +38,57 @@ Highly inspired by: For binary op, top value puts on right. -| Code | Usage | Note | -| -------------------- | ------------------------------------------------------- | ----------------- | -| `local.set $idx` | set value at `$idx` | | -| `local.tee $idx` | set value at `$idx`, and also load it | | -| `local.get $idx` | get value at `$idx` load on stack | | -| `local.new` | increase size of array of locals | | -| `global.set $idx` | set global value at `$idx` | | -| `global.get $idx` | get global value at `$idx` | | -| `local.new` | increase size of array of globals | | -| `const $v` | push value `$v` on stack | | -| `dup` | duplicate top value on stack | | -| `drop` | drop top value from stack | | -| `i.add` | add two i64 numbers on stack into one | | -| `i.mul` | multiply two i64 numbers on stack into one | | -| `i.div` | divide two i64 numbers on stack into one | | -| `i.rem` | calculate reminder two i64 numbers on stack into one | | -| `i.neg` | negate i64 numbers on top of stack | | -| `i.shr $bits` | call SHR `$bits` bits on i64 numbers on top of stack | | -| `i.shl $bits` | call SHL `$bits` bits on i64 numbers on top of stack | | -| `i.eq` | detects if two i64 numbers on stack equal | | -| `i.ne` | detects if two i64 numbers on stack not equal | | -| `i.lt` | litter than, compares two i64 numbers on stack | | -| `i.gt` | greater than, compares two i64 numbers on stack | | -| `i.le` | litter/equal than, compares two i64 numbers on stack | | -| `i.ge` | greater/equal than, compares two i64 numbers on stack | | -| `add` | add two f64 numbers on stack into one | | -| `mul` | multiply two f64 numbers on stack into one | | -| `div` | divide two f64 numbers on stack into one | | -| `neg` | negate f64 numbers on top of stack | | -| `list.new` | | TODO | -| `list.get` | | TODO | -| `list.set` | | TODO | -| `link.new` | | TODO | -| `and` | | TODO | -| `or` | | TODO | -| `br $n` | break `$n` level of block, 0 means end of current block | | -| `br-if $n` | like `br $n` but detects top value on stack first | -| `block $types $body` | declare a block | | -| `loop $types $body` | declare a loop block | | -| (BlockEnd) | internal mark for ending a block | Internal | -| `echo` | pop value from stack and print | | -| `call $f` | call function `$f` | | -| `call-import $f` | call imported function `$f` | | -| `unreachable` | throw unreachable panic | | -| `nop` | No op | | -| `quit $code` | quit program and return exit code `$code` | | -| `return` | | TODO | -| `fn $types $body` | | Global definition | -| `assert` | `quit(1)` if not `true` | for testing | +| Code | Usage | Note | +| -------------------- | -------------------------------------------------------- | ----------------- | +| `local.set $idx` | set value at `$idx` | | +| `local.tee $idx` | set value at `$idx`, and also load it | | +| `local.get $idx` | get value at `$idx` load on stack | | +| `local.new` | increase size of array of locals | | +| `global.set $idx` | set global value at `$idx` | | +| `global.get $idx` | get global value at `$idx` | | +| `local.new` | increase size of array of globals | | +| `const $v` | push value `$v` on stack | | +| `dup` | duplicate top value on stack | | +| `drop` | drop top value from stack | | +| `i.add` | add two i64 numbers on stack into one | | +| `i.mul` | multiply two i64 numbers on stack into one | | +| `i.div` | divide two i64 numbers on stack into one | | +| `i.rem` | calculate reminder two i64 numbers on stack into one | | +| `i.neg` | negate i64 numbers on top of stack | | +| `i.shr $bits` | call SHR `$bits` bits on i64 numbers on top of stack | | +| `i.shl $bits` | call SHL `$bits` bits on i64 numbers on top of stack | | +| `i.eq` | detects if two i64 numbers on stack equal | | +| `i.ne` | detects if two i64 numbers on stack not equal | | +| `i.lt` | litter than, compares two i64 numbers on stack | | +| `i.gt` | greater than, compares two i64 numbers on stack | | +| `i.le` | litter/equal than, compares two i64 numbers on stack | | +| `i.ge` | greater/equal than, compares two i64 numbers on stack | | +| `add` | add two f64 numbers on stack into one | | +| `mul` | multiply two f64 numbers on stack into one | | +| `div` | divide two f64 numbers on stack into one | | +| `neg` | negate f64 numbers on top of stack | | +| `list.new` | | TODO | +| `list.get` | | TODO | +| `list.set` | | TODO | +| `link.new` | | TODO | +| `and` | | TODO | +| `or` | | TODO | +| `br $n` | branch `$n` level of block, 0 means end of current block | | +| `br-if $n` | like `br $n` but detects top value on stack first | Internal | +| (JMP `$l`) | jump to line `$l` | Internal | +| (JMP_IF `$l`) | conditional jump to `$l` | +| `block $types $body` | declare a block | | +| `loop $types $body` | declare a loop block | | +| (BlockEnd) | internal mark for ending a block | Internal | +| `echo` | pop value from stack and print | | +| `call $f` | call function `$f` | | +| `call-import $f` | call imported function `$f` | | +| `unreachable` | throw unreachable panic | | +| `nop` | No op | | +| `quit $code` | quit program and return exit code `$code` | | +| `return` | | TODO | +| `fn $types $body` | | Global definition | +| `assert` | `quit(1)` if not `true` | for testing | For `$types`, it can be `($t1 $t2 -> $t3 $t4)`, where supported types are: diff --git a/examples/sum.cirru b/examples/sum.cirru index 0c33ade..048d6e3 100644 --- a/examples/sum.cirru +++ b/examples/sum.cirru @@ -3,7 +3,7 @@ fn f1 (-> i64) (const "|data in f1") (echo) (const 10) (return) fn f2 (i64 -> i64) (local.get 0) (echo) (const 10) (return) -fn demo (-> i64) +fn blocks (-> i64) , (const 1) (echo) (const 4.) (const 2.) (add) (echo) block (->) (br 0) (const 1.) (const 2.) (neg) (add) (echo) block (->) (const 1.) (const 2.) (neg) (add) (echo) @@ -12,33 +12,49 @@ fn demo (-> i64) echo block (->) (br 0) (const 1.) (const 2.) (neg) (add) (echo) , (const "|demo of string") (echo) + const 0 + +fn sum () + local.new block (-> i64) - local.new const 0 local.set 0 const 0 loop (i64) + ;; "echo inspect i" + ;; const |inspect + ;; echo + ;; dup + ;; echo i + + ;; "i += 1" const 1 i.add + + ;; "acc += i" dup local.get 0 i.add local.set 0 + + ;; "if >= 1M" dup const 1000000 i.ge br-if 1 + br 0 + drop const "|check sum" echo local.get 0 echo -fn main (-> i64) +fn echos (-> i64) const "|loading program" echo - call demo + call blocks const 2 const 3 @@ -46,3 +62,7 @@ fn main (-> i64) echo return + + +fn main () + call sum diff --git a/src/bin/calx.rs b/src/bin/calx.rs index 152e10a..c63a4d3 100644 --- a/src/bin/calx.rs +++ b/src/bin/calx.rs @@ -20,6 +20,14 @@ fn main() -> Result<(), String> { .about("display processed instructions of functions") .takes_value(false), ) + .arg( + Arg::new("DISABLE_PRE") + .short('D') + .long("disable-pre") + .value_name("disable-pre") + .about("disabled preprocess") + .takes_value(false), + ) .arg( Arg::new("SOURCE") .about("A *.cirru file for loading code") @@ -30,6 +38,7 @@ fn main() -> Result<(), String> { let source = matches.value_of("SOURCE").unwrap(); let show_code = matches.is_present("SHOW_CODE"); + let disable_pre = matches.is_present("DISABLE_PRE"); let contents = fs::read_to_string(source).expect("Cirru file for instructions"); let code = parse(&contents).expect("Some Cirru content"); @@ -51,12 +60,25 @@ fn main() -> Result<(), String> { imports.insert(String::from("log3"), (log_calx_value, 3)); let mut vm = CalxVM::new(fns, vec![], imports); + + // if show_code { + // for func in vm.funcs.to_owned() { + // println!("loaded fn: {}", func); + // } + // } + + let now = Instant::now(); + if !disable_pre { + vm.preprocess()?; + } else { + println!("Preprocess disabled.") + } + if show_code { for func in vm.funcs.to_owned() { println!("loaded fn: {}", func); } } - let now = Instant::now(); match vm.run() { Ok(()) => { diff --git a/src/parser.rs b/src/parser.rs index c2bf4ba..ef4fa84 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -339,7 +339,7 @@ pub fn parse_block(ptr_base: usize, xs: &[Cirru], looped: bool) -> Result) -> fmt::Result { - f.write_str("CalxFunc (")?; + write!(f, "CalxFunc {} (", self.name)?; for p in &self.params_types { write!(f, "{:?} ", p)?; } + f.write_str("-> ")?; + for p in &self.ret_types { + write!(f, "{:?} ", p)?; + } f.write_str(")")?; for (idx, instr) in self.instrs.iter().enumerate() { write!(f, "\n {:02} {:?}", idx, instr)?; @@ -85,7 +89,6 @@ impl fmt::Display for CalxFunc { #[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum CalxInstr { // Param, // load variable from parameter - Local, // new local variable LocalSet(usize), LocalTee(usize), // set and also load to stack LocalGet(usize), @@ -133,6 +136,8 @@ pub enum CalxInstr { // control stuctures Br(usize), BrIf(usize), + Jmp(usize), // internal + JmpIf(usize), // internal Block { params_types: Vec, ret_types: Vec, @@ -141,7 +146,7 @@ pub enum CalxInstr { from: usize, to: usize, }, - BlockEnd, + BlockEnd(bool), /// pop and println current value Echo, /// TODO use function name at first, during running, only use index, diff --git a/src/vm.rs b/src/vm.rs index a25c51e..7e73dbb 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -60,6 +60,17 @@ impl CalxVM { continue; } match self.top_frame.instrs[self.top_frame.pointer].to_owned() { + CalxInstr::Jmp(line) => { + self.top_frame.pointer = line; + continue; // point reset, goto next loop + } + CalxInstr::JmpIf(line) => { + let v = self.stack_pop()?; + if v == Calx::Bool(true) || v == Calx::I64(1) { + self.top_frame.pointer = line; + continue; // point reset, goto next loop + } + } CalxInstr::Br(size) => { self.shrink_blocks_by(size)?; @@ -109,7 +120,10 @@ impl CalxVM { }); println!("TODO check params type: {:?}", params_types); } - CalxInstr::BlockEnd => { + CalxInstr::BlockEnd(looped) => { + if looped { + return Err(self.gen_err(String::from("loop end expected to be branched"))); + } let last_block = self.top_frame.blocks_track.pop().unwrap(); if self.stack.len() != last_block.initial_stack_size + last_block.ret_types.len() { return Err(self.gen_err(format!( @@ -122,7 +136,6 @@ impl CalxVM { ))); } } - CalxInstr::Local => self.top_frame.locals.push(Calx::Nil), CalxInstr::LocalSet(idx) => { let v = self.stack_pop()?; if idx >= self.top_frame.locals.len() { @@ -480,6 +493,150 @@ impl CalxVM { } } + pub fn preprocess(&mut self) -> Result<(), String> { + for i in 0..self.funcs.len() { + let mut stack_size = 0; + let mut ops: Vec = vec![]; + let mut blocks_track: Vec = vec![]; + + // println!("\nFUNC {} {}", self.funcs[i].name, stack_size); + + for j in 0..self.funcs[i].instrs.len() { + // println!("* {:?}", self.funcs[i].instrs[j].to_owned()); + match self.funcs[i].instrs[j].to_owned() { + CalxInstr::Block { + looped, + params_types, + ret_types, + from, + to, + } => { + if stack_size < params_types.len() { + return Err(format!( + "insufficient params {} for block: {:?}", + stack_size, params_types + )); + } + blocks_track.push(BlockData { + looped, + ret_types, + from, + to, + initial_stack_size: stack_size, + }); + ops.push(CalxInstr::Nop); + } + CalxInstr::Br(size) => { + if size > blocks_track.len() { + return Err(format!("br {} too large", size)); + } + + let target_block = blocks_track[blocks_track.len() - size - 1].to_owned(); + if target_block.looped { + // not checking + ops.push(CalxInstr::Jmp(target_block.from)) + } else { + ops.push(CalxInstr::Jmp(target_block.to)) + } + } + CalxInstr::BrIf(size) => { + if blocks_track.is_empty() { + return Err(format!("cannot branch with no blocks, {}", size)); + } + if size > blocks_track.len() { + return Err(format!("br {} too large", size)); + } + + let target_block = blocks_track[blocks_track.len() - size - 1].to_owned(); + if target_block.looped { + // not checking + ops.push(CalxInstr::JmpIf(target_block.from)) + } else { + ops.push(CalxInstr::JmpIf(target_block.to)) + } + stack_size -= 1 + } + CalxInstr::BlockEnd(looped) => { + // println!("checking: {:?}", blocks_track); + if blocks_track.is_empty() { + return Err(format!("invalid block end, {:?}", blocks_track)); + } + + let prev_block = blocks_track.pop().unwrap(); + if looped { + // nothing, branched during runtime + } else if stack_size != prev_block.initial_stack_size + prev_block.ret_types.len() { + return Err(format!( + "not match {} and {} + {:?} at block end", + stack_size, prev_block.initial_stack_size, prev_block.ret_types + )); + } + + ops.push(CalxInstr::Nop) + } + CalxInstr::Call(f_name) => match find_func(&self.funcs, &f_name) { + Some(f) => { + if stack_size < f.params_types.len() { + return Err(format!( + "insufficient size to call: {} {:?}", + stack_size, f.params_types + )); + } + stack_size = stack_size - f.params_types.len() + f.ret_types.len(); + } + None => return Err(format!("cannot find function named: {}", f_name)), + }, + CalxInstr::CallImport(f_name) => match &self.imports.get(&f_name) { + Some((_f, size)) => { + if stack_size < *size { + return Err(format!( + "insufficient size to call import: {} {:?}", + stack_size, size + )); + } + stack_size = stack_size - size + 1; + } + None => return Err(format!("missing imported function {}", f_name)), + }, + CalxInstr::Return => { + if stack_size != self.funcs[i].ret_types.len() { + return Err(format!( + "invalid return size {} of {:?} in {}", + stack_size, self.funcs[i].ret_types, self.funcs[i].name + )); + } + ops.push(CalxInstr::Return); + } + a => { + // checks + let (params_size, ret_size) = instr_stack_arity(&a); + if stack_size < params_size { + return Err(format!( + "insufficient stack {} to call {:?} of {}", + stack_size, a, params_size + )); + } + stack_size = stack_size - params_size + ret_size; + // println!( + // " sizes: {:?} {} {} -> {}", + // a, params_size, ret_size, stack_size + // ); + ops.push(a.to_owned()); + } + } + } + if stack_size != self.funcs[i].ret_types.len() { + return Err(format!( + "invalid return size {} of {:?} in {}", + stack_size, self.funcs[i].ret_types, self.funcs[i].name + )); + } + + self.funcs[i].instrs = ops; + } + Ok(()) + } + #[inline(always)] fn check_func_return(&mut self) -> Result<(), CalxError> { if self.stack.len() != self.top_frame.initial_stack_size + self.top_frame.ret_types.len() { @@ -561,3 +718,61 @@ pub fn find_func(funcs: &[CalxFunc], name: &str) -> Option { } None } + +/// notice that some of the instrs are special and need to handle manually +pub fn instr_stack_arity(op: &CalxInstr) -> (usize, usize) { + match op { + CalxInstr::LocalSet(_) => (1, 0), + CalxInstr::LocalTee(_) => (1, 1), // TODO need check + CalxInstr::LocalGet(_) => (0, 1), + CalxInstr::LocalNew => (0, 0), + CalxInstr::GlobalSet(_) => (1, 0), + CalxInstr::GlobalGet(_) => (0, 1), + CalxInstr::GlobalNew => (0, 0), + CalxInstr::Const(_) => (0, 1), + CalxInstr::Dup => (1, 2), + CalxInstr::Drop => (1, 0), + CalxInstr::IntAdd => (2, 1), + CalxInstr::IntMul => (2, 1), + CalxInstr::IntDiv => (2, 1), + CalxInstr::IntRem => (2, 1), + CalxInstr::IntNeg => (1, 1), + CalxInstr::IntShr => (2, 1), + CalxInstr::IntShl => (2, 1), + CalxInstr::IntEq => (2, 1), + CalxInstr::IntNe => (2, 1), + CalxInstr::IntLt => (2, 1), + CalxInstr::IntLe => (2, 1), + CalxInstr::IntGt => (2, 1), + CalxInstr::IntGe => (2, 1), + CalxInstr::Add => (2, 1), + CalxInstr::Mul => (2, 1), + CalxInstr::Div => (2, 1), + CalxInstr::Neg => (1, 1), + // string operations + // list operations + CalxInstr::NewList => (0, 1), + CalxInstr::ListGet => (2, 1), + CalxInstr::ListSet => (3, 0), + // Link + CalxInstr::NewLink => (0, 1), + // bool operations + CalxInstr::And => (2, 1), + CalxInstr::Or => (2, 1), + // control stuctures + CalxInstr::Br(_) => (0, 0), + CalxInstr::BrIf(_) => (1, 0), + CalxInstr::Jmp(_) => (0, 0), + CalxInstr::JmpIf(_) => (1, 0), + CalxInstr::Block { .. } => (0, 0), + CalxInstr::BlockEnd(_) => (0, 0), + CalxInstr::Echo => (1, 0), + CalxInstr::Call(_) => (0, 0), // TODO + CalxInstr::CallImport(_) => (0, 0), // import + CalxInstr::Unreachable => (0, 0), // TODO + CalxInstr::Nop => (0, 0), + CalxInstr::Quit(_) => (0, 0), + CalxInstr::Return => (0, 0), // TODO + CalxInstr::Assert(_) => (1, 0), + } +} From fefb8c521a4ee82d1a41ba09ac9d9be90e88b877 Mon Sep 17 00:00:00 2001 From: jiyinyiyong Date: Sat, 18 Sep 2021 21:02:25 +0800 Subject: [PATCH 4/4] boring syntax change for performance --- src/vm.rs | 186 +++++++++++++++++++++++++++++------------------------- 1 file changed, 101 insertions(+), 85 deletions(-) diff --git a/src/vm.rs b/src/vm.rs index 7e73dbb..88d57e7 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -84,8 +84,8 @@ impl CalxVM { continue; // point reset, goto next loop } CalxInstr::BrIf(size) => { - let v = self.stack_pop()?; - if v == Calx::Bool(true) || v == Calx::I64(1) { + let last_idx = self.stack.len() - 1; + if self.stack[last_idx] == Calx::Bool(true) || self.stack[last_idx] == Calx::I64(1) { self.shrink_blocks_by(size)?; let last_idx = self.top_frame.blocks_track.len() - 1; @@ -191,8 +191,7 @@ impl CalxVM { CalxInstr::GlobalNew => self.globals.push(Calx::Nil), CalxInstr::Const(v) => self.stack_push(v.to_owned()), CalxInstr::Dup => { - let v = self.stack_peek()?; - self.stack_push(v); + self.stack_push(self.stack[self.stack.len() - 1].to_owned()); } CalxInstr::Drop => { let _ = self.stack_pop()?; @@ -200,24 +199,27 @@ impl CalxVM { CalxInstr::IntAdd => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64(n1 + n2)), + let last_idx = self.stack.len() - 1; + match (&(self.stack[last_idx]), &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 + n2), (_, _) => { - return Err(self.gen_err(format!("expected 2 integers to add, {:?} {:?}", v1, v2))) + return Err(self.gen_err(format!( + "expected 2 integers to add, {:?} {:?}", + self.stack[last_idx], v2 + ))) } } } CalxInstr::IntMul => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64(n1 * n2)), + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 * n2), (_, _) => { return Err(self.gen_err(format!( "expected 2 integers to multiply, {:?} {:?}", - v1, v2 + self.stack[last_idx], v2 ))) } } @@ -225,67 +227,80 @@ impl CalxVM { CalxInstr::IntDiv => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64(n1 / n2)), + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 / n2), (_, _) => { - return Err(self.gen_err(format!("expected 2 integers to divide, {:?} {:?}", v1, v2))) + return Err(self.gen_err(format!( + "expected 2 integers to divide, {:?} {:?}", + self.stack[last_idx], v2 + ))) } } } CalxInstr::IntRem => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::I64((*n1).rem(n2))), + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64((*n1).rem(n2)), (_, _) => { - return Err(self.gen_err(format!("expected 2 integers to add, {:?} {:?}", v1, v2))) + return Err(self.gen_err(format!( + "expected 2 integers to add, {:?} {:?}", + self.stack[last_idx], v2 + ))) } } } CalxInstr::IntNeg => { - let v = self.stack_pop()?; - if let Calx::I64(n) = v { - self.stack_push(Calx::I64(-n)) + let last_idx = self.stack.len() - 1; + if let Calx::I64(n) = self.stack[last_idx] { + self.stack[last_idx] = Calx::I64(-n) } else { - return Err(self.gen_err(format!("expected int, got {}", v))); + return Err(self.gen_err(format!("expected int, got {}", self.stack[last_idx]))); } } CalxInstr::IntShr => { let bits = self.stack_pop()?; - let v = self.stack_pop()?; - match (&v, &bits) { + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &bits) { (Calx::I64(n), Calx::I64(b)) => { - self.stack_push(Calx::I64(n.checked_shr(*b as u32).unwrap())) + self.stack[last_idx] = Calx::I64(n.checked_shr(*b as u32).unwrap()) } (_, _) => { - return Err(self.gen_err(format!("invalid number for SHR, {:?} {:?}", v, bits))) + return Err(self.gen_err(format!( + "invalid number for SHR, {:?} {:?}", + self.stack[last_idx], bits + ))) } } } CalxInstr::IntShl => { let bits = self.stack_pop()?; - let v = self.stack_pop()?; - match (&v, &bits) { + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &bits) { (Calx::I64(n), Calx::I64(b)) => { - self.stack_push(Calx::I64(n.checked_shl(*b as u32).unwrap())) + self.stack[last_idx] = Calx::I64(n.checked_shl(*b as u32).unwrap()) } (_, _) => { - return Err(self.gen_err(format!("invalid number for SHL, {:?} {:?}", v, bits))) + return Err(self.gen_err(format!( + "invalid number for SHL, {:?} {:?}", + self.stack[last_idx], bits + ))) } } } CalxInstr::IntEq => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 == n2)), + let last_idx = self.stack.len() - 1; + + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 == n2), (_, _) => { return Err(self.gen_err(format!( "expected 2 integers to eq compare, {:?} {:?}", - v1, v2 + self.stack[last_idx], v2 ))) } } @@ -294,13 +309,13 @@ impl CalxVM { CalxInstr::IntNe => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 != n2)), + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 != n2), (_, _) => { return Err(self.gen_err(format!( "expected 2 integers to ne compare, {:?} {:?}", - v1, v2 + self.stack[last_idx], v2 ))) } } @@ -308,13 +323,13 @@ impl CalxVM { CalxInstr::IntLt => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 < n2)), + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 < n2), (_, _) => { return Err(self.gen_err(format!( "expected 2 integers to le compare, {:?} {:?}", - v1, v2 + self.stack[last_idx], v2 ))) } } @@ -322,13 +337,13 @@ impl CalxVM { CalxInstr::IntLe => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 <= n2)), + let last_idx = self.stack.len() - 1; + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 <= n2), (_, _) => { return Err(self.gen_err(format!( "expected 2 integers to le compare, {:?} {:?}", - v1, v2 + self.stack[last_idx], v2 ))) } } @@ -336,13 +351,14 @@ impl CalxVM { CalxInstr::IntGt => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 > n2)), + let last_idx = self.stack.len() - 1; + + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 > n2), (_, _) => { return Err(self.gen_err(format!( "expected 2 integers to gt compare, {:?} {:?}", - v1, v2 + self.stack[last_idx], v2 ))) } } @@ -350,13 +366,14 @@ impl CalxVM { CalxInstr::IntGe => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::I64(n1), Calx::I64(n2)) => self.stack_push(Calx::Bool(n1 >= n2)), + let last_idx = self.stack.len() - 1; + + match (&self.stack[last_idx], &v2) { + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::Bool(n1 >= n2), (_, _) => { return Err(self.gen_err(format!( "expected 2 integers to ge compare, {:?} {:?}", - v1, v2 + self.stack[last_idx], v2 ))) } } @@ -364,44 +381,54 @@ impl CalxVM { CalxInstr::Add => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::F64(n1), Calx::F64(n2)) => self.stack_push(Calx::F64(n1 + n2)), + let last_idx = self.stack.len() - 1; + + match (&self.stack[last_idx], &v2) { + (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 + n2), (_, _) => { - return Err(self.gen_err(format!("expected 2 numbers to +, {:?} {:?}", v1, v2))) + return Err(self.gen_err(format!( + "expected 2 numbers to +, {:?} {:?}", + self.stack[last_idx], v2 + ))) } } } CalxInstr::Mul => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::F64(n1), Calx::F64(n2)) => self.stack_push(Calx::F64(n1 * n2)), + let last_idx = self.stack.len() - 1; + + match (&self.stack[last_idx], &v2) { + (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 * n2), (_, _) => { - return Err( - self.gen_err(format!("expected 2 numbers to multiply, {:?} {:?}", v1, v2)), - ) + return Err(self.gen_err(format!( + "expected 2 numbers to multiply, {:?} {:?}", + self.stack[last_idx], v2 + ))) } } } CalxInstr::Div => { // reversed order let v2 = self.stack_pop()?; - let v1 = self.stack_pop()?; - match (&v1, &v2) { - (Calx::F64(n1), Calx::F64(n2)) => self.stack_push(Calx::F64(n1 / n2)), + let last_idx = self.stack.len() - 1; + + match (&self.stack[last_idx], &v2) { + (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 / n2), (_, _) => { - return Err(self.gen_err(format!("expected 2 numbers to divide, {:?} {:?}", v1, v2))) + return Err(self.gen_err(format!( + "expected 2 numbers to divide, {:?} {:?}", + self.stack[last_idx], v2 + ))) } } } CalxInstr::Neg => { - let v = self.stack_pop()?; - if let Calx::F64(n) = v { - self.stack_push(Calx::F64(-n)) + let last_idx = self.stack.len() - 1; + if let Calx::F64(n) = self.stack[last_idx] { + self.stack[last_idx] = Calx::F64(-n) } else { - return Err(self.gen_err(format!("expected float, got {}", v))); + return Err(self.gen_err(format!("expected float, got {}", self.stack[last_idx]))); } } CalxInstr::NewList => { @@ -668,17 +695,6 @@ impl CalxVM { self.stack.push(x) } - #[inline(always)] - fn stack_peek(&mut self) -> Result { - if self.stack.is_empty() { - Err(self.gen_err(String::from("cannot peek empty stack"))) - } else if self.stack.len() <= self.top_frame.initial_stack_size { - Err(self.gen_err(String::from("cannot peek parent stack"))) - } else { - Ok(self.stack.last().unwrap().to_owned()) - } - } - /// assumed that the size already checked #[inline(always)] fn shrink_blocks_by(&mut self, size: usize) -> Result<(), CalxError> {