Skip to content

Commit

Permalink
Merge pull request #6 from calcit-lang/preprocess
Browse files Browse the repository at this point in the history
improve error; implement preprocess phase
  • Loading branch information
TCXX authored Sep 18, 2021
2 parents 056839e + fefb8c5 commit 1f8e10a
Show file tree
Hide file tree
Showing 10 changed files with 579 additions and 200 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ jobs:

- run: cargo run examples/hello.cirru
- run: cargo run examples/sum.cirru
- run: cargo run examples/assert.cirru
112 changes: 63 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -26,55 +38,57 @@ Highly inspired by:

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 | |
| `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 |
| 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:

Expand Down
8 changes: 8 additions & 0 deletions examples/assert.cirru
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

fn main ()
const 1
const 1
i.add
const 2
i.eq
assert "should be have 2"
28 changes: 24 additions & 4 deletions examples/sum.cirru
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -12,37 +12,57 @@ 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
call-import log2
echo

return


fn main ()
call sum
38 changes: 31 additions & 7 deletions src/bin/calx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -17,7 +17,15 @@ 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(
Arg::new("DISABLE_PRE")
.short('D')
.long("disable-pre")
.value_name("disable-pre")
.about("disabled preprocess")
.takes_value(false),
)
.arg(
Expand All @@ -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");
Expand All @@ -41,20 +50,35 @@ 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 {
// 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(()) => {
Expand All @@ -66,15 +90,15 @@ 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 {
Err(String::from("TODO not cirru code"))
}
}

fn log2_calx_value(xs: Vec<Calx>) -> Result<Calx, String> {
fn log_calx_value(xs: Vec<Calx>) -> Result<Calx, CalxError> {
println!("log: {:?}", xs);
Ok(Calx::Nil)
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
15 changes: 14 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@ pub fn parse_instr(ptr_base: usize, node: &Cirru) -> Result<Vec<CalxInstr>, 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)),
},
}
Expand Down Expand Up @@ -326,7 +339,7 @@ pub fn parse_block(ptr_base: usize, xs: &[Cirru], looped: bool) -> Result<Vec<Ca
}
}
}
chunk.push(CalxInstr::BlockEnd);
chunk.push(CalxInstr::BlockEnd(looped));

if looped && !ret_types.is_empty() {
println!("return types for loop actuall not checked: {:?}", ret_types);
Expand Down
Loading

0 comments on commit 1f8e10a

Please sign in to comment.