Compare commits

...

11 Commits

73 changed files with 5934 additions and 3386 deletions
Generated
+2 -57
View File
@@ -52,28 +52,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "bitmatch"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a53e105d41966c9b4594b8e3b7cf8e81ae63cc83664880b049af8a11381a3ad"
dependencies = [
"boolean_expression",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "boolean_expression"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c33ef624481a2d2252fd352266c050e83203343d0884622f7ba09782abbfa83"
dependencies = [
"itertools",
"smallvec",
]
[[package]]
name = "clap"
version = "4.6.1"
@@ -105,7 +83,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.117",
"syn",
]
[[package]]
@@ -120,12 +98,6 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "heck"
version = "0.5.0"
@@ -138,20 +110,10 @@ version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "lysp"
version = "0.1.0"
dependencies = [
"bitmatch",
"clap",
"nom",
"thiserror",
@@ -196,29 +158,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.117"
@@ -247,7 +192,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
"syn",
]
[[package]]
-1
View File
@@ -7,4 +7,3 @@ edition = "2024"
thiserror = "2.0.18"
clap = { version = "4.6.1", features = ["derive"] }
nom = "8.0.0"
bitmatch = "0.1.1"
+94
View File
@@ -0,0 +1,94 @@
(defun err? (v) (= 'err (car v)))
(defun no-arguments () 1234)
(defun only-required (a b c) (+ a b c))
(defun only-optional (&optional a b c) (list a b c))
(defun only-rest (&rest r) (length r))
(defun required-and-optional (a b &optional c d) (list a b c d))
(defun required-and-rest (a b &rest r) (list a b r))
(defun optional-and-rest (&optional a b &rest r) (list a b r))
(defun required-and-optional-and-rest (a b &optional c d &rest r) (list a b c d r))
; no-arguments
(assert (= '(ok 1234) (eval '(no-arguments))))
(assert (err? (eval '(no-arguments 1))))
; only-required
(assert (= '(ok 6) (eval '(only-required 1 2 3))))
(assert (err? (eval '(only-required 1 2))))
; only-optional
(assert
(=
'(ok (nil nil nil))
(eval '(only-optional))
)
)
(assert
(=
'(ok (1 nil nil))
(eval '(only-optional 1))
)
)
(assert
(=
'(ok (1 2 nil))
(eval '(only-optional 1 2))
)
)
(assert
(=
'(ok (1 2 3))
(eval '(only-optional 1 2 3))
)
)
(assert (err? (eval '(only-optional 1 2 3 4))))
; only-rest
(assert (= 0 (only-rest)))
(assert (= 1 (only-rest 1)))
(assert (= 2 (only-rest 1 2)))
(assert (= 3 (only-rest 1 2 3)))
; required-and-optional
(assert (err? (eval '(required-and-optional))))
(assert (err? (eval '(required-and-optional 1))))
(assert
(=
'(1 2 nil nil)
(required-and-optional 1 2)
)
)
(assert
(=
'(1 2 3 nil)
(required-and-optional 1 2 3)
)
)
(assert
(=
'(1 2 3 4)
(required-and-optional 1 2 3 4)
)
)
(assert (err? (eval '(required-and-optional 1 2 3 4 5))))
; required-and-rest
(assert (err? (eval '(required-and-rest))))
(assert (err? (eval '(required-and-rest 1))))
(assert (= '(1 2 nil) (required-and-rest 1 2)))
(assert (= '(1 2 (3)) (required-and-rest 1 2 3)))
(assert (= '(1 2 (3 4 5)) (required-and-rest 1 2 3 4 5)))
; optional-and-rest
(assert (= '(nil nil nil) (optional-and-rest)))
(assert (= '(1 nil nil) (optional-and-rest 1)))
(assert (= '(1 2 nil) (optional-and-rest 1 2)))
(assert (= '(1 2 (3)) (optional-and-rest 1 2 3)))
(assert (= '(1 2 (3 4 5)) (optional-and-rest 1 2 3 4 5)))
; required-and-optional-and-rest
(assert (err? (eval '(required-and-optional-and-rest))))
(assert (err? (eval '(required-and-optional-and-rest 1))))
(assert (= '(1 2 nil nil nil) (required-and-optional-and-rest 1 2)))
(assert (= '(1 2 3 nil nil) (required-and-optional-and-rest 1 2 3)))
(assert (= '(1 2 3 4 nil) (required-and-optional-and-rest 1 2 3 4)))
(assert (= '(1 2 3 4 (5)) (required-and-optional-and-rest 1 2 3 4 5)))
(assert (= '(1 2 3 4 (5 6 7 8)) (required-and-optional-and-rest 1 2 3 4 5 6 7 8)))
+11
View File
@@ -0,0 +1,11 @@
(assert (= (->string 1234) "1234"))
(assert (= (->string '(1 2)) "(1 2)"))
(assert (= (->string #t) "#T"))
(assert (= 1234 (string->number "1234")))
(assert (= -1234 (string->number "-1234")))
(assert (= 0x1234 (string->number "1234" 16)))
(assert (= 0o1234 (string->number "1234" 8)))
(assert (= (/ 1 2) (string->number "0.5")))
(assert (= (/ -1 2) (string->number "-0.5")))
(assert (= (/ -1 2) (string->number "-5e-1")))
+12
View File
@@ -0,0 +1,12 @@
(print "Argument count:" (length *args*))
(print "Arguments as a list:" *args*)
(print "Iterated:")
(let
(index 0 current *args*)
(while current
(print index ":" (car current))
(setq index (+ index 1))
(setq current (cdr current))
)
)
+16
View File
@@ -0,0 +1,16 @@
; empty env
(setq custom-env (env/create nil))
(setq
custom-env-expr
'(progn
(defun my-function () 1234)
)
)
(defun my-function () 4321)
(assert (= 'ok (car (eval custom-env custom-env-expr))))
(assert (= 4321 (my-function)))
(assert (= '(ok 1234) (eval custom-env '(my-function))))
(assert (= 'err (car (eval custom-env '(print 1234))))) ;; print is not defined in custom-env
(env/load-prelude custom-env)
(assert (= '(ok nil) (eval custom-env '(print 1234))))
-2
View File
@@ -1,5 +1,3 @@
;; vi:ft=lisp:sw=2:ts=2
(defun factorial (x)
(if (= x 0)
1
+11
View File
@@ -0,0 +1,11 @@
(print (+ (car *args*) ":"))
(setq handle (fopen (car *args*)))
(loop
(let (data (fread handle))
(if data
(fwrite *stdout* data)
(break)
)
)
)
+28 -20
View File
@@ -1,32 +1,40 @@
;; vi:ft=lisp:sw=2:ts=2
;; loop, broken by (return)
(setq looped-loop (let (i 0)
(loop
(if (< i 10)
(setq i (+ i 1))
(return)
(setq
looped-loop
(let (i 0)
(loop
(if (< i 10)
(setq i (+ i 1))
(break)
)
)
i
)
i
))
)
;; while, broken prematurely
(setq looped-while (let (i 0)
(while (< i 20)
(setq i (+ i 1))
(if (= i 10) (return))
(setq
looped-while
(let (i 0)
(while (< i 20)
(setq i (+ i 1))
(if (= i 10) (break))
)
i
)
i
))
)
;; while, broken by condition
(setq looped-while-full (let (i 0)
(while (< i 10)
(setq i (+ i 1))
(setq
looped-while-full
(let (i 0)
(while (< i 10)
(setq i (+ i 1))
)
i
)
i
))
)
;; All loops execute the same count of times
(assert (= looped-loop looped-while looped-while-full))
(print "Test succeeded")
+55
View File
@@ -0,0 +1,55 @@
; quoting rules
(setq glob0 123)
(setq glob1 '(2 3 4))
(assert (= (list 1 2 3) '(1 2 3) `(1 2 3)))
(assert (= '(1 glob0 3) `(1 glob0 3)))
(assert (= '(1 123 3) `(1 ,glob0 3)))
(assert (= '(1 glob1 5) `(1 glob1 5)))
(assert (= '(1 (2 3 4) 5) `(1 ,glob1 5)))
(assert (= '(1 2 3 4 5) `(1 ,@glob1 5)))
(assert (= '(2 3 4 5) `(,@glob1 5)))
(assert (= '(1 2 3 4) `(1 ,@glob1)))
(assert (= '(2 3 4) `(,@glob1)))
(assert (= '((2 3 4)) `(,glob1)))
; Nested
(assert (= '((123 123) (123 123)) `((,glob0 ,glob0) (,glob0 ,glob0))))
(assert (= '(2 3 4 2 3 4) `(,@glob1 ,@glob1)))
(assert (= '((((2 3 4)))) `(((,glob1)))))
; those are prelude, but defined in lysp itself:
(print "The previously printed expression is AFTER this one in the code")
(compile-debug
(when #t
(print "a")
(print "b")
)
)
(runtime-debug
(when #t
(print "a")
(print "b")
)
)
(when 1
(print "a")
(print "b")
)
(unless nil
(print "c")
(print "d")
)
; catch macro
(print "Failing in a catch block...")
(catch
(print a)
e (print "Caught an error:" e)
)
(print "... doesn't fail the execution")
+45
View File
@@ -0,0 +1,45 @@
; add
(assert (= (+ 1 2 3) 6))
(assert (= (+) 0))
(assert (= (+ 1) 1))
; sub
(assert (= (- 1) -1))
(assert (= (- 1 2) -1))
; mul
(assert (= (*) 1))
(assert (= (* 2) 2))
(assert (= (* 2 3 4) 24))
; div
(assert (= (/ 1 0) #inf))
(assert (= (/ -1 0) -#inf))
(assert (= (/ 6 2) 3))
(assert (= (/ 12 3 2) 2))
(assert (= (/ 6 4) (/ 3 2)))
(assert (= (/ 4) (/ 25 100))) ; reciprocal
; rem
(assert (= (% 6 2) 0))
(assert (= (% 6 4) 2))
;;;; NaN/infinity handling
(assert (≠ #nan #nan))
(assert (= #inf #inf))
(assert (≠ -#inf #inf))
(assert (> #inf 0 -#inf))
;;;; Ordering
(assert (< 1 2 3))
(assert (not (< 1 1 2 3)))
(assert (<= 1 1 2 3 3))
(assert (> 3 2 1))
(assert (not (> 3 3 2 1)))
(assert (>= 3 3 2 1))
(assert (= 1 1 1 1))
(assert (not (= 1 1 2 1)))
(assert (≠ 1 2 3 4))
(assert (not (≠ 1 2 2 3)))
+34
View File
@@ -0,0 +1,34 @@
(defun cadr (x) (car (cdr x)))
(defun map-ok-err (f-ok f-err result)
(if (= (car result) 'ok)
`(ok ,(f-ok (cadr result)))
`(err ,(f-err (cadr result)))
)
)
(defun map-ok (f-ok result) (map-ok-err f-ok identity result))
(defun map-err (f-err result) (map-ok-err identity f-err result))
(defun repl-print (value)
(print "==>" value)
)
(defun repl-eval-print (expression)
(map-ok-err repl-print repl-eval-error (eval expression))
)
(defun repl-eval-error (error)
(print "Evaluation error:")
(print error)
)
(defun repl-read-error (error)
(print "Parse error:")
(print error)
)
(loop
(let (expression (read))
(if expression NIL (break))
(setq expression (unquote expression))
(map-ok-err repl-eval-print repl-read-error expression)
)
)
+23 -2
View File
@@ -1,5 +1,3 @@
;; vi:ft=lisp:sw=2:ts=2
(defun replace-argument (x) (let (x 1) x))
(defun replace-let ()
@@ -23,3 +21,26 @@
(assert (= (replace-argument 3) 1))
(assert (= (replace-let) 2))
(assert (= (reassignment-in-a-loop 4) 16))
;; capture upvalue
(assert
(=
123
(let
(loc0 123)
((lambda () loc0))
)
)
)
;; mutate upvalue
(assert
(=
321
(let
(loc0 123)
((lambda () (setq loc0 321)))
loc0
)
)
)
+52
View File
@@ -0,0 +1,52 @@
;; Conditional
(if #t 1 2)
(cond
[(= 1 2 3) #f]
[(= 1 1 1) #t]
[`otherwise 1234]
)
;; Loops
(while #f
1
2
3)
(loop
1
2
3
(break))
;; Functions
(defun a (x y z w) 1 2 3 x)
(lambda (x y z w) 1 2 3 x)
;; Macros
(defmacro my-quote (x) `(quote ,x))
(print (my-quote (a b c)))
;; vectors
#[1 2 3]
; alternate syntax for lists
[] () nil NIL
;; progn
(progn
1
2
3)
;; setq
(setq glob-value "my-global")
;; let
(let
(x 1 y 2)
x
)
(let* (x 1 y x) y)
;; Quoting
`(ok ,glob-value)
+564 -871
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -0,0 +1,44 @@
use crate::compile::{
block::{Compile, CompileContext, CompileValue},
error::CompileError,
syntax::{LetExpression, SetqExpression},
};
impl Compile for LetExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
cx.push_scope();
let mut binding_indices = vec![];
for binding in self.bindings.iter() {
let index = cx.bind_local(binding.identifier.clone())?;
let value = binding.value.compile(cx)?;
cx.push(value)?;
if self.sequential {
// Mark initialized
cx.init_local(index);
} else {
binding_indices.push(index);
}
}
if !self.sequential {
for index in binding_indices {
// Mark initialized
cx.init_local(index);
}
}
let value = self.body.compile(cx)?;
cx.pop_scope(Some(value))?;
Ok(CompileValue::Stack)
}
}
impl Compile for SetqExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
for assignment in self.pairs.iter() {
let value = assignment.value.compile(cx)?;
cx.compile_assign(assignment.identifier.clone(), value)?;
}
Ok(CompileValue::Nil)
}
}
+119
View File
@@ -0,0 +1,119 @@
use crate::compile::{
block::{Compile, CompileContext, CompileValue},
error::CompileError,
instruction::Emitted,
syntax::{CondExpression, IfExpression, LoopExpression, PrognExpression, WhileExpression},
};
impl Compile for WhileExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_entry = cx.new_label();
let label_exit = cx.new_label();
// Loop condition
cx.adjust_label(label_entry);
let condition_value = self.condition.compile(cx)?;
cx.push(condition_value)?;
cx.emit(Emitted::Branch(label_exit));
cx.enter_loop(label_entry, label_exit);
let body_value = self.body.compile(cx)?;
cx.discard(body_value);
cx.leave_loop();
cx.emit(Emitted::Jump(label_entry));
cx.adjust_label(label_exit);
Ok(CompileValue::Nil)
}
}
impl Compile for LoopExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_entry = cx.new_label();
let label_exit = cx.new_label();
cx.adjust_label(label_entry);
cx.enter_loop(label_entry, label_exit);
let body_value = self.body.compile(cx)?;
cx.discard(body_value);
cx.leave_loop();
cx.emit(Emitted::Jump(label_entry));
cx.adjust_label(label_exit);
Ok(CompileValue::Nil)
}
}
impl Compile for IfExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_false = cx.new_label();
let label_end = cx.new_label();
// Emit condition
let condition_value = self.condition.compile(cx)?;
cx.push(condition_value)?;
cx.emit(Emitted::Branch(label_false));
// True branch
let true_value = self.if_true.compile(cx)?;
cx.push(true_value)?;
cx.emit(Emitted::Jump(label_end));
// False branch
cx.adjust_label(label_false);
let false_value = match self.if_false.as_ref() {
Some(expr) => expr.compile(cx)?,
None => CompileValue::Nil,
};
cx.push(false_value)?;
cx.adjust_label(label_end);
Ok(CompileValue::Stack)
}
}
impl Compile for CondExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_end = cx.new_label();
for condition in self.arms.iter() {
let label_otherwise = cx.new_label();
let condition_value = condition.condition.compile(cx)?;
cx.push(condition_value)?;
cx.emit(Emitted::Branch(label_otherwise));
// Condition true
let then_value = condition.then.compile(cx)?;
cx.push(then_value)?;
cx.emit(Emitted::Jump(label_end));
// Condition false
cx.adjust_label(label_otherwise);
}
let otherwise_value = if let Some(otherwise_arm) = self.otherwise_arm.as_ref() {
otherwise_arm.compile(cx)?
} else {
CompileValue::Nil
};
cx.push(otherwise_value)?;
cx.adjust_label(label_end);
Ok(CompileValue::Stack)
}
}
impl Compile for PrognExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let value = self.body.compile(cx)?;
cx.discard(value);
Ok(CompileValue::Nil)
}
}
pub(super) fn compile_break(cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
cx.emit_break()?;
Ok(CompileValue::Nil)
}
pub(super) fn compile_continue(cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
cx.emit_continue()?;
Ok(CompileValue::Nil)
}
+309
View File
@@ -0,0 +1,309 @@
use crate::{
compile::{
block::{Compile, CompileContext, CompileValue},
error::CompileError,
syntax::{
CallExpression, DefmacroExpression, DefunExpression, Expression, FunctionBody,
LambdaExpression,
},
},
vm::instruction::{ArgumentCount, Instruction},
};
fn builtin_identifier_callee(identifier: &str) -> Option<Instruction> {
match identifier {
"+" => Some(Instruction::Add),
"-" => Some(Instruction::Sub),
"*" | "·" => Some(Instruction::Mul),
"/" | "÷" => Some(Instruction::Div),
"%" => Some(Instruction::Mod),
"=" => Some(Instruction::Eq),
"/=" | "" => Some(Instruction::Ne),
">" => Some(Instruction::Gt),
"<" => Some(Instruction::Lt),
">=" | "" => Some(Instruction::Ge),
"<=" | "" => Some(Instruction::Le),
"not" => Some(Instruction::Not),
_ => None,
}
}
impl Compile for FunctionBody {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
for expression in self.head.iter() {
cx.compile_statement(expression)?;
}
self.tail.compile(cx)
}
}
impl Compile for CallExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let argument_count = ArgumentCount::try_from(self.arguments.len())
.map_err(|_| CompileError::TooManyArgumentsInCall)?;
let instruction = if let Expression::Identifier(callee) = self.callee.as_ref() {
if let Some(instruction) = builtin_identifier_callee(callee.as_ref()) {
instruction
} else {
Instruction::Call
}
} else {
Instruction::Call
};
if instruction == Instruction::Call {
let callee = self.callee.compile(cx)?;
cx.push(callee)?;
cx.guard_argument();
}
for expression in self.arguments.iter() {
let value = expression.compile(cx)?;
cx.push(value)?;
cx.guard_argument();
}
cx.emit(instruction);
cx.emit(argument_count);
let mut unguard_count = usize::from(argument_count);
if instruction == Instruction::Call {
unguard_count += 1;
}
for _ in 0..unguard_count {
cx.unguard_argument();
}
Ok(CompileValue::Stack)
}
}
impl Compile for LambdaExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let function = cx.compile_function(
Some("lambda".into()),
self.docstring.clone(),
&self.signature,
&self.body,
)?;
Ok(CompileValue::LocalFunction(function))
}
}
impl Compile for DefunExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let function = cx.compile_function(
Some(self.name.clone()),
self.docstring.clone(),
&self.signature,
&self.body,
)?;
cx.compile_set_global(self.name.clone(), CompileValue::LocalFunction(function))?;
Ok(CompileValue::Nil)
}
}
impl Compile for DefmacroExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let function = cx.compile_function(
Some(self.name.clone()),
self.docstring.clone(),
&self.signature,
&self.body,
)?;
cx.compile_declare_macro(self.name.clone(), CompileValue::LocalFunction(function))?;
Ok(CompileValue::Nil)
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use crate::{
compile::{
UpvalueDef,
block::{CompileValue, test_compile},
function::FunctionSignature,
syntax::{
Assignment, CallExpression, DefunExpression, Expression, FunctionBody,
LambdaExpression, LetExpression,
},
},
vm::instruction::{Instruction, LocalId},
};
#[test]
fn test_lambda_capture_compile() {
// (let (x 1)
// ((lambda (y) (+ x y)) 2))
let e = Expression::Let(LetExpression {
sequential: false,
bindings: vec![Assignment {
identifier: "x".into(),
value: Rc::new(Expression::IntegerLiteral(1.into())),
}],
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Call(CallExpression {
callee: Rc::new(Expression::Lambda(LambdaExpression {
docstring: None,
signature: FunctionSignature {
required_arguments: vec!["y".into()],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Call(CallExpression {
callee: Rc::new(Expression::Identifier("+".into())),
arguments: vec![
Rc::new(Expression::Identifier("x".into())),
Rc::new(Expression::Identifier("y".into())),
],
})),
},
})),
arguments: vec![Rc::new(Expression::IntegerLiteral(2.into()))],
})),
},
});
let (cx, _v) = test_compile(&e).unwrap();
assert_eq!(cx.current, 0);
assert_eq!(cx.function_blocks.len(), 2);
let root_function = cx.function_blocks[0].to_bytecode().unwrap();
let lambda_function = cx.function_blocks[1].to_bytecode().unwrap();
// lambda
assert_eq!(
lambda_function.instructions.as_ref(),
&[
Instruction::GetUpvalue.into(),
0,
Instruction::GetLocal.into(),
0,
Instruction::Add.into(),
2,
Instruction::Return.into(),
]
);
assert_eq!(lambda_function.required_count, 1);
assert_eq!(lambda_function.optional_count, 0);
assert!(!lambda_function.has_rest);
assert_eq!(
lambda_function.upvalues.as_ref(),
&[UpvalueDef {
index: LocalId::from(0),
is_local: true
}]
);
// root
assert_eq!(
root_function.instructions.as_ref(),
&[
// (let ...
// ... (x 1) ...
Instruction::PushInteger.into(),
1,
0,
// ... ((lambda (y) (+ x y)) 2) ...
// (lambda ...)
Instruction::PushConstant.into(),
0,
0,
Instruction::MakeClosure.into(),
// 2
Instruction::PushInteger.into(),
2,
0,
// ((lambda ...) 2)
Instruction::Call.into(),
1,
// (let ... ) exit
Instruction::SetTemp.into(),
Instruction::CloseUpvalue.into(),
Instruction::GetTemp.into(),
]
);
}
#[test]
fn test_lambda_compile() {
let e = Expression::Lambda(LambdaExpression {
docstring: None,
signature: FunctionSignature {
required_arguments: vec!["a".into()],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::IntegerLiteral(1234.into())),
},
});
let (cx, v) = test_compile(&e).unwrap();
assert_eq!(cx.current, 0);
assert!(cx.function_blocks[0].instructions.is_empty());
let CompileValue::LocalFunction(index) = v else {
panic!("lambda did not compile to a function value")
};
let lambda_function = cx.function_blocks[index].to_bytecode().unwrap();
assert_eq!(
lambda_function.instructions.as_ref(),
&[
Instruction::PushInteger.into(),
210,
4,
Instruction::Return.into(),
]
);
}
#[test]
fn test_compile_defun() {
let e = Expression::Defun(DefunExpression {
name: "my-function".into(),
docstring: None,
signature: FunctionSignature {
required_arguments: vec!["a".into()],
optional_arguments: vec![],
rest_argument: None,
},
body: FunctionBody {
head: vec![],
tail: Rc::new(Expression::Identifier("a".into())),
},
});
let (cx, _v) = test_compile(&e).unwrap();
assert_eq!(cx.current, 0);
assert_eq!(cx.function_blocks.len(), 2);
let root_function = cx.function_blocks[0].to_bytecode().unwrap();
let defun_function = cx.function_blocks[1].to_bytecode().unwrap();
// identifier + function
assert_eq!(root_function.constants.len(), 2);
assert_eq!(
root_function.instructions.as_ref(),
&[
// Identifier
Instruction::PushConstant.into(),
0,
0,
// Function
Instruction::PushConstant.into(),
1,
0,
Instruction::SetGlobal.into(),
]
);
// inner function
assert!(defun_function.constants.is_empty());
assert_eq!(defun_function.required_count, 1);
assert_eq!(defun_function.optional_count, 0);
assert!(!defun_function.has_rest);
assert_eq!(
defun_function.instructions.as_ref(),
&[
// a
Instruction::GetLocal.into(),
0,
Instruction::Return.into(),
]
);
}
}
+63
View File
@@ -0,0 +1,63 @@
use crate::compile::{
block::{Compile, CompileContext, CompileValue},
error::CompileError,
syntax::Expression,
};
mod binding;
mod flow;
mod function;
impl Compile for Expression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
match self {
Self::Nil => Ok(CompileValue::Nil),
Self::Break => flow::compile_break(cx),
Self::Continue => flow::compile_continue(cx),
Self::Call(call) => call.compile(cx),
Self::If(if_) => if_.compile(cx),
Self::Cond(cond) => cond.compile(cx),
Self::Let(let_) => let_.compile(cx),
Self::Setq(setq) => setq.compile(cx),
Self::While(while_) => while_.compile(cx),
Self::Loop(loop_) => loop_.compile(cx),
Self::Defun(defun) => defun.compile(cx),
Self::Lambda(lambda) => lambda.compile(cx),
Self::Progn(progn) => progn.compile(cx),
Self::Defmacro(defmacro) => defmacro.compile(cx),
Self::Identifier(identifier) => Ok(CompileValue::Identifier(identifier.clone())),
&Self::IntegerLiteral(value) => Ok(CompileValue::Integer(value)),
&Self::BooleanLiteral(value) => Ok(CompileValue::Boolean(value)),
Self::StringLiteral(value) => Ok(CompileValue::String(value.clone())),
Self::Vector(vector) => Ok(CompileValue::Value(vector.clone().into())),
Self::Quote(value) => Ok(CompileValue::Quote(value.clone())),
Self::SyntaxError(_) => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use crate::compile::{
block::{CompileValue, test_compile},
syntax::Expression,
};
#[test]
fn test_nil_compile() {
let e = Expression::Nil;
let (cx, v) = test_compile(&e).unwrap();
assert_eq!(cx.current, 0);
assert!(cx.function_blocks[0].instructions.is_empty());
assert_eq!(v, CompileValue::Nil);
}
#[test]
fn test_integer_literal_compile() {
let e = Expression::IntegerLiteral(1.into());
let (cx, v) = test_compile(&e).unwrap();
assert_eq!(cx.current, 0);
assert!(cx.function_blocks[0].instructions.is_empty());
assert_eq!(v, CompileValue::Integer(1.into()));
}
}
+16 -2
View File
@@ -1,11 +1,25 @@
#![coverage(off)]
use crate::compile::syntax::ParseError;
use crate::{compile::syntax::ParseError, vm::value::IdentifierValue};
#[derive(Debug, thiserror::Error)]
#[derive(Debug, PartialEq, thiserror::Error)]
pub enum CompileError {
#[error("parse error: {0:?}")]
Parse(Vec<ParseError>),
#[error("block has too many local variables")]
TooManyLocals,
#[error("module has too many constants")]
TooManyConstants,
#[error("function call has too many arguments")]
TooManyArgumentsInCall,
#[error("(break) used outside of a loop")]
BreakOutsideOfLoop,
#[error("(continue) used outside of a loop")]
ContinueOutsideOfLoop,
#[error("cannot emit branch instruction from {0} to {1}")]
CannotEmitBranch(usize, usize),
#[error("undefined local value reference: {0}")]
UndefinedLocalReference(IdentifierValue),
}
impl From<Vec<ParseError>> for CompileError {
+16 -4
View File
@@ -1,10 +1,10 @@
use std::rc::Rc;
use crate::vm::value::IdentifierValue;
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionSignature {
pub required_arguments: Vec<Rc<str>>,
pub optional_arguments: Vec<Rc<str>>,
pub rest_argument: Option<Rc<str>>,
pub required_arguments: Vec<IdentifierValue>,
pub optional_arguments: Vec<IdentifierValue>,
pub rest_argument: Option<IdentifierValue>,
}
impl FunctionSignature {
@@ -37,4 +37,16 @@ impl FunctionSignature {
None
}
}
pub fn min_arity(&self) -> usize {
self.required_arguments.len()
}
pub fn max_arity(&self) -> usize {
if self.rest_argument.is_none() {
self.required_arguments.len() + self.optional_arguments.len()
} else {
usize::MAX
}
}
}
+35 -3
View File
@@ -1,10 +1,16 @@
use crate::vm::instruction::Instruction;
use crate::vm::instruction::{ArgumentCount, ConstantId, ImmediateInteger, Instruction, LocalId};
#[derive(Debug, Clone, Copy)]
pub enum Emitted {
Branch(u32),
Jump(u32),
Branch(usize),
Jump(usize),
Instruction(Instruction),
LocalId(LocalId),
ConstantId(ConstantId),
ArgumentCount(ArgumentCount),
ImmediateInteger(ImmediateInteger),
Bool(bool),
// Instruction(Instruction),
}
impl From<Instruction> for Emitted {
@@ -12,3 +18,29 @@ impl From<Instruction> for Emitted {
Self::Instruction(value)
}
}
impl From<LocalId> for Emitted {
fn from(value: LocalId) -> Self {
Self::LocalId(value)
}
}
impl From<ConstantId> for Emitted {
fn from(value: ConstantId) -> Self {
Self::ConstantId(value)
}
}
impl From<ArgumentCount> for Emitted {
fn from(value: ArgumentCount) -> Self {
Self::ArgumentCount(value)
}
}
impl From<ImmediateInteger> for Emitted {
fn from(value: ImmediateInteger) -> Self {
Self::ImmediateInteger(value)
}
}
impl From<bool> for Emitted {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
+5 -11
View File
@@ -1,20 +1,14 @@
mod block;
mod codegen;
mod error;
mod function;
mod instruction;
mod module;
mod syntax;
mod value;
pub use error::CompileError;
pub use function::FunctionSignature;
pub use module::CompilationModule;
pub use syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody, LambdaExpression,
ParseError, ParseErrorKind,
};
pub mod syntax;
#[derive(Default, Clone)]
pub struct CompileOptions {
pub trace_compile: bool,
}
pub use block::{Compile, CompileContext, UpvalueDef};
pub use error::CompileError;
-107
View File
@@ -1,107 +0,0 @@
use std::{collections::HashMap, rc::Rc};
use crate::{
compile::{
CompileOptions,
block::{CompiledFunction, FunctionBlock},
error::CompileError,
function::FunctionSignature,
syntax::{DefmacroExpression, FunctionBody},
value::CompileConstant,
},
vm::{
instruction::ConstantId,
module::{Module, ModuleConstant},
pool::Pool,
},
};
#[derive(Default)]
pub struct CompilationModule {
pub(crate) constant_pool: Pool<CompileConstant, { ConstantId::BITS }>,
pub(crate) local_functions: HashMap<u32, CompiledFunction>,
pub(crate) options: CompileOptions,
macros: HashMap<Rc<str>, DefmacroExpression>,
local_function_index: u32,
root: Option<u32>,
}
impl CompilationModule {
pub fn new(options: CompileOptions) -> Self {
Self {
options,
..Default::default()
}
}
pub fn define_macro(&mut self, defmacro: DefmacroExpression) {
self.macros.insert(defmacro.name.clone(), defmacro);
}
pub fn constant(&mut self, value: CompileConstant) -> Result<ConstantId, CompileError> {
match self.constant_pool.key(value) {
Some(key) => Ok(key),
None => todo!(),
}
}
pub fn compile_function(
&mut self,
signature: FunctionSignature,
body: &FunctionBody,
root: bool,
) -> Result<u32, CompileError> {
let index = self.local_function_index;
if root && self.root.is_some() {
todo!()
}
self.local_function_index += 1;
let mut function = FunctionBlock::new(signature);
function.compile_body(self, body)?;
let function = function.resolve_labels(self.options.trace_compile);
self.local_functions.insert(index, function);
if root {
self.root = Some(index);
}
Ok(index)
}
pub fn compile_module(self) -> Result<Module, CompileError> {
// Emit all function code first
let mut function_offsets = HashMap::new();
let mut instructions = vec![];
let root = self.root.unwrap();
for (index, function) in self.local_functions.into_iter() {
function_offsets.insert(index, instructions.len());
instructions.extend(function.instructions.into_iter().map(u32::from));
}
let entry = *function_offsets.get(&root).unwrap();
let constants = self
.constant_pool
.into_iter()
.map(|(value, key)| {
(
key,
match value {
CompileConstant::Integer(value) => ModuleConstant::Integer(value),
CompileConstant::Identifier(identifier) => {
ModuleConstant::Identifier(identifier)
}
CompileConstant::LocalFunction(index, required_count) => {
let address = *function_offsets.get(&index).unwrap();
ModuleConstant::LocalFunction(address, required_count)
}
CompileConstant::String(value) => ModuleConstant::String(value),
CompileConstant::Value(value) => ModuleConstant::Value(value),
},
)
})
.collect();
Ok(Module {
constants,
instructions,
entry,
})
}
}
+16 -16
View File
@@ -1,16 +1,16 @@
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::CollectErrors,
compile::syntax::{
CollectErrors, Expression, FunctionBody,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
},
vm::value::{ConsCell, Keyword, Value},
vm::value::{ConsCell, IdentifierValue, Keyword, Value},
};
#[derive(Debug, PartialEq)]
pub struct Assignment {
pub identifier: Rc<str>,
pub identifier: IdentifierValue,
pub value: Rc<Expression>,
}
@@ -138,9 +138,9 @@ mod tests {
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::{Assignment, LetExpression, SetqExpression},
compile::syntax::{
Assignment, Expression, FunctionBody, LetExpression, SetqExpression,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
},
vm::value::{ConsCell, Keyword, Value},
};
@@ -149,7 +149,7 @@ mod tests {
fn test_parse_let() {
let v = Value::list_or_nil([
Value::Keyword(Keyword::Let),
Value::list_or_nil([Value::Identifier("a".into()), Value::Integer(1)]),
Value::list_or_nil([Value::Identifier("a".into()), Value::Number(1.into())]),
Value::Nil,
]);
let e = Expression::parse(&v).unwrap();
@@ -159,7 +159,7 @@ mod tests {
sequential: false,
bindings: vec![Assignment {
identifier: "a".into(),
value: Rc::new(Expression::IntegerLiteral(1))
value: Rc::new(Expression::IntegerLiteral(1.into()))
},],
body: FunctionBody {
head: vec![],
@@ -171,9 +171,9 @@ mod tests {
Value::Keyword(Keyword::LetStar),
Value::list_or_nil([
Value::Identifier("a".into()),
Value::Integer(1),
Value::Number(1.into()),
Value::Identifier("b".into()),
Value::Integer(2),
Value::Number(2.into()),
]),
Value::Nil,
]);
@@ -185,11 +185,11 @@ mod tests {
bindings: vec![
Assignment {
identifier: "a".into(),
value: Rc::new(Expression::IntegerLiteral(1))
value: Rc::new(Expression::IntegerLiteral(1.into()))
},
Assignment {
identifier: "b".into(),
value: Rc::new(Expression::IntegerLiteral(2))
value: Rc::new(Expression::IntegerLiteral(2.into()))
}
],
body: FunctionBody {
@@ -259,7 +259,7 @@ mod tests {
let v = Value::list_or_nil([
Value::Keyword(Keyword::Setq),
Value::Identifier("a".into()),
Value::Integer(1),
Value::Number(1.into()),
Value::Identifier("b".into()),
]);
let e = Expression::parse(&v).unwrap_err();
@@ -275,7 +275,7 @@ mod tests {
);
let v = Value::list_or_nil([
Value::Keyword(Keyword::Setq),
Value::Integer(1),
Value::Number(1.into()),
Value::Identifier("a".into()),
]);
let e = Expression::parse(&v).unwrap_err();
+5 -7
View File
@@ -1,9 +1,8 @@
use std::rc::Rc;
use crate::{
compile::{
Expression, ParseError,
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
compile::syntax::{
CollectErrors, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
},
vm::value::{ConsCell, Value},
};
@@ -58,9 +57,8 @@ mod tests {
use std::rc::Rc;
use crate::{
compile::{
CallExpression, Expression, ParseError,
syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind},
compile::syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
},
vm::value::Value,
};
@@ -78,7 +76,7 @@ mod tests {
);
// Syntax errors
let v = Value::Identifier("a".into()).cons(Value::Integer(1));
let v = Value::Identifier("a".into()).cons(Value::Number(1.into()));
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
+52 -52
View File
@@ -1,9 +1,8 @@
use std::rc::Rc;
use crate::{
compile::{
Expression, ParseError,
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
compile::syntax::{
CollectErrors, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
},
vm::value::{ConsCell, Keyword, Value},
};
@@ -217,12 +216,9 @@ mod tests {
use std::rc::Rc;
use crate::{
compile::{
Expression, ParseError,
syntax::{
CondExpression, CondExpressionArm, ExpectedWhat, ExpectedWhere, IfExpression,
ParseErrorKind,
},
compile::syntax::{
CondExpression, CondExpressionArm, ExpectedWhat, ExpectedWhere, Expression,
IfExpression, ParseError, ParseErrorKind,
},
vm::value::{ConsCell, Keyword, Value},
};
@@ -230,8 +226,8 @@ mod tests {
#[test]
fn test_parse_cond() {
// without &otherwise branch
let b0 = Value::list_or_nil([Value::Boolean(true), Value::Identifier("a".into())]);
let b1 = Value::list_or_nil([Value::Boolean(false), Value::Identifier("b".into())]);
let b0 = Value::list_or_nil([Value::Boolean(true.into()), Value::Identifier("a".into())]);
let b1 = Value::list_or_nil([Value::Boolean(false.into()), Value::Identifier("b".into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
@@ -239,11 +235,11 @@ mod tests {
&Expression::Cond(CondExpression {
arms: vec![
CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(true)),
condition: Rc::new(Expression::BooleanLiteral(true.into())),
then: Rc::new(Expression::Identifier("a".into()))
},
CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(false)),
condition: Rc::new(Expression::BooleanLiteral(false.into())),
then: Rc::new(Expression::Identifier("b".into()))
}
],
@@ -252,7 +248,7 @@ mod tests {
);
// with &otherwise branch
let b0 = Value::list_or_nil([Value::Boolean(true), Value::Identifier("a".into())]);
let b0 = Value::list_or_nil([Value::Boolean(true.into()), Value::Identifier("a".into())]);
let b1 = Value::list_or_nil([
Value::Keyword(Keyword::Otherwise),
Value::Identifier("b".into()),
@@ -263,7 +259,7 @@ mod tests {
e.as_ref(),
&Expression::Cond(CondExpression {
arms: vec![CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(true)),
condition: Rc::new(Expression::BooleanLiteral(true.into())),
then: Rc::new(Expression::Identifier("a".into()))
},],
otherwise_arm: Some(Rc::new(Expression::Identifier("b".into())))
@@ -297,7 +293,7 @@ mod tests {
}]
);
let b0 = Value::list_or_nil([Value::Boolean(false)]);
let b0 = Value::list_or_nil([Value::Boolean(false.into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -310,7 +306,7 @@ mod tests {
)
}]
);
let b0 = Value::Boolean(false);
let b0 = Value::Boolean(false.into());
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -336,7 +332,7 @@ mod tests {
)
}]
);
let b0 = Value::Keyword(Keyword::Otherwise).cons(Value::Integer(1));
let b0 = Value::Keyword(Keyword::Otherwise).cons(Value::Number(1.into()));
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -349,7 +345,7 @@ mod tests {
)
}]
);
let b0 = Value::Boolean(false).cons(Value::Boolean(false));
let b0 = Value::Boolean(false.into()).cons(Value::Boolean(false.into()));
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -362,20 +358,10 @@ mod tests {
)
}]
);
let b0 = Value::list_or_nil([Value::Boolean(false), Value::Integer(1), Value::Integer(2)]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: b0,
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Integer(2)]))
}]
);
let b0 = Value::list_or_nil([
Value::Keyword(Keyword::Otherwise),
Value::Integer(1),
Value::Integer(2),
Value::Boolean(false.into()),
Value::Number(1.into()),
Value::Number(2.into()),
]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
@@ -383,10 +369,24 @@ mod tests {
e,
vec![ParseError {
input: b0,
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Integer(2)]))
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Number(2.into())]))
}]
);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Integer(1)]);
let b0 = Value::list_or_nil([
Value::Keyword(Keyword::Otherwise),
Value::Number(1.into()),
Value::Number(2.into()),
]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: b0,
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Number(2.into())]))
}]
);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Number(1.into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone(), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -396,8 +396,8 @@ mod tests {
error: ParseErrorKind::extraneous(&b0)
}]
);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Integer(1)]);
let b1 = Value::list_or_nil([Value::Boolean(false), Value::Integer(1)]);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Number(1.into())]);
let b1 = Value::list_or_nil([Value::Boolean(false.into()), Value::Number(1.into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -414,15 +414,15 @@ mod tests {
// without false branch
let v = Value::list_or_nil([
Value::Keyword(Keyword::If),
Value::Boolean(true),
Value::Integer(1),
Value::Boolean(true.into()),
Value::Number(1.into()),
]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
e.as_ref(),
&Expression::If(IfExpression {
condition: Rc::new(Expression::BooleanLiteral(true)),
if_true: Rc::new(Expression::IntegerLiteral(1)),
condition: Rc::new(Expression::BooleanLiteral(true.into())),
if_true: Rc::new(Expression::IntegerLiteral(1.into())),
if_false: None
})
);
@@ -430,17 +430,17 @@ mod tests {
// with false branch
let v = Value::list_or_nil([
Value::Keyword(Keyword::If),
Value::Boolean(false),
Value::Integer(1),
Value::Integer(2),
Value::Boolean(false.into()),
Value::Number(1.into()),
Value::Number(2.into()),
]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
e.as_ref(),
&Expression::If(IfExpression {
condition: Rc::new(Expression::BooleanLiteral(false)),
if_true: Rc::new(Expression::IntegerLiteral(1)),
if_false: Some(Rc::new(Expression::IntegerLiteral(2)))
condition: Rc::new(Expression::BooleanLiteral(false.into())),
if_true: Rc::new(Expression::IntegerLiteral(1.into())),
if_false: Some(Rc::new(Expression::IntegerLiteral(2.into())))
})
);
@@ -457,7 +457,7 @@ mod tests {
)
}]
);
let v = Value::list_or_nil([Value::Keyword(Keyword::If), Value::Boolean(true)]);
let v = Value::list_or_nil([Value::Keyword(Keyword::If), Value::Boolean(true.into())]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
@@ -471,10 +471,10 @@ mod tests {
);
let v = Value::list_or_nil([
Value::Keyword(Keyword::If),
Value::Boolean(true),
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Boolean(true.into()),
Value::Number(1.into()),
Value::Number(2.into()),
Value::Number(3.into()),
]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -482,7 +482,7 @@ mod tests {
vec![ParseError {
input: v,
error: ParseErrorKind::ExtraneousExpressions(Rc::new(ConsCell::end(
Value::Integer(3)
Value::Number(3.into())
)))
}]
);
+8 -2
View File
@@ -93,13 +93,19 @@ impl ParseErrorKind {
}
}
pub fn collect_extraneous<I: IntoIterator<Item = Value>>(iter: I) -> Self {
pub fn collect_extraneous<I: IntoIterator<Item = Value>>(iter: I) -> Self
where
I::IntoIter: ExactSizeIterator + DoubleEndedIterator,
{
Self::extraneous(&Value::list_or_nil(iter))
}
pub fn try_collect_extraneous<E, I: IntoIterator<Item = Result<Value, E>>>(
iter: I,
) -> Result<Self, E> {
) -> Result<Self, E>
where
I::IntoIter: ExactSizeIterator + DoubleEndedIterator,
{
Ok(Self::extraneous(&Value::try_list_or_nil(iter)?))
}
}
+129 -14
View File
@@ -2,10 +2,13 @@ use std::rc::Rc;
use crate::{
compile::{
Expression, FunctionSignature, ParseError,
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
function::FunctionSignature,
syntax::{
CollectErrors, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
maybe_docstring,
},
},
vm::value::{ConsCell, Keyword, Value},
vm::value::{ConsCell, IdentifierValue, Keyword, StringValue, Value},
};
#[derive(Debug, PartialEq)]
@@ -14,9 +17,17 @@ pub struct FunctionBody {
pub tail: Rc<Expression>,
}
#[derive(Debug, PartialEq)]
pub struct LambdaExpression {
pub docstring: Option<StringValue>,
pub signature: FunctionSignature,
pub body: FunctionBody,
}
#[derive(Debug, PartialEq)]
pub struct DefunExpression {
pub name: Rc<str>,
pub name: IdentifierValue,
pub docstring: Option<StringValue>,
pub signature: FunctionSignature,
pub body: FunctionBody,
}
@@ -183,6 +194,29 @@ impl PrognExpression {
}
}
impl LambdaExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda),
),
});
};
let ConsCell(car, cdr) = value.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let (cdr, docstring) = maybe_docstring(cdr);
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self {
docstring,
signature,
body,
})
}
}
impl DefunExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
@@ -216,11 +250,12 @@ impl DefunExpression {
});
};
let ConsCell(car, cdr) = cdr.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let (cdr, docstring) = maybe_docstring(cdr);
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self {
name: identifier.clone(),
docstring,
signature,
body,
})
@@ -238,6 +273,12 @@ impl CollectErrors<ParseError> for FunctionBody {
}
}
impl CollectErrors<ParseError> for LambdaExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
}
}
impl CollectErrors<ParseError> for DefunExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
@@ -256,34 +297,108 @@ mod tests {
use crate::{
compile::{
Expression, FunctionBody, FunctionSignature, ParseError,
syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind},
function::FunctionSignature,
syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody,
LambdaExpression, ParseError, ParseErrorKind,
},
},
vm::value::{Keyword, Value},
};
#[test]
fn test_parse_lambda() {
let args = Value::list_or_nil([
Value::Identifier("a".into()),
Value::Keyword(Keyword::Optional),
Value::Identifier("b".into()),
Value::Keyword(Keyword::Rest),
Value::Identifier("c".into()),
]);
let body = Value::list_or_nil([
Value::Identifier("+".into()),
Value::Identifier("a".into()),
Value::Number(1.into()),
]);
let lambda = Value::Keyword(Keyword::Lambda).cons(args.cons(body.cons(Value::Nil)));
let expr = Expression::parse(&lambda).unwrap();
assert_eq!(
expr.as_ref(),
&Expression::Lambda(LambdaExpression {
docstring: None,
signature: FunctionSignature {
required_arguments: vec!["a".into()],
optional_arguments: vec!["b".into()],
rest_argument: Some("c".into())
},
body: FunctionBody {
head: vec![],
tail: Expression::Call(CallExpression {
callee: Expression::Identifier("+".into()).into(),
arguments: vec![
Rc::new(Expression::Identifier("a".into())),
Rc::new(Expression::IntegerLiteral(1.into()))
]
})
.into()
}
})
);
// syntax errors
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda)]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda)
)
}]
);
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda), Value::Nil]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::Expression,
ExpectedWhere::AfterArgumentList(Keyword::Lambda)
)
}]
);
}
#[test]
fn test_parse_function_body() {
let v = Value::list_or_nil([Value::Integer(1)]);
let v = Value::list_or_nil([Value::Number(1.into())]);
let e = FunctionBody::parse(&v, &v, Keyword::Lambda).unwrap();
assert_eq!(
e,
FunctionBody {
head: vec![],
tail: Rc::new(Expression::IntegerLiteral(1))
tail: Rc::new(Expression::IntegerLiteral(1.into()))
}
);
let v = Value::list_or_nil([Value::Integer(1), Value::Integer(2), Value::Integer(3)]);
let v = Value::list_or_nil([
Value::Number(1.into()),
Value::Number(2.into()),
Value::Number(3.into()),
]);
let e = FunctionBody::parse(&v, &v, Keyword::Lambda).unwrap();
assert_eq!(
e,
FunctionBody {
head: vec![
Rc::new(Expression::IntegerLiteral(1)),
Rc::new(Expression::IntegerLiteral(2))
Rc::new(Expression::IntegerLiteral(1.into())),
Rc::new(Expression::IntegerLiteral(2.into()))
],
tail: Rc::new(Expression::IntegerLiteral(3))
tail: Rc::new(Expression::IntegerLiteral(3.into()))
}
);
@@ -435,7 +550,7 @@ mod tests {
)
}
);
let args = Value::list_or_nil([Value::Boolean(false)]);
let args = Value::list_or_nil([Value::Boolean(false.into())]);
let e = FunctionSignature::parse(&args, &args).unwrap_err();
assert_eq!(
e,
-117
View File
@@ -1,117 +0,0 @@
use crate::{
compile::{
FunctionBody, FunctionSignature, ParseError,
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
},
vm::value::{ConsCell, Keyword, Value},
};
#[derive(Debug, PartialEq)]
pub struct LambdaExpression {
pub signature: FunctionSignature,
pub body: FunctionBody,
}
impl LambdaExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda),
),
});
};
let ConsCell(car, cdr) = value.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self { signature, body })
}
}
impl CollectErrors<ParseError> for LambdaExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use crate::{
compile::{
CallExpression, Expression, FunctionBody, FunctionSignature, LambdaExpression,
ParseError,
syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind},
},
vm::value::{Keyword, Value},
};
#[test]
fn test_parse_lambda() {
let args = Value::list_or_nil([
Value::Identifier("a".into()),
Value::Keyword(Keyword::Optional),
Value::Identifier("b".into()),
Value::Keyword(Keyword::Rest),
Value::Identifier("c".into()),
]);
let body = Value::list_or_nil([
Value::Identifier("+".into()),
Value::Identifier("a".into()),
Value::Integer(1),
]);
let lambda = Value::Keyword(Keyword::Lambda).cons(args.cons(body.cons(Value::Nil)));
let expr = Expression::parse(&lambda).unwrap();
assert_eq!(
expr.as_ref(),
&Expression::Lambda(LambdaExpression {
signature: FunctionSignature {
required_arguments: vec!["a".into()],
optional_arguments: vec!["b".into()],
rest_argument: Some("c".into())
},
body: FunctionBody {
head: vec![],
tail: Expression::Call(CallExpression {
callee: Expression::Identifier("+".into()).into(),
arguments: vec![
Rc::new(Expression::Identifier("a".into())),
Rc::new(Expression::IntegerLiteral(1))
]
})
.into()
}
})
);
// syntax errors
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda)]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::ArgumentList,
ExpectedWhere::AfterKeyword(Keyword::Lambda)
)
}]
);
let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda), Value::Nil]);
let e = Expression::parse(&lambda).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: lambda,
error: ParseErrorKind::Expected(
ExpectedWhat::Expression,
ExpectedWhere::AfterArgumentList(Keyword::Lambda)
)
}]
);
}
}
+3 -3
View File
@@ -1,9 +1,9 @@
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::CollectErrors,
compile::syntax::{
CollectErrors, Expression, FunctionBody,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
},
vm::value::{ConsCell, Keyword, Value},
};
+11 -6
View File
@@ -1,16 +1,19 @@
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, FunctionBody, FunctionSignature, ParseError, ParseErrorKind,
syntax::CollectErrors,
function::FunctionSignature,
syntax::{
CollectErrors, FunctionBody,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
maybe_docstring,
},
},
vm::value::{ConsCell, Keyword, Value},
vm::value::{ConsCell, IdentifierValue, Keyword, StringValue, Value},
};
#[derive(Debug, PartialEq)]
pub struct DefmacroExpression {
pub name: Rc<str>,
pub name: IdentifierValue,
pub docstring: Option<StringValue>,
pub signature: FunctionSignature,
pub body: FunctionBody,
}
@@ -50,9 +53,11 @@ impl DefmacroExpression {
let ConsCell(car, cdr) = cdr.as_ref();
let signature = FunctionSignature::parse(car, input)?;
let (cdr, docstring) = maybe_docstring(cdr);
let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?;
Ok(Self {
name: identifier.clone(),
docstring,
signature,
body,
})
+40 -24
View File
@@ -1,13 +1,14 @@
use std::rc::Rc;
use crate::vm::value::{ConsCell, Keyword, Value, ValueString};
use crate::vm::value::{
BooleanValue, ConsCell, IdentifierValue, Keyword, NumberValue, StringValue, Value, Vector,
};
mod binding;
mod call;
mod condition;
mod error;
mod function;
mod lambda;
mod loops;
mod macros;
@@ -16,17 +17,16 @@ pub use call::*;
pub use condition::*;
pub use error::*;
pub use function::*;
pub use lambda::*;
pub use loops::*;
pub use macros::*;
#[derive(Debug, PartialEq)]
pub enum Expression {
Nil,
BooleanLiteral(bool),
StringLiteral(ValueString),
IntegerLiteral(i64),
Identifier(Rc<str>),
BooleanLiteral(BooleanValue),
StringLiteral(StringValue),
IntegerLiteral(NumberValue),
Identifier(IdentifierValue),
Lambda(LambdaExpression),
Defun(DefunExpression),
Call(CallExpression),
@@ -40,7 +40,9 @@ pub enum Expression {
While(WhileExpression),
Loop(LoopExpression),
Progn(PrognExpression),
Return,
Vector(Rc<Vector>),
Break,
Continue,
}
impl Expression {
@@ -63,15 +65,17 @@ impl Expression {
fn parse_inner(value: &Value) -> Rc<Self> {
match value {
Value::Nil => Rc::new(Self::Nil),
Value::Boolean(value) => Rc::new(Self::BooleanLiteral(*value)),
Value::Integer(value) => Rc::new(Self::IntegerLiteral(*value)),
Value::Vector(vector) => Rc::new(Self::Vector(vector.clone())),
Value::String(value) => Rc::new(Self::StringLiteral(value.clone())),
Value::Vector(_vector) => todo!(),
Value::Identifier(value) => Rc::new(Self::Identifier(value.clone())),
Value::Quasi(_value) => todo!("{value}"),
Value::Unquote(_value) => todo!("Unquote {_value}"),
Value::UnquoteSplice(_value) => todo!("UnquoteSplice {_value}"),
Value::Quote(value) => Rc::new(Self::Quote(value.clone())),
Value::Nil => Rc::new(Self::Nil),
&Value::Number(value) => Rc::new(Self::IntegerLiteral(value)),
&Value::Boolean(value) => Rc::new(Self::BooleanLiteral(value)),
Value::Identifier(value) => Rc::new(Self::Identifier(value.clone())),
Value::Cons(cons) => {
let ConsCell(car, cdr) = cons.as_ref();
match car {
@@ -112,14 +116,16 @@ impl Expression {
Value::Keyword(Keyword::While) => {
Self::map_or(WhileExpression::parse(cdr, value), Expression::While)
}
Value::Keyword(Keyword::Return) => Rc::new(Self::Return),
Value::Keyword(Keyword::Break) => Rc::new(Self::Break),
Value::Keyword(Keyword::Continue) => Rc::new(Self::Continue),
_ => Self::map_or(CallExpression::parse(cons, value), Expression::Call),
}
}
Value::Keyword(_) => todo!(),
Value::NativeFunction(_) | Value::BytecodeFunction(_) | Value::OpaqueValue(_) => {
todo!()
}
Value::Keyword(keyword) => todo!("Error here: keyword {keyword}"),
Value::Closure(_) => todo!("Error here"),
Value::Function(_) => todo!("Error here"),
Value::NativeFunction(_) => todo!("Error here"),
Value::NativeValue(_) => todo!("Error here"),
}
}
}
@@ -143,7 +149,9 @@ impl CollectErrors<ParseError> for Expression {
Self::Loop(cloop) => cloop.collect_errors(errors),
Self::Progn(progn) => progn.collect_errors(errors),
Self::Nil
| Self::Return
| Self::Vector(_)
| Self::Break
| Self::Continue
| Self::IntegerLiteral(_)
| Self::Identifier(_)
| Self::BooleanLiteral(_)
@@ -153,6 +161,14 @@ impl CollectErrors<ParseError> for Expression {
}
}
fn maybe_docstring(input: &Value) -> (&Value, Option<StringValue>) {
if let Some((Value::String(docstring), cdr)) = input.uncons_ref() {
(cdr, Some(docstring.clone()))
} else {
(input, None)
}
}
#[cfg(test)]
mod tests {
use crate::{
@@ -166,13 +182,13 @@ mod tests {
let e = Expression::parse(&v).unwrap();
assert_eq!(e.as_ref(), &Expression::Nil);
let v = Value::Integer(1234);
let v = Value::Number(1234.into());
let e = Expression::parse(&v).unwrap();
assert_eq!(e.as_ref(), &Expression::IntegerLiteral(1234));
assert_eq!(e.as_ref(), &Expression::IntegerLiteral(1234.into()));
let v = Value::Boolean(false);
let v = Value::Boolean(false.into());
let e = Expression::parse(&v).unwrap();
assert_eq!(e.as_ref(), &Expression::BooleanLiteral(false));
assert_eq!(e.as_ref(), &Expression::BooleanLiteral(false.into()));
let v = Value::Identifier("a".into());
let e = Expression::parse(&v).unwrap();
@@ -187,7 +203,7 @@ mod tests {
Value::Keyword(Keyword::If),
inner_if.clone(),
inner_lambda.clone(),
Value::Integer(3),
Value::Number(3.into()),
]);
let e = Expression::parse(&outer_if).unwrap_err();
assert_eq!(
+6
View File
@@ -0,0 +1,6 @@
use crate::vm::value::Value;
#[derive(Debug, PartialEq)]
pub struct VectorExpression {
pub elements: Vec<Value>,
}
+8 -8
View File
@@ -19,14 +19,14 @@ pub enum CompileValue {
Nil,
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub enum CompileConstant {
Integer(i64),
LocalFunction(u32, usize),
Identifier(Rc<str>),
String(ValueString),
Value(Rc<Value>),
}
// #[derive(Debug, Hash, PartialEq, Eq)]
// pub enum CompileConstant {
// Integer(i64),
// LocalFunction(u32, usize),
// Identifier(Rc<str>),
// String(ValueString),
// Value(Rc<Value>),
// }
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum BuiltinFunction {
+171 -40
View File
@@ -1,53 +1,184 @@
#![coverage(off)]
use std::{io, rc::Rc};
use std::{fmt, io, rc::Rc};
use crate::{
compile::CompileError,
vm::{instruction::InstructionError, machine::InstructionPointer},
vm::{
Value,
instruction::{Instruction, InstructionDecodeError},
value::{BytecodeFunction, IdentifierValue},
},
};
#[derive(Debug, thiserror::Error)]
pub enum EvalError {
#[error("machine error: {0}")]
Machine(MachineError),
#[error("compilation error: {0}")]
Compile(#[from] CompileError),
#[error("syntax error: {0}")]
Syntax(nom::Err<nom::error::Error<Rc<str>>, nom::error::Error<Rc<str>>>),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("{error}")]
pub struct MachineErrorAt {
// TODO ip where the error occured
pub error: MachineError,
pub location: Option<MachineErrorLocation>,
}
#[derive(Debug)]
pub struct MachineErrorLocation {
pub function: Rc<BytecodeFunction>,
pub offset: usize,
}
#[derive(Debug, PartialEq, thiserror::Error)]
#[error("expected {expected}, got {got}")]
pub struct ValueConversionError {
pub expected: String,
pub got: Value,
}
#[derive(Debug, thiserror::Error)]
#[error("{ip:?}: {error}")]
pub struct MachineError {
pub ip: Option<InstructionPointer>,
pub error: MachineErrorKind,
pub enum ReadError {
#[error("{0}")]
Lexical(nom::Err<nom::error::Error<String>, nom::error::Error<String>>),
#[error("{0}")]
Io(io::Error),
}
#[derive(Debug, thiserror::Error)]
pub enum MachineErrorKind {
#[error("Instruction error: {0}")]
Instruction(#[from] InstructionError),
#[error("Instruction out of bounds: {0}")]
InstructionOutOfBounds(InstructionPointer),
#[error("Instruction pointer is undefined")]
UndefinedInstructionPointer,
#[error("Data stack underflowed")]
ValueStackUnderflow,
#[error("Data stack overflowed")]
ValueStackOverflow,
#[error("Call stack underflowed")]
CallStackUnderflow,
#[error("Call stack overflowed")]
#[derive(Debug, PartialEq)]
pub struct ArgumentCountError {
pub function: Rc<BytecodeFunction>,
pub actual: usize,
}
#[derive(Debug, PartialEq, thiserror::Error)]
pub enum MachineError {
// VM itself
#[error("instruction pointer is undefined")]
InstructionPointerUndefined,
#[error("instruction pointer is out of bounds")]
InstructionPointerOutOfBounds,
#[error("instruction fetch failed")]
InstructionFetch,
#[error("instruction decode error: {0}")]
InstructionDecode(#[from] InstructionDecodeError),
#[error("data stack overflowed")]
DataStackOverflow,
#[error("data stack underflowed")]
DataStackUnderflow,
#[error("call stack overflowed")]
CallStackOverflow,
#[error("Unbound identifier: {0:?}")]
UnboundIdentifier(Rc<str>),
#[error("Expected at least {0} arguments, got {1}")]
ArgumentCountMismatch(usize, usize),
#[error("Invalid argument provided in a call")]
InvalidArgument,
#[error("Aborted: {0}")]
Aborted(String),
#[error("call stack underflowed")]
CallStackUnderflow,
#[error("undefined upvalue reference")]
UndefinedUpvalueReference,
#[error("undefined local reference")]
UndefinedLocalReference,
#[error("undefined constant reference")]
UndefinedConstantReference,
#[error("unbound identifier reference: {0}")]
UnboundIdentifier(IdentifierValue),
#[error("invalid {0} argument: {1}")]
InvalidInstructionArgument(Instruction, ValueConversionError),
#[error("invalid branch target: {0}{1:+}")]
InvalidBranchTarget(usize, isize),
#[error("GET_TEMP with an empty temp register")]
TempRegisterEmpty,
#[error("{0}")]
ArgumentCount(ArgumentCountError),
// Syntax+evaluation
// #[error("evaluation error: {0}")]
// EvaluationError(EvalError),
#[error("invalid argument count")]
InvalidArgumentCount,
#[error("aborted: {0}")]
Abort(Rc<str>),
#[error("value conversion error: {0}")]
ValueConversion(#[from] ValueConversionError),
#[error("syntax error: {0}")]
Read(ReadError),
#[error("compile error: {0}")]
Compile(#[from] CompileError),
}
impl MachineError {
pub fn at(self, location: Option<MachineErrorLocation>) -> MachineErrorAt {
MachineErrorAt {
error: self,
location,
}
}
pub fn at_unknown(self) -> MachineErrorAt {
self.at(None)
}
}
impl MachineErrorAt {
pub fn at_unknown(error: MachineError) -> Self {
Self::at(error, None)
}
pub fn at(error: MachineError, location: Option<MachineErrorLocation>) -> Self {
Self { error, location }
}
}
impl MachineErrorLocation {
pub fn disassemble_chunk(&self, address: usize, before: usize, after: usize, arrow: bool) {
self.function.disassemble(address, before, after, arrow);
}
}
impl fmt::Display for MachineErrorLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:p}+{}", self.function, self.offset)
}
}
impl PartialEq for ReadError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Lexical(a), Self::Lexical(b)) => a == b,
(Self::Io(a), Self::Io(b)) => a.raw_os_error() == b.raw_os_error(),
_ => false,
}
}
}
impl fmt::Display for ArgumentCountError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let too_few = self.actual < self.function.required_count;
write!(
f,
"too {} arguments for function {}: expected ",
if too_few { "few" } else { "many" },
self.function
)?;
if self.function.has_rest {
let singular = self.function.required_count == 1;
write!(
f,
"at least {} argument{}",
self.function.required_count,
if singular { "" } else { "s" }
)?;
} else if self.function.optional_count > 0 {
write!(
f,
"{}-{} arguments",
self.function.required_count,
self.function.required_count + self.function.optional_count
)?;
} else {
let singular = self.function.required_count == 1;
write!(
f,
"{} argument{}",
self.function.required_count,
if singular { "" } else { "s" }
)?;
}
write!(f, ", got {}", self.actual)?;
Ok(())
}
}
+10 -5
View File
@@ -1,14 +1,19 @@
#![feature(
coverage_attribute,
debug_closure_helpers,
unboxed_closures,
iter_next_chunk,
exact_size_is_empty
exact_size_is_empty,
exact_div
)]
//
// pub mod error;
pub mod parse;
pub mod read;
#[macro_use]
pub mod util;
pub mod compile;
pub mod error;
pub mod parse;
pub mod read;
pub mod util;
pub mod vm;
+73 -40
View File
@@ -3,25 +3,29 @@ use std::{
io::{self, BufReader},
path::{Path, PathBuf},
process::ExitCode,
rc::Rc,
str::FromStr,
};
use clap::Parser;
use lysp::{
compile::{CompileError, CompileOptions, ParseError},
error::{EvalError, MachineErrorKind},
compile::{CompileOptions, syntax::ParseError},
error::{MachineError, MachineErrorAt},
read::{InteractiveReader, ModuleReader, read},
util::Either,
vm::{env::Environment, machine::Machine, prelude, value::Value},
vm::{
env::Environment,
machine::Machine,
prelude,
value::{ClosureValue, Value},
},
};
#[derive(Debug, thiserror::Error)]
#[error("{0}")]
enum Error {
Machine(#[from] MachineErrorKind),
Eval(#[from] EvalError),
Machine(#[from] MachineErrorAt),
Io(#[from] io::Error),
Compile(#[from] CompileError),
#[error("Error already printed")]
Printed,
}
@@ -30,6 +34,10 @@ enum Error {
pub enum Trace {
Compile,
Execute,
Call,
Return,
Stack,
Macro,
}
impl FromStr for Trace {
@@ -39,6 +47,10 @@ impl FromStr for Trace {
match s {
"compile" => Ok(Self::Compile),
"execute" => Ok(Self::Execute),
"call" => Ok(Self::Call),
"return" => Ok(Self::Return),
"stack" => Ok(Self::Stack),
"macro" => Ok(Self::Macro),
_ => Err(format!("Unknown trace flag: {s:?}")),
}
}
@@ -53,6 +65,7 @@ struct Args {
)]
trace: Vec<Trace>,
module: Option<PathBuf>,
arguments: Vec<String>,
}
fn print_syntax_errors(errors: &[ParseError]) {
@@ -72,30 +85,28 @@ fn print_syntax_errors(errors: &[ParseError]) {
}
}
fn handle_eval_error(value: Option<&Value>, input: EvalError) -> Error {
match input {
EvalError::Machine(error) => {
if let Some(value) = value {
eprintln!("Error in expression:");
eprintln!();
eprintln!(" {value}:");
eprintln!();
}
eprintln!(":: {}", error.error);
fn handle_eval_error(value: Option<&Value>, input: MachineErrorAt) -> Error {
if let Some(value) = value {
eprintln!("Error in expression:");
eprintln!();
eprintln!(" {value}");
eprintln!();
}
match input.error {
MachineError::Compile(error) => {
eprintln!("Compilation error:");
eprintln!();
if let Some(ip) = error.ip.as_ref() {
ip.module.dump(Some(ip.address), 8);
}
eprintln!(":: {error}");
}
EvalError::Compile(CompileError::Parse(errors)) => {
print_syntax_errors(&errors);
MachineError::Read(error) => {
eprintln!("Syntax error:");
eprintln!();
eprintln!(":: {error}");
}
error => {
if let Some(value) = value {
eprintln!("Error in expression:");
eprintln!();
eprintln!(" {value}:");
eprintln!();
if let Some(location) = input.location {
eprintln!("At location {location}:");
location.disassemble_chunk(location.offset, 5, 1, true);
}
eprintln!(":: {error}");
}
@@ -103,7 +114,7 @@ fn handle_eval_error(value: Option<&Value>, input: EvalError) -> Error {
Error::Printed
}
fn handle_module_error(input: Either<EvalError, Vec<ParseError>>) -> Error {
fn handle_module_error(input: Either<MachineErrorAt, Vec<ParseError>>) -> Error {
match input {
Either::Left(error) => handle_eval_error(None, error),
Either::Right(errors) => {
@@ -116,10 +127,15 @@ fn handle_module_error(input: Either<EvalError, Vec<ParseError>>) -> Error {
fn eval(
options: &CompileOptions,
vm: &mut Machine,
env: &mut Environment,
env: &Rc<Environment>,
value: Value,
) -> Option<Value> {
let result = vm.eval_value(options.clone(), env, value.clone());
let result = vm.evaluate_value(
options.clone(),
Some("interactive".into()),
env,
value.clone(),
);
match result {
Ok(r) => Some(r),
Err(error) => {
@@ -132,7 +148,7 @@ fn eval(
fn run_interactive(
compile_options: &CompileOptions,
vm: &mut Machine,
env: &mut Environment,
env: &Rc<Environment>,
) -> Result<(), Error> {
let mut reader = InteractiveReader::new("> ", ">> ");
@@ -158,18 +174,22 @@ fn run_interactive(
fn run_module<P: AsRef<Path>>(
compile_options: &CompileOptions,
vm: &mut Machine,
env: &mut Environment,
env: &Rc<Environment>,
path: P,
) -> Result<(), Error> {
let path = path.as_ref();
let name = format!("{}", path.display());
let reader = BufReader::new(File::open(path)?);
let module_reader = ModuleReader::new(reader);
let module = match module_reader.compile(compile_options, env) {
Ok(module) => module,
let module_reader = ModuleReader::new(reader, vm.trace_macros);
let function = match module_reader.compile(Some(name.into()), compile_options, env) {
Ok(function) => function,
Err(error) => return Err(handle_module_error(error)),
};
match vm.eval_module(env, module) {
let closure = ClosureValue {
function,
upvalues: vec![],
};
match vm.evaluate_closure(env, closure, 0) {
Ok(_) => Ok(()),
Err(error) => Err(handle_eval_error(None, error)),
}
@@ -182,11 +202,24 @@ fn main() -> ExitCode {
trace_compile: args.trace.contains(&Trace::Compile),
};
vm.trace_instructions = args.trace.contains(&Trace::Execute);
let mut env = Environment::default();
prelude::load(&mut env);
vm.trace_calls = args.trace.contains(&Trace::Call);
vm.trace_returns = args.trace.contains(&Trace::Return);
vm.trace_stack = args.trace.contains(&Trace::Stack);
vm.trace_macros = args.trace.contains(&Trace::Macro);
let env = Rc::new(Environment::default());
prelude::load(&env);
let mut arguments = vec![];
if let Some(script) = args.module.as_ref() {
arguments.push(format!("{}", script.display()));
}
arguments.extend(args.arguments);
env.set_global_value(
"*args*",
Value::list_or_nil(arguments.into_iter().map(|arg| Value::String(arg.into()))),
);
let result = match args.module.as_ref() {
Some(module) => run_module(&compile_options, &mut vm, &mut env, module),
None => run_interactive(&compile_options, &mut vm, &mut env),
Some(module) => run_module(&compile_options, &mut vm, &env, module),
None => run_interactive(&compile_options, &mut vm, &env),
};
match result {
Ok(()) => ExitCode::SUCCESS,
+79 -41
View File
@@ -14,20 +14,20 @@ use nom::{
sequence::{delimited, preceded},
};
use crate::vm::value::{Keyword, Value};
use crate::vm::value::{Keyword, NumberValue, Value, Vector};
struct IdentifierHead;
struct IdentifierTail;
impl FindToken<char> for IdentifierHead {
fn find_token(&self, token: char) -> bool {
token.is_alphabetic() || "~!@$%^&*-=_+<>?/|".contains(token)
token.is_alphabetic() || "~!@$%^&*-=_+<>?/|≠≥≤·".contains(token)
}
}
impl FindToken<char> for IdentifierTail {
fn find_token(&self, token: char) -> bool {
token.is_alphanumeric() || "~!@$%^&*-=_+<>?/|".contains(token)
token.is_alphanumeric() || "~!@$%^&*-=_+<>?/|≠≥≤·".contains(token)
}
}
@@ -144,12 +144,27 @@ fn parse_integer(input: &str) -> IResult<&str, Value> {
alt((parse_integer_hex, parse_integer_oct, parse_integer_dec)),
),
|(minus, value)| {
i64::try_from(value).map(|value| Value::Integer(if minus { -value } else { value }))
i64::try_from(value)
.map(|value| Value::Number((if minus { -value } else { value }).into()))
},
)
.parse(input)
}
fn parse_number(input: &str) -> IResult<&str, Value> {
alt((
value(Value::Number(NumberValue::nan()), tag("#NAN")),
value(Value::Number(NumberValue::nan()), tag("#NaN")),
value(Value::Number(NumberValue::nan()), tag("#nan")),
value(Value::Number(NumberValue::infinity()), tag("#inf")),
value(Value::Number(NumberValue::infinity()), tag("#INF")),
value(Value::Number(NumberValue::neg_infinity()), tag("-#inf")),
value(Value::Number(NumberValue::neg_infinity()), tag("-#INF")),
parse_integer,
))
.parse(input)
}
fn parse_identifier(input: &str) -> IResult<&str, &str> {
recognize(preceded(
one_of(IdentifierHead),
@@ -168,27 +183,30 @@ fn parse_identifier_or_keyword_or_nil(input: &str) -> IResult<&str, Value> {
}
fn parse_list_or_nil(input: &str) -> IResult<&str, Value> {
map(
delimited(
char('('),
many0(preceded(skip_comment_and_whitespace, parse_value)),
preceded(skip_comment_and_whitespace, char(')')),
),
Value::list_or_nil,
)
.parse(input)
let list_parens = delimited(
char('('),
many0(preceded(skip_comment_and_whitespace, parse_value)),
preceded(skip_comment_and_whitespace, char(')')),
);
let list_brackets = delimited(
char('['),
many0(preceded(skip_comment_and_whitespace, parse_value)),
preceded(skip_comment_and_whitespace, char(']')),
);
alt((list_parens, list_brackets))
.map(Value::list_or_nil)
.parse(input)
}
fn parse_boolean(input: &str) -> IResult<&str, Value> {
map(
alt((
value(true, tag("#t")),
value(true, tag("#T")),
value(false, tag("#f")),
value(false, tag("#F")),
)),
Value::Boolean,
)
alt((
value(true, tag("#t")),
value(true, tag("#T")),
value(false, tag("#f")),
value(false, tag("#F")),
))
.map(Into::into)
.parse(input)
}
@@ -287,6 +305,12 @@ fn parse_unquote(input: &str) -> IResult<&str, Value> {
.map(Value::Unquote)
.parse(input)
}
fn parse_unquote_splice(input: &str) -> IResult<&str, Value> {
preceded(tag(",@"), parse_value)
.map(Rc::new)
.map(Value::UnquoteSplice)
.parse(input)
}
fn parse_quote(input: &str) -> IResult<&str, Value> {
preceded(char('\''), parse_value)
@@ -295,14 +319,28 @@ fn parse_quote(input: &str) -> IResult<&str, Value> {
.parse(input)
}
fn parse_vector(input: &str) -> IResult<&str, Value> {
delimited(
tag("#["),
many0(preceded(skip_comment_and_whitespace, parse_value)),
preceded(skip_comment_and_whitespace, char(']')),
)
.map(Vector::from_iter)
.map(Into::into)
.map(Value::Vector)
.parse(input)
}
pub fn parse_value(input: &str) -> IResult<&str, Value> {
alt((
parse_list_or_nil,
parse_vector,
parse_boolean,
parse_unquote_splice,
parse_quote,
parse_quasi,
parse_unquote,
parse_integer,
parse_number,
parse_string,
parse_identifier_or_keyword_or_nil,
))
@@ -380,33 +418,33 @@ mod tests {
// Dec path
let (r, v) = parse_integer("1234\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(1234));
assert_eq!(v, Value::Number(1234.into()));
let (r, v) = parse_integer("+1234\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(1234));
assert_eq!(v, Value::Number(1234.into()));
let (r, v) = parse_integer("-1234\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(-1234));
assert_eq!(v, Value::Number((-1234).into()));
// Oct path
let (r, v) = parse_integer("0o1234\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(0o1234));
assert_eq!(v, Value::Number(0o1234.into()));
let (r, v) = parse_integer("+0O1234\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(0o1234));
assert_eq!(v, Value::Number(0o1234.into()));
let (r, v) = parse_integer("-0o1234\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(-0o1234));
assert_eq!(v, Value::Number((-0o1234).into()));
// Hex path
let (r, v) = parse_integer("0x1234AF\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(0x1234AF));
assert_eq!(v, Value::Number(0x1234AF.into()));
let (r, v) = parse_integer("+0X1234AF\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(0x1234AF));
assert_eq!(v, Value::Number(0x1234AF.into()));
let (r, v) = parse_integer("-0x1234AF\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(-0x1234AF));
assert_eq!(v, Value::Number((-0x1234AF).into()));
// Illegal path
let e = parse_integer("0x12V34").unwrap_err();
@@ -455,7 +493,7 @@ mod tests {
Value::list_or_nil([
Value::Identifier("a".into()),
Value::Identifier("b".into()),
Value::Integer(-1),
Value::Number((-1).into()),
])
);
}
@@ -464,26 +502,26 @@ mod tests {
fn test_boolean() {
let (r, v) = parse_boolean("#t\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Boolean(true));
assert_eq!(v, Value::Boolean(true.into()));
let (r, v) = parse_boolean("#T\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Boolean(true));
assert_eq!(v, Value::Boolean(true.into()));
let (r, v) = parse_boolean("#F\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Boolean(false));
assert_eq!(v, Value::Boolean(false.into()));
let (r, v) = parse_boolean("#F\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Boolean(false));
assert_eq!(v, Value::Boolean(false.into()));
}
#[test]
fn test_value() {
let (r, v) = parse_value("+123\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(123));
assert_eq!(v, Value::Number(123.into()));
let (r, v) = parse_value("-0x123\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Integer(-0x123));
assert_eq!(v, Value::Number((-0x123).into()));
let (r, v) = parse_value("abcdef-ghijkl\n").unwrap();
assert_eq!(r, "\n");
assert_eq!(v, Value::Identifier("abcdef-ghijkl".into()));
@@ -505,8 +543,8 @@ mod tests {
v,
Value::list_or_nil([
Value::Identifier("f".into()),
Value::Boolean(true),
Value::Integer(-0x1)
Value::Boolean(true.into()),
Value::Number((-0x1).into())
])
);
}
+70
View File
@@ -0,0 +1,70 @@
; Convenience flow control macros
(defmacro when (condition body-head &rest body)
"If condition is true, evaluates the expressions in body, otherwise returns nil"
`(if ,condition (progn ,body-head ,@body))
)
(defmacro unless (condition body-head &rest body)
"If condition is false, evaluates the expressions in body, otherwise returns nil"
`(if (not ,condition) (progn ,body-head ,@body))
)
; Result handling functions
(defun result/ok? (x)
"Returns #t if x is an ok"
(= 'ok (car x))
)
(defun result/err? (x)
"Returns #t if x is an error"
(= 'err (car x))
)
(defun result/map (f x)
"If x is an ok, applies f to its value, otherwise does nothing"
(if (result/ok? x)
`(ok ,(f (cadr x)))
x
)
)
(defun result/map-err (f x)
"If x is an error, applies f to its value, otherwise does nothing"
(if (result/err? x)
`(err ,(f (cadr x)))
x
)
)
; Convenience error handling macros
(defmacro catch (expression error-symbol handler)
"Evaluates expression, running the handler if evaluation fails, introducing error-symbol as error"
(let (eval-result-symbol (gensym))
`(let (,eval-result-symbol (eval (quote ,expression)))
(cond
((result/ok? ,eval-result-symbol) (cadr ,eval-result-symbol))
(&otherwise
(let (,error-symbol (cadr ,eval-result-symbol))
,handler
)
)
)
)
)
)
(defmacro compile-debug (expression)
"Prints the input expression during macro expansion/compile time and evaluates the expression in runtime"
(print expression)
expression
)
(defmacro runtime-debug (expression)
"Prints the input expression and evaluates it in runtime"
`(progn
(print (quote ,expression))
,expression
)
)
; Convenience list functions
(defun cadr (x) "Alias for (car (cdr x))" (car (cdr x)))
(defun cdar (x) "Alias for (cdr (car x))" (cdr (car x)))
(defun caddr (x) "Alias for (car (cdr (cdr x)))" (car (cdr (cdr x))))
(defun cadar (x) "Alias for (car (cdr (car x)))" (car (cdr (car x))))
+49 -46
View File
@@ -6,22 +6,22 @@ use std::{
use crate::{
compile::{
CompilationModule, CompileOptions, Expression, FunctionBody, FunctionSignature, ParseError,
Compile, CompileContext, CompileOptions,
syntax::{Expression, FunctionBody, ParseError},
},
error::EvalError,
error::{MachineError, MachineErrorAt, ReadError},
parse::{self, parse_value},
util::Either,
vm::{
env::Environment,
machine::{InstructionPointer, Machine},
macros::MacroExpand,
module::{Module, ModuleRef},
value::Value,
instruction::Instruction,
machine::Machine,
value::{BytecodeFunction, IdentifierValue, Value},
},
};
pub trait Reader {
type Error: Into<EvalError>;
type Error: Into<MachineErrorAt>;
fn read(&mut self) -> Result<Option<Value>, Self::Error>;
}
@@ -60,7 +60,7 @@ impl InteractiveReader {
}
impl Reader for InteractiveReader {
type Error = EvalError;
type Error = MachineErrorAt;
fn read(&mut self) -> Result<Option<Value>, Self::Error> {
let stdin = stdin();
@@ -85,7 +85,7 @@ impl<R: BufRead> FileReader<R> {
}
impl<R: BufRead> Reader for FileReader<R> {
type Error = EvalError;
type Error = MachineErrorAt;
fn read(&mut self) -> Result<Option<Value>, Self::Error> {
read_inner(&mut self.reader, &mut self.buffer, None)
@@ -93,13 +93,9 @@ impl<R: BufRead> Reader for FileReader<R> {
}
impl<R: BufRead> ModuleReader<R> {
pub fn new(reader: R) -> Self {
pub fn new(reader: R, trace_macros: bool) -> Self {
let mut macro_machine = Machine::default();
let dummy = ModuleRef::from(Module::dummy());
macro_machine.ip = Some(InstructionPointer {
module: dummy,
address: 0,
});
macro_machine.trace_macros = trace_macros;
Self {
reader: FileReader::new(reader),
macro_machine,
@@ -109,8 +105,8 @@ impl<R: BufRead> ModuleReader<R> {
pub fn read_expression(
&mut self,
options: &CompileOptions,
env: &mut Environment,
) -> Result<Option<Rc<Expression>>, Either<EvalError, Vec<ParseError>>> {
env: &Rc<Environment>,
) -> Result<Option<Rc<Expression>>, Either<MachineErrorAt, Vec<ParseError>>> {
loop {
let value =
read(&mut self.reader, &mut self.macro_machine, env).map_err(Either::Left)?;
@@ -120,7 +116,7 @@ impl<R: BufRead> ModuleReader<R> {
let expression = Expression::parse(&value).map_err(Either::Right)?;
if let Expression::Defmacro(_) = expression.as_ref() {
self.macro_machine
.eval_value(options.clone(), env, value)
.evaluate_value(options.clone(), Some("defmacro".into()), env, value)
.map_err(Either::Left)?;
continue;
}
@@ -130,10 +126,11 @@ impl<R: BufRead> ModuleReader<R> {
pub fn compile(
mut self,
module_name: Option<IdentifierValue>,
options: &CompileOptions,
env: &mut Environment,
) -> Result<ModuleRef, Either<EvalError, Vec<ParseError>>> {
let mut module = CompilationModule::new(options.clone());
env: &Rc<Environment>,
) -> Result<Rc<BytecodeFunction>, Either<MachineErrorAt, Vec<ParseError>>> {
let mut cx = CompileContext::new(options.clone(), module_name);
let mut body = FunctionBody {
head: vec![],
tail: Rc::new(Expression::Nil),
@@ -159,25 +156,24 @@ impl<R: BufRead> ModuleReader<R> {
return Err(Either::Right(syntax_errors));
}
module
.compile_function(
FunctionSignature {
required_arguments: vec![],
optional_arguments: vec![],
rest_argument: None,
},
&body,
true,
)
.map_err(EvalError::from)
let value = body
.compile(&mut cx)
.map_err(MachineError::Compile)
.map_err(MachineErrorAt::at_unknown)
.map_err(Either::Left)?;
cx.push(value)
.map_err(MachineError::Compile)
.map_err(MachineErrorAt::at_unknown)
.map_err(Either::Left)?;
cx.emit(Instruction::Return);
let function = cx
.to_bytecode()
.map_err(MachineError::Compile)
.map_err(MachineErrorAt::at_unknown)
.map_err(Either::Left)?;
let module = module
.compile_module()
.map_err(EvalError::from)
.map_err(Either::Left)?;
Ok(module.into())
Ok(function)
}
}
@@ -185,7 +181,7 @@ fn read_inner<R: BufRead>(
reader: &mut R,
buffer: &mut String,
prompt: Option<(&str, &str)>,
) -> Result<Option<Value>, EvalError> {
) -> Result<Option<Value>, MachineErrorAt> {
loop {
let mut incomplete = None;
let mut i = buffer.trim_start();
@@ -211,9 +207,10 @@ fn read_inner<R: BufRead>(
break;
}
Err(error) => {
let error = EvalError::Syntax(error.map_input(|i| i.into()));
let error = ReadError::Lexical(error.map_input(|i| i.into()));
let error = MachineError::Read(error);
buffer.clear();
return Err(error);
return Err(error.at_unknown());
}
};
@@ -232,10 +229,16 @@ fn read_inner<R: BufRead>(
stdout().flush().ok();
}
let len = reader.read_line(buffer)?;
let len = reader
.read_line(buffer)
.map_err(ReadError::Io)
.map_err(MachineError::Read)
.map_err(MachineErrorAt::at_unknown)?;
if len == 0 {
return if let Some(incomplete) = incomplete {
Err(EvalError::Syntax(nom::Err::Incomplete(incomplete)))
let error = ReadError::Lexical(nom::Err::Incomplete(incomplete));
let error = MachineError::Read(error);
Err(error.at_unknown())
} else {
assert!(buffer.is_empty());
Ok(None)
@@ -247,12 +250,12 @@ fn read_inner<R: BufRead>(
pub fn read<R: Reader>(
reader: &mut R,
vm: &mut Machine,
env: &mut Environment,
) -> Result<Option<Value>, EvalError> {
env: &Rc<Environment>,
) -> Result<Option<Value>, MachineErrorAt> {
let raw_value = reader.read().map_err(Into::into)?;
let Some(raw_value) = raw_value else {
return Ok(None);
};
let exp_value = raw_value.macro_expand(vm, env, false)?;
let exp_value = vm.macro_expand(env, &raw_value)?;
Ok(Some(exp_value))
}
+33
View File
@@ -22,6 +22,39 @@ pub trait IteratorExt<I, E1, T> {
E2: From<E1>;
}
#[macro_export]
macro_rules! primitive_enum {
(
$(#[$meta:meta])*
$vis:vis enum $ident:ident: $repr:ty [$conversion_err:ident] {
$($variant:ident = $discriminant:literal),+ $(,)?
}
) => {
$(#[$meta])*
#[repr($repr)]
$vis enum $ident {
$($variant = $discriminant),+
}
impl From<$ident> for $repr {
fn from(value: $ident) -> $repr {
value as $repr
}
}
impl TryFrom<$repr> for $ident {
type Error = $conversion_err;
fn try_from(value: $repr) -> Result<$ident, $conversion_err> {
match value {
$($discriminant => Ok($ident::$variant),)+
_ => Err($conversion_err(value))
}
}
}
};
}
impl<A: fmt::Debug, B: fmt::Debug> fmt::Debug for Either<A, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
+103 -22
View File
@@ -1,44 +1,83 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use std::{
borrow::Borrow,
cell::RefCell,
collections::HashMap,
fmt,
hash::Hash,
rc::Rc,
sync::atomic::{AtomicU32, Ordering},
};
use crate::{
error::MachineError,
vm::{
machine::Machine,
value::{BytecodeFunction, Macro, NativeFunction, NativeFunctionImpl, Value},
value::{
BytecodeFunction, IdentifierValue, NativeFunction, NativeObject, StringValue, Value,
convert::{AnyFunction, TryFromValue},
},
},
};
#[derive(Default)]
#[derive(Clone, Debug)]
pub enum Macro {
Native(NativeFunction),
Bytecode(Rc<BytecodeFunction>),
}
#[derive(Default, Debug)]
pub struct Environment {
globals: RefCell<HashMap<Rc<str>, Value>>,
macros: RefCell<HashMap<Rc<str>, Macro>>,
globals: RefCell<HashMap<IdentifierValue, Value>>,
macros: RefCell<HashMap<IdentifierValue, Macro>>,
parent: Option<Rc<Environment>>,
gensym_index: AtomicU32,
}
impl Environment {
pub fn defun_native<S, F>(&mut self, identifier: S, function: F)
where
S: Into<Rc<str>>,
F: Fn(&mut Machine, &mut Environment, &[Value]) -> Result<Value, MachineError> + 'static,
{
let identifier = identifier.into();
let native = NativeFunction::new(identifier.clone(), function);
self.set_global_value(identifier, Value::NativeFunction(native));
pub fn new(parent: Option<Rc<Self>>) -> Self {
Self {
parent,
..Default::default()
}
}
pub fn defmacro_native<S, F>(&mut self, identifier: S, function: F)
pub fn gensym(&self) -> IdentifierValue {
// TODO do this in a root environment?
let index = self.gensym_index.fetch_add(1, Ordering::SeqCst);
format!("___gensym{index}").into()
}
pub fn defun_native<S, D, F>(&self, identifier: S, docstring: D, function: F) -> Value
where
S: Into<Rc<str>>,
F: Fn(&mut Machine, &mut Environment, &[Value]) -> Result<Value, MachineError> + 'static,
S: Into<IdentifierValue>,
D: Into<StringValue>,
F: Fn(&mut Machine, &Rc<Environment>, &[Value]) -> Result<Value, MachineError> + 'static,
{
let identifier = identifier.into();
let native: NativeFunctionImpl = Rc::new(function);
let native = NativeFunction::new(identifier.clone(), docstring, function);
let value = Value::NativeFunction(native);
self.set_global_value(identifier, value.clone());
value
}
pub fn defmacro_native<S, D, F>(&self, identifier: S, docstring: D, function: F)
where
S: Into<IdentifierValue>,
D: Into<StringValue>,
F: Fn(&mut Machine, &Rc<Environment>, &[Value]) -> Result<Value, MachineError> + 'static,
{
let identifier = identifier.into();
let native = NativeFunction::new(identifier.clone(), docstring, function);
self.macros
.borrow_mut()
.insert(identifier, Macro::Builtin(native));
.insert(identifier, Macro::Native(native));
}
pub fn set_global_macro<S: Into<Rc<str>>>(&mut self, identifier: S, value: BytecodeFunction) {
pub fn defmacro_bytecode<S: Into<IdentifierValue>>(
&self,
identifier: S,
value: Rc<BytecodeFunction>,
) {
let identifier = identifier.into();
// println!("Export macro: {identifier}: {value}");
self.macros
@@ -46,7 +85,11 @@ impl Environment {
.insert(identifier, Macro::Bytecode(value));
}
pub fn global_value(&self, identifier: &str) -> Option<Value> {
pub fn global_value<Q>(&self, identifier: &Q) -> Option<Value>
where
IdentifierValue: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
self.globals.borrow().get(identifier).cloned().or_else(|| {
self.parent
.as_ref()
@@ -54,17 +97,55 @@ impl Environment {
})
}
pub fn set_global_value<S: Into<Rc<str>>, V: Into<Value>>(&mut self, identifier: S, value: V) {
pub fn set_global_value<S: Into<IdentifierValue>, V: Into<Value>>(
&self,
identifier: S,
value: V,
) {
self.globals
.borrow_mut()
.insert(identifier.into(), value.into());
}
pub fn global_macro(&self, identifier: &str) -> Option<Macro> {
pub fn global_macro<Q>(&self, identifier: &Q) -> Option<Macro>
where
IdentifierValue: Borrow<Q>,
Q: Hash + Eq,
{
self.macros.borrow().get(identifier).cloned().or_else(|| {
self.parent
.as_ref()
.and_then(|parent| parent.global_macro(identifier))
})
}
pub fn evaluate_global_function<Q>(
self: &Rc<Self>,
vm: &mut Machine,
identifier: &Q,
arguments: &[Value],
) -> Result<Value, MachineError>
where
IdentifierValue: Borrow<Q>,
Q: Hash + Eq + ?Sized,
for<'a> &'a Q: Into<IdentifierValue>,
{
let value = self
.global_value(identifier)
.ok_or_else(|| MachineError::UnboundIdentifier(identifier.into()))?;
let function = AnyFunction::try_from_value(&value)?;
function.invoke(vm, self, arguments)
}
}
impl fmt::Display for Environment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "environment")
}
}
impl NativeObject for Environment {
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
+188 -206
View File
@@ -1,247 +1,229 @@
use std::fmt;
use bitmatch::bitmatch;
use crate::{
error::MachineError,
vm::{machine::Machine, value::NumberValue},
};
#[derive(Debug, thiserror::Error)]
pub enum InstructionError {
#[error("invalid instruction")]
Invalid,
#[derive(Debug, PartialEq, thiserror::Error)]
#[error("unrecognized instruction: {0}")]
pub struct InstructionDecodeError(u8);
primitive_enum! {
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Instruction: u8 [InstructionDecodeError] {
// Stack manipulation
PushNil = 0,
PushInteger = 1,
PushTrue = 2,
PushFalse = 3,
PushConstant = 4,
CloseUpvalue = 5,
Drop = 6,
SetTemp = 7,
GetTemp = 8,
// Binding manupulation
GetLocal = 10,
SetLocal = 11,
GetUpvalue = 12,
SetUpvalue = 13,
GetGlobal = 14,
SetGlobal = 15,
DeclareGlobal = 16,
DeclareMacro = 17,
// Arithmetic
Add = 20,
Sub = 21,
Mul = 22,
Div = 23,
Mod = 24,
Gt = 25,
Lt = 26,
Eq = 27,
Ge = 28,
Le = 29,
Ne = 30,
Not = 31,
Negate = 32,
// Branching
Branch = 40,
Jump = 41,
// Functions and closures
Call = 50,
Return = 51,
MakeClosure = 52,
}
}
#[derive(Debug, thiserror::Error)]
pub enum InstructionEncodeError {}
macro_rules! impl_fixed_unsigned {
($vis:vis $ident:ident : $repr:ty, $bits:literal) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
$vis struct $ident($repr);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct U<const N: usize>(u32);
impl $ident {
pub const BITS: usize = $bits;
macro_rules! primitive_enum {
(
$(#[$meta:meta])*
$vis:vis enum $ident:ident: $repr:ty {
$($variant:ident = $value:literal),* $(,)?
}
) => {
$(#[$meta])*
#[repr($repr)]
$vis enum $ident {
$($variant = $value),*
pub fn to_bytes(&self) -> [u8; $bits / 8] {
self.0.to_le_bytes()
}
}
impl TryFrom<$repr> for $ident {
type Error = InstructionError;
impl TryFrom<usize> for $ident {
type Error = FixedConvertError<usize, $bits>;
fn try_from(value: $repr) -> Result<Self, Self::Error> {
match value {
$($value => Ok(Self::$variant),)*
_ => Err(InstructionError::Invalid)
fn try_from(value: usize) -> Result<Self, Self::Error> {
if let Ok(value) = <$repr>::try_from(value) {
Ok(Self(value))
} else {
Err(FixedConvertError(value))
}
}
}
impl From<$repr> for $ident {
fn from(value: $repr) -> $ident {
$ident(value)
}
}
impl From<$ident> for usize {
fn from(value: $ident) -> usize {
value.0.into()
}
}
impl From<$ident> for $repr {
fn from(value: $ident) -> $repr {
match value {
$($ident::$variant => $value,)*
}
value.0
}
}
};
}
macro_rules! impl_fixed {
($vis:vis $ident:ident: u8) => {
impl_fixed_unsigned!($vis $ident : u8, 8);
impl ReadEncoded for $ident {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError> {
let b0 = stream.fetch_byte()?;
Ok(Self(b0))
}
}
};
($vis:vis $ident:ident: u16) => {
impl_fixed_unsigned!($vis $ident : u16, 16);
impl ReadEncoded for $ident {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError> {
let b0 = stream.fetch_byte()?;
let b1 = stream.fetch_byte()?;
Ok(Self(u16::from_le_bytes([b0, b1])))
}
}
};
}
primitive_enum! {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MathInstruction: u32 {
Add = 0,
Sub = 1,
Mul = 2,
Div = 3,
Mod = 4,
And = 5,
Or = 6,
BitwiseAnd = 7,
BitwiseOr = 8,
BitwiseXor = 9,
Gt = 10,
Lt = 11,
Eq = 12,
Ne = 13,
Ge = 14,
Le = 15,
#[derive(Debug, thiserror::Error)]
#[error("cannot fit {0} into {N} bits")]
pub struct FixedConvertError<T: fmt::Display, const N: usize>(T);
impl_fixed!(pub LocalId: u8);
impl_fixed!(pub ConstantId: u16);
impl_fixed!(pub ArgumentCount: u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ImmediateInteger(i16);
impl ImmediateInteger {
pub fn sign_extend_i64(self) -> i64 {
self.0 as i64
}
pub fn to_bytes(&self) -> [u8; 2] {
self.0.to_le_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Instruction {
PushNil,
PushInteger(PackedInteger),
PushBool(bool),
PushConstant(ConstantId),
PushArgument(ArgumentId),
Drop,
SetGlobal,
GetGlobal,
SetLocal(LocalId),
GetLocal(LocalId),
ExportMacro(ConstantId),
Call(ArgumentCount),
Return,
Math(MathInstruction, ArgumentCount),
Branch(FunctionOffset),
Jump(FunctionOffset),
}
pub type ConstantId = U<12>;
pub type FunctionOffset = U<10>;
pub type LocalId = U<8>;
pub type ArgumentId = U<6>;
pub type ArgumentCount = U<6>;
pub type PackedInteger = U<12>;
impl<const N: usize> U<N> {
pub const BITS: usize = N;
pub const ZERO: Self = Self(0);
const UNSIGNED_MASK: i64 = (1 << (N - 1)) - 1;
const SIGNED_MASK: i64 = (1 << N) - 1;
pub fn sign_extend_i64(&self) -> i64 {
if self.0 & (1 << (N - 1)) != 0 {
self.0 as i64 | !Self::SIGNED_MASK
} else {
self.0 as i64
}
}
pub const fn new(value: u32) -> Option<Self> {
if value >= (1u32 << N) {
None
} else {
Some(Self(value))
}
}
pub fn from_signed(value: i64) -> Option<Self> {
if value > 0 && value & !Self::UNSIGNED_MASK == 0 {
Some(Self(value as u32))
} else if value < 0 && value & !Self::SIGNED_MASK == !Self::SIGNED_MASK {
Some(Self((value & Self::SIGNED_MASK) as u32))
} else {
None
}
}
pub const fn truncate(value: u32) -> Self {
Self(value & ((1 << N) - 1))
}
pub const unsafe fn new_unchecked(value: u32) -> Self {
impl From<i16> for ImmediateInteger {
fn from(value: i16) -> Self {
Self(value)
}
}
impl<const N: usize> fmt::Debug for U<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
impl ReadEncoded for ImmediateInteger {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError> {
let b0 = stream.fetch_byte()?;
let b1 = stream.fetch_byte()?;
Ok(Self(i16::from_le_bytes([b0, b1])))
}
}
impl<const N: usize> From<U<N>> for usize {
fn from(value: U<N>) -> Self {
value.0 as usize
}
}
impl TryFrom<NumberValue> for ImmediateInteger {
type Error = FixedConvertError<NumberValue, 16>;
impl<const N: usize> From<U<N>> for u32 {
fn from(value: U<N>) -> Self {
value.0
}
}
impl From<Instruction> for u32 {
fn from(instruction: Instruction) -> u32 {
match instruction {
Instruction::Drop => 0b00000000_00000000,
Instruction::PushNil => 0b00000000_00000001,
Instruction::PushBool(value) => 0b00000000_00000010 | (value as u32),
Instruction::Return => 0b00000000_00000100,
Instruction::SetGlobal => 0b00000000_00000101,
Instruction::GetGlobal => 0b00000000_00000110,
Instruction::GetLocal(value) => 0b00000010_00000000 | value.0,
Instruction::SetLocal(value) => 0b00000011_00000000 | value.0,
Instruction::Call(count) => 0b00000000_01000000 | count.0,
Instruction::PushInteger(value) => 0b00010000_00000000 | value.0,
Instruction::PushConstant(index) => 0b00100000_00000000 | index.0,
Instruction::PushArgument(index) => 0b00000000_10000000 | index.0,
Instruction::ExportMacro(index) => 0b00110000_00000000 | index.0,
Instruction::Branch(offset) => 0b00001000_00000000 | offset.0,
Instruction::Jump(offset) => 0b00001100_00000000 | offset.0,
Instruction::Math(math, count) => {
0b00000100_00000000 | (u32::from(math) << 6) | count.0
}
}
}
}
impl TryFrom<u32> for Instruction {
type Error = InstructionError;
#[bitmatch]
fn try_from(value: u32) -> Result<Self, Self::Error> {
#[bitmatch]
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
match value {
"00000000_00000000" => Ok(Instruction::Drop),
"00000000_00000001" => Ok(Instruction::PushNil),
"00000000_0000001x" => Ok(Instruction::PushBool(x != 0)),
"00000000_00000100" => Ok(Instruction::Return),
"00000000_00000101" => Ok(Instruction::SetGlobal),
"00000000_00000110" => Ok(Instruction::GetGlobal),
"00000000_00000111" => todo!(),
"00000000_00001???" => todo!(),
NumberValue::Int(value) if let Ok(value) = value.try_into() => Ok(Self(value)),
_ => Err(FixedConvertError(value)),
}
}
}
impl TryFrom<i64> for ImmediateInteger {
type Error = FixedConvertError<i64, 16>;
"00000000_0001????" => todo!(),
"00000000_001?????" => todo!(),
"00000000_01xxxxxx" => Ok(Instruction::Call(U(x))),
"00000000_10xxxxxx" => Ok(Instruction::PushArgument(U(x))),
"00000000_11??????" => todo!(),
"00000001_????????" => todo!(),
"00000010_xxxxxxxx" => Ok(Instruction::GetLocal(U(x))),
"00000011_xxxxxxxx" => Ok(Instruction::SetLocal(U(x))),
"000001xx_xxyyyyyy" => Ok(Instruction::Math(x.try_into()?, U(y))),
"000010xx_xxxxxxxx" => Ok(Instruction::Branch(U(x))),
"000011xx_xxxxxxxx" => Ok(Instruction::Jump(U(x))),
"0001xxxx_xxxxxxxx" => Ok(Instruction::PushInteger(U(x))),
"0010xxxx_xxxxxxxx" => Ok(Instruction::PushConstant(U(x))),
"0011xxxx_xxxxxxxx" => Ok(Instruction::ExportMacro(U(x))),
"01??????_????????" => todo!(),
"1???????_????????" => todo!("{value:032b}"),
fn try_from(value: i64) -> Result<Self, Self::Error> {
if let Ok(value) = value.try_into() {
Ok(Self(value))
} else {
Err(FixedConvertError(value))
}
}
}
#[cfg(test)]
mod tests {
use crate::vm::instruction::{FunctionOffset, U};
pub trait ReadEncoded: Sized {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError>;
}
#[test]
fn test_u_convert() {
type T = U<24>;
let t = T::from_signed(-1234).unwrap();
assert_eq!(t.0, (-1234i64 & 0xFFFFFF) as u32);
assert_eq!(t.sign_extend_i64(), -1234);
let t = T::from_signed(1234).unwrap();
assert_eq!(t.0, 1234);
let t = T::from_signed(0x800000);
assert!(t.is_none());
let t = T::from_signed(-0x1000001);
assert!(t.is_none());
}
#[test]
fn test_branch_target_extend() {
let target = FunctionOffset::truncate(1022);
assert_eq!(target.sign_extend_i64(), -2);
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Self::PushNil => "PUSH_NIL",
Self::PushInteger => "PUSH_INTEGER",
Self::PushTrue => "PUSH_TRUE",
Self::PushFalse => "PUSH_FALSE",
Self::PushConstant => "PUSH_CONSTANT",
Self::CloseUpvalue => "CLOSE_UPVALUE",
Self::Drop => "DROP",
Self::SetTemp => "SET_TEMP",
Self::GetTemp => "GET_TEMP",
Self::GetLocal => "GET_LOCAL",
Self::SetLocal => "SET_LOCAL",
Self::GetUpvalue => "GET_UPVALUE",
Self::SetUpvalue => "SET_UPVALUE",
Self::GetGlobal => "GET_GLOBAL",
Self::SetGlobal => "SET_GLOBAL",
Self::DeclareGlobal => "DECLARE_GLOBAL",
Self::DeclareMacro => "DECLARE_MACRO",
Self::Add => "ADD",
Self::Sub => "SUB",
Self::Mul => "MUL",
Self::Div => "DIV",
Self::Mod => "MOD",
Self::Gt => "GT",
Self::Lt => "LT",
Self::Eq => "EQ",
Self::Ge => "GE",
Self::Le => "LE",
Self::Ne => "NE",
Self::Not => "NOT",
Self::Negate => "NEGATE",
Self::Branch => "BRANCH",
Self::Jump => "JUMP",
Self::Call => "CALL",
Self::Return => "RETURN",
Self::MakeClosure => "MAKE_CLOSURE",
};
f.write_str(name)
}
}
-1
View File
@@ -1 +0,0 @@
+790 -569
View File
File diff suppressed because it is too large Load Diff
+60 -47
View File
@@ -1,11 +1,11 @@
use std::rc::Rc;
use crate::{
error::EvalError,
error::MachineErrorAt,
vm::{
env::Environment,
env::{Environment, Macro},
machine::Machine,
value::{ConsCell, Keyword, Macro, Value},
value::{ClosureValue, ConsCell, Value},
},
};
@@ -13,30 +13,36 @@ pub trait MacroExpand: Sized {
fn macro_expand(
&self,
vm: &mut Machine,
env: &mut Environment,
env: &Rc<Environment>,
tail: bool,
) -> Result<Self, EvalError>;
) -> Result<Self, MachineErrorAt>;
}
impl MacroExpand for Value {
fn macro_expand(
&self,
vm: &mut Machine,
env: &mut Environment,
env: &Rc<Environment>,
tail: bool,
) -> Result<Self, EvalError> {
) -> Result<Self, MachineErrorAt> {
match self {
Self::Nil
| Self::Identifier(_)
| Self::Integer(_)
| Self::Number(_)
| Self::Boolean(_)
| Self::String(_)
| Self::Keyword(_)
| Self::OpaqueValue(_)
| Self::BytecodeFunction(_)
// | Self::OpaqueValue(_)
// | Self::BytecodeFunction(_)
| Self::Quote(_)
| Self::Unquote(_)
| Self::NativeFunction(_) => Ok(self.clone()),
| Self::Closure(_)
| Self::Function(_)
| Self::NativeFunction(_)
| Self::NativeValue(_)
| Self::Vector(_)
| Self::UnquoteSplice(_)
| Self::Unquote(_) => Ok(self.clone()),
// | Self::NativeFunction(_) => Ok(self.clone()),
Self::Cons(cons) => {
let ConsCell(car, cdr) = cons.as_ref();
let car = car.macro_expand(vm, env, false)?;
@@ -52,30 +58,28 @@ impl MacroExpand for Value {
let cons = Rc::new(ConsCell(car, cdr.clone()));
return Ok(Self::Cons(cons));
};
let Some(mac) = env.global_macro(identifier.as_ref()) else {
let Some(mac) = env.global_macro(identifier) else {
// eprintln!("{identifier} is not a macro");
let cons = Rc::new(ConsCell(car, cdr.clone()));
return Ok(Self::Cons(cons));
};
match mac {
Macro::Builtin(native) => {
let result = (native)(vm, env, &args[..]).expect("TODO");
Ok(result)
}
Macro::Bytecode(bytecode) => {
let result = vm
.eval_bytecode_call(env, bytecode, &args[..])
.expect("TODO");
Ok(result)
}
}
match mac {
Macro::Native(native) => {
let result = native.invoke(vm, env, &args[..]).expect("TODO");
Ok(result)
}
Macro::Bytecode(bytecode) => {
let closure = ClosureValue { function: bytecode.clone(), upvalues: vec![] };
let result = vm.evaluate_closure_args(env, closure, &args[..])?;
Ok(result)
}
}
}
Self::Quasi(value) => {
let value = expand_quasiquote(value);
value.macro_expand(vm, env, false)
}
Self::Vector(_) => todo!(),
}
}
}
@@ -83,28 +87,37 @@ impl MacroExpand for Value {
fn expand_quasiquote(value: &Value) -> Value {
match value {
Value::Nil => Value::Nil,
// Toplevel-only
Value::Unquote(inner) => inner.as_ref().clone(),
Value::Cons(cons) => {
// x . y -> (cons <exp-car> <exp-cdr>)
let ConsCell(car, cdr) = cons.as_ref();
let exp_car = expand_quasiquote(car);
let exp_cdr = expand_quasiquote(cdr);
Value::Identifier("cons".into()).cons(exp_car.cons(exp_cdr.cons(Value::Nil)))
Value::UnquoteSplice(inner) => inner.as_ref().clone(),
Value::Cons(_) => {
let mut elements = vec![];
let mut current = value;
elements.push(Value::Identifier("append".into()));
while !current.is_nil() {
let Value::Cons(cons) = current else { todo!() };
let ConsCell(car, cdr) = cons.as_ref();
match car {
Value::UnquoteSplice(splice) => {
elements.push(splice.as_ref().clone());
}
_ => {
let exp_car = expand_quasiquote(car);
elements.push(Value::list_or_nil([
Value::Identifier("list".into()),
exp_car,
]));
}
}
current = cdr;
}
Value::list_or_nil(elements)
}
Value::Quote(value) => {
// (cons 'quote inner)
let cons = Value::Identifier("cons".into());
let quote_kw = Value::Quote(Rc::new(Value::Keyword(Keyword::Quote)));
let quote_nil = Value::Quote(Rc::new(Value::Nil));
let exp_inner = expand_quasiquote(value);
let cons_inner = cons
.clone()
.cons(exp_inner.cons(quote_nil.clone().cons(Value::Nil)));
cons.cons(quote_kw.cons(cons_inner.cons(Value::Nil)))
}
_ => Value::Quote(Rc::new(value.clone())),
_ => value.clone().quote(),
}
}
+2 -3
View File
@@ -1,10 +1,9 @@
pub mod env;
pub mod instruction;
pub mod loader;
pub mod machine;
pub mod macros;
pub mod module;
pub mod pool;
pub mod prelude;
pub mod stack;
pub mod value;
pub use value::Value;
-251
View File
@@ -1,251 +0,0 @@
use std::{
collections::HashMap,
fmt,
hash::{Hash, Hasher},
ops::Deref,
rc::Rc,
};
use crate::{
compile::{
CompilationModule, CompileError, CompileOptions, Expression, FunctionBody,
FunctionSignature,
},
vm::{
instruction::{ConstantId, Instruction},
pool::Pool,
value::{Value, ValueString},
},
};
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub enum ModuleConstant {
Integer(i64),
LocalFunction(usize, usize),
Identifier(Rc<str>),
String(ValueString),
Value(Rc<Value>),
}
#[derive(Clone)]
pub struct ModuleRef {
inner: Rc<Module>,
}
impl Hash for ModuleRef {
fn hash<H: Hasher>(&self, state: &mut H) {
(&raw const *self.inner.as_ref()).addr().hash(state)
}
}
impl Eq for ModuleRef {}
pub struct Module {
pub constants: HashMap<ConstantId, ModuleConstant>,
pub instructions: Vec<u32>,
pub entry: usize,
}
#[derive(Default)]
pub struct ModuleBuilder {
constants: Pool<ModuleConstant, { ConstantId::BITS }>,
instructions: Vec<u32>,
entry: Option<usize>,
}
impl ModuleRef {
pub fn ptr_eq(a: &Self, b: &Self) -> bool {
Rc::ptr_eq(&a.inner, &b.inner)
}
}
impl From<Module> for ModuleRef {
fn from(value: Module) -> Self {
Self {
inner: Rc::new(value),
}
}
}
impl fmt::Debug for ModuleRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Module({:p})", self.inner)
}
}
impl fmt::Pointer for ModuleRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Pointer::fmt(&self.inner, f)
}
}
impl PartialEq for ModuleRef {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.inner, &other.inner)
}
}
impl Deref for ModuleRef {
type Target = Module;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
// #[cfg(test)]
impl Module {
pub fn dummy() -> Self {
Self {
constants: HashMap::new(),
instructions: vec![0],
entry: 0,
}
}
}
impl Module {
pub fn instruction(&self, ip: usize) -> Option<u32> {
self.instructions.get(ip).copied()
}
pub fn constant(&self, id: ConstantId) -> Option<ModuleConstant> {
self.constants.get(&id).cloned()
}
pub fn len(&self) -> usize {
self.instructions.len()
}
pub fn is_empty(&self) -> bool {
self.instructions.is_empty()
}
pub fn entry(&self) -> usize {
self.entry
}
pub fn compile_value(options: CompileOptions, value: &Value) -> Result<Self, CompileError> {
let expression = Expression::parse(value).map_err(CompileError::Parse)?;
let mut module = CompilationModule::new(options);
module.compile_function(
FunctionSignature::EMPTY,
&FunctionBody {
head: vec![],
tail: expression,
},
true,
)?;
module.compile_module()
}
pub fn dump(&self, highlight: Option<usize>, context: usize) {
let window = highlight
.map(|end| (end + 1).min(self.instructions.len()))
.map(|end| end.saturating_sub(context)..end)
.unwrap_or(0..self.instructions.len());
let start = window.start;
for (i, instruction) in self.instructions[window].iter().enumerate() {
let offset = i + start;
if highlight.is_some_and(|a| a == offset) {
print!(">");
} else {
print!(" ");
}
print!("{offset:4}: {instruction:08x}");
if let Ok(instruction) = Instruction::try_from(*instruction) {
print!(" {instruction:?}");
if let Instruction::PushConstant(id) = instruction {
if let Some(value) = self.constant(id) {
print!(" [-> {value}]");
} else {
print!(" UNDEFINED");
}
}
}
println!();
}
}
}
impl ModuleBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn build(self) -> Module {
Module {
constants: self.constants.into_map(),
instructions: self.instructions,
entry: self.entry.unwrap(),
}
}
pub fn constant(&mut self, value: ModuleConstant) -> ConstantId {
self.constants.key(value).unwrap()
}
pub fn add_all<I: IntoIterator<Item = Instruction>>(&mut self, insns: I) -> &mut Self {
self.instructions.extend(insns.into_iter().map(u32::from));
self
}
pub fn add(&mut self, instruction: Instruction) -> &mut Self {
self.instructions.push(instruction.into());
self
}
pub fn entry(&mut self, entry: usize) -> &mut Self {
self.entry = Some(entry);
self
}
}
impl fmt::Display for ModuleConstant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::String(value) => write!(f, "string {value:?}"),
Self::Integer(value) => fmt::Display::fmt(value, f),
Self::Identifier(ident) => write!(f, "ident {ident:?}"),
Self::LocalFunction(address, _) => write!(f, "label {address}"),
Self::Value(value) => write!(f, "{value}"),
}
}
}
#[cfg(test)]
mod tests {
use crate::{
compile::CompileOptions,
vm::{
instruction::{Instruction, MathInstruction, U},
module::Module,
value::Value,
},
};
#[test]
fn test_compile_value_basic() {
let v = Value::list_or_nil([
Value::Identifier("+".into()),
Value::Integer(1),
Value::Integer(2),
]);
let m = Module::compile_value(CompileOptions::default(), &v).unwrap();
assert!(m.constants.is_empty());
let is = [
Instruction::PushInteger(U::truncate(2)),
Instruction::PushInteger(U::truncate(1)),
Instruction::Math(MathInstruction::Add, U::truncate(2)),
Instruction::Return,
]
.into_iter()
.map(Into::into)
.collect::<Vec<u32>>();
assert_eq!(m.instructions, is);
assert_eq!(m.entry, 0);
}
}
-50
View File
@@ -1,50 +0,0 @@
use std::{
collections::{HashMap, hash_map},
hash::Hash,
};
use crate::vm::instruction::U;
pub struct Pool<T, const N: usize> {
map: HashMap<T, U<N>>,
index: u32,
}
impl<T: Hash + Eq, const N: usize> Pool<T, N> {
pub fn key(&mut self, value: T) -> Option<U<N>> {
match self.map.entry(value) {
hash_map::Entry::Vacant(slot) => {
let index = U::new(self.index + 1)?;
self.index += 1;
Some(*slot.insert(index))
}
hash_map::Entry::Occupied(slot) => Some(*slot.get()),
}
}
pub fn into_map(mut self) -> HashMap<U<N>, T> {
let mut result = HashMap::new();
for (value, key) in self.map.drain() {
result.insert(key, value);
}
result
}
}
impl<T, const N: usize> IntoIterator for Pool<T, N> {
type Item = (T, U<N>);
type IntoIter = <HashMap<T, U<N>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.map.into_iter()
}
}
impl<T, const N: usize> Default for Pool<T, N> {
fn default() -> Self {
Self {
map: HashMap::new(),
index: 0,
}
}
}
+132
View File
@@ -0,0 +1,132 @@
use std::rc::Rc;
use crate::{
error::{MachineError, ValueConversionError},
vm::{
Value,
env::Environment,
value::{ConsCell, convert::TryFromValue},
},
};
pub fn load(env: &Rc<Environment>) {
// // vectors
// env.defun_native("getv", |vm, _, args| {
// let [vec, index] = args else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let Value::Vector(vec) = vec else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let index = i64::try_from_value(index).map_err(|e| vm.error_at_ip(e))? as isize;
// let value = if index < 0 {
// todo!()
// } else {
// vec.value_at(index as usize).unwrap_or(Value::Nil)
// };
// Ok(value)
// });
// env.defun_native("setv", |vm, _, args| {
// let [vec, index, value] = args else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let Value::Vector(vec) = vec else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let index = i64::try_from_value(index).map_err(|e| vm.error_at_ip(e))? as isize;
// if index < 0 {
// todo!()
// } else {
// vec.set_value_at(index as usize, value.clone());
// }
// Ok(Value::Nil)
// });
// lists
env.defun_native(
"append",
"Concatenates the lists into one list",
|_, _, args| match args {
[] => Ok(Value::Nil),
[xs] => Ok(xs.clone()),
[head @ .., tail] => {
let mut elements = vec![];
for arg in head {
let iter = arg.proper_iter(ValueConversionError {
expected: "proper list".into(),
got: arg.clone(),
});
for element in iter {
elements.push(element?.clone());
}
}
let mut output = tail.clone();
for element in elements.into_iter().rev() {
output = element.cons(output);
}
Ok(output)
}
},
);
env.defun_native(
"car",
"Returns the CAR value of a cons-cell",
|_, _, args| {
let [x] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let cons: &ConsCell = TryFromValue::try_from_value(x)?;
Ok(cons.0.clone())
},
);
env.defun_native(
"cdr",
"Returns the CDR value of a cons-cell",
|_, _, args| {
let [x] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let cons: &ConsCell = TryFromValue::try_from_value(x)?;
Ok(cons.1.clone())
},
);
env.defun_native(
"cons",
"Constructs a cons-cell from CAR and CDR arguments",
|_, _, args| {
let [car, cdr] = args else {
return Err(MachineError::InvalidArgumentCount);
};
Ok(Value::Cons(Rc::new(ConsCell(car.clone(), cdr.clone()))))
},
);
env.defun_native(
"list",
"Constructs a list from the arguments",
|_, _, args| {
let out = Value::list_or_nil(args.iter().cloned());
Ok(out)
},
);
env.defun_native(
"length",
"Returns the length of the given list",
|_, _, args| {
let [xs] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let mut xs = xs;
let mut count = 0;
while !xs.is_nil() {
let Value::Cons(cons) = xs else {
break;
};
let ConsCell(_, cdr) = cons.as_ref();
count += 1;
xs = cdr;
}
Ok(count.into())
},
);
}
+69
View File
@@ -0,0 +1,69 @@
use std::rc::Rc;
use crate::{
error::MachineError,
vm::{
Value,
env::Environment,
value::{NumberValue, StringValue, convert::TryFromValue},
},
};
pub fn load(env: &Rc<Environment>) {
env.defun_native(
"->string",
"Converts a value to string representation",
|_, _, args| {
let [arg] = args else {
return Err(MachineError::InvalidArgumentCount);
};
Ok(Value::String(format!("{arg}").into()))
},
);
env.defun_native(
"string->number",
"Converts a string to a number with optional radix (default=10)",
|_, _, args| {
let (string, radix) = match args {
[string] => (StringValue::try_from_value(string)?, 10),
[string, radix] => (
StringValue::try_from_value(string)?,
i64::try_from_value(radix)?,
),
_ => return Err(MachineError::InvalidArgumentCount),
};
let Ok(radix) = u32::try_from(radix) else {
return Ok(Value::Nil);
};
match NumberValue::from_str_radix(string.as_ref(), radix) {
Some(value) => Ok(Value::Number(value)),
None => Ok(Value::Nil),
}
},
);
// // conversion
// env.defun_native("string->int", |vm, _, args| {
// let [arg] = args else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let arg = ValueString::try_from_value(arg).map_err(|e| vm.error_at_ip(e))?;
// let result = arg.parse::<i64>().map(Value::Integer).unwrap_or(Value::Nil);
// Ok(result)
// });
// env.defun_native("int->string", |vm, _, args| {
// let [arg] = args else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let arg = i64::try_from_value(arg).map_err(|e| vm.error_at_ip(e))?;
// let result = Value::String(format!("{arg}").into());
// Ok(result)
// });
// env.defun_native("type", |vm, _, args| {
// let [arg] = args else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// Ok(arg.typeid())
// });
}
+102
View File
@@ -0,0 +1,102 @@
use std::rc::Rc;
use crate::{
error::MachineError,
vm::{Value, env::Environment, value::Keyword},
};
pub fn load(env: &Rc<Environment>) {
env.defun_native("gensym", "Provides an unique symbol name", |_, env, _| {
Ok(env.gensym().into())
});
env.defun_native(
"explain",
"Provides an explanation for a given value",
|_, _, args| {
let [argument] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let explanation = match argument {
Value::Nil => "an empty list".into(),
Value::Cons(_) => "a cons-cell".into(),
Value::Number(_) => "a number".into(),
Value::Boolean(_) => "a boolean value".into(),
Value::Quasi(_) => "a quasi-quoted value".into(),
Value::Quote(_) => "a quoted value".into(),
Value::Unquote(_) => "an unquoted value".into(),
Value::UnquoteSplice(_) => "an unquote-spliced value".into(),
Value::Identifier(identifier) => format!("an identifier {:?}", identifier.as_ref()),
Value::Vector(_) => "a vector".into(),
Value::String(_) => "a string".into(),
Value::Keyword(_) => "a keyword".into(),
Value::Closure(closure) => {
format!(
"function {}: {}",
closure.function.name(),
closure.function.docstring()
)
}
Value::Function(function) => {
format!("function {}: {}", function.name(), function.docstring())
}
Value::NativeFunction(function) => {
format!(
"built-in function {}: {}",
function.name(),
function.docstring()
)
}
Value::NativeValue(_) => "a native value".into(),
};
Ok(Value::String(explanation.into()))
},
);
env.defun_native(
"type",
"Returns the data type of the argument",
|_, _, args| {
let [argument] = args else {
return Err(MachineError::InvalidArgumentCount);
};
Ok(argument.type_id())
},
);
env.defmacro_native(
"assert",
"Checks that the condition is true, otherwise aborts the execution",
|vm, _, args| match args {
[] => Err(MachineError::InvalidArgumentCount),
[cond] => {
let assertion_failure_msg = match vm.current_location() {
Some(ip) => format!("{ip}: assertion failed: {cond}"),
None => format!("<undefined>: assertion failed: {cond}"),
};
let assertion_failure = Value::list_or_nil([
Value::Identifier("abort".into()),
Value::String(assertion_failure_msg.into()),
]);
Ok(Value::list_or_nil([
Value::Keyword(Keyword::If),
cond.clone(),
Value::Nil,
assertion_failure,
]))
}
_ => Err(MachineError::InvalidArgumentCount),
},
);
env.defun_native(
"abort",
"Aborts the execution with the given reason message",
|_, _, args| {
let mut message = String::new();
for (i, arg) in args.iter().enumerate() {
if i != 0 {
message.push(' ');
}
message.push_str(&format!("{arg}"));
}
Err(MachineError::Abort(message.into()))
},
);
}
+97
View File
@@ -0,0 +1,97 @@
use std::rc::Rc;
use crate::{
error::MachineError,
read::{self, InteractiveReader},
vm::{Value, env::Environment, value::NativeObject},
};
pub fn load(env: &Rc<Environment>) {
env.defun_native("eval", "Evaluates the given expression", |vm, env, args| {
let (env, value) = match args {
[value] => (env.clone(), value),
[env, value] => (env.as_native()?, value),
_ => return Err(MachineError::InvalidArgumentCount),
};
let outcome =
match vm.evaluate_value(Default::default(), Some("eval".into()), &env, value.clone()) {
Ok(result) => Value::list_or_nil([Value::Identifier("ok".into()), result]),
Err(error) => Value::list_or_nil([
Value::Identifier("err".into()),
Value::String(format!("{error}").into()),
]),
};
Ok(outcome)
});
env.defun_native("env/create", "Create a new environment", |_, env, args| {
let parent = match args {
[] => Some(env.clone()),
[env] if env.is_nil() => None,
[env] => Some(env.as_native()?),
_ => return Err(MachineError::InvalidArgumentCount),
};
let environment: Rc<dyn NativeObject> = Rc::new(Environment::new(parent));
Ok(Value::NativeValue(environment.into()))
});
env.defun_native(
"env/load-prelude",
"Adds prelude definitions to the environment",
|_, _, args| {
let [env] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let env = env.as_native::<Environment>()?;
super::load(&env);
Ok(Value::Nil)
},
);
env.defun_native(
"read",
"Reads a form from standard input",
|vm, env, _args| {
let mut reader = InteractiveReader::new("> ", ">> ");
let value = read::read(&mut reader, vm, env);
let value = match value {
Ok(Some(value)) => {
Value::list_or_nil([Value::Identifier("ok".into()), value]).quote()
}
Ok(None) => Value::Nil,
Err(error) => Value::list_or_nil([
Value::Identifier("err".into()),
Value::String(format!("{error}").into()),
])
.quote(),
};
Ok(value)
},
);
env.defun_native(
"unquote",
"For quoted values, strips the quote and returns the inner value",
|_, _, args| {
let [x] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let unquoted = x.unquote()?;
Ok(unquoted)
},
);
env.defun_native(
"print",
"Prints the arguments to the standard output",
|_, _, args| {
for (i, arg) in args.iter().enumerate() {
if i != 0 {
print!(" ");
}
if let Value::String(string) = arg {
print!("{}", &**string);
} else {
print!("{arg}");
}
}
println!();
Ok(Value::Nil)
},
);
}
+59
View File
@@ -0,0 +1,59 @@
use std::rc::Rc;
use crate::{
error::{MachineError, ValueConversionError},
vm::{
env::Environment,
value::convert::{AnyFunction, TryFromValue},
},
};
pub fn load(env: &Rc<Environment>) {
// env.defun_native("map", |vm, env, args| {
// let [f, xs] = args else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
// let xs = xs.proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, env, slice::from_ref(v?))))?;
// Ok(out)
// });
// env.defun_native("filter", |vm, env, args| {
// let [f, xs] = args else {
// return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
// };
// let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
// let xs = xs
// .proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument))
// .map(|x| x.cloned());
// let out = Value::try_list_or_nil(xs.try_filter(|v| {
// let result = f.invoke(vm, env, slice::from_ref(v))?;
// Ok(bool::try_from_value(&result).unwrap_or_default())
// }))?;
// Ok(out)
// });
env.defun_native(
"apply",
"Applies the function to a given argument list",
|vm, env, args| {
let [f, xs] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let f = AnyFunction::try_from_value(f)?;
let args = xs
.proper_iter(MachineError::ValueConversion(ValueConversionError {
expected: "proper list".into(),
got: xs.clone(),
}))
.map(|x| x.cloned())
.collect::<Result<Vec<_>, _>>()?;
f.invoke(vm, env, &args[..])
},
);
env.defun_native("identity", "Returns the argument as is", |_, _, args| {
let [arg] = args else {
return Err(MachineError::InvalidArgumentCount);
};
Ok(arg.clone())
});
}
+152
View File
@@ -0,0 +1,152 @@
use std::{
any::Any,
cell::RefCell,
fmt,
fs::File,
io::{Read, Write, stdin, stdout},
rc::Rc,
};
use crate::{
error::{MachineError, ValueConversionError},
vm::{
Value,
env::Environment,
value::{NativeObject, NativeValue, StringValue, convert::TryFromValue},
},
};
#[derive(Debug)]
enum Stream {
Stdin,
Stdout,
File(RefCell<Option<File>>),
}
impl NativeObject for Stream {
fn as_any(&self) -> &dyn Any {
self
}
}
impl fmt::Display for Stream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("stream")
}
}
impl Stream {
pub fn read(&self, amount: usize) -> Option<Value> {
let mut output = vec![];
let mut remaining = amount;
let mut buffer = [0; 4096];
while remaining != 0 {
let want = remaining.min(buffer.len());
let len = match self {
Self::Stdin => stdin().read(&mut buffer[..want]).ok()?,
Self::File(cell) => match &mut *cell.borrow_mut() {
Some(file) => file.read(&mut buffer[..want]).ok()?,
None => return None,
},
Self::Stdout => return None,
};
if len == 0 {
break;
}
output.extend_from_slice(&buffer[..len]);
remaining -= len;
}
Some(output.into())
}
fn write(&self, bytes: &[u8]) -> Option<Value> {
let len = match self {
Self::Stdin => return None,
Self::Stdout => stdout().write(bytes).ok()?,
Self::File(cell) => match &mut *cell.borrow_mut() {
Some(file) => file.write(bytes).ok()?,
None => return None,
},
};
Some(Value::Number(len.into()))
}
}
pub fn load(env: &Rc<Environment>) {
let stdin: Rc<dyn NativeObject> = Rc::new(Stream::Stdin);
let stdout: Rc<dyn NativeObject> = Rc::new(Stream::Stdout);
env.set_global_value("*stdin*", NativeValue::from(stdin));
env.set_global_value("*stdout*", NativeValue::from(stdout));
env.defun_native("fopen", "Opens a file", |_, _, args| {
let [path] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let path = StringValue::try_from_value(path)?;
match File::open(&*path) {
Ok(file) => {
let object: Rc<dyn NativeObject> = Rc::new(Stream::File(RefCell::new(Some(file))));
Ok(Value::NativeValue(object.into()))
}
Err(error) => {
eprintln!("TODO: not sure whether to use result/exceptions");
eprintln!("{path}: {error}");
todo!()
}
}
});
env.defun_native(
"fread",
"Reads data as a byte vector from the stream",
|_, _, args| {
let (stream, amount) = match args {
// TODO read to end instead
[stream] => (stream.as_native::<Stream>()?, 4096),
[stream, amount] => (
stream.as_native::<Stream>()?,
usize::try_from_value(amount)?,
),
_ => return Err(MachineError::InvalidArgumentCount),
};
match stream.read(amount) {
Some(value) => Ok(value),
None => todo!(),
}
},
);
env.defun_native("fwrite", "Writes data to the stream", |_, _, args| {
let [stream, data] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let stream = stream.as_native::<Stream>()?;
let result = match data {
Value::Vector(value) if let Some(bytes) = value.as_bytes() => stream.write(&bytes[..]),
Value::String(value) => stream.write(value.as_bytes()),
_ => {
return Err(MachineError::ValueConversion(ValueConversionError {
expected: "byte vector or string".into(),
got: data.clone(),
}));
}
};
match result {
Some(value) => Ok(value),
None => todo!(),
}
});
env.defun_native("fclose", "Closes the stream", |_, _, args| {
let [stream] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let stream = stream.as_native::<Stream>()?;
if let Stream::File(cell) = stream.as_ref() {
cell.replace(None);
}
Ok(Value::Nil)
});
}
+208 -122
View File
@@ -1,32 +1,99 @@
use std::{
cmp::Ordering,
ops::{BitAnd, BitOr, BitXor},
};
use std::{cmp::Ordering, ops::Mul, rc::Rc};
use crate::{
error::{MachineError, MachineErrorKind},
error::MachineError,
vm::{
env::Environment,
instruction::Instruction,
machine::Machine,
value::{TryFromValue, Value},
value::{NumberValue, Value, convert::TryFromValue},
},
};
fn value_add(a: &Value, b: &Value) -> Result<Value, MachineErrorKind> {
#[allow(clippy::type_complexity)]
pub(crate) fn dispatch_arithmetic(
instruction: Instruction,
) -> fn(&mut Machine, &Rc<Environment>, &[Value]) -> Result<Value, MachineError> {
match instruction {
Instruction::Add => builtin_add,
Instruction::Sub => builtin_sub,
Instruction::Mul => builtin_mul,
Instruction::Div => builtin_div,
Instruction::Mod => builtin_mod,
Instruction::Gt => builtin_cmp_gt,
Instruction::Lt => builtin_cmp_lt,
Instruction::Ge => builtin_cmp_ge,
Instruction::Le => builtin_cmp_le,
Instruction::Eq => builtin_cmp_eq,
Instruction::Ne => builtin_cmp_ne,
Instruction::Not => builtin_not,
_ => unreachable!(),
}
}
pub(super) fn load(env: &Rc<Environment>) {
env.defun_native("+", "Adds values", builtin_add);
env.defun_native("-", "Subtracts values", builtin_sub);
let mul = env.defun_native("*", "Multiplies values", builtin_mul);
env.set_global_value("·", mul); // alias for *
let div = env.defun_native("/", "Divides values", builtin_div);
env.set_global_value("÷", div); // alias for /
env.defun_native(
"%",
"Calculates a modulo/remainder of value divison",
builtin_mod,
);
env.defun_native(
">",
"Returns #t if the arguments are monotonically decreasing",
builtin_cmp_gt,
);
let ge = env.defun_native(
">=",
"Returns #t if the arguments are monotonically non-increasing",
builtin_cmp_ge,
);
env.set_global_value("", ge); // alias for >=
env.defun_native(
"<",
"Returns #t if the arguments are monotonically increasing",
builtin_cmp_lt,
);
let le = env.defun_native(
"<=",
"Returns #t if the arguments are monotonically non-decreasing",
builtin_cmp_le,
);
env.set_global_value("", le); // alias for <=
env.defun_native(
"=",
"Returns #t if all the arguments are equal",
builtin_cmp_eq,
);
let ne = env.defun_native(
"/=",
"Returns #t if none of the arguments are equal",
builtin_cmp_ne,
);
env.set_global_value("", ne); // alias for /=
// env.defun_native("&&", builtin_and);
// env.defun_native("||", builtin_or);
// env.defun_native("&", builtin_bitwise_and);
// env.defun_native("|", builtin_bitwise_or);
// env.defun_native("^", builtin_bitwise_xor);
}
fn value_add(a: &Value, b: &Value) -> Result<Value, MachineError> {
match (a, b) {
(Value::String(a), _) => {
let b = b.stringify()?;
Ok(Value::String(format!("{a}{b}").into()))
}
(_, Value::String(b)) => {
let a = a.stringify()?;
Ok(Value::String(format!("{a}{b}").into()))
}
(Value::Integer(a), Value::Integer(b)) => Ok(Value::Integer(a.wrapping_add(*b))),
(Value::Integer(a), _) => Ok(Value::Integer(a.wrapping_add(i64::try_from_value(b)?))),
(_, Value::Integer(b)) => Ok(Value::Integer(i64::try_from_value(a)?.wrapping_add(*b))),
(Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Integer(*a as i64 + *b as i64)),
_ => Err(MachineErrorKind::InvalidArgument),
(Value::String(a), _) => Ok(Value::String(format!("{a}{b}").into())),
(_, Value::String(b)) => Ok(Value::String(format!("{a}{b}").into())),
(Value::Number(a), Value::Number(b)) => Ok(Value::Number(*a + *b)),
(Value::Number(a), _) => Ok(Value::Number(*a + NumberValue::try_from_value(b)?)),
(_, Value::Number(b)) => Ok(Value::Number(NumberValue::try_from_value(a)? + *b)),
(Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Number(*a + *b)),
_ => todo!("TODO error"),
}
}
@@ -39,13 +106,9 @@ pub(crate) enum CompareOperation {
Ge,
}
fn builtin_fold<F>(
fold: F,
mut accumulator: Value,
args: &[Value],
) -> Result<Value, MachineErrorKind>
fn builtin_fold<F>(fold: F, mut accumulator: Value, args: &[Value]) -> Result<Value, MachineError>
where
F: Fn(&Value, &Value) -> Result<Value, MachineErrorKind>,
F: Fn(&Value, &Value) -> Result<Value, MachineError>,
{
for (i, arg) in args.iter().enumerate() {
if i == 0 {
@@ -58,7 +121,6 @@ where
}
fn builtin_fold_t<'a, T, F>(
vm: &mut Machine,
fold: F,
mut accumulator: T,
args: &'a [Value],
@@ -68,139 +130,129 @@ where
T: TryFromValue<'a> + Into<Value>,
{
for arg in args {
let arg = T::try_from_value(arg).map_err(|e| vm.error_at_ip(e))?;
let arg = T::try_from_value(arg)?;
accumulator = fold(accumulator, arg);
}
Ok(accumulator.into())
}
pub(crate) fn builtin_add(
vm: &mut Machine,
_env: &mut Environment,
_vm: &mut Machine,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_fold(value_add, Value::Integer(0), args).map_err(|e| vm.error_at_ip(e))
builtin_fold(value_add, Value::Number(0.into()), args)
}
pub(crate) fn builtin_sub(
vm: &mut Machine,
_env: &mut Environment,
_vm: &mut Machine,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
match args {
[] => Err(vm.error_at_ip(MachineErrorKind::InvalidArgument)),
[x] if let &Value::Integer(x) = x => Ok(Value::Integer(-x)),
[x] => todo!("Math for {x}"),
[] => Err(MachineError::InvalidArgumentCount),
[x] => {
let x = NumberValue::try_from_value(x)?;
Ok((-x).into())
}
[x, rest @ ..] => {
let &Value::Integer(x) = x else {
todo!("Math for {x}");
};
let mut accumulator = x;
for arg in rest {
match arg {
&Value::Integer(arg) => {
accumulator = accumulator.saturating_sub(arg);
}
_ => todo!("Math for {arg}"),
}
let mut accumulator = NumberValue::try_from_value(x)?;
for argument in rest {
let value = NumberValue::try_from_value(argument)?;
accumulator -= value;
}
Ok(Value::Integer(accumulator))
Ok(Value::Number(accumulator))
}
}
}
pub(crate) fn builtin_mul(
vm: &mut Machine,
_env: &mut Environment,
_vm: &mut Machine,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_fold_t(vm, i64::wrapping_mul, 1, args)
builtin_fold_t(Mul::mul, NumberValue::from(1), args)
}
pub(crate) fn builtin_mod(
vm: &mut Machine,
_env: &mut Environment,
_vm: &mut Machine,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
let [x, y] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
return Err(MachineError::InvalidArgumentCount);
};
let (&Value::Integer(x), &Value::Integer(y)) = (x, y) else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
Ok(Value::Integer(x % y))
let x = NumberValue::try_from_value(x)?;
let y = NumberValue::try_from_value(y)?;
Ok(Value::Number(x % y))
}
pub(crate) fn builtin_div(
vm: &mut Machine,
_env: &mut Environment,
_vm: &mut Machine,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
match args {
[] => Err(vm.error_at_ip(MachineErrorKind::InvalidArgument)),
[x] if let &Value::Integer(_x) = x => todo!("Fractionals"),
[x] => todo!("Math for {x}"),
[] => Err(MachineError::InvalidArgumentCount),
[x] => {
let x = NumberValue::try_from_value(x)?;
Ok(x.reciprocal().into())
}
[x, rest @ ..] => {
let &Value::Integer(x) = x else {
todo!("Math for {x}");
};
let mut accumulator = x;
for arg in rest {
match arg {
&Value::Integer(arg) => {
accumulator = accumulator.saturating_div(arg);
}
_ => todo!("Math for {arg}"),
}
let mut accumulator = NumberValue::try_from_value(x)?;
for argument in rest {
let argument = NumberValue::try_from_value(argument)?;
accumulator /= argument;
}
Ok(Value::Integer(accumulator))
Ok(accumulator.into())
}
}
}
pub(crate) fn builtin_and(
vm: &mut Machine,
_env: &mut Environment,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_fold_t(vm, BitAnd::bitand, true, args)
}
pub(crate) fn builtin_or(
vm: &mut Machine,
_env: &mut Environment,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_fold_t(vm, BitOr::bitor, false, args)
}
pub(crate) fn builtin_bitwise_and(
vm: &mut Machine,
_env: &mut Environment,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_fold_t(vm, BitAnd::bitand, 1, args)
}
pub(crate) fn builtin_bitwise_or(
vm: &mut Machine,
_env: &mut Environment,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_fold_t(vm, BitOr::bitor, 0, args)
}
pub(crate) fn builtin_bitwise_xor(
vm: &mut Machine,
_env: &mut Environment,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_fold_t(vm, BitXor::bitxor, 0, args)
}
// pub(crate) fn builtin_and(
// vm: &mut Machine,
// _env: &Rc<Environment>,
// args: &[Value],
// ) -> Result<Value, MachineError> {
// builtin_fold_t(BitAnd::bitand, true, args)
// }
// pub(crate) fn builtin_or(
// vm: &mut Machine,
// _env: &Rc<Environment>,
// args: &[Value],
// ) -> Result<Value, MachineError> {
// builtin_fold_t(BitOr::bitor, false, args)
// }
// pub(crate) fn builtin_bitwise_and(
// vm: &mut Machine,
// _env: &Rc<Environment>,
// args: &[Value],
// ) -> Result<Value, MachineError> {
// builtin_fold_t(BitAnd::bitand, 1, args)
// }
// pub(crate) fn builtin_bitwise_or(
// vm: &mut Machine,
// _env: &Rc<Environment>,
// args: &[Value],
// ) -> Result<Value, MachineError> {
// builtin_fold_t(BitOr::bitor, 0, args)
// }
// pub(crate) fn builtin_bitwise_xor(
// vm: &mut Machine,
// _env: &Rc<Environment>,
// args: &[Value],
// ) -> Result<Value, MachineError> {
// builtin_fold_t(BitXor::bitxor, 0, args)
// }
pub(crate) fn builtin_cmp(
_vm: &mut Machine,
args: &[Value],
op: CompareOperation,
) -> Result<Value, MachineError> {
fn ordering(a: &Value, b: &Value) -> Ordering {
fn ordering(a: &Value, b: &Value) -> Option<Ordering> {
match (a, b) {
(Value::Integer(a), Value::Integer(b)) => Ord::cmp(a, b),
(Value::Boolean(a), Value::Boolean(b)) => Ord::cmp(a, b),
(Value::String(a), Value::String(b)) => Ord::cmp(a, b),
_ => Ordering::Less,
(Value::Number(a), Value::Number(b)) => PartialOrd::partial_cmp(a, b),
(Value::Boolean(a), Value::Boolean(b)) => Some(Ord::cmp(a, b)),
(Value::String(a), Value::String(b)) => Some(Ord::cmp(a, b)),
(Value::Identifier(a), Value::Identifier(b)) => Some(Ord::cmp(a, b)),
_ => None,
}
}
@@ -209,8 +261,27 @@ pub(crate) fn builtin_cmp(
for i in 0..args.len() - 1 {
let a = &args[i];
let b = &args[i + 1];
match op {
CompareOperation::Eq => {
accumulator &= a == b;
continue;
}
CompareOperation::Ne => {
accumulator &= a != b;
continue;
}
_ => (),
}
let ord = ordering(a, b);
// No ordering is specified
let Some(ord) = ord else {
accumulator = false;
continue;
};
let check = match op {
CompareOperation::Eq => ord.is_eq(),
CompareOperation::Ne => ord.is_ne(),
@@ -222,47 +293,62 @@ pub(crate) fn builtin_cmp(
accumulator &= check;
}
}
Ok(Value::Boolean(accumulator))
Ok(accumulator.into())
}
pub(crate) fn builtin_cmp_eq(
vm: &mut Machine,
_env: &mut Environment,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Eq)
}
pub(crate) fn builtin_cmp_ne(
vm: &mut Machine,
_env: &mut Environment,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Ne)
}
pub(crate) fn builtin_cmp_gt(
vm: &mut Machine,
_env: &mut Environment,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Gt)
}
pub(crate) fn builtin_cmp_lt(
vm: &mut Machine,
_env: &mut Environment,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Lt)
}
pub(crate) fn builtin_cmp_ge(
vm: &mut Machine,
_env: &mut Environment,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Ge)
}
pub(crate) fn builtin_cmp_le(
vm: &mut Machine,
_env: &mut Environment,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Le)
}
pub(crate) fn builtin_not(
_vm: &mut Machine,
_env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
let [x] = args else {
return Err(MachineError::InvalidArgumentCount);
};
let result = match x {
Value::Boolean(value) => Value::Boolean(!*value),
_ => Value::Boolean((!x.is_trueish()).into()),
};
Ok(result)
}
+22 -155
View File
@@ -1,164 +1,31 @@
use std::{rc::Rc, slice};
use std::rc::Rc;
use crate::{
error::MachineErrorKind,
util::IteratorExt,
vm::{
env::Environment,
value::{AnyFunction, ConsCell, Keyword, TryFromValue, Value, ValueString},
},
};
use crate::vm::{env::Environment, machine::Machine};
mod collections;
mod convert;
mod debug;
mod eval;
mod functional;
mod io;
mod math;
pub(crate) use math::*;
pub fn load(env: &mut Environment) {
// math
env.defun_native("+", builtin_add);
env.defun_native("-", builtin_sub);
env.defun_native("*", builtin_mul);
env.defun_native("%", builtin_mod);
env.defun_native("/", builtin_div);
const PRELUDE_SOURCE: &str = include_str!("../../prelude.lysp");
env.defun_native("&&", builtin_and);
env.defun_native("||", builtin_or);
env.defun_native("&", builtin_bitwise_and);
env.defun_native("|", builtin_bitwise_or);
env.defun_native("^", builtin_bitwise_xor);
pub fn load(env: &Rc<Environment>) {
let mut vm = Machine::default();
env.defun_native(">", builtin_cmp_gt);
env.defun_native("<", builtin_cmp_lt);
env.defun_native("=", builtin_cmp_eq);
env.defun_native("/=", builtin_cmp_ne);
env.defun_native(">=", builtin_cmp_ge);
env.defun_native("<=", builtin_cmp_le);
math::load(env);
eval::load(env);
functional::load(env);
collections::load(env);
convert::load(env);
debug::load(env);
io::load(env);
// conversion
env.defun_native("string->int", |vm, _, args| {
let [arg] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
let arg = ValueString::try_from_value(arg).map_err(|e| vm.error_at_ip(e))?;
let result = arg.parse::<i64>().map(Value::Integer).unwrap_or(Value::Nil);
Ok(result)
});
env.defun_native("int->string", |vm, _, args| {
let [arg] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
let arg = i64::try_from_value(arg).map_err(|e| vm.error_at_ip(e))?;
let result = Value::String(format!("{arg}").into());
Ok(result)
});
// lists
env.defun_native("cons", |vm, _env, args| {
let [car, cdr] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
Ok(Value::Cons(Rc::new(ConsCell(car.clone(), cdr.clone()))))
});
env.defun_native("map", |vm, env, args| {
let [f, xs] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
let xs = xs.proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument));
let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, env, slice::from_ref(v?))))?;
Ok(out)
});
env.defun_native("filter", |vm, env, args| {
let [f, xs] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
let xs = xs
.proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument))
.map(|x| x.cloned());
let out = Value::try_list_or_nil(xs.try_filter(|v| {
let result = f.invoke(vm, env, slice::from_ref(v))?;
Ok(bool::try_from_value(&result).unwrap_or_default())
}))?;
Ok(out)
});
env.defun_native("list", |_, _, args| {
let out = Value::list_or_nil(args.iter().cloned());
Ok(out)
});
// functional
env.defun_native("identity", |vm, _, args| {
let [arg] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
Ok(arg.clone())
});
// eval
env.defun_native("eval", |vm, env, args| {
let value = match args {
[] => todo!(),
[value] => value,
[_, _] => todo!(),
_ => todo!(),
};
let value = match vm.eval_value(Default::default(), env, value.clone()) {
Ok(result) => result,
_ => todo!(),
};
Ok(value)
});
env.defun_native("apply", |vm, env, args| {
let [f, xs] = args else {
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
};
let f = AnyFunction::try_from_value(f).map_err(|e| vm.error_at_ip(e))?;
let args = xs
.proper_iter(vm.error_at_ip(MachineErrorKind::InvalidArgument))
.map(|x| x.cloned())
.collect::<Result<Vec<_>, _>>()?;
f.invoke(vm, env, &args[..])
});
// io
env.defun_native("print", |_, _, args| {
for (i, arg) in args.iter().enumerate() {
if i != 0 {
print!(" ");
}
print!("{arg}");
}
println!();
Ok(Value::Nil)
});
env.defmacro_native("assert", |vm, _, args| match args {
[] => Err(vm.error_at_ip(MachineErrorKind::InvalidArgument)),
[cond] => {
let assertion_failure_msg = match vm.ip() {
Some(ip) => format!("{ip}: assertion failed: {cond}"),
None => format!("<undefined>: assertion failed: {cond}"),
};
let assertion_failure = Value::list_or_nil([
Value::Identifier("abort".into()),
Value::String(assertion_failure_msg.into()),
]);
Ok(Value::list_or_nil([
Value::Keyword(Keyword::If),
cond.clone(),
Value::Nil,
assertion_failure,
]))
}
_ => Err(vm.error_at_ip(MachineErrorKind::InvalidArgument)),
});
env.defun_native("abort", |vm, _, args| {
let mut message = String::new();
for (i, arg) in args.iter().enumerate() {
if i != 0 {
message.push(' ');
}
message.push_str(&format!("{arg}"));
}
Err(vm.error_at_ip(MachineErrorKind::Aborted(message)))
});
// Load the lysp part of the prelude
vm.evaluate_str(Default::default(), None, env, PRELUDE_SOURCE)
.expect("Couldn't evaluate prelude lysp part");
}
+188 -39
View File
@@ -1,71 +1,220 @@
use std::{fmt, mem::MaybeUninit, ops::Deref};
use std::{
mem::MaybeUninit,
ops::{Bound, Index, RangeBounds},
};
pub struct Stack<T> {
data: Box<[MaybeUninit<T>]>,
pointer: usize,
}
impl<T: fmt::Debug> fmt::Debug for Stack<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = &self[..];
f.debug_struct("Stack")
.field("pointer", &self.pointer)
.field("data", &data)
.finish()
}
}
impl<T> Deref for Stack<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
let slice = &self.data[self.pointer..];
unsafe { slice.assume_init_ref() }
}
}
impl<T> Stack<T> {
pub fn new(limit: usize) -> Self {
assert_ne!(limit, 0);
Self {
data: Box::new_uninit_slice(limit),
pointer: limit,
pointer: 0,
}
}
pub fn current(&self) -> Option<&T> {
if self.pointer >= self.data.len() {
None
pub fn is_empty(&self) -> bool {
self.pointer == 0
}
pub fn checked_slice<I: RangeBounds<usize>>(&self, range: I) -> Option<&[T]> {
let start = match range.start_bound() {
Bound::Unbounded => 0,
Bound::Included(start) | Bound::Excluded(start) => *start,
};
let end = match range.end_bound() {
Bound::Unbounded => self.pointer,
Bound::Excluded(end) => *end,
Bound::Included(end) => *end + 1,
};
if start <= self.pointer && end <= self.pointer {
Some(unsafe { self.data[start..end].assume_init_ref() })
} else {
Some(unsafe { self.data[self.pointer].assume_init_ref() })
None
}
}
pub fn current_mut(&mut self) -> Option<&mut T> {
if self.pointer >= self.data.len() {
None
pub fn checked_last(&self, n: usize) -> Option<&[T]> {
if n < self.pointer {
Some(unsafe { self.data[self.pointer - n..self.pointer].assume_init_ref() })
} else {
Some(unsafe { self.data[self.pointer].assume_init_mut() })
None
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index < self.pointer {
Some(unsafe { self.data[index].assume_init_mut() })
} else {
None
}
}
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.pointer {
Some(unsafe { self.data[index].assume_init_ref() })
} else {
None
}
}
pub fn head(&self) -> Option<&T> {
if self.pointer > 0 {
Some(unsafe { self.data[self.pointer - 1].assume_init_ref() })
} else {
None
}
}
pub fn head_mut(&mut self) -> Option<&mut T> {
if self.pointer > 0 {
Some(unsafe { self.data[self.pointer - 1].assume_init_mut() })
} else {
None
}
}
pub fn pointer(&self) -> usize {
self.pointer
}
pub fn set_pointer(&mut self, pointer: usize) {
if pointer < self.pointer {
// Call drop for values between
for slot in &mut self.data[pointer..self.pointer] {
unsafe { slot.assume_init_drop() };
}
} else if pointer > self.pointer {
panic!("Cannot set stack pointer higher than it currently is");
}
self.pointer = pointer;
}
pub fn pop(&mut self) -> Option<T> {
if self.pointer > 0 {
self.pointer -= 1;
let value = unsafe { self.data[self.pointer].assume_init_read() };
Some(value)
} else {
None
}
}
pub fn push(&mut self, value: T) -> Result<(), T> {
if self.pointer > 0 {
self.pointer -= 1;
if self.pointer < self.data.len() {
self.data[self.pointer].write(value);
self.pointer += 1;
Ok(())
} else {
Err(value)
}
}
}
pub fn pop(&mut self) -> Option<T> {
if self.pointer >= self.data.len() {
None
} else {
let value = unsafe { self.data[self.pointer].assume_init_read() };
self.pointer += 1;
Some(value)
}
impl<T> Index<usize> for Stack<T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.get(index).expect("stack index out of bounds")
}
}
#[cfg(test)]
mod tests {
use std::sync::atomic::{AtomicBool, Ordering};
use crate::vm::stack::Stack;
#[derive(Debug)]
struct DropTracker(&'static AtomicBool);
impl Drop for DropTracker {
fn drop(&mut self) {
self.0.store(true, Ordering::Release);
}
}
#[test]
fn test_checked_slice() {
let mut s = Stack::<u32>::new(32);
s.push(1).unwrap();
s.push(2).unwrap();
s.push(3).unwrap();
s.push(4).unwrap();
s.push(5).unwrap();
// unbound..unbound
assert_eq!(s.checked_slice(..).unwrap(), &[1, 2, 3, 4, 5]);
// unbound..included
assert_eq!(s.checked_slice(..=3).unwrap(), &[1, 2, 3, 4]);
// unbound..excluded
assert_eq!(s.checked_slice(..3).unwrap(), &[1, 2, 3]);
// included..unbound
assert_eq!(s.checked_slice(2..).unwrap(), &[3, 4, 5]);
// included..included
assert_eq!(s.checked_slice(2..=3).unwrap(), &[3, 4]);
// included..excluded
assert_eq!(s.checked_slice(2..3).unwrap(), &[3]);
// Invalid
assert!(s.checked_slice(..10).is_none());
assert!(s.checked_slice(10..).is_none());
assert!(s.checked_slice(10..9).is_none());
}
#[test]
fn test_underflow() {
let mut s = Stack::<u32>::new(128);
assert!(s.pop().is_none());
}
#[test]
fn test_overflow() {
let mut s = Stack::<u32>::new(4);
for i in 0..4 {
s.push(i).unwrap();
}
assert_eq!(s.push(1234).unwrap_err(), 1234);
}
#[test]
fn test_no_drop_on_pop() {
static DROP0: AtomicBool = AtomicBool::new(false);
let mut s = Stack::<DropTracker>::new(4);
s.push(DropTracker(&DROP0)).unwrap();
assert!(!DROP0.load(Ordering::Acquire));
let v = s.pop().unwrap();
assert!(!DROP0.load(Ordering::Acquire));
drop(v);
assert!(DROP0.load(Ordering::Acquire));
}
#[test]
fn test_drop_on_set_pointer_lower() {
static DROP0: AtomicBool = AtomicBool::new(false);
static DROP1: AtomicBool = AtomicBool::new(false);
let mut s = Stack::<DropTracker>::new(4);
s.push(DropTracker(&DROP0)).unwrap();
s.push(DropTracker(&DROP1)).unwrap();
s.set_pointer(1);
assert!(!DROP0.load(Ordering::Acquire));
assert!(DROP1.load(Ordering::Acquire));
}
#[test]
fn test_stack_head() {
let mut s = Stack::<u32>::new(4);
assert!(s.head().is_none());
s.push(1234).unwrap();
assert_eq!(s.head().unwrap(), &1234);
}
}
+40
View File
@@ -0,0 +1,40 @@
use std::{
fmt,
ops::{Add, Not},
};
use crate::vm::value::NumberValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BooleanValue(pub bool);
impl From<bool> for BooleanValue {
fn from(value: bool) -> Self {
Self(value)
}
}
impl Not for BooleanValue {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
impl Add for BooleanValue {
type Output = NumberValue;
fn add(self, rhs: Self) -> Self::Output {
NumberValue::Int(self.0 as i64 + rhs.0 as i64)
}
}
impl fmt::Display for BooleanValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
true => write!(f, "#T"),
false => write!(f, "#F"),
}
}
}
-16
View File
@@ -1,16 +0,0 @@
use std::fmt;
use crate::vm::module::ModuleRef;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct BytecodeFunction {
pub module: ModuleRef,
pub required_count: usize,
pub address: usize,
}
impl fmt::Display for BytecodeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<bytecode {:p}:{}>", self.module, self.address)
}
}
+35
View File
@@ -0,0 +1,35 @@
use std::{fmt, rc::Rc};
use crate::vm::{Value, value::BytecodeFunction};
#[derive(Debug, Clone, PartialEq)]
pub enum UpvalueValue {
Open(usize),
Closed(Box<Value>),
}
#[derive(Clone, PartialEq)]
pub struct ClosureValue {
pub function: Rc<BytecodeFunction>,
pub upvalues: Vec<usize>,
}
impl ClosureValue {
pub fn instruction_byte(&self, address: usize) -> Option<u8> {
self.function.instructions.get(address).copied()
}
}
impl fmt::Debug for ClosureValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClosureValue")
.field("function", &self.function)
.finish_non_exhaustive()
}
}
impl fmt::Display for ClosureValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<closure {}@{:p}>", self.function.name(), self.function)
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ use std::fmt;
use crate::vm::value::Value;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub struct ConsCell(pub Value, pub Value);
impl ConsCell {
+128 -100
View File
@@ -1,151 +1,179 @@
use std::rc::Rc;
use crate::{
error::{MachineError, MachineErrorKind},
error::{MachineError, ValueConversionError},
vm::{
Value,
env::Environment,
machine::Machine,
value::{BytecodeFunction, ConsCell, Keyword, NativeFunction, Value, ValueString},
value::{
BooleanValue, BytecodeFunction, ClosureValue, ConsCell, IdentifierValue,
NativeFunction, NativeValue, NumberValue, StringValue, Vector,
},
},
};
pub enum AnyFunction {
Bytecode(BytecodeFunction),
Native(NativeFunction),
}
pub trait TryFromValue<'a>: Sized {
fn try_from_value(value: &'a Value) -> Result<Self, MachineErrorKind>;
fn try_from_value_list(mut list_value: &'a Value) -> Result<Vec<Self>, MachineErrorKind> {
let mut output = vec![];
while !list_value.is_nil() {
let Value::Cons(cons) = list_value else {
return Err(MachineErrorKind::InvalidArgument);
};
let ConsCell(car, cdr) = cons.as_ref();
output.push(Self::try_from_value(car)?);
list_value = cdr;
}
Ok(output)
}
fn try_from_value(value: &'a Value) -> Result<Self, ValueConversionError>;
}
// From values
macro_rules! impl_primitive_value {
($($t:ty),+ $(,)?) => {
pub enum AnyFunction {
Native(NativeFunction),
Function(Rc<BytecodeFunction>),
Closure(ClosureValue),
}
macro_rules! impl_integer {
($($ty:ty : $bits:literal),+ $(,)?) => {
$(
impl From<$t> for Value {
fn from(value: $t) -> Self {
Self::Integer(value as i64)
impl TryFromValue<'_> for $ty {
fn try_from_value(value: &Value) -> Result<Self, ValueConversionError> {
match value {
#[allow(irrefutable_let_patterns)]
Value::Number(value) if let Ok(value) = <$ty>::try_from(*value) => Ok(value),
_ => Err(ValueConversionError {
expected: format!("{}-bit integer", $bits),
got: value.clone()
})
}
}
}
impl TryFromValue<'_> for $t {
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
match value {
#[allow(irrefutable_let_patterns)]
&Value::Integer(value) if let Ok(value) = value.try_into() => Ok(value),
&Value::Boolean(true) => Ok(1),
&Value::Boolean(false) => Ok(0),
_ => Err(MachineErrorKind::InvalidArgument)
}
impl From<$ty> for Value {
fn from(value: $ty) -> Value {
Value::Number(value.into())
}
}
)+
};
}
impl AnyFunction {
pub fn invoke(
&self,
vm: &mut Machine,
env: &mut Environment,
args: &[Value],
) -> Result<Value, MachineError> {
match self {
Self::Bytecode(bytecode) => {
Ok(vm.eval_bytecode_call(env, bytecode.clone(), args).unwrap())
}
Self::Native(native) => native.invoke(vm, env, args),
}
}
}
impl TryFromValue<'_> for Keyword {
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
match value {
Value::Keyword(value) => Ok(*value),
_ => Err(MachineErrorKind::InvalidArgument),
}
}
}
impl TryFromValue<'_> for bool {
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
match value {
Value::Nil => Ok(false),
Value::Cons(_) => Ok(true),
Value::Vector(vector) => Ok(!vector.is_empty()),
Value::String(value) => Ok(!value.is_empty()),
Value::Boolean(value) => Ok(*value),
Value::Integer(value) => Ok(*value != 0),
Value::Keyword(_)
| Value::Quasi(_)
| Value::Unquote(_)
| Value::Quote(_)
| Value::Identifier(_)
| Value::OpaqueValue(_)
| Value::NativeFunction(_)
| Value::BytecodeFunction(_) => Ok(true),
Value::Boolean(BooleanValue(value)) => Ok(*value),
_ => Err(ValueConversionError {
expected: "#T or #F".into(),
got: value.clone(),
}),
}
}
}
impl From<bool> for Value {
fn from(value: bool) -> Self {
Self::Boolean(value)
Value::Boolean(value.into())
}
}
impl TryFromValue<'_> for ValueString {
fn try_from_value(value: &'_ Value) -> Result<Self, MachineErrorKind> {
impl TryFromValue<'_> for NumberValue {
fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
match value {
Value::String(value) => Ok(value.clone()),
_ => Err(MachineErrorKind::InvalidArgument),
Value::Number(value) => Ok(*value),
_ => Err(ValueConversionError {
expected: "integer value".into(),
got: value.clone(),
}),
}
}
}
impl From<ValueString> for Value {
fn from(value: ValueString) -> Self {
Self::String(value)
impl From<NumberValue> for Value {
fn from(value: NumberValue) -> Self {
Self::Number(value)
}
}
impl TryFromValue<'_> for Value {
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
Ok(value.clone())
impl<'a> TryFromValue<'a> for &'a ConsCell {
fn try_from_value(value: &'a Value) -> Result<Self, ValueConversionError> {
match value {
Value::Cons(cons) => Ok(cons.as_ref()),
_ => Err(ValueConversionError {
expected: "cons cell".into(),
got: value.clone(),
}),
}
}
}
impl<'a> TryFromValue<'a> for &'a Value {
fn try_from_value(value: &'a Value) -> Result<Self, MachineErrorKind> {
Ok(value)
impl From<Rc<Vector>> for Value {
fn from(value: Rc<Vector>) -> Self {
Self::Vector(value)
}
}
impl TryFromValue<'_> for StringValue {
fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
match value {
Value::String(value) => Ok(value.clone()),
_ => Err(ValueConversionError {
expected: "string".into(),
got: value.clone(),
}),
}
}
}
impl From<NativeValue> for Value {
fn from(value: NativeValue) -> Self {
Self::NativeValue(value)
}
}
impl From<Vec<u8>> for Value {
fn from(value: Vec<u8>) -> Self {
Value::Vector(Rc::new(Vector::from_iter(value)))
}
}
impl From<IdentifierValue> for Value {
fn from(value: IdentifierValue) -> Self {
Value::Identifier(value)
}
}
impl TryFromValue<'_> for AnyFunction {
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
fn try_from_value(value: &'_ Value) -> Result<Self, ValueConversionError> {
match value {
Value::BytecodeFunction(bytecode) => Ok(Self::Bytecode(bytecode.clone())),
Value::NativeFunction(native) => Ok(Self::Native(native.clone())),
_ => Err(MachineErrorKind::InvalidArgument),
Value::NativeFunction(function) => Ok(Self::Native(function.clone())),
Value::Closure(closure) => Ok(Self::Closure(closure.clone())),
Value::Function(function) => Ok(Self::Function(function.clone())),
_ => Err(ValueConversionError {
expected: "function".into(),
got: value.clone(),
}),
}
}
}
impl_primitive_value!(i8, i16, i32, i64);
impl_primitive_value!(u8, u16, u32, u64);
impl From<Value> for Result<Value, MachineErrorKind> {
fn from(value: Value) -> Self {
Ok(value)
impl AnyFunction {
pub fn invoke(
&self,
vm: &mut Machine,
env: &Rc<Environment>,
args: &[Value],
) -> Result<Value, MachineError> {
match self {
Self::Native(function) => function.invoke(vm, env, args),
Self::Closure(closure) => vm
.evaluate_closure_args(env, closure.clone(), args)
.map_err(|error| error.error),
Self::Function(function) => {
let closure = ClosureValue {
function: function.clone(),
upvalues: vec![],
};
vm.evaluate_closure_args(env, closure, args)
.map_err(|error| error.error)
}
}
}
}
impl_integer!(
i8: 8,
i16: 16,
i32: 32,
i64: 64,
usize: 64,
);
+218
View File
@@ -0,0 +1,218 @@
use std::fmt;
use crate::{
compile::UpvalueDef,
vm::{
Value,
instruction::{ConstantId, ImmediateInteger, Instruction},
value::{IdentifierValue, StringValue},
},
};
#[derive(Debug, PartialEq)]
pub struct BytecodeFunction {
pub identifier: Option<IdentifierValue>,
pub docstring: Option<StringValue>,
pub instructions: Box<[u8]>,
pub constants: Box<[Value]>,
pub upvalues: Box<[UpvalueDef]>,
pub required_count: usize,
pub optional_count: usize,
pub has_rest: bool,
}
enum TraceArgument {
Constant,
ImmediateInteger,
BranchTarget,
Byte,
None,
}
impl fmt::Display for BytecodeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<function {}@{:p}>", self.name(), self)
}
}
impl BytecodeFunction {
pub fn name(&self) -> &str {
match self.identifier.as_ref() {
Some(name) => name.as_ref(),
None => "unnamed",
}
}
pub fn docstring(&self) -> &str {
match self.docstring.as_ref() {
Some(docstring) => docstring.as_ref(),
None => "",
}
}
pub fn min_arity(&self) -> usize {
self.required_count
}
pub fn rest_argument_start(&self) -> Option<usize> {
self.has_rest
.then_some(self.required_count + self.optional_count)
}
pub fn max_arity(&self) -> usize {
if self.has_rest {
usize::MAX
} else {
self.required_count + self.optional_count
}
}
fn trace_immediate_integer_at(&self, address: usize) -> Option<ImmediateInteger> {
let Some(b0) = self.instructions.get(address).copied() else {
eprint!(" <??> <??>");
return None;
};
eprint!(" {b0:02x}");
let Some(b1) = self.instructions.get(address + 1).copied() else {
eprint!(" <??>");
return None;
};
eprint!(" {b1:02x}");
Some(ImmediateInteger::from(i16::from_le_bytes([b0, b1])))
}
fn trace_constant_at(&self, address: usize) -> Option<Option<&Value>> {
let Some(b0) = self.instructions.get(address).copied() else {
eprint!(" <??> <??>");
return None;
};
eprint!(" {b0:02x}");
let Some(b1) = self.instructions.get(address + 1).copied() else {
eprint!(" <??>");
return None;
};
eprint!(" {b1:02x}");
let id = usize::from(ConstantId::from(u16::from_le_bytes([b0, b1])));
Some(self.constants.get(id))
}
fn trace_byte_at(&self, address: usize) -> Option<u8> {
let Some(b0) = self.instructions.get(address).copied() else {
eprint!(" <??>");
return None;
};
eprint!(" {b0:02x}");
Some(b0)
}
pub fn disassemble(&self, address: usize, before: usize, after: usize, arrow: bool) {
let start = address.saturating_sub(before);
let end = (address + after + 1).min(self.instructions.len());
let mut position = start;
while position < end {
let arrow = if arrow && position == address {
"==>"
} else {
" "
};
eprint!("{self:p}:{position:<4}{arrow} ");
let opcode = self.instructions[position];
eprint!("{opcode:02x}");
let Ok(instruction) = Instruction::try_from(opcode) else {
eprintln!(" <UNKNOWN>");
position += 1;
continue;
};
let argument = match instruction {
// Stack
Instruction::PushNil => TraceArgument::None,
Instruction::PushTrue | Instruction::PushFalse => TraceArgument::None,
Instruction::PushInteger => TraceArgument::ImmediateInteger,
Instruction::PushConstant => TraceArgument::Constant,
Instruction::SetTemp | Instruction::GetTemp => TraceArgument::None,
Instruction::SetLocal | Instruction::GetLocal => TraceArgument::Byte,
Instruction::SetGlobal | Instruction::GetGlobal => TraceArgument::None,
Instruction::SetUpvalue | Instruction::GetUpvalue => TraceArgument::Byte,
Instruction::Drop => TraceArgument::None,
Instruction::DeclareGlobal => TraceArgument::None,
Instruction::DeclareMacro => TraceArgument::None,
// Arithmetic
Instruction::Ne
| Instruction::Gt
| Instruction::Lt
| Instruction::Eq
| Instruction::Ge
| Instruction::Le
| Instruction::Add
| Instruction::Sub
| Instruction::Mul
| Instruction::Div
| Instruction::Mod
| Instruction::Negate
| Instruction::Not => TraceArgument::Byte,
// Function
Instruction::Call => TraceArgument::Byte,
Instruction::Return => TraceArgument::None,
Instruction::MakeClosure => TraceArgument::None,
Instruction::CloseUpvalue => TraceArgument::Byte,
// Branch
Instruction::Branch => TraceArgument::BranchTarget,
Instruction::Jump => TraceArgument::BranchTarget,
};
position += 1;
match argument {
TraceArgument::Byte => {
let Some(byte) = self.trace_byte_at(position) else {
eprintln!("\t\t{instruction} <invalid>");
break;
};
position += 1;
eprintln!("\t\t{instruction} {byte}");
}
TraceArgument::Constant => {
let Some(constant) = self.trace_constant_at(position) else {
eprintln!("\t\t{instruction} <invalid>");
break;
};
position += 2;
let Some(constant) = constant else {
eprintln!("\t\t{instruction} <invalid>");
continue;
};
eprintln!("\t\t{instruction} {constant}");
}
TraceArgument::ImmediateInteger => {
let Some(value) = self.trace_immediate_integer_at(position) else {
eprintln!("\t\t{instruction} <invalid>");
break;
};
position += 2;
eprintln!("\t\t{instruction} {}", value.sign_extend_i64());
}
TraceArgument::None => {
eprintln!("\t\t{instruction}");
}
TraceArgument::BranchTarget => {
let Some(byte) = self.trace_byte_at(position) else {
eprintln!("\t\t{instruction} <invalid>");
break;
};
position += 1;
eprintln!(
"\t\t{instruction} {:+} (-> {})",
byte as i8,
position.saturating_add_signed(isize::from(byte as i8))
);
}
}
}
}
}
+34
View File
@@ -0,0 +1,34 @@
use std::{borrow::Borrow, fmt, rc::Rc};
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct IdentifierValue(Rc<str>);
impl From<&str> for IdentifierValue {
fn from(value: &str) -> Self {
Self(value.into())
}
}
impl From<String> for IdentifierValue {
fn from(value: String) -> Self {
Self(value.into())
}
}
impl AsRef<str> for IdentifierValue {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl Borrow<str> for IdentifierValue {
fn borrow(&self) -> &str {
self.as_ref()
}
}
impl fmt::Display for IdentifierValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
+4 -1
View File
@@ -49,7 +49,10 @@ impl_keyword! {
Quote => "quote",
Progn => "progn",
Loop => "loop",
Return => "return",
Break => "break",
Continue => "continue",
Declare => "declare",
Error => "&error",
// Cons => "cons",
}
}
+184 -117
View File
@@ -1,58 +1,166 @@
use std::{fmt, hash::Hash, rc::Rc};
use crate::{
compile::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
error::MachineErrorKind,
vm::value::iter::ProperListIter,
use std::{
fmt::{self, Write},
rc::Rc,
};
mod bytecode;
mod boolean;
mod closure;
mod cons;
mod function;
mod identifier;
mod iter;
mod keyword;
mod native;
mod number;
mod string;
mod vector;
mod convert;
mod iter;
pub mod convert;
pub use bytecode::BytecodeFunction;
pub use boolean::BooleanValue;
pub use closure::{ClosureValue, UpvalueValue};
pub use cons::ConsCell;
pub use function::BytecodeFunction;
pub use identifier::IdentifierValue;
pub use keyword::Keyword;
pub use native::{NativeFunction, NativeFunctionImpl, OpaqueValue};
pub use string::ValueString;
pub use native::{NativeFunction, NativeObject, NativeValue};
pub use number::NumberValue;
pub use string::StringValue;
pub use vector::Vector;
pub use convert::{AnyFunction, TryFromValue};
pub use iter::ProperListIter;
#[derive(Clone)]
pub enum Macro {
Builtin(NativeFunctionImpl),
Bytecode(BytecodeFunction),
}
use crate::{
compile::syntax::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
error::ValueConversionError,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
// "Expression" values
// Syntactic
Nil,
Boolean(bool),
Integer(i64),
Identifier(Rc<str>),
Cons(Rc<ConsCell>),
Keyword(Keyword),
String(ValueString),
Vector(Rc<Vector>),
// Quoting
Number(NumberValue),
Boolean(BooleanValue),
Keyword(Keyword),
Identifier(IdentifierValue),
String(StringValue),
Quasi(Rc<Value>),
Unquote(Rc<Value>),
Quote(Rc<Value>),
// "Runtime" values
BytecodeFunction(BytecodeFunction),
Unquote(Rc<Value>),
UnquoteSplice(Rc<Value>),
// Semantic
Closure(ClosureValue),
Function(Rc<BytecodeFunction>),
// Native
NativeFunction(NativeFunction),
OpaqueValue(OpaqueValue),
NativeValue(NativeValue),
}
impl Value {
pub fn is_nil(&self) -> bool {
matches!(self, Self::Nil)
}
pub fn unquote(&self) -> Result<Value, ValueConversionError> {
match self {
Self::Quote(value) => Ok(value.as_ref().clone()),
_ => Err(ValueConversionError {
expected: "quoted value".into(),
got: self.clone(),
}),
}
}
pub fn uncons_ref(&self) -> Option<(&Value, &Value)> {
match self {
Self::Cons(cons) => Some((&cons.0, &cons.1)),
_ => None,
}
}
pub fn quote(self) -> Value {
Value::Quote(self.into())
}
pub fn type_id(&self) -> Value {
match self {
Self::Nil | Self::Cons(_) => Value::Identifier("list".into()),
Self::Identifier(_) => Value::Identifier("identifier".into()),
Self::Number(_) => Value::Identifier("number".into()),
Self::Boolean(_) => Value::Identifier("boolean".into()),
Self::Quote(value) => {
Value::list_or_nil([Self::Identifier("quote".into()), value.type_id()])
}
Self::Quasi(value) => {
Value::list_or_nil([Self::Identifier("quasi".into()), value.type_id()])
}
Self::Unquote(value) => {
Value::list_or_nil([Self::Identifier("unquote".into()), value.type_id()])
}
Self::UnquoteSplice(value) => {
Value::list_or_nil([Self::Identifier("unquote-splice".into()), value.type_id()])
}
Self::Vector(_) => Self::Identifier("vector".into()),
Self::Keyword(_) => Self::Identifier("keyword".into()),
Self::String(_) => Self::Identifier("string".into()),
Self::NativeValue(_) => Self::Identifier("native".into()),
Self::NativeFunction(_) | Self::Function(_) | Self::Closure(_) => {
Self::Identifier("function".into())
}
}
}
pub fn is_trueish(&self) -> bool {
match self {
Self::Nil => false,
Self::Cons(_) => true,
Self::Vector(vector) => !vector.is_empty(),
Self::Number(value) => value.is_trueish(),
Self::String(value) => !value.is_empty(),
Self::Boolean(BooleanValue(value)) => *value,
Self::Keyword(_)
| Self::Identifier(_)
| Self::Quasi(_)
| Self::Quote(_)
| Self::Unquote(_)
| Self::UnquoteSplice(_)
| Self::Closure(_)
| Self::NativeFunction(_)
| Self::NativeValue(_)
| Self::Function(_) => true,
}
}
pub fn cons(self, cdr: Value) -> Self {
Self::Cons(Rc::new(ConsCell(self, cdr)))
}
pub fn try_list_or_nil<E, I: IntoIterator<Item = Result<Self, E>>>(items: I) -> Result<Self, E>
where
I::IntoIter: ExactSizeIterator + DoubleEndedIterator,
{
let iter = items.into_iter();
let mut result = Value::Nil;
for element in iter.rev() {
result = element?.cons(result);
}
Ok(result)
}
pub fn list_or_nil<I: IntoIterator<Item = Self>>(items: I) -> Self
where
I::IntoIter: ExactSizeIterator + DoubleEndedIterator,
{
let iter = items.into_iter();
let mut result = Value::Nil;
for element in iter.rev() {
result = element.cons(result);
}
result
}
pub fn proper_iter<E>(&self, error: E) -> ProperListIter<'_, E> {
ProperListIter {
head: Some(self),
@@ -74,63 +182,23 @@ impl Value {
.ok()
}
pub fn is_nil(&self) -> bool {
matches!(self, Self::Nil)
}
pub fn as_opaque<T: 'static>(&self) -> Result<&T, MachineErrorKind> {
pub fn as_native<T: NativeObject>(&self) -> Result<Rc<T>, ValueConversionError> {
match self {
Self::OpaqueValue(opaque) => opaque.cast(),
_ => Err(MachineErrorKind::InvalidArgument),
Self::NativeValue(value) if let Some(value) = value.cast_rc() => Ok(value),
_ => Err(ValueConversionError {
expected: "native value".into(),
got: self.clone(),
}),
}
}
pub fn stringify(&self) -> Result<String, MachineErrorKind> {
pub fn as_native_ref<T: NativeObject>(&self) -> Result<&T, ValueConversionError> {
match self {
Self::Integer(value) => Ok(format!("{value}")),
Self::Boolean(true) => Ok("#t".into()),
Self::Boolean(false) => Ok("#f".into()),
Self::Identifier(value) => Ok(format!("{value}")),
Self::String(value) => Ok(format!("{value}")),
Self::Quasi(_) => todo!(),
Self::Quote(_) => todo!(),
Self::Unquote(_) => todo!(),
Self::Nil => todo!(),
Self::Cons(_) => todo!(),
Self::Vector(_) => todo!(),
Self::Keyword(_) => todo!(),
Self::OpaqueValue(_) => todo!(),
Self::NativeFunction(_) | Self::BytecodeFunction(_) => todo!(),
}
}
pub fn cons(self, cdr: Value) -> Self {
Self::Cons(Rc::new(ConsCell(self, cdr)))
}
pub fn try_list_or_nil<E, I: IntoIterator<Item = Result<Self, E>>>(
items: I,
) -> Result<Self, E> {
Self::try_list_or_nil_inner(&mut items.into_iter())
}
pub fn list_or_nil<I: IntoIterator<Item = Self>>(items: I) -> Self {
Self::list_or_nil_inner(&mut items.into_iter())
}
fn try_list_or_nil_inner<E, I: Iterator<Item = Result<Self, E>>>(
items: &mut I,
) -> Result<Self, E> {
match items.next() {
Some(value) => Ok(value?.cons(Self::try_list_or_nil_inner(items)?)),
None => Ok(Self::Nil),
}
}
fn list_or_nil_inner<I: Iterator<Item = Self>>(items: &mut I) -> Self {
match items.next() {
Some(value) => value.cons(Self::list_or_nil_inner(items)),
None => Self::Nil,
Self::NativeValue(value) if let Some(value) = value.cast_ref() => Ok(value),
_ => Err(ValueConversionError {
expected: "native value".into(),
got: self.clone(),
}),
}
}
}
@@ -139,42 +207,41 @@ impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Nil => write!(f, "NIL"),
Self::Boolean(true) => write!(f, "#T"),
Self::Boolean(false) => write!(f, "#F"),
Self::Integer(value) => write!(f, "{value}"),
Self::Identifier(value) => write!(f, "{value}"),
Self::Cons(cons) => {
f.write_char('(')?;
fmt::Display::fmt(cons, f)?;
f.write_char(')')
}
Self::Vector(vector) => {
f.write_str("#[")?;
fmt::Display::fmt(vector, f)?;
f.write_char(']')
}
Self::Keyword(keyword) => write!(f, "{keyword}"),
Self::Cons(cons) => write!(f, "({cons})"),
Self::Quasi(value) => write!(f, "`{value}"),
Self::Unquote(value) => write!(f, ",{value}"),
Self::Quote(value) => write!(f, "'{value}"),
Self::BytecodeFunction(bytecode) => write!(f, "{bytecode}"),
Self::NativeFunction(native) => write!(f, "{native}"),
Self::OpaqueValue(opaque) => write!(f, "{opaque}"),
Self::Vector(vector) => write!(f, "{vector}"),
Self::String(value) => write!(f, "{value}"),
Self::Identifier(identifier) => write!(f, "{identifier}"),
Self::String(value) => fmt::Display::fmt(value, f),
Self::Quote(value) => {
f.write_char('\'')?;
fmt::Display::fmt(value, f)
}
Self::Quasi(value) => {
f.write_char('`')?;
fmt::Display::fmt(value, f)
}
Self::Unquote(value) => {
f.write_char(',')?;
fmt::Display::fmt(value, f)
}
Self::UnquoteSplice(value) => {
f.write_str(",@")?;
fmt::Display::fmt(value, f)
}
Self::Number(value) => fmt::Display::fmt(value, f),
Self::Boolean(value) => fmt::Display::fmt(value, f),
Self::Closure(value) => fmt::Display::fmt(value, f),
Self::Function(value) => fmt::Display::fmt(value, f),
Self::NativeValue(value) => fmt::Display::fmt(value, f),
Self::NativeFunction(value) => fmt::Display::fmt(value, f),
}
}
}
#[cfg(test)]
mod tests {
use crate::vm::value::{Keyword, Value};
#[test]
fn test_value_formatting() {
let v = Value::Nil;
assert_eq!(&format!("{v}"), "NIL");
let v = Value::Integer(1234);
assert_eq!(&format!("{v}"), "1234");
let v = Value::Boolean(true);
assert_eq!(&format!("{v}"), "#T");
let v = Value::Boolean(false);
assert_eq!(&format!("{v}"), "#F");
let v = Value::Boolean(false).cons(Value::Integer(1234));
assert_eq!(&format!("{v}"), "(#F . 1234)");
let v = Value::Boolean(false)
.cons(Value::Integer(1234).cons(Value::Keyword(Keyword::Lambda).cons(Value::Nil)));
assert_eq!(&format!("{v}"), "(#F 1234 lambda)");
}
}
+62 -52
View File
@@ -6,80 +6,57 @@ use std::{
};
use crate::{
error::{MachineError, MachineErrorKind},
vm::{env::Environment, machine::Machine, value::Value},
error::MachineError,
vm::{
env::Environment,
machine::Machine,
value::{IdentifierValue, StringValue, Value},
},
};
#[derive(Clone)]
pub struct OpaqueValue {
inner: Rc<dyn Any>,
pub trait NativeObject: Any + fmt::Debug + fmt::Display {
fn as_any(&self) -> &dyn Any;
}
#[derive(Clone, Debug)]
pub struct NativeValue(Rc<dyn NativeObject>);
pub type NativeFunctionImpl =
Rc<dyn Fn(&mut Machine, &mut Environment, &[Value]) -> Result<Value, MachineError> + 'static>;
Rc<dyn Fn(&mut Machine, &Rc<Environment>, &[Value]) -> Result<Value, MachineError> + 'static>;
#[derive(Clone)]
pub struct NativeFunction {
name: Rc<str>,
name: IdentifierValue,
inner: NativeFunctionImpl,
}
impl OpaqueValue {
pub fn cast<T: 'static>(&self) -> Result<&T, MachineErrorKind> {
self.inner
.downcast_ref()
.ok_or(MachineErrorKind::InvalidArgument)
}
}
impl Hash for OpaqueValue {
fn hash<H: Hasher>(&self, state: &mut H) {
(&raw const *self.inner.as_ref()).addr().hash(state);
}
}
impl Eq for OpaqueValue {}
impl fmt::Debug for OpaqueValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OpaqueValue")
.field_with(|f| write!(f, "{:p}", self.inner))
.finish()
}
}
impl PartialEq for OpaqueValue {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.inner, &other.inner)
}
}
impl From<Rc<dyn Any>> for OpaqueValue {
fn from(value: Rc<dyn Any>) -> Self {
Self { inner: value }
}
}
impl fmt::Display for OpaqueValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<opaque {:p}>", self.inner)
}
docstring: StringValue,
}
impl NativeFunction {
pub fn new<S, F>(name: S, function: F) -> Self
pub fn new<S, D, F>(name: S, docstring: D, function: F) -> Self
where
S: Into<Rc<str>>,
F: Fn(&mut Machine, &mut Environment, &[Value]) -> Result<Value, MachineError> + 'static,
S: Into<IdentifierValue>,
D: Into<StringValue>,
F: Fn(&mut Machine, &Rc<Environment>, &[Value]) -> Result<Value, MachineError> + 'static,
{
Self {
name: name.into(),
docstring: docstring.into(),
inner: Rc::new(function),
}
}
pub fn name(&self) -> &IdentifierValue {
&self.name
}
pub fn docstring(&self) -> &StringValue {
&self.docstring
}
pub fn invoke(
&self,
machine: &mut Machine,
environment: &mut Environment,
environment: &Rc<Environment>,
arguments: &[Value],
) -> Result<Value, MachineError> {
(self.inner)(machine, environment, arguments)
@@ -112,6 +89,39 @@ impl fmt::Debug for NativeFunction {
impl fmt::Display for NativeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<native {:?} {:p}>", self.name, self.inner)
write!(
f,
"<native {}@{:#x}>",
self.name,
(&raw const *self.inner.as_ref()).addr()
)
}
}
impl NativeValue {
pub fn cast_rc<T: NativeObject>(&self) -> Option<Rc<T>> {
Rc::downcast(self.0.clone()).ok()
}
pub fn cast_ref<T: NativeObject>(&self) -> Option<&T> {
self.0.as_any().downcast_ref()
}
}
impl From<Rc<dyn NativeObject + 'static>> for NativeValue {
fn from(value: Rc<dyn NativeObject>) -> Self {
Self(value)
}
}
impl PartialEq for NativeValue {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl fmt::Display for NativeValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<native {}>", self.0)
}
}
+266
View File
@@ -0,0 +1,266 @@
use std::{
cmp::Ordering,
fmt,
num::TryFromIntError,
ops::{Add, Div, DivAssign, Mul, Neg, Rem, RemAssign, Sub, SubAssign},
};
pub struct NumberConvertError;
impl From<TryFromIntError> for NumberConvertError {
fn from(_: TryFromIntError) -> Self {
Self
}
}
#[derive(Debug, Clone, Copy)]
pub enum NumberValue {
Int(i64),
Float(f64),
}
impl NumberValue {
pub const fn nan() -> Self {
Self::Float(f64::NAN)
}
pub const fn infinity() -> Self {
Self::Float(f64::INFINITY)
}
pub const fn neg_infinity() -> Self {
Self::Float(f64::NEG_INFINITY)
}
pub fn reciprocal(self) -> Self {
Self::Float(1.0 / self.as_float())
}
pub fn from_str_radix(string: &str, radix: u32) -> Option<Self> {
if !(2..=36).contains(&radix) {
return None;
}
if let Ok(value) = i64::from_str_radix(string, radix) {
Some(Self::Int(value))
} else if radix == 10
&& let Ok(value) = string.parse()
{
Some(Self::Float(value))
} else {
None
}
}
pub fn as_float(self) -> f64 {
match self {
Self::Int(value) => value as f64,
Self::Float(value) => value,
}
}
pub fn is_trueish(&self) -> bool {
match self {
Self::Int(value) => *value != 0,
Self::Float(value) => *value != 0.0 && !value.is_nan(),
}
}
}
impl PartialEq for NumberValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Int(a), Self::Int(b)) => a == b,
_ => self.as_float() == other.as_float(),
}
}
}
impl PartialOrd for NumberValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match (self, other) {
(Self::Int(a), Self::Int(b)) => PartialOrd::partial_cmp(a, b),
_ => PartialOrd::partial_cmp(&self.as_float(), &other.as_float()),
}
}
}
impl Add for NumberValue {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Int(a), Self::Int(b)) if let Some(c) = a.checked_add(b) => Self::Int(c),
_ => todo!("TODO promote to bigint"),
}
}
}
impl Sub for NumberValue {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Int(a), Self::Int(b)) if let Some(c) = a.checked_sub(b) => Self::Int(c),
_ => todo!("TODO promote to bigint"),
}
}
}
impl SubAssign for NumberValue {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Mul for NumberValue {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Int(a), Self::Int(b)) if let Some(c) = a.checked_mul(b) => Self::Int(c),
_ => todo!("TODO promote to bigint"),
}
}
}
impl Div for NumberValue {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
match (self, rhs) {
// TODO convert inexact division to rational
(Self::Int(a), Self::Int(b)) if let Some(c) = a.checked_div_exact(b) => Self::Int(c),
_ => Self::Float(self.as_float() / rhs.as_float()),
}
}
}
impl DivAssign for NumberValue {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl Rem for NumberValue {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::Int(a), Self::Int(b)) if let Some(c) = a.checked_rem(b) => Self::Int(c),
_ => todo!("TODO NaN"),
}
}
}
impl RemAssign for NumberValue {
fn rem_assign(&mut self, rhs: Self) {
*self = *self % rhs;
}
}
impl Neg for NumberValue {
type Output = NumberValue;
fn neg(self) -> Self::Output {
match self {
Self::Int(a) if let Some(b) = a.checked_neg() => Self::Int(b),
_ => todo!("TODO promote to bigint"),
}
}
}
impl From<i8> for NumberValue {
fn from(value: i8) -> Self {
Self::Int(value.into())
}
}
impl From<i16> for NumberValue {
fn from(value: i16) -> Self {
Self::Int(value.into())
}
}
impl From<i32> for NumberValue {
fn from(value: i32) -> Self {
Self::Int(value.into())
}
}
impl From<i64> for NumberValue {
fn from(value: i64) -> Self {
Self::Int(value)
}
}
impl From<usize> for NumberValue {
fn from(value: usize) -> Self {
if let Ok(value) = i64::try_from(value) {
Self::Int(value)
} else {
todo!("TODO: promote to bigint")
}
}
}
impl TryFrom<NumberValue> for i8 {
type Error = NumberConvertError;
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
let NumberValue::Int(value) = value else {
return Err(NumberConvertError);
};
value.try_into().map_err(From::from)
}
}
impl TryFrom<NumberValue> for i16 {
type Error = NumberConvertError;
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
let NumberValue::Int(value) = value else {
return Err(NumberConvertError);
};
value.try_into().map_err(From::from)
}
}
impl TryFrom<NumberValue> for i32 {
type Error = NumberConvertError;
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
let NumberValue::Int(value) = value else {
return Err(NumberConvertError);
};
value.try_into().map_err(From::from)
}
}
impl TryFrom<NumberValue> for i64 {
type Error = NumberConvertError;
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
let NumberValue::Int(value) = value else {
return Err(NumberConvertError);
};
Ok(value)
}
}
impl TryFrom<NumberValue> for usize {
type Error = NumberConvertError;
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
match value {
NumberValue::Int(value) if let Ok(value) = value.try_into() => Ok(value),
_ => Err(NumberConvertError),
}
}
}
impl fmt::Display for NumberValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Int(value) => fmt::Display::fmt(value, f),
Self::Float(value) if value.is_infinite() && value.is_sign_negative() => {
write!(f, "-#inf")
}
Self::Float(value) if value.is_infinite() => write!(f, "#inf"),
Self::Float(value) if value.is_nan() => write!(f, "#nan"),
Self::Float(value) => fmt::Display::fmt(value, f),
}
}
}
+7 -7
View File
@@ -1,21 +1,21 @@
use std::{fmt, ops::Deref, rc::Rc};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ValueString(Rc<str>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct StringValue(Rc<str>);
impl From<&str> for ValueString {
impl From<&str> for StringValue {
fn from(value: &str) -> Self {
Self(value.into())
}
}
impl From<String> for ValueString {
impl From<String> for StringValue {
fn from(value: String) -> Self {
Self(value.into())
}
}
impl Deref for ValueString {
impl Deref for StringValue {
type Target = str;
fn deref(&self) -> &Self::Target {
@@ -23,8 +23,8 @@ impl Deref for ValueString {
}
}
impl fmt::Display for ValueString {
impl fmt::Display for StringValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.0.as_ref(), f)
fmt::Debug::fmt(self.0.as_ref(), f)
}
}
+335 -27
View File
@@ -1,21 +1,22 @@
use std::{
cell::RefCell,
fmt,
hash::{Hash, Hasher},
ops::Deref,
cell::{Ref, RefCell},
fmt, mem,
};
use crate::vm::value::Value;
use crate::vm::value::{NumberValue, Value};
#[derive(Debug, PartialEq, Eq)]
pub struct Vector(RefCell<Vec<Value>>);
impl Hash for Vector {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.borrow().hash(state);
}
#[derive(Debug, PartialEq)]
pub enum VectorStorage {
I8(Vec<i8>),
I16(Vec<i16>),
I32(Vec<i32>),
I64(Vec<i64>),
Any(Vec<Value>),
}
#[derive(Debug, PartialEq)]
pub struct Vector(RefCell<VectorStorage>);
impl Vector {
pub fn is_empty(&self) -> bool {
self.0.borrow().is_empty()
@@ -24,31 +25,338 @@ impl Vector {
pub fn len(&self) -> usize {
self.0.borrow().len()
}
}
impl FromIterator<Value> for Vector {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
Self(RefCell::new(Vec::from_iter(iter)))
pub fn borrow(&self) -> Ref<'_, VectorStorage> {
self.0.borrow()
}
}
impl Deref for Vector {
type Target = RefCell<Vec<Value>>;
pub fn value_at(&self, index: usize) -> Option<Value> {
self.0.borrow().value_at(index)
}
pub fn set_value_at(&self, index: usize, value: Value) {
self.0.borrow_mut().set_value_at(index, value);
}
fn deref(&self) -> &Self::Target {
&self.0
pub fn as_bytes(&self) -> Option<Ref<'_, [u8]>> {
Ref::filter_map(self.borrow(), |r| match r {
VectorStorage::I8(values) => {
Some(unsafe { mem::transmute::<&[i8], &[u8]>(&values[..]) })
}
_ => None,
})
.ok()
}
}
impl fmt::Display for Vector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
for (i, element) in self.0.borrow().iter().enumerate() {
if i != 0 {
write!(f, " ")?;
}
write!(f, "{element}")?;
fmt::Display::fmt(&*self.0.borrow(), f)
}
}
impl<A> FromIterator<A> for Vector
where
VectorStorage: FromIterator<A>,
{
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
Self(RefCell::new(VectorStorage::from_iter(iter)))
}
}
macro_rules! static_dispatch {
([$input:expr] $value:ident => $body:expr) => {
match $input {
Self::I8($value) => $body,
Self::I16($value) => $body,
Self::I32($value) => $body,
Self::I64($value) => $body,
Self::Any($value) => $body,
}
};
}
macro_rules! dispatch_integer_convert {
($self:ident ($storage:ident, $value:ident) => $f:expr) => {
match $self {
Self::I8($storage) if let Ok($value) = i8::try_from($value) => {
$f;
}
Self::I8($storage) if let Ok($value) = i16::try_from($value) => {
let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
$f;
*$self = Self::I16($storage);
}
Self::I8($storage) if let Ok($value) = i32::try_from($value) => {
let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
$f;
*$self = Self::I32($storage);
}
Self::I8($storage) => {
let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
$f;
*$self = Self::I64($storage);
}
Self::I16($storage) if let Ok($value) = i16::try_from($value) => {
$f;
}
Self::I16($storage) if let Ok($value) = i32::try_from($value) => {
let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
$f;
*$self = Self::I32($storage);
}
Self::I16($storage) => {
let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
$f;
*$self = Self::I64($storage);
}
Self::I32($storage) if let Ok($value) = i32::try_from($value) => {
$f;
}
Self::I32($storage) => {
let mut $storage = $storage.iter().copied().map(Into::into).collect::<Vec<_>>();
$f;
*$self = Self::I64($storage);
}
Self::I64($storage) => {
$f;
}
Self::Any($storage) => {
let $value = Value::Number(NumberValue::Int($value));
$f;
}
}
};
}
macro_rules! dispatch_any_convert {
($self:ident ($storage:ident) => $f:expr) => {
match $self {
Self::I8($storage) => {
let mut $storage = $storage
.iter()
.copied()
.map(Into::into)
.map(Value::Number)
.collect::<Vec<_>>();
$f;
*$self = Self::Any($storage);
}
Self::I16($storage) => {
let mut $storage = $storage
.iter()
.copied()
.map(Into::into)
.map(Value::Number)
.collect::<Vec<_>>();
$f;
*$self = Self::Any($storage);
}
Self::I32($storage) => {
let mut $storage = $storage
.iter()
.copied()
.map(Into::into)
.map(Value::Number)
.collect::<Vec<_>>();
$f;
*$self = Self::Any($storage);
}
Self::I64($storage) => {
let mut $storage = $storage
.iter()
.copied()
.map(Into::into)
.map(Value::Number)
.collect::<Vec<_>>();
$f;
*$self = Self::Any($storage);
}
Self::Any($storage) => {
$f;
}
}
};
}
impl VectorStorage {
fn value_at(&self, index: usize) -> Option<Value> {
match self {
Self::I8(values) if let Some(value) = values.get(index).copied() => {
Some(Value::Number(value.into()))
}
Self::I16(values) if let Some(value) = values.get(index).copied() => {
Some(Value::Number(value.into()))
}
Self::I32(values) if let Some(value) = values.get(index).copied() => {
Some(Value::Number(value.into()))
}
Self::I64(values) if let Some(value) = values.get(index).copied() => {
Some(Value::Number(value.into()))
}
Self::Any(values) if let Some(value) = values.get(index).cloned() => Some(value),
_ => None,
}
}
fn len(&self) -> usize {
static_dispatch!([self] vec => vec.len())
}
fn is_empty(&self) -> bool {
self.len() == 0
}
fn push(&mut self, value: Value) {
match value {
Value::Number(NumberValue::Int(value)) => self.push_integer(value),
value => match self {
Self::Any(vec) => vec.push(value),
_ => self.push_any(value),
},
}
}
fn set_value_at(&mut self, index: usize, value: Value) {
if index >= self.len() {
return;
}
match value {
Value::Number(NumberValue::Int(value)) => self.set_integer(index, value),
value => match self {
Self::Any(vec) => vec[index] = value,
_ => self.set_any(index, value),
},
}
}
fn push_any(&mut self, value: Value) {
dispatch_any_convert!(self (w) => {
w.push(value);
});
}
fn set_any(&mut self, index: usize, value: Value) {
dispatch_any_convert!(self (w) => {
w[index] = value;
});
}
fn push_integer(&mut self, value: i64) {
dispatch_integer_convert!(self (w, value) => {
w.push(value);
});
}
fn set_integer(&mut self, index: usize, value: i64) {
dispatch_integer_convert!(self (w, value) => {
w[index] = value;
});
}
}
impl FromIterator<Value> for VectorStorage {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
let mut accumulator = Self::I8(vec![]);
for item in iter {
accumulator.push(item);
}
accumulator
}
}
fn display_vector<T: fmt::Display>(value: &[T], f: &mut fmt::Formatter) -> fmt::Result {
for (i, value) in value.iter().enumerate() {
if i != 0 {
write!(f, " ")?;
}
write!(f, "{value}")?;
}
Ok(())
}
impl fmt::Display for VectorStorage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
match self {
Self::I8(vec) => display_vector(vec, f),
Self::I16(vec) => display_vector(vec, f),
Self::I32(vec) => display_vector(vec, f),
Self::I64(vec) => display_vector(vec, f),
Self::Any(vec) => display_vector(vec, f),
}?;
write!(f, "]")
}
}
macro_rules! impl_primitive_from_iter {
($signed_ty:ty, $unsigned_ty:ty => $variant:ident) => {
impl FromIterator<$signed_ty> for VectorStorage {
fn from_iter<T: IntoIterator<Item = $signed_ty>>(iter: T) -> Self {
Self::$variant(iter.into_iter().collect())
}
}
impl FromIterator<$unsigned_ty> for VectorStorage {
fn from_iter<T: IntoIterator<Item = $unsigned_ty>>(iter: T) -> Self {
Self::$variant(iter.into_iter().map(|x| x as $signed_ty).collect())
}
}
};
}
impl_primitive_from_iter!(i8, u8 => I8);
impl_primitive_from_iter!(i16, u16 => I16);
impl_primitive_from_iter!(i32, u32 => I32);
impl_primitive_from_iter!(i64, u64 => I64);
#[cfg(test)]
mod tests {
use crate::vm::value::{Value, vector::VectorStorage};
#[test]
fn test_vector_storage_from_iter() {
let v = VectorStorage::from_iter([1i8, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
let v = VectorStorage::from_iter([1u8, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I8(vec![1, 2, 3, 4, 5, 6, 7]), v);
let v = VectorStorage::from_iter([1i16, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
let v = VectorStorage::from_iter([1u16, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I16(vec![1, 2, 3, 4, 5, 6, 7]), v);
let v = VectorStorage::from_iter([1i32, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
let v = VectorStorage::from_iter([1u32, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I32(vec![1, 2, 3, 4, 5, 6, 7]), v);
let v = VectorStorage::from_iter([1i64, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
let v = VectorStorage::from_iter([1u64, 2, 3, 4, 5, 6, 7]);
assert_eq!(VectorStorage::I64(vec![1, 2, 3, 4, 5, 6, 7]), v);
}
#[test]
fn test_vector_unspecialize() {
let mut v = VectorStorage::from_iter([1i8, 2, 3]);
assert_eq!(VectorStorage::I8(vec![1, 2, 3]), v);
v.push(Value::Number(1234.into()));
assert_eq!(VectorStorage::I16(vec![1, 2, 3, 1234]), v);
v.push(Value::Number(12341234.into()));
assert_eq!(VectorStorage::I32(vec![1, 2, 3, 1234, 12341234]), v);
v.push(Value::Number(1234123412341234i64.into()));
assert_eq!(
VectorStorage::I64(vec![1, 2, 3, 1234, 12341234, 1234123412341234]),
v
);
v.push(Value::String("a".into()));
assert_eq!(
VectorStorage::Any(vec![
Value::Number(1.into()),
Value::Number(2.into()),
Value::Number(3.into()),
Value::Number(1234.into()),
Value::Number(12341234.into()),
Value::Number(1234123412341234i64.into()),
Value::String("a".into())
]),
v
);
}
}
+131 -99
View File
@@ -1,112 +1,128 @@
use std::io::{self, BufReader, Read};
use std::{fs, path::Path, rc::Rc};
use lysp::{
error::{EvalError, MachineError, MachineErrorKind},
read::{FileReader, read},
error::MachineErrorAt,
vm::{env::Environment, machine::Machine, prelude, value::Value},
};
struct SliceReader<'a>(&'a [u8]);
impl Read for SliceReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let count = self.0.len().min(buf.len());
buf[..count].copy_from_slice(&self.0[..count]);
self.0 = &self.0[count..];
Ok(count)
}
}
fn eval_str_in(code: &str, env: &mut Environment) -> Result<Value, EvalError> {
#[track_caller]
fn eval_str_in(code: &str, env: &Rc<Environment>) -> Result<Value, MachineErrorAt> {
let mut machine = Machine::default();
let reader = BufReader::new(SliceReader(code.as_bytes()));
let mut reader = FileReader::new(reader);
let mut last_value = None;
loop {
let value = match read(&mut reader, &mut machine, env) {
Ok(Some(value)) => value,
Ok(None) => break,
Err(error) => panic!("{error}"),
};
last_value = Some(machine.eval_value(Default::default(), env, value));
}
last_value.expect("no expressions evaluated")
machine.evaluate_str(Default::default(), None, env, code)
}
#[track_caller]
fn eval_str(code: &str) -> Value {
let mut env = Environment::default();
prelude::load(&mut env);
eval_str_in(code, &mut env).expect("expression evaluation failed")
let env = Rc::new(Environment::default());
prelude::load(&env);
match eval_str_in(code, &env) {
Ok(value) => value,
Err(error) => {
eprintln!("Couldn't evaluate expression:");
eprintln!();
eprintln!(" {code}");
eprintln!();
eprintln!(":: {error}");
panic!("TEST FAILED");
}
}
}
fn eval_str_err(code: &str) -> EvalError {
let mut env = Environment::default();
prelude::load(&mut env);
eval_str_in(code, &mut env).expect_err("expression was expected to fail")
#[track_caller]
fn eval_str_err(code: &str) -> MachineErrorAt {
let env = Rc::new(Environment::default());
prelude::load(&env);
match eval_str_in(code, &env) {
Ok(value) => {
eprintln!("Expected the code to fail to evaluate, but it returned success:");
eprintln!();
eprintln!(" {code}");
eprintln!();
eprintln!("Returned");
eprintln!();
eprintln!(":: {value}");
panic!("TEST FAILED");
}
Err(error) => error,
}
}
#[track_caller]
fn eval_file<P: AsRef<Path>>(path: P) -> Value {
let code = fs::read_to_string(path).expect("file read failed");
eval_str(&code)
}
#[test]
fn test_math() {
// math
assert_eq!(eval_str("(+ 1 2 3)"), Value::Integer(6));
assert_eq!(eval_str("(- 3 2 1)"), Value::Integer(0));
assert_eq!(eval_str("(* 2 3 4)"), Value::Integer(24));
assert_eq!(eval_str("(/ 16 4 2)"), Value::Integer(2));
assert_eq!(eval_str("(% 35 16)"), Value::Integer(3));
assert_eq!(eval_str("(| 1 2 4)"), Value::Integer(7));
assert_eq!(eval_str("(& 1 2 4)"), Value::Integer(0));
assert_eq!(eval_str("(& 1 3 7)"), Value::Integer(1));
assert_eq!(eval_str("(^ 1 3 8)"), Value::Integer(10));
assert_eq!(eval_str("(+ 1 2 3)"), Value::Number(6.into()));
assert_eq!(eval_str("(- 3 2 1)"), Value::Number(0.into()));
assert_eq!(eval_str("(* 2 3 4)"), Value::Number(24.into()));
assert_eq!(eval_str("(/ 16 4 2)"), Value::Number(2.into()));
assert_eq!(eval_str("(% 35 16)"), Value::Number(3.into()));
// TODO
// assert_eq!(eval_str("(| 1 2 4)"), Value::Number(7.into()));
// assert_eq!(eval_str("(& 1 2 4)"), Value::Number(0.into()));
// assert_eq!(eval_str("(& 1 3 7)"), Value::Number(1.into()));
// assert_eq!(eval_str("(^ 1 3 8)"), Value::Number(10.into()));
// comparison
assert_eq!(eval_str("(< 1 2 3 4 5)"), Value::Boolean(true));
assert_eq!(eval_str("(< 1 2 3 3 4 5)"), Value::Boolean(false));
assert_eq!(eval_str("(> 1 2 3 4 5)"), Value::Boolean(false));
assert_eq!(eval_str("(>= 5 5 4 3 2 1)"), Value::Boolean(true));
assert_eq!(eval_str("(> 5 5 4 3 2 1)"), Value::Boolean(false));
assert_eq!(eval_str("(<= 1 2 3 3 3 4)"), Value::Boolean(true));
assert_eq!(eval_str("(/= 1 2 3 4)"), Value::Boolean(true));
assert_eq!(eval_str("(/= 1 2 2 4)"), Value::Boolean(false));
assert_eq!(eval_str("(= 1 2 3 4)"), Value::Boolean(false));
assert_eq!(eval_str("(= 1 1 1 1)"), Value::Boolean(true));
assert_eq!(eval_str("(< 1 2 3 4 5)"), Value::Boolean(true.into()));
assert_eq!(eval_str("(< 1 2 3 3 4 5)"), Value::Boolean(false.into()));
assert_eq!(eval_str("(> 1 2 3 4 5)"), Value::Boolean(false.into()));
assert_eq!(eval_str("(>= 5 5 4 3 2 1)"), Value::Boolean(true.into()));
assert_eq!(eval_str("(> 5 5 4 3 2 1)"), Value::Boolean(false.into()));
assert_eq!(eval_str("(<= 1 2 3 3 3 4)"), Value::Boolean(true.into()));
assert_eq!(eval_str("(/= 1 2 3 4)"), Value::Boolean(true.into()));
assert_eq!(eval_str("(/= 1 2 2 4)"), Value::Boolean(false.into()));
assert_eq!(eval_str("(= 1 2 3 4)"), Value::Boolean(false.into()));
assert_eq!(eval_str("(= 1 1 1 1)"), Value::Boolean(true.into()));
// logic
assert_eq!(eval_str("(&& 1 0)"), Value::Boolean(false));
assert_eq!(eval_str("(&& #t 1 \"yes\")"), Value::Boolean(true));
assert_eq!(eval_str("(|| #f NIL \"\" ())"), Value::Boolean(false));
assert_eq!(eval_str("(|| #f '(1 2 3) \"\" ())"), Value::Boolean(true));
// TODO
// assert_eq!(eval_str("(&& 1 0)"), Value::Boolean(false.into()));
// assert_eq!(eval_str("(&& #t 1 \"yes\")"), Value::Boolean(true.into()));
// assert_eq!(
// eval_str("(|| #f NIL \"\" ())"),
// Value::Boolean(false.into())
// );
// assert_eq!(
// eval_str("(|| #f '(1 2 3) \"\" ())"),
// Value::Boolean(true.into())
// );
}
#[test]
fn test_abort() {
let err = eval_str_err("(assert (= 1 (+ 1 1)))");
let EvalError::Machine(MachineError {
error: MachineErrorKind::Aborted(message),
..
}) = err
else {
panic!("Invalid error returned")
};
assert_eq!(&message, "<undefined>: assertion failed: (= 1 (+ 1 1))");
// let err = eval_str_err("(assert (= 1 (+ 1 1)))");
// let EvalError::Machine(MachineError {
// error: MachineErrorKind::Aborted(message),
// ..
// }) = err
// else {
// panic!("Invalid error returned")
// };
// assert_eq!(&message, "<undefined>: assertion failed: (= 1 (+ 1 1))");
let err = eval_str_err("(abort 1234)");
let EvalError::Machine(MachineError {
error: MachineErrorKind::Aborted(message),
..
}) = err
else {
panic!("Invalid error returned")
};
assert_eq!(&message, "1234");
// let err = eval_str_err("(abort 1234)");
// let EvalError::Machine(MachineError {
// error: MachineErrorKind::Aborted(message),
// ..
// }) = err
// else {
// panic!("Invalid error returned")
// };
// assert_eq!(&message, "1234");
}
#[test]
fn test_lambda() {
assert_eq!(eval_str("((lambda (x) (+ x 1)) 1)"), Value::Integer(2));
assert_eq!(
eval_str("((lambda (x) (+ x 1)) 1)"),
Value::Number(2.into())
);
assert_eq!(
eval_str("((lambda (x) (+ ((lambda () 2)) 1)) 1)"),
Value::Integer(3)
Value::Number(3.into())
);
}
@@ -117,46 +133,43 @@ fn test_math_behaves_the_same_inside_apply() {
assert_eq!(eval_str("(apply - '(1 2 3 4))"), eval_str("(- 1 2 3 4)"));
assert_eq!(eval_str("(apply / '(100 25 2))"), eval_str("(/ 100 25 2)"));
assert_eq!(eval_str("(apply % '(90 37))"), eval_str("(% 90 37)"));
assert_eq!(eval_str("(apply | '(1 2 4))"), eval_str("(| 1 2 4)"));
assert_eq!(eval_str("(apply & '(1 3 7))"), eval_str("(& 1 3 7)"));
assert_eq!(eval_str("(apply ^ '(1 3 7))"), eval_str("(^ 1 3 7)"));
assert_eq!(eval_str("(apply < '(1 2 3 4))"), eval_str("(< 1 2 3 4)"));
assert_eq!(eval_str("(apply > '(1 2 3 4))"), eval_str("(> 1 2 3 4)"));
// TODO
// assert_eq!(eval_str("(apply | '(1 2 4))"), eval_str("(| 1 2 4)"));
// assert_eq!(eval_str("(apply & '(1 3 7))"), eval_str("(& 1 3 7)"));
// assert_eq!(eval_str("(apply ^ '(1 3 7))"), eval_str("(^ 1 3 7)"));
// assert_eq!(eval_str("(apply < '(1 2 3 4))"), eval_str("(< 1 2 3 4)"));
// assert_eq!(eval_str("(apply > '(1 2 3 4))"), eval_str("(> 1 2 3 4)"));
}
#[test]
fn test_setq() {
assert_eq!(eval_str("(setq a 1234) a\n"), Value::Integer(1234));
assert_eq!(eval_str("(setq a 1234) a\n"), Value::Number(1234.into()));
}
#[test]
fn test_let() {
// Should fail
eval_str_err("(let (a 1234 b (+ a 1)) NIL)");
eval_str_err("(let (a 1234) (let (a 9999 b (+ a 4321)) b))");
assert_eq!(
eval_str("(let (a 1234 b 4321) (+ a b))"),
Value::Integer(5555)
Value::Number(5555.into())
);
assert_eq!(
eval_str("(let* (a 1234 b (+ a 4321)) (+ b 4444))"),
Value::Integer(9999)
Value::Number(9999.into())
);
// Nested let
assert_eq!(
eval_str("(let (a 1234) (let (b 4321) (+ a b)))"),
Value::Integer(5555)
);
// Doesn't shadow
assert_eq!(
eval_str("(let (a 1234) (let (a 9999 b (+ a 4321)) b))"),
Value::Integer(5555)
Value::Number(5555.into())
);
// Does shadow
assert_eq!(
eval_str("(let (a 1234) (let* (a 9999 b (+ a 4321)) b))"),
Value::Integer(14320)
Value::Number(14320.into())
);
}
@@ -165,10 +178,29 @@ fn test_macro() {
assert_eq!(
eval_str("(defmacro stringify (x) (cons 'list `,x)) (stringify (1 2 3 4))"),
Value::list_or_nil([
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Integer(4)
Value::Number(1.into()),
Value::Number(2.into()),
Value::Number(3.into()),
Value::Number(4.into())
])
);
}
#[test]
fn test_examples_work() {
const EXCLUDE: &[&str] = &["repl.lysp", "echo.lysp", "io.lysp"];
// None of them should crash at least
for file in fs::read_dir("examples").unwrap() {
let entry = file.unwrap();
let filename = entry.file_name();
let filename = filename.to_str().unwrap();
if !filename.ends_with(".lysp") || EXCLUDE.contains(&filename) {
continue;
}
eprintln!("Eval {}", entry.path().display());
eval_file(entry.path());
}
}