Skip to content

Commit

Permalink
update docs; tag 0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
tiye committed Jan 22, 2024
1 parent d7e2baf commit 367af18
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 167 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@

#/target
Cargo.lock


profile.json
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "calx_vm"
version = "0.2.0-a1"
version = "0.2.0"
authors = ["jiyinyiyong <[email protected]>"]
edition = "2021"
license = "MIT"
Expand Down
126 changes: 46 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,55 @@ it starts with a `main` function:

```cirru
fn main ()
const "|hello world"
const 1
call demo
const 0
call demo
fn demo (($a i64) ->)
local.get $a
if (->)
do
const 11
echo
do
const 20
echo
const 3
echo
```

`-S` to show instructions:
`-s` to show instructions:

```bash
calx -S hello.cirru
```

`-D` to disable preprocess:

```bash
calx -D hello.cirru
$ calx demos/if.cirru -s
[calx] start preprocessing
loaded fn: CalxFunc main (-> )
00 Const(I64(1))
01 Call("demo")
02 Const(I64(0))
03 Call("demo")

loaded fn: CalxFunc demo (I64 -> )
local_names: 0_$a .
00 LocalGet(0)
01 JmpIf(5)
02 Const(I64(20))
03 Echo
04 Jmp(8)
05 Const(I64(11))
06 Echo
07 Jmp(8)
08 Const(I64(3))
09 Echo

[calx] start running
11
3
20
3
[calx] took 67.250µs: Nil
```

`--emit-binary filename` for encode functions into a binary file.
Expand Down Expand Up @@ -73,82 +108,13 @@ fn main ()

### Instructions

> _TODO_ update to `0.2.x`...
Find docs on https://docs.rs/calx_vm/ .

Highly inspired by:

- WASM https://github.com/WebAssembly/design/blob/main/Semantics.md
- Lox https://github.com/Darksecond/lox/blob/master/lox-vm/src/bettervm/vm.rs

For binary op, top value puts on right.

Calx Binary Edition `0.1`:

