Skip to content
Draft

vm #63

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
150 commits
Select commit Hold shift + click to select a range
f66a6ea
Add first version of vm
shsms Dec 10, 2023
d3a2583
Add numeric conditional jump instructions
shsms Dec 15, 2023
626742b
Rename `Pos::Absolute/Relative` → `Abs/Rel`
shsms Dec 15, 2023
4531c2e
Add support for calling functions
shsms Dec 15, 2023
a1c242e
Add bytecode for a recursive fib calculator
shsms Dec 15, 2023
6adaba2
Rename instructions `Plus/Minus` → `Add/Sub`
shsms Dec 15, 2023
e95c98c
Print mnemonics for instructions
shsms Dec 25, 2023
4452280
Pass context to vm
shsms Dec 25, 2023
ec0556f
Add a `RustCall` instruction
shsms Dec 25, 2023
7b0cc0e
Add frame for byte-compiler
shsms Dec 25, 2023
c2b19a0
Add a macro for compiling single-argument functions
shsms Dec 25, 2023
9ebeec3
Add function for compiling 2-arg functions, and `setq`
shsms Dec 25, 2023
4e08ba7
Replace macro `function_1_arg` with `fn byte_compile_1_arg_fn`
shsms Dec 25, 2023
e58b4a4
Create `Compiler` struct and move all compile methods inside it
shsms Dec 25, 2023
53c069d
Add a `Label` instruction type
shsms Dec 25, 2023
32b61cb
Add `if`, `le`, `plus` and `progn`
shsms Dec 26, 2023
034fb10
Keep result in stack only when necessary
shsms Dec 26, 2023
7fca3ed
Support `rest` arguments in `compile_1_arg_call`
shsms Dec 26, 2023
e271c52
Refactor lisp function-call compilers
shsms Dec 26, 2023
d5ca353
Use a map-lookup to identify function call compilers
shsms Dec 26, 2023
f7975c2
Refactor byte_compile into a submodule
shsms Dec 26, 2023
a4f41b1
Increment program counter after an instruction has been executed
shsms Dec 29, 2023
93d9a13
Update recursive fib instructions to use labels
shsms Dec 29, 2023
dd775aa
Rewrite relative positions to absolute on first encounter
shsms Dec 29, 2023
36a8da2
Support defun with required arguments
shsms Dec 29, 2023
6efb874
Improve printing of instructions
shsms Dec 29, 2023
4c348b8
Skip going to label instructions
shsms Dec 29, 2023
e4a9d35
Optimize if to compare and jump in same instruction
shsms Dec 29, 2023
30efaca
Fix bug in `if` compilation for non-comparison operations
shsms Dec 29, 2023
140fdbe
Add a `TulispContext::vm_eval_string` method
shsms Dec 29, 2023
26f0cbc
Rename `compile` → `compile_progn`
shsms Dec 30, 2023
c579bc9
Use a vector to store symbol bindings
shsms Dec 30, 2023
527b051
Wrap bytecode into a type
shsms Dec 30, 2023
94cc944
Add `Equal` instruction
shsms Dec 30, 2023
1fcacab
Specialize compilation of quoted values
shsms Dec 30, 2023
351493f
Add a `Cons` instruction and a compilation function
shsms Dec 30, 2023
1c95126
Extract `if` compiler into a `conditionals` module
shsms Dec 30, 2023
b4bd194
Switch to the more generic `jumpIfNil` idiom
shsms Dec 30, 2023
6e3458f
Add a `while` compiler
shsms Dec 30, 2023
af1744e
Ensure stack gets cleaned up
shsms Dec 30, 2023
18b8e7c
Move fn `mark_tail_calls` to the parse module
shsms Dec 30, 2023
0d017d5
Add a compiler for the `list` function
shsms Dec 30, 2023
d80ad84
Add a `Pop` instruction, to pop unused result after function call
shsms Dec 30, 2023
6af658c
Update `Bounce` value to hold the name of the function
shsms Dec 30, 2023
c1e06ec
Add instructions for beginning and ending a scope for a binding
shsms Dec 31, 2023
87b22cb
Bypass program counter increment when jumping
shsms Dec 31, 2023
5711be8
Support tail-call optimization
shsms Dec 31, 2023
4d5fe7a
Remove instruction limit
shsms Dec 31, 2023
3f4f2b5
Create a new (lexical) binding for each scope
shsms Dec 31, 2023
a7dfb21
Add a `cond` compiler
shsms Dec 31, 2023
024010d
Improve error handling of cond branches in `mark_tail_calls`
shsms Dec 31, 2023
b59eb2a
Return `nil` from `if` when there are no else-clauses
shsms Dec 31, 2023
b6012d5
Update arithmetic functions to accept arbitrary number of arguments
shsms Dec 31, 2023
14dc02f
Recursively optimize jump_if_nil to skip labels
shsms Dec 31, 2023
8a14d36
Add a JumpIfNilElsePop instruction
shsms Dec 31, 2023
5dc2664
Update comparison functions to accept arbitrary number of arguments
shsms Dec 31, 2023
c1ad925
Detect use of unbound symbols
shsms Jan 1, 2024
6d21090
Add a `let*` compiler, and add an alias to `let`
shsms Jan 1, 2024
a0cf459
Remove nested optimization of jumps
shsms Jan 1, 2024
026b09b
Make `defmacro` and `macroexpand` as no-op in the vm
shsms Jan 1, 2024
33e1784
Hold fundamental type values directly on stack
shsms Jan 1, 2024
2b21595
Remove `Deref` for `VMStackValue`
shsms Jan 1, 2024
0161756
Remove unnecessary assignments
shsms Jan 6, 2024
4536a8e
Add basic backquote compiler with splicing support for in-place lists
shsms Jan 7, 2024
e004f8d
Reverse operand order for `Cons` instruction
shsms Jan 7, 2024
0a9beb7
Add an `Append` instruction and a corresponding compiler function
shsms Jan 7, 2024
31390c3
Support splicing of variables in backquote expressions
shsms Jan 7, 2024
c70e9f5
Support splicing in cdr of cons cells
shsms Jan 7, 2024
b384932
Extract fundamental values when converting TulispObject to stack value
shsms Jan 7, 2024
6da645f
Handle splicing results of arbitrary operations in backquote compiler
shsms Jan 13, 2024
56cce48
Add cxr instructions and compiler
shsms Jan 13, 2024
72191a5
Update append to support multiple arguments
shsms Jan 13, 2024
2560707
Improve `scope` instruction performance by eliminate `Vec::pop`
shsms Jan 13, 2024
992b698
Improve arithmetic instruction performance by better stack access
shsms Jan 13, 2024
6eb07cb
Improve conditional jump performance by better stack access
shsms Jan 13, 2024
50926f9
Group `BinaryOp`s under a sub instruction set
shsms Jan 13, 2024
4a15ba9
Remove old hand-written bytecode programs
shsms Jan 13, 2024
0a6f397
Move interpreter into `bytecode` module
shsms Jan 13, 2024
68cde74
Move instructions out of the interpreter
shsms Jan 13, 2024
5281f36
Move compiler to bytecode/compiler
shsms Jan 13, 2024
b95a400
Simplify instruction imports
shsms Jan 13, 2024
e236927
Add a compiler for the `quote` special-form
shsms Jan 14, 2024
4a31204
Lisp-like printing of booleans
shsms Jan 14, 2024
235a116
Add a `Null` instruction and a `not` compiler
shsms Jan 14, 2024
cd1f874
Add an `and` compiler
shsms Jan 14, 2024
bd4f749
Merge branch 'main' into vm
shsms Jan 14, 2024
b7dab0c
Use the last value in `and` when all previous values are truthy
shsms Feb 3, 2024
e13fd1e
Add a `or` compiler
shsms Feb 3, 2024
e8b353f
Merge branch 'main' into vm
shsms Feb 3, 2024
68ba573
Remove separate binding storage, store values in TulispObjects directly
shsms Feb 9, 2024
3772812
Remove `VMStackValue`
shsms Feb 9, 2024
6ba18e2
Call functions from the tree walker when not known in the vm
shsms Feb 9, 2024
ce6ad56
Move `Bytecode` into a separate submodule
shsms Feb 9, 2024
43dd84f
Rename global namespace in Bytecode
shsms Feb 9, 2024
b997e41
Compile functions into their own instruction sets
shsms Feb 9, 2024
fca2845
Make instructions modifiable again
shsms Feb 9, 2024
a32ad2e
Revert back to better formatted output for fib.lisp
shsms Feb 9, 2024
288b5cf
Add a `LoadFile` instruction
shsms Feb 9, 2024
24e900e
Rewrite how function arguments are set at call time
shsms Feb 10, 2024
edb2526
Declare `defmacro` to be a noop in the VM
shsms Feb 10, 2024
120ca65
Compile calls to `set`
shsms Feb 10, 2024
42a4688
Display function names in errors and bytecode outputs
shsms Feb 10, 2024
aca2453
Implement `Display` for Bytecode, rather than a custom `print` method
shsms Feb 10, 2024
fe9914b
Use sequentially numbered labels instead of arbitrary ones
shsms Feb 10, 2024
85634f9
Load values only when necessary
shsms Feb 11, 2024
1c20eec
Allow side-effects of arguments when compiling numeric comparisons
shsms Feb 11, 2024
91d41e6
Evaluate keywords to themselves
shsms Feb 11, 2024
cba6c1d
Add unit tests for comparison of numbers
shsms Feb 11, 2024
0846af9
Add unit tests for equality predicates
shsms Feb 11, 2024
29d56cf
Allow side-effects of arguments when compiling arithmetic operations
shsms Feb 11, 2024
a60186a
Improve code generation for more functions wrt side effects
shsms Feb 11, 2024
9497cec
Offload runtime macro-expansions to the tree walking interpreter
shsms Feb 11, 2024
7a58d2d
Support optional and rest defun parameters
shsms Feb 12, 2024
6baa16d
Compile inplace calls to lambdas
shsms Feb 13, 2024
dda5dde
Drop references to expr before calling `compile_form`
shsms Feb 13, 2024
9be5947
Fix macroexpansion in unquote expressions
shsms Feb 13, 2024
1df9d9b
Keep result from rust calls only when necessary
shsms Feb 13, 2024
9d6cf2e
Rename `VMFunctions` -> `VMCompilers`
shsms Feb 13, 2024
1f38417
Expand macros when parsing unquote values
shsms Feb 13, 2024
038aa60
Reuse Compiler so previous compiled functions are not lost
shsms Feb 13, 2024
52e8165
Merge branch 'main' into vm
shsms Feb 14, 2024
b70cfbb
Compile calls to `plist-get`
shsms Feb 14, 2024
fdf77f3
Merge branch 'main' into vm
shsms Feb 14, 2024
e77198a
Merge branch 'main' into vm
shsms Feb 25, 2024
e552e14
Eliminate function lookup everytime when calling a function
shsms Feb 25, 2024
7f98778
Merge branch 'main' into vm
shsms Jul 20, 2024
268294d
Intern keywords once in context
shsms Aug 17, 2024
c4a9541
Simplify function execution from the VM
shsms Aug 17, 2024
6db95e4
Update VM error messages to match the tree walker
shsms Aug 17, 2024
e61b286
Merge branch 'main' into vm
shsms Aug 17, 2024
d8d7fde
Handle divide by zero
shsms Aug 17, 2024
69c2165
Update VM error messages to match tree walker
shsms Aug 17, 2024
4bccab7
Fix compilation issue for calls to `cons`
shsms Aug 17, 2024
fbf7eb1
Remove unused variable
shsms Aug 17, 2024
d182089
Group defun instructions and params into `CompiledDefun` type
shsms Aug 17, 2024
9de6244
Improve formatting
shsms Aug 17, 2024
0aeb44d
Improve variable names in `defun` compiler
shsms Aug 17, 2024
de62f34
Improve test, to be compatible with both VM and tree walker
shsms Aug 17, 2024
8ba26f6
Simplify `defun` compilation further
shsms Aug 17, 2024
d6ee93d
Check at compile-time that let variables are symbols
shsms Aug 17, 2024
9b7cafe
Add a `name` parameter to `CompiledDefun`
shsms Aug 17, 2024
fdba3fa
Reuse `Machine` instance from `TulispContext`
shsms Aug 17, 2024
b4a5db8
Move value setting functions to a separate module
shsms Aug 17, 2024
c751869
Support storing `CompiledDefun` as values
shsms Aug 18, 2024
8cf55ec
Check parameter count in function calls
shsms Aug 18, 2024
153bb85
Fix tail-call compilation
shsms Aug 23, 2024
1608b44
Fix backquote compilation edge-cases
shsms Aug 23, 2024
6a074ab
Update `load` test to not have non-functions in loaded files
shsms Aug 23, 2024
9c83ffa
Merge branch 'main' into vm
shsms Dec 7, 2025
2d6742b
Run tests against VM also
shsms Dec 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/fib-tail.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
"Tail recursive Fib"
(fib-impl n 0 1))

