From 6630ef9682c559743139a44c61de808237e4abbf Mon Sep 17 00:00:00 2001 From: tiye Date: Wed, 20 Jul 2022 22:39:01 +0800 Subject: [PATCH] a toy version of return-call --- .github/workflows/publish.yaml | 1 + .github/workflows/test.yaml | 1 + README.md | 1 + examples/recur.cirru | 24 +++++++++++++++++ src/parser.rs | 11 ++++++++ src/primes.rs | 4 ++- src/vm.rs | 49 +++++++++++++++++++++++++++++++++- 7 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 examples/recur.cirru diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 4f61872..b5f970e 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -19,6 +19,7 @@ jobs: - run: cargo run -- -S examples/assert.cirru - run: cargo run -- -S examples/nested.cirru - run: cargo run -- -S examples/named.cirru + - run: cargo run -- examples/recur.cirru - run: cargo run -- --emit-binary target/a.calx examples/named.cirru && cargo run -- --eval-binary target/a.calx # - run: cargo test diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 22c729d..995b422 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -29,6 +29,7 @@ jobs: - run: cargo run -- -S examples/assert.cirru - run: cargo run -- -S examples/nested.cirru - run: cargo run -- -S examples/named.cirru + - run: cargo run -- examples/recur.cirru - run: cargo run -- --emit-binary target/a.calx examples/named.cirru && cargo run -- --eval-binary target/a.calx - uses: actions-rs/clippy-check@v1 diff --git a/README.md b/README.md index e4882cb..55682eb 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ Calx Binary Edition `0.1`: | (BlockEnd) | internal mark for ending a block | Internal | | `echo` | pop value from stack and print | | | `call $f` | call function `$f` | | +| `return-call $f` | tail call function `$f` | | | `call-import $f` | call imported function `$f` | | | `unreachable` | throw unreachable panic | | | `nop` | No op | | diff --git a/examples/recur.cirru b/examples/recur.cirru new file mode 100644 index 0000000..fd3d173 --- /dev/null +++ b/examples/recur.cirru @@ -0,0 +1,24 @@ + +fn main () + const 0 + const 100000 + call sum + echo + +fn sum (($acc i64) ($x i64) -> i64) + local.get $acc + local.get $x + add + ;; dup + ;; echo + block (->) + block (->) + local.get $x + const 1 + i.le + br-if 1 + local.get $x + const -1 + add + return-call sum + return diff --git a/src/parser.rs b/src/parser.rs index 0bf26f6..2ee7230 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -204,6 +204,17 @@ pub fn parse_instr(ptr_base: usize, node: &Cirru, collector: &mut LocalsCollecto Ok(vec![CalxInstr::Call((*name).to_owned())]) } + "return-call" => { + if xs.len() != 2 { + return Err(format!("return-call expected function name, {:?}", xs)); + } + let name: Box = match &xs[1] { + Cirru::Leaf(s) => s.to_owned(), + Cirru::List(_) => return Err(format!("expected a name, got {:?}", xs[1])), + }; + + Ok(vec![CalxInstr::ReturnCall((*name).to_owned())]) + } "call-import" => { if xs.len() != 2 { return Err(format!("call expected function name, {:?}", xs)); diff --git a/src/primes.rs b/src/primes.rs index 2d48b9c..4f0e60b 100644 --- a/src/primes.rs +++ b/src/primes.rs @@ -171,8 +171,10 @@ pub enum CalxInstr { BlockEnd(bool), /// pop and println current value Echo, - /// TODO use function name at first, during running, only use index, + /// TODO use function name at first, during running, index can be faster Call(String), + /// for tail recursion + ReturnCall(String), CallImport(String), Unreachable, Nop, diff --git a/src/vm.rs b/src/vm.rs index d2d0afa..43367bc 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -321,6 +321,7 @@ impl CalxVM { match (&self.stack[last_idx], &v2) { (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 + n2), + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 + n2), (_, _) => return Err(self.gen_err(format!("expected 2 numbers to +, {:?} {:?}", self.stack[last_idx], v2))), } } @@ -331,6 +332,7 @@ impl CalxVM { match (&self.stack[last_idx], &v2) { (Calx::F64(n1), Calx::F64(n2)) => self.stack[last_idx] = Calx::F64(n1 * n2), + (Calx::I64(n1), Calx::I64(n2)) => self.stack[last_idx] = Calx::I64(n1 * n2), (_, _) => return Err(self.gen_err(format!("expected 2 numbers to multiply, {:?} {:?}", self.stack[last_idx], v2))), } } @@ -374,6 +376,7 @@ impl CalxVM { // TODO } CalxInstr::Call(f_name) => { + // println!("frame size: {}", self.frames.len()); match find_func(&self.funcs, &f_name) { Some(f) => { let mut locals: Vec = vec![]; @@ -397,6 +400,39 @@ impl CalxVM { None => return Err(self.gen_err(format!("cannot find function named: {}", f_name))), } } + CalxInstr::ReturnCall(f_name) => { + // println!("frame size: {}", self.frames.len()); + match find_func(&self.funcs, &f_name) { + Some(f) => { + // println!("examine stack: {:?}", self.stack); + let mut locals: Vec = vec![]; + for _ in 0..f.params_types.len() { + let v = self.stack_pop()?; + locals.insert(0, v); + } + let prev_frame = self.top_frame.to_owned(); + if prev_frame.initial_stack_size != self.stack.len() { + return Err(self.gen_err(format!( + "expected constant initial stack size: {}, got: {}", + prev_frame.initial_stack_size, + self.stack.len() + ))); + } + self.top_frame = CalxFrame { + blocks_track: vec![], + initial_stack_size: self.stack.len(), + locals, + pointer: 0, + instrs: f.instrs, + ret_types: f.ret_types, + }; + + // start in new frame + continue; + } + 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(self.gen_err(format!("missing imported function {}", f_name))), Some((f, size)) => { @@ -451,7 +487,7 @@ impl CalxVM { // 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()); + // println!("{} * {:?}", stack_size, self.funcs[i].instrs[j].to_owned()); match self.funcs[i].instrs[j].to_owned() { CalxInstr::Block { looped, @@ -530,6 +566,16 @@ impl CalxVM { } None => return Err(format!("cannot find function named: {}", f_name)), }, + CalxInstr::ReturnCall(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(); + ops.push(CalxInstr::ReturnCall(f_name)) + } + 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 { @@ -711,6 +757,7 @@ pub fn instr_stack_arity(op: &CalxInstr) -> (usize, usize) { CalxInstr::BlockEnd(_) => (0, 0), CalxInstr::Echo => (1, 0), CalxInstr::Call(_) => (0, 0), // TODO + CalxInstr::ReturnCall(_) => (0, 0), // TODO CalxInstr::CallImport(_) => (0, 0), // import CalxInstr::Unreachable => (0, 0), // TODO CalxInstr::Nop => (0, 0),