| Code | Usage | Note |
| -------------------- | -------------------------------------------------------- | -------------------------------------- |
| `local.set $idx` | pop from stack, 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 $name` | increase size of array of locals | name is optional, defaults to `$<idx>` |
| `global.set $idx` | set global value at `$idx` | |
| `global.get $idx` | get global value at `$idx` | |
| `global.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 |
| `not` | | 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` | |
| `return-call $f` | tail 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 |
| `inspect` | println inspection information | |

For `$types`, it can be `($t1 $t2 -> $t3 $t4)`, where supported types are:

- nil
- i64
- f64
- bool _TODO_
- str _TODO_
- list _TODO_
- link _TODO_

### Preprocess

Before Calx running the instructions, Calx performs preprocessing to them. There are several tasks:
Expand All @@ -158,7 +124,7 @@ Before Calx running the instructions, Calx performs preprocessing to them. There
- stack size is checked to ensure it's consistent among branches, and tidied up at function end
- local variables are renamed to indexes

these steps simplies debugging, although it's sure that's good features.
The codebase would be updated as I'm learning more about WASM.

### License

Expand Down
6 changes: 6 additions & 0 deletions src/calx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@ pub use types::CalxType;
/// Simplied from Calcit, but trying to be basic and mutable
#[derive(Debug, Clone, PartialEq, PartialOrd, Decode, Encode)]
pub enum Calx {
/// TODO
Nil,
/// TODO
Bool(bool),
/// `i64`
I64(i64),
/// `f64`
F64(f64),
// TODO
Str(String),
/// TODO
List(Vec<Calx>),
// to simultate linked structures
// Link(Box<Calx>, Box<Calx>, Box<Calx>),
Expand Down
8 changes: 8 additions & 0 deletions src/calx/types.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
use bincode::{Decode, Encode};
use std::str::FromStr;

/// syntax like `(i64 -> i64)` can be used to types of functions and blocks
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Decode, Encode)]
pub enum CalxType {
/// TODO
Nil,
/// TODO
Bool,
/// i64 value
I64,
/// f64 value
F64,
/// TODO
Str,
/// TODO
List,
/// TODO
Link,
}

Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! Calx VM is a toy VM for learning WebAssembly.
//! It is a stack machine, and it is dynamically typed. Being an experiment, for Calcit project.
mod calx;
mod parser;
mod syntax;
Expand All @@ -6,5 +9,6 @@ mod vm;

pub use calx::{Calx, CalxType};
pub use parser::{extract_nested, parse_function};
pub use syntax::CalxSyntax;
pub use util::log_calx_value;
pub use vm::{func::CalxFunc, instr::CALX_INSTR_EDITION, CalxImportsDict, CalxVM};
pub use vm::{func::CalxFunc, instr::CalxInstr, instr::CALX_INSTR_EDITION, CalxImportsDict, CalxVM};
87 changes: 61 additions & 26 deletions src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,87 +7,122 @@ use crate::{Calx, CalxType};
/// learning from WASM but for dynamic data
#[derive(Debug, Clone, PartialEq, PartialOrd, Decode, Encode)]
pub enum CalxSyntax {
// Param, // load variable from parameter
/// `local.set`, pop from stack, set value at position
LocalSet(usize),
LocalTee(usize), // set and also load to stack
/// `local.tee`, set value at position, and also load to stack
LocalTee(usize),
/// `local.get`, get value at position load on stack
LocalGet(usize),
/// `local.new`, increase size of array of locals
LocalNew,
/// `global.set`, set global value at position
GlobalSet(usize),
/// `global.get`, get global value from position
GlobalGet(usize),
/// `global.new`, increase size of array of globals
GlobalNew,
/// `const`, push value to stack
Const(Calx),
/// `dup`, duplicate value on stack
Dup,
/// `drop`, drop top value from stack
Drop,
// number operations
/// `i.add`, add two i64 numbers on stack into a i64
IntAdd,
/// `i.mul`, multiply two i64 numbers on stack into a i64
IntMul,
/// `i.div`, divide two i64 numbers on stack into a i64
IntDiv,
/// `i.rem`, remainder of two i64 numbers on stack into a i64
IntRem,
/// `i.neg`, negate a i64 number on stack
IntNeg,
/// `i.shr`, shift right a i64 number on stack
IntShr,
/// `i.shl`, shift left a i64 number on stack
IntShl,
/// equal
/// `i.eq`, equal of two i64 numbers on stack into a bool
IntEq,
/// not equal
/// `i.ne`, not equal of two i64 numbers on stack into a bool
IntNe,
/// littler than
/// `i.lt`, littler than, compares two i64 numbers on stack
IntLt,
/// littler than, or equal
/// `i.le`, littler than, or equal, compares two i64 numbers on stack
IntLe,
/// greater than
/// `i.gt`, greater than, compares two i64 numbers on stack
IntGt,
/// greater than, or equal
/// `i.ge`, greater than, or equal, compares two i64 numbers on stack
IntGe,
/// `add`, add two f64 numbers on stack into a f64
Add,
/// `mul`, multiply two f64 numbers on stack into a f64
Mul,
/// `div`, divide two f64 numbers on stack into a f64
Div,
/// `neg`, negate a f64 number on stack
Neg,
// string operations
// list operations
/// TODO list operations
NewList,
/// TODO
ListGet,
/// TODO
ListSet,
// Link
/// TODO Link
NewLink,
// bool operations
/// TODO
And,
/// TODO
Or,
/// TODO
Not,
// control stuctures
Br(usize),
BrIf(usize),
/// `block`, creates block, for `block` and `loop`
Block {
params_types: Rc<Vec<CalxType>>,
ret_types: Rc<Vec<CalxType>>,
/// bool to indicate loop
looped: bool,
params_types: Rc<Vec<CalxType>>,
ret_types: Rc<Vec<CalxType>>,
/// index of `end` instruction
from: usize,
/// index of `end` instruction
to: usize,
},
/// `br`, break from block, level `0` indicates the innermost block
Br(usize),
/// `br-if`, break from block conditionally, level `0` indicates the innermost block
BrIf(usize),
/// (parsed) end of block, for `block` and `loop`
BlockEnd(bool),
/// just a list of instructions nested
/// `do`, just a list of instructions nested, used inside `if` area
Do(Vec<CalxSyntax>),
/// pop and println current value
/// `echo`, pop and println current value
Echo,
/// TODO use function name at first, during running, index can be faster
/// `call`, call function
/// TODO optimize with index
Call(String),
/// for tail recursion
/// `return-call`, tail recursion call function name
ReturnCall(String),
/// `call-import`, call import function
CallImport(String),
/// `unreachable`, unreachable panic
Unreachable,
/// `nop`, no operation placeholder
Nop,
Quit(usize), // quit and return value
/// `quit`, quit and return value
Quit(usize),
/// `return`, return from function
Return,
/// TODO might also be a foreign function instead
/// `assert`, TODO might also be a foreign function instead
Assert(String),
/// inspecting stack
/// `inspect`, inspecting stack
Inspect,
/// if takes 1 value from stack, returns values as ret_types
/// `if`, takes 1 value from stack, returns values as ret_types
If {
ret_types: Rc<Vec<CalxType>>,
else_at: usize,
to: usize,
},
/// (parsed) end of then instructions
ThenEnd,
/// (parsed) end of else instructions
ElseEnd,
}
Loading

0 comments on commit 367af18

Please sign in to comment.