(princ (fib 30))
(print (fib 30))
5 changes: 3 additions & 2 deletions examples/fib.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
(+ (fib (- n 1))
(fib (- n 2)))))

(let ((tgt 30))
(print (format "\n (fib %d)\n %d\n" tgt (fib tgt))))
(let* ((tgt 30)
(res (fib tgt)))
(print (format "\n (fib %d)\n %d\n" tgt res)))
3 changes: 2 additions & 1 deletion examples/tail-recursion.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
(if (equal counter 0) lis
(build (- counter 1) (cons counter lis))))

(princ (build 1000000 '()))
(print (build 1000000 '()))

15 changes: 15 additions & 0 deletions src/bin/tulisp_vm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use std::env;

use tulisp::TulispContext;

fn main() {
let mut ctx = TulispContext::new();
let args: Vec<String> = env::args().skip(1).collect();

for arg in args {
if let Err(e) = ctx.vm_eval_file(&arg) {
println!("{}", e.format(&ctx));
std::process::exit(-1);
}
}
}
63 changes: 4 additions & 59 deletions src/builtin/functions/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::TulispValue;
use crate::cons::Cons;
use crate::context::Scope;
use crate::context::TulispContext;
use crate::destruct_bind;
use crate::destruct_eval_bind;
use crate::error::Error;
use crate::error::ErrorKind;
Expand All @@ -12,8 +13,8 @@ use crate::eval::eval;
use crate::eval::eval_and_then;
use crate::eval::eval_check_null;
use crate::lists;
use crate::parse::mark_tail_calls;
use crate::value::DefunParams;
use crate::{destruct_bind, list};
use std::convert::TryInto;
use std::rc::Rc;

Expand All @@ -33,63 +34,6 @@ pub(super) fn reduce_with(
Ok(first)
}

fn mark_tail_calls(
ctx: &mut TulispContext,
name: TulispObject,
body: TulispObject,
) -> Result<TulispObject, Error> {
if !body.consp() {
return Ok(body);
}
let ret = TulispObject::nil();
let mut body_iter = body.base_iter();
let mut tail = body_iter.next().unwrap();
for next in body_iter {
ret.push(tail)?;
tail = next;
}
if !tail.consp() {
return Ok(body);
}
let span = tail.span();
let ctxobj = tail.ctxobj();
let tail_ident = tail.car()?;
let tail_name_str = tail_ident.as_symbol()?;
let new_tail = if tail_ident.eq(&name) {
let ret_tail = TulispObject::nil().append(tail.cdr()?)?.to_owned();
list!(,ctx.intern("list")
,TulispValue::Bounce.into_ref(None)
,@ret_tail)?
} else if tail_name_str == "progn" || tail_name_str == "let" || tail_name_str == "let*" {
list!(,tail_ident ,@mark_tail_calls(ctx, name, tail.cdr()?)?)?
} else if tail_name_str == "if" {
destruct_bind!((_if condition then_body &rest else_body) = tail);
list!(,tail_ident
,condition.clone()
,mark_tail_calls(
ctx,
name.clone(),
list!(,then_body)?
)?.car()?
,@mark_tail_calls(ctx, name, else_body)?
)?
} else if tail_name_str == "cond" {
destruct_bind!((_cond &rest conds) = tail);
let mut ret = list!(,tail_ident)?;
for cond in conds.base_iter() {
destruct_bind!((condition &rest body) = cond);
ret = list!(,@ret
,list!(,condition.clone()
,@mark_tail_calls(ctx, name.clone(), body)?)?)?;
}
ret
} else {
tail
};
ret.push(new_tail.with_ctxobj(ctxobj).with_span(span))?;
Ok(ret)
}

pub(crate) fn add(ctx: &mut TulispContext) {
ctx.add_special_form("load", |ctx, args| {
destruct_eval_bind!(ctx, (filename) = args);
Expand Down Expand Up @@ -309,7 +253,8 @@ pub(crate) fn add(ctx: &mut TulispContext) {
"varitems inside a let-varlist should be a var or a binding: {}",
varitem
),
));
)
.with_trace(varitem.clone()));
};
}

Expand Down
42 changes: 42 additions & 0 deletions src/bytecode/bytecode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::{cell::RefCell, collections::HashMap, fmt, rc::Rc};

use super::Instruction;
use crate::{bytecode::compiler::VMDefunParams, TulispObject};

#[derive(Default, Clone)]
pub(crate) struct CompiledDefun {
pub(crate) name: TulispObject,
pub(crate) instructions: Rc<RefCell<Vec<Instruction>>>,
pub(crate) params: VMDefunParams,
}

#[derive(Default, Clone)]
pub(crate) struct Bytecode {
pub(crate) global: Rc<RefCell<Vec<Instruction>>>,
pub(crate) functions: HashMap<usize, CompiledDefun>, // key: fn_name.addr_as_usize()
}

impl fmt::Display for Bytecode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, instr) in self.global.borrow().iter().enumerate() {
write!(f, "\n{:<40} # {}", instr.to_string(), i)?;
}
for (name, func) in &self.functions {
write!(f, "\n\n{}:", name)?;
for (i, instr) in func.instructions.borrow().iter().enumerate() {
write!(f, "\n{:<40} # {}", instr.to_string(), i)?;
}
}
Ok(())
}
}

impl Bytecode {
pub(crate) fn new() -> Self {
Self::default()
}

pub(crate) fn import_functions(&mut self, other: &Bytecode) {
self.functions.extend(other.functions.clone());
}
}
Loading