Implement closures+upvalues, rework virtual machine
This commit is contained in:
@@ -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")))
|
||||
@@ -0,0 +1 @@
|
||||
(print (explain explain))
|
||||
+11
-3
@@ -1,4 +1,12 @@
|
||||
;; vi:ft=lisp:sw=2:ts=2
|
||||
(print "Argument count:" (length *args*))
|
||||
(print "Arguments as a list:" *args*)
|
||||
|
||||
(print (length *args*))
|
||||
(print *args*)
|
||||
(print "Iterated:")
|
||||
(let
|
||||
(index 0 current *args*)
|
||||
(while current
|
||||
(print index ":" (car current))
|
||||
(setq index (+ index 1))
|
||||
(setq current (cdr current))
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
;; vi:ft=lisp:sw=2:ts=2
|
||||
|
||||
(defun factorial (x)
|
||||
(if (= x 0)
|
||||
1
|
||||
|
||||
+28
-20
@@ -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")
|
||||
|
||||
@@ -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)))
|
||||
+1
-3
@@ -1,5 +1,3 @@
|
||||
;; vi:ft=lisp:sw=2:ts=2
|
||||
|
||||
(defun cadr (x) (car (cdr x)))
|
||||
|
||||
(defun map-ok-err (f-ok f-err result)
|
||||
@@ -29,7 +27,7 @@
|
||||
|
||||
(loop
|
||||
(let (expression (read))
|
||||
(if expression NIL (return))
|
||||
(if expression NIL (break))
|
||||
(setq expression (unquote expression))
|
||||
(map-ok-err repl-eval-print repl-read-error expression)
|
||||
)
|
||||
|
||||
+23
-2
@@ -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
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
;; 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]
|
||||
|
||||
;; 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)
|
||||
+522
-876
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
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)?;
|
||||
}
|
||||
for expression in self.arguments.iter().rev() {
|
||||
let value = expression.compile(cx)?;
|
||||
cx.push(value)?;
|
||||
}
|
||||
cx.emit(instruction);
|
||||
cx.emit(argument_count);
|
||||
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.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.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.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 {
|
||||
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();
|
||||
let lambda_function = cx.function_blocks[1].to_bytecode();
|
||||
|
||||
// lambda
|
||||
assert_eq!(
|
||||
lambda_function.instructions.as_ref(),
|
||||
&[
|
||||
Instruction::GetLocal.into(),
|
||||
0,
|
||||
Instruction::GetUpvalue.into(),
|
||||
0,
|
||||
Instruction::Add.into(),
|
||||
2,
|
||||
Instruction::Return.into(),
|
||||
]
|
||||
);
|
||||
assert_eq!(lambda_function.arity, 1);
|
||||
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 {
|
||||
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();
|
||||
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(),
|
||||
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();
|
||||
let defun_function = cx.function_blocks[1].to_bytecode();
|
||||
// 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.arity, 1);
|
||||
assert_eq!(
|
||||
defun_function.instructions.as_ref(),
|
||||
&[
|
||||
// a
|
||||
Instruction::GetLocal.into(),
|
||||
0,
|
||||
Instruction::Return.into(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
+13
-1
@@ -2,10 +2,22 @@
|
||||
|
||||
use crate::compile::syntax::ParseError;
|
||||
|
||||
#[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),
|
||||
}
|
||||
|
||||
impl From<Vec<ParseError>> for CompileError {
|
||||
|
||||
@@ -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,8 @@ impl FunctionSignature {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arity(&self) -> usize {
|
||||
self.required_arguments.len()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -1,111 +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) name: Option<Rc<str>>,
|
||||
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(name: Option<Rc<str>>, options: CompileOptions) -> Self {
|
||||
Self {
|
||||
name,
|
||||
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 name = self.name;
|
||||
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 {
|
||||
name,
|
||||
constants,
|
||||
instructions,
|
||||
entry,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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())
|
||||
)))
|
||||
}]
|
||||
);
|
||||
|
||||
@@ -2,10 +2,12 @@ use std::rc::Rc;
|
||||
|
||||
use crate::{
|
||||
compile::{
|
||||
Expression, FunctionSignature, ParseError,
|
||||
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
|
||||
function::FunctionSignature,
|
||||
syntax::{
|
||||
CollectErrors, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
|
||||
},
|
||||
},
|
||||
vm::value::{ConsCell, Keyword, Value},
|
||||
vm::value::{ConsCell, IdentifierValue, Keyword, Value},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -16,7 +18,7 @@ pub struct FunctionBody {
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct DefunExpression {
|
||||
pub name: Rc<str>,
|
||||
pub name: IdentifierValue,
|
||||
pub signature: FunctionSignature,
|
||||
pub body: FunctionBody,
|
||||
}
|
||||
@@ -256,34 +258,40 @@ mod tests {
|
||||
|
||||
use crate::{
|
||||
compile::{
|
||||
Expression, FunctionBody, FunctionSignature, ParseError,
|
||||
syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind},
|
||||
function::FunctionSignature,
|
||||
syntax::{
|
||||
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
|
||||
},
|
||||
},
|
||||
vm::value::{Keyword, Value},
|
||||
};
|
||||
|
||||
#[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 +443,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,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::{
|
||||
compile::{
|
||||
FunctionBody, FunctionSignature, ParseError,
|
||||
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
|
||||
function::FunctionSignature,
|
||||
syntax::{
|
||||
CollectErrors, ExpectedWhat, ExpectedWhere, FunctionBody, ParseError, ParseErrorKind,
|
||||
},
|
||||
},
|
||||
vm::value::{ConsCell, Keyword, Value},
|
||||
};
|
||||
@@ -42,9 +44,11 @@ mod tests {
|
||||
|
||||
use crate::{
|
||||
compile::{
|
||||
CallExpression, Expression, FunctionBody, FunctionSignature, LambdaExpression,
|
||||
ParseError,
|
||||
syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind},
|
||||
function::FunctionSignature,
|
||||
syntax::{
|
||||
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody,
|
||||
LambdaExpression, ParseError, ParseErrorKind,
|
||||
},
|
||||
},
|
||||
vm::value::{Keyword, Value},
|
||||
};
|
||||
@@ -61,7 +65,7 @@ mod tests {
|
||||
let body = Value::list_or_nil([
|
||||
Value::Identifier("+".into()),
|
||||
Value::Identifier("a".into()),
|
||||
Value::Integer(1),
|
||||
Value::Number(1.into()),
|
||||
]);
|
||||
let lambda = Value::Keyword(Keyword::Lambda).cons(args.cons(body.cons(Value::Nil)));
|
||||
let expr = Expression::parse(&lambda).unwrap();
|
||||
@@ -80,7 +84,7 @@ mod tests {
|
||||
callee: Expression::Identifier("+".into()).into(),
|
||||
arguments: vec![
|
||||
Rc::new(Expression::Identifier("a".into())),
|
||||
Rc::new(Expression::IntegerLiteral(1))
|
||||
Rc::new(Expression::IntegerLiteral(1.into()))
|
||||
]
|
||||
})
|
||||
.into()
|
||||
|
||||
@@ -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},
|
||||
};
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
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},
|
||||
},
|
||||
},
|
||||
vm::value::{ConsCell, Keyword, Value},
|
||||
vm::value::{ConsCell, IdentifierValue, Keyword, Value},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct DefmacroExpression {
|
||||
pub name: Rc<str>,
|
||||
pub name: IdentifierValue,
|
||||
pub signature: FunctionSignature,
|
||||
pub body: FunctionBody,
|
||||
}
|
||||
|
||||
+28
-22
@@ -1,6 +1,8 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::vm::value::{ConsCell, Keyword, Value, ValueString, Vector};
|
||||
use crate::vm::value::{
|
||||
BooleanValue, ConsCell, IdentifierValue, Keyword, NumberValue, StringValue, Value, Vector,
|
||||
};
|
||||
|
||||
mod binding;
|
||||
mod call;
|
||||
@@ -23,10 +25,10 @@ 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),
|
||||
@@ -41,7 +43,8 @@ pub enum Expression {
|
||||
Loop(LoopExpression),
|
||||
Progn(PrognExpression),
|
||||
Vector(Rc<Vector>),
|
||||
Return,
|
||||
Break,
|
||||
Continue,
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
@@ -64,15 +67,16 @@ 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::String(value) => Rc::new(Self::StringLiteral(value.clone())),
|
||||
Value::Vector(vector) => Rc::new(Self::Vector(vector.clone())),
|
||||
Value::Identifier(value) => Rc::new(Self::Identifier(value.clone())),
|
||||
Value::String(value) => Rc::new(Self::StringLiteral(value.clone())),
|
||||
Value::Quasi(_value) => todo!("{value}"),
|
||||
Value::Unquote(_value) => todo!("Unquote {_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 {
|
||||
@@ -113,14 +117,15 @@ 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(_) => todo!("Error here"),
|
||||
Value::Closure(_) => todo!("Error here"),
|
||||
Value::Function(_) => todo!("Error here"),
|
||||
Value::NativeFunction(_) => todo!("Error here"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,7 +150,8 @@ impl CollectErrors<ParseError> for Expression {
|
||||
Self::Progn(progn) => progn.collect_errors(errors),
|
||||
Self::Nil
|
||||
| Self::Vector(_)
|
||||
| Self::Return
|
||||
| Self::Break
|
||||
| Self::Continue
|
||||
| Self::IntegerLiteral(_)
|
||||
| Self::Identifier(_)
|
||||
| Self::BooleanLiteral(_)
|
||||
@@ -168,13 +174,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();
|
||||
@@ -189,7 +195,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!(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+120
-40
@@ -1,53 +1,133 @@
|
||||
#![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, 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,
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-5
@@ -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;
|
||||
|
||||
+38
-33
@@ -8,20 +8,23 @@ use std::{
|
||||
|
||||
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,
|
||||
}
|
||||
@@ -77,30 +80,24 @@ 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(_) => todo!(),
|
||||
MachineError::Read(error) => {
|
||||
eprintln!("Syntax error:");
|
||||
eprintln!();
|
||||
if let Some(ip) = error.ip.as_ref() {
|
||||
ip.module.dump(Some(ip.address), 8, 2);
|
||||
}
|
||||
}
|
||||
EvalError::Compile(CompileError::Parse(errors)) => {
|
||||
print_syntax_errors(&errors);
|
||||
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}");
|
||||
}
|
||||
@@ -108,7 +105,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) => {
|
||||
@@ -124,7 +121,12 @@ fn eval(
|
||||
env: &mut Environment,
|
||||
value: Value,
|
||||
) -> Option<Value> {
|
||||
let result = vm.eval_value(options.clone(), env, value.clone(), false);
|
||||
let result = vm.evaluate_value(
|
||||
options.clone(),
|
||||
Some("interactive".into()),
|
||||
env,
|
||||
value.clone(),
|
||||
);
|
||||
match result {
|
||||
Ok(r) => Some(r),
|
||||
Err(error) => {
|
||||
@@ -170,12 +172,15 @@ fn run_module<P: AsRef<Path>>(
|
||||
let name = format!("{}", path.display());
|
||||
let reader = BufReader::new(File::open(path)?);
|
||||
let module_reader = ModuleReader::new(reader);
|
||||
let module = match module_reader.compile(Some(name.into()), compile_options, env) {
|
||||
Ok(module) => module,
|
||||
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, false) {
|
||||
let closure = ClosureValue {
|
||||
function,
|
||||
upvalues: vec![],
|
||||
};
|
||||
match vm.evaluate_closure(env, closure, 0) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => Err(handle_eval_error(None, error)),
|
||||
}
|
||||
|
||||
+60
-42
@@ -14,20 +14,20 @@ use nom::{
|
||||
sequence::{delimited, preceded},
|
||||
};
|
||||
|
||||
use crate::vm::value::{Keyword, Value, Vector};
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -297,7 +315,7 @@ fn parse_quote(input: &str) -> IResult<&str, Value> {
|
||||
|
||||
fn parse_vector(input: &str) -> IResult<&str, Value> {
|
||||
delimited(
|
||||
char('['),
|
||||
tag("#["),
|
||||
many0(preceded(skip_comment_and_whitespace, parse_value)),
|
||||
preceded(skip_comment_and_whitespace, char(']')),
|
||||
)
|
||||
@@ -315,7 +333,7 @@ pub fn parse_value(input: &str) -> IResult<&str, Value> {
|
||||
parse_quote,
|
||||
parse_quasi,
|
||||
parse_unquote,
|
||||
parse_integer,
|
||||
parse_number,
|
||||
parse_string,
|
||||
parse_identifier_or_keyword_or_nil,
|
||||
))
|
||||
@@ -393,33 +411,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();
|
||||
@@ -468,7 +486,7 @@ mod tests {
|
||||
Value::list_or_nil([
|
||||
Value::Identifier("a".into()),
|
||||
Value::Identifier("b".into()),
|
||||
Value::Integer(-1),
|
||||
Value::Number((-1).into()),
|
||||
])
|
||||
);
|
||||
}
|
||||
@@ -477,26 +495,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()));
|
||||
@@ -518,8 +536,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())
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
+44
-43
@@ -6,22 +6,23 @@ 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},
|
||||
instruction::Instruction,
|
||||
machine::Machine,
|
||||
macros::MacroExpand,
|
||||
module::{Module, ModuleRef},
|
||||
value::Value,
|
||||
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 +61,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 +86,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)
|
||||
@@ -94,15 +95,9 @@ impl<R: BufRead> Reader for FileReader<R> {
|
||||
|
||||
impl<R: BufRead> ModuleReader<R> {
|
||||
pub fn new(reader: R) -> Self {
|
||||
let mut macro_machine = Machine::default();
|
||||
let dummy = ModuleRef::from(Module::dummy());
|
||||
macro_machine.ip = Some(InstructionPointer {
|
||||
module: dummy,
|
||||
address: 0,
|
||||
});
|
||||
Self {
|
||||
reader: FileReader::new(reader),
|
||||
macro_machine,
|
||||
macro_machine: Machine::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +105,7 @@ impl<R: BufRead> ModuleReader<R> {
|
||||
&mut self,
|
||||
options: &CompileOptions,
|
||||
env: &mut Environment,
|
||||
) -> Result<Option<Rc<Expression>>, Either<EvalError, Vec<ParseError>>> {
|
||||
) -> 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 +115,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, false)
|
||||
.evaluate_value(options.clone(), Some("defmacro".into()), env, value)
|
||||
.map_err(Either::Left)?;
|
||||
continue;
|
||||
}
|
||||
@@ -130,11 +125,11 @@ impl<R: BufRead> ModuleReader<R> {
|
||||
|
||||
pub fn compile(
|
||||
mut self,
|
||||
module_name: Option<Rc<str>>,
|
||||
module_name: Option<IdentifierValue>,
|
||||
options: &CompileOptions,
|
||||
env: &mut Environment,
|
||||
) -> Result<ModuleRef, Either<EvalError, Vec<ParseError>>> {
|
||||
let mut module = CompilationModule::new(module_name, options.clone());
|
||||
) -> 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),
|
||||
@@ -160,25 +155,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +180,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();
|
||||
@@ -212,9 +206,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());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -233,10 +228,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)
|
||||
@@ -249,7 +250,7 @@ pub fn read<R: Reader>(
|
||||
reader: &mut R,
|
||||
vm: &mut Machine,
|
||||
env: &mut Environment,
|
||||
) -> Result<Option<Value>, EvalError> {
|
||||
) -> Result<Option<Value>, MachineErrorAt> {
|
||||
let raw_value = reader.read().map_err(Into::into)?;
|
||||
let Some(raw_value) = raw_value else {
|
||||
return Ok(None);
|
||||
|
||||
+33
@@ -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 {
|
||||
|
||||
+42
-16
@@ -1,44 +1,58 @@
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
use std::{borrow::Borrow, cell::RefCell, collections::HashMap, hash::Hash, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
error::MachineError,
|
||||
vm::{
|
||||
machine::Machine,
|
||||
value::{BytecodeFunction, Macro, NativeFunction, NativeFunctionImpl, Value},
|
||||
value::{BytecodeFunction, IdentifierValue, NativeFunction, StringValue, Value},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Macro {
|
||||
Native(NativeFunction),
|
||||
Bytecode(Rc<BytecodeFunction>),
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
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>>,
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
pub fn defun_native<S, F>(&mut self, identifier: S, function: F)
|
||||
pub fn defun_native<S, D, F>(&mut self, identifier: S, docstring: D, function: F) -> Value
|
||||
where
|
||||
S: Into<Rc<str>>,
|
||||
S: Into<IdentifierValue>,
|
||||
D: Into<StringValue>,
|
||||
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));
|
||||
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, F>(&mut self, identifier: S, function: F)
|
||||
pub fn defmacro_native<S, D, F>(&mut self, identifier: S, docstring: D, function: F)
|
||||
where
|
||||
S: Into<Rc<str>>,
|
||||
S: Into<IdentifierValue>,
|
||||
D: Into<StringValue>,
|
||||
F: Fn(&mut Machine, &mut Environment, &[Value]) -> Result<Value, MachineError> + 'static,
|
||||
{
|
||||
let identifier = identifier.into();
|
||||
let native: NativeFunctionImpl = Rc::new(function);
|
||||
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>>(
|
||||
&mut self,
|
||||
identifier: S,
|
||||
value: Rc<BytecodeFunction>,
|
||||
) {
|
||||
let identifier = identifier.into();
|
||||
// println!("Export macro: {identifier}: {value}");
|
||||
self.macros
|
||||
@@ -46,7 +60,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,
|
||||
{
|
||||
self.globals.borrow().get(identifier).cloned().or_else(|| {
|
||||
self.parent
|
||||
.as_ref()
|
||||
@@ -54,13 +72,21 @@ 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>>(
|
||||
&mut 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()
|
||||
|
||||
+188
-206
@@ -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 +0,0 @@
|
||||
|
||||
+688
-679
File diff suppressed because it is too large
Load Diff
+26
-24
@@ -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, Keyword, Value},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ pub trait MacroExpand: Sized {
|
||||
vm: &mut Machine,
|
||||
env: &mut Environment,
|
||||
tail: bool,
|
||||
) -> Result<Self, EvalError>;
|
||||
) -> Result<Self, MachineErrorAt>;
|
||||
}
|
||||
|
||||
impl MacroExpand for Value {
|
||||
@@ -24,19 +24,23 @@ impl MacroExpand for Value {
|
||||
vm: &mut Machine,
|
||||
env: &mut 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::Vector(_)
|
||||
| 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 +56,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(vector) => Ok(Self::Vector(vector.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -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;
|
||||
|
||||
@@ -1,254 +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 name: Option<Rc<str>>,
|
||||
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 {
|
||||
name: None,
|
||||
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(None, options);
|
||||
module.compile_function(
|
||||
FunctionSignature::EMPTY,
|
||||
&FunctionBody {
|
||||
head: vec![],
|
||||
tail: expression,
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
module.compile_module()
|
||||
}
|
||||
|
||||
pub fn dump(&self, highlight: Option<usize>, context_backward: usize, context_forward: usize) {
|
||||
let window = highlight
|
||||
.map(|end| (end + 1).min(self.instructions.len()))
|
||||
.map(|end| end.saturating_sub(context_backward)..end.saturating_add(context_forward))
|
||||
.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 {
|
||||
name: None,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{
|
||||
error::MachineError,
|
||||
vm::{
|
||||
Value,
|
||||
env::Environment,
|
||||
value::{ConsCell, convert::TryFromValue},
|
||||
},
|
||||
};
|
||||
|
||||
pub fn load(env: &mut 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(
|
||||
"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())
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
use crate::{
|
||||
error::MachineError,
|
||||
vm::{
|
||||
Value,
|
||||
env::Environment,
|
||||
value::{NumberValue, StringValue, convert::TryFromValue},
|
||||
},
|
||||
};
|
||||
|
||||
pub fn load(env: &mut 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())
|
||||
// });
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
use crate::{
|
||||
error::MachineError,
|
||||
vm::{Value, env::Environment, value::Keyword},
|
||||
};
|
||||
|
||||
pub fn load(env: &mut Environment) {
|
||||
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 un-quoted 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()
|
||||
)
|
||||
}
|
||||
};
|
||||
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()))
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
use crate::{
|
||||
error::MachineError,
|
||||
read::{self, InteractiveReader},
|
||||
vm::{Value, env::Environment},
|
||||
};
|
||||
|
||||
pub fn load(env: &mut Environment) {
|
||||
env.defun_native("eval", "Evaluates the given expression", |vm, env, args| {
|
||||
// TODO eval in env
|
||||
let [value] = args else {
|
||||
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]).quote(),
|
||||
Err(error) => Value::list_or_nil([
|
||||
Value::Identifier("err".into()),
|
||||
Value::String(format!("{error}").into()),
|
||||
])
|
||||
.quote(),
|
||||
};
|
||||
Ok(outcome)
|
||||
});
|
||||
// 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[..])
|
||||
// });
|
||||
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!(" ");
|
||||
}
|
||||
print!("{arg}");
|
||||
}
|
||||
println!();
|
||||
Ok(Value::Nil)
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use crate::{error::MachineError, vm::env::Environment};
|
||||
|
||||
pub fn load(env: &mut 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("identity", "Returns the argument as is", |_, _, args| {
|
||||
let [arg] = args else {
|
||||
return Err(MachineError::InvalidArgumentCount);
|
||||
};
|
||||
Ok(arg.clone())
|
||||
});
|
||||
}
|
||||
+196
-112
@@ -1,32 +1,98 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
ops::{BitAnd, BitOr, BitXor},
|
||||
};
|
||||
use std::{cmp::Ordering, ops::Mul};
|
||||
|
||||
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> {
|
||||
pub(crate) fn dispatch_arithmetic(
|
||||
instruction: Instruction,
|
||||
) -> fn(&mut Machine, &mut 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: &mut 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 +105,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 +120,6 @@ where
|
||||
}
|
||||
|
||||
fn builtin_fold_t<'a, T, F>(
|
||||
vm: &mut Machine,
|
||||
fold: F,
|
||||
mut accumulator: T,
|
||||
args: &'a [Value],
|
||||
@@ -68,140 +129,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,
|
||||
_vm: &mut Machine,
|
||||
_env: &mut 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,
|
||||
_vm: &mut Machine,
|
||||
_env: &mut 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,
|
||||
_vm: &mut Machine,
|
||||
_env: &mut 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,
|
||||
_vm: &mut Machine,
|
||||
_env: &mut 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,
|
||||
_vm: &mut Machine,
|
||||
_env: &mut 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: &mut Environment,
|
||||
// args: &[Value],
|
||||
// ) -> Result<Value, MachineError> {
|
||||
// builtin_fold_t(BitAnd::bitand, true, args)
|
||||
// }
|
||||
// pub(crate) fn builtin_or(
|
||||
// vm: &mut Machine,
|
||||
// _env: &mut Environment,
|
||||
// args: &[Value],
|
||||
// ) -> Result<Value, MachineError> {
|
||||
// builtin_fold_t(BitOr::bitor, false, args)
|
||||
// }
|
||||
// pub(crate) fn builtin_bitwise_and(
|
||||
// vm: &mut Machine,
|
||||
// _env: &mut Environment,
|
||||
// args: &[Value],
|
||||
// ) -> Result<Value, MachineError> {
|
||||
// builtin_fold_t(BitAnd::bitand, 1, args)
|
||||
// }
|
||||
// pub(crate) fn builtin_bitwise_or(
|
||||
// vm: &mut Machine,
|
||||
// _env: &mut Environment,
|
||||
// args: &[Value],
|
||||
// ) -> Result<Value, MachineError> {
|
||||
// builtin_fold_t(BitOr::bitor, 0, args)
|
||||
// }
|
||||
// pub(crate) fn builtin_bitwise_xor(
|
||||
// vm: &mut Machine,
|
||||
// _env: &mut 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),
|
||||
(Value::Identifier(a), Value::Identifier(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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,8 +260,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(),
|
||||
@@ -223,7 +292,7 @@ pub(crate) fn builtin_cmp(
|
||||
accumulator &= check;
|
||||
}
|
||||
}
|
||||
Ok(Value::Boolean(accumulator))
|
||||
Ok(accumulator.into())
|
||||
}
|
||||
pub(crate) fn builtin_cmp_eq(
|
||||
vm: &mut Machine,
|
||||
@@ -267,3 +336,18 @@ pub(crate) fn builtin_cmp_le(
|
||||
) -> Result<Value, MachineError> {
|
||||
builtin_cmp(vm, args, CompareOperation::Le)
|
||||
}
|
||||
|
||||
pub(crate) fn builtin_not(
|
||||
_vm: &mut Machine,
|
||||
_env: &mut 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)
|
||||
}
|
||||
|
||||
+13
-263
@@ -1,269 +1,19 @@
|
||||
use std::{rc::Rc, slice};
|
||||
|
||||
use crate::{
|
||||
error::MachineErrorKind,
|
||||
read::{self, InteractiveReader},
|
||||
util::IteratorExt,
|
||||
vm::{
|
||||
env::Environment,
|
||||
value::{AnyFunction, ConsCell, Keyword, TryFromValue, Value, ValueString},
|
||||
},
|
||||
};
|
||||
use crate::vm::env::Environment;
|
||||
|
||||
mod collections;
|
||||
mod convert;
|
||||
mod debug;
|
||||
mod eval;
|
||||
mod functional;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
// 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())
|
||||
});
|
||||
|
||||
// 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("car", |vm, _env, args| {
|
||||
let [x] = args else {
|
||||
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
||||
};
|
||||
let Value::Cons(cons) = x else {
|
||||
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
||||
};
|
||||
Ok(cons.0.clone())
|
||||
});
|
||||
env.defun_native("cdr", |vm, _env, args| {
|
||||
let [x] = args else {
|
||||
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
||||
};
|
||||
let Value::Cons(cons) = x else {
|
||||
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
||||
};
|
||||
Ok(cons.1.clone())
|
||||
});
|
||||
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)
|
||||
});
|
||||
env.defun_native("length", |vm, _, args| {
|
||||
let [xs] = args else {
|
||||
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
||||
};
|
||||
|
||||
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(Value::Integer(count))
|
||||
});
|
||||
|
||||
// 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(), true) {
|
||||
Ok(result) => Value::Quote(Rc::new(Value::list_or_nil([
|
||||
Value::Identifier("ok".into()),
|
||||
result,
|
||||
]))),
|
||||
Err(error) => Value::Quote(Rc::new(Value::list_or_nil([
|
||||
Value::Identifier("err".into()),
|
||||
Value::String(format!("{error}").into()),
|
||||
]))),
|
||||
};
|
||||
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("read", |vm, env, _args| {
|
||||
let mut reader = InteractiveReader::new("> ", ">> ");
|
||||
let value = read::read(&mut reader, vm, env);
|
||||
let value = match value {
|
||||
Ok(Some(value)) => Value::Quote(Rc::new(Value::list_or_nil([
|
||||
Value::Identifier("ok".into()),
|
||||
value,
|
||||
]))),
|
||||
Ok(None) => Value::Nil,
|
||||
Err(error) => Value::Quote(Rc::new(Value::list_or_nil([
|
||||
Value::Identifier("err".into()),
|
||||
Value::String(format!("{error}").into()),
|
||||
]))),
|
||||
};
|
||||
Ok(value)
|
||||
});
|
||||
env.defun_native("unquote", |vm, _env, args| {
|
||||
let [x] = args else {
|
||||
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
||||
};
|
||||
let Value::Quote(value) = x else {
|
||||
return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument));
|
||||
};
|
||||
Ok(value.as_ref().clone())
|
||||
});
|
||||
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)))
|
||||
});
|
||||
math::load(env);
|
||||
eval::load(env);
|
||||
functional::load(env);
|
||||
collections::load(env);
|
||||
convert::load(env);
|
||||
debug::load(env);
|
||||
}
|
||||
|
||||
+188
-39
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
+77
-117
@@ -1,151 +1,111 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{
|
||||
error::{MachineError, MachineErrorKind},
|
||||
error::ValueConversionError,
|
||||
vm::{
|
||||
env::Environment,
|
||||
machine::Machine,
|
||||
value::{BytecodeFunction, ConsCell, Keyword, NativeFunction, Value, ValueString},
|
||||
Value,
|
||||
value::{BooleanValue, ConsCell, 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),+ $(,)?) => {
|
||||
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::Number(value) => Ok(*value),
|
||||
_ => Err(ValueConversionError {
|
||||
expected: "integer value".into(),
|
||||
got: value.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NumberValue> for Value {
|
||||
fn from(value: NumberValue) -> Self {
|
||||
Self::Number(value)
|
||||
}
|
||||
}
|
||||
|
||||
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 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(MachineErrorKind::InvalidArgument),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<ValueString> for Value {
|
||||
fn from(value: ValueString) -> Self {
|
||||
Self::String(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for Value {
|
||||
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
|
||||
Ok(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFromValue<'a> for &'a Value {
|
||||
fn try_from_value(value: &'a Value) -> Result<Self, MachineErrorKind> {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromValue<'_> for AnyFunction {
|
||||
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
|
||||
match value {
|
||||
Value::BytecodeFunction(bytecode) => Ok(Self::Bytecode(bytecode.clone())),
|
||||
Value::NativeFunction(native) => Ok(Self::Native(native.clone())),
|
||||
_ => Err(MachineErrorKind::InvalidArgument),
|
||||
_ => Err(ValueConversionError {
|
||||
expected: "string".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_integer!(
|
||||
i8: 8,
|
||||
i16: 16,
|
||||
i32: 32,
|
||||
i64: 64
|
||||
);
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
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 instructions: Box<[u8]>,
|
||||
pub constants: Box<[Value]>,
|
||||
pub upvalues: Box<[UpvalueDef]>,
|
||||
pub arity: usize,
|
||||
}
|
||||
|
||||
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) -> &StringValue {
|
||||
todo!()
|
||||
}
|
||||
|
||||
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))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use std::{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 fmt::Display for IdentifierValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,9 @@ impl_keyword! {
|
||||
Quote => "quote",
|
||||
Progn => "progn",
|
||||
Loop => "loop",
|
||||
Return => "return",
|
||||
// Cons => "cons",
|
||||
Break => "break",
|
||||
Continue => "continue",
|
||||
Declare => "declare",
|
||||
Cons => "cons",
|
||||
}
|
||||
}
|
||||
|
||||
+127
-138
@@ -1,146 +1,120 @@
|
||||
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 vector::{Vector, VectorStorage};
|
||||
pub use native::{NativeFunction, OpaqueValue};
|
||||
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>),
|
||||
// Semantic
|
||||
Closure(ClosureValue),
|
||||
Function(Rc<BytecodeFunction>),
|
||||
// Native
|
||||
NativeFunction(NativeFunction),
|
||||
OpaqueValue(OpaqueValue),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn proper_iter<E>(&self, error: E) -> ProperListIter<'_, E> {
|
||||
ProperListIter {
|
||||
head: Some(self),
|
||||
error: Some(error),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn syntax_iter(&self, location: ExpectedWhere) -> ProperListIter<'_, ParseError> {
|
||||
self.proper_iter(ParseError {
|
||||
input: self.clone(),
|
||||
error: ParseErrorKind::Expected(ExpectedWhat::ProperList, location),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_proper_list(&self) -> Option<Vec<Self>> {
|
||||
self.proper_iter(())
|
||||
.map(|x| x.cloned())
|
||||
.collect::<Result<_, _>>()
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn is_nil(&self) -> bool {
|
||||
matches!(self, Self::Nil)
|
||||
}
|
||||
|
||||
pub fn as_opaque<T: 'static>(&self) -> Result<&T, MachineErrorKind> {
|
||||
pub fn unquote(&self) -> Result<Value, ValueConversionError> {
|
||||
match self {
|
||||
Self::OpaqueValue(opaque) => opaque.cast(),
|
||||
_ => Err(MachineErrorKind::InvalidArgument),
|
||||
Self::Quote(value) => Ok(value.as_ref().clone()),
|
||||
_ => Err(ValueConversionError {
|
||||
expected: "quoted value".into(),
|
||||
got: self.clone(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn typeid(&self) -> Value {
|
||||
pub fn quote(self) -> Value {
|
||||
Value::Quote(self.into())
|
||||
}
|
||||
|
||||
pub fn type_id(&self) -> Value {
|
||||
match self {
|
||||
Value::Nil | Value::Cons(_) => Value::list_or_nil([Value::Identifier("list".into())]),
|
||||
Value::Integer(_) => Value::list_or_nil([Value::Identifier("integer".into())]),
|
||||
Value::Vector(vector) => Value::list_or_nil([
|
||||
Value::Identifier("vector".into()),
|
||||
match &*vector.borrow() {
|
||||
VectorStorage::I8(_) => Value::Identifier("i8".into()),
|
||||
VectorStorage::I16(_) => Value::Identifier("i16".into()),
|
||||
VectorStorage::I32(_) => Value::Identifier("i32".into()),
|
||||
VectorStorage::I64(_) => Value::Identifier("i64".into()),
|
||||
VectorStorage::Any(_) => Value::Identifier("*".into()),
|
||||
},
|
||||
]),
|
||||
Value::Boolean(_) => Value::list_or_nil([Value::Identifier("boolean".into())]),
|
||||
Value::String(_) => Value::list_or_nil([Value::Identifier("string".into())]),
|
||||
Value::Quasi(value) => {
|
||||
Value::list_or_nil([Value::Identifier("quasi".into()), value.typeid()])
|
||||
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()])
|
||||
}
|
||||
Value::Quote(value) => {
|
||||
Value::list_or_nil([Value::Identifier("quote".into()), value.typeid()])
|
||||
Self::Quasi(value) => {
|
||||
Value::list_or_nil([Self::Identifier("quasi".into()), value.type_id()])
|
||||
}
|
||||
Value::Unquote(value) => {
|
||||
Value::list_or_nil([Value::Identifier("unquote".into()), value.typeid()])
|
||||
Self::Unquote(value) => {
|
||||
Value::list_or_nil([Self::Identifier("unquote".into()), value.type_id()])
|
||||
}
|
||||
Value::NativeFunction(_) => {
|
||||
Value::list_or_nil([Value::Identifier("native-function".into())])
|
||||
Self::Vector(_) => Self::Identifier("vector".into()),
|
||||
Self::Keyword(_) => Self::Identifier("keyword".into()),
|
||||
Self::String(_) => Self::Identifier("string".into()),
|
||||
Self::NativeFunction(_) | Self::Function(_) | Self::Closure(_) => {
|
||||
Self::Identifier("function".into())
|
||||
}
|
||||
Value::BytecodeFunction(_) => {
|
||||
Value::list_or_nil([Value::Identifier("bytecode-function".into())])
|
||||
}
|
||||
Value::OpaqueValue(_) => Value::list_or_nil([Value::Identifier("opaque".into())]),
|
||||
Value::Keyword(keyword) => Value::list_or_nil([
|
||||
Value::Identifier("keyword".into()),
|
||||
Value::Identifier(format!("{keyword}").into()),
|
||||
]),
|
||||
Value::Identifier(_) => Value::list_or_nil([Value::Identifier("identifier".into())]),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stringify(&self) -> Result<String, MachineErrorKind> {
|
||||
pub fn is_trueish(&self) -> bool {
|
||||
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!(),
|
||||
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::Closure(_)
|
||||
| Self::NativeFunction(_)
|
||||
| Self::Function(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,48 +147,63 @@ impl Value {
|
||||
None => Self::Nil,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn proper_iter<E>(&self, error: E) -> ProperListIter<'_, E> {
|
||||
ProperListIter {
|
||||
head: Some(self),
|
||||
error: Some(error),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn syntax_iter(&self, location: ExpectedWhere) -> ProperListIter<'_, ParseError> {
|
||||
self.proper_iter(ParseError {
|
||||
input: self.clone(),
|
||||
error: ParseErrorKind::Expected(ExpectedWhat::ProperList, location),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_proper_list(&self) -> Option<Vec<Self>> {
|
||||
self.proper_iter(())
|
||||
.map(|x| x.cloned())
|
||||
.collect::<Result<_, _>>()
|
||||
.ok()
|
||||
}
|
||||
}
|
||||
|
||||
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::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::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::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::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::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)");
|
||||
}
|
||||
}
|
||||
|
||||
+31
-10
@@ -6,8 +6,12 @@ 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)]
|
||||
@@ -20,15 +24,17 @@ pub type NativeFunctionImpl =
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NativeFunction {
|
||||
name: Rc<str>,
|
||||
name: IdentifierValue,
|
||||
inner: NativeFunctionImpl,
|
||||
docstring: StringValue,
|
||||
}
|
||||
|
||||
impl OpaqueValue {
|
||||
pub fn cast<T: 'static>(&self) -> Result<&T, MachineErrorKind> {
|
||||
self.inner
|
||||
.downcast_ref()
|
||||
.ok_or(MachineErrorKind::InvalidArgument)
|
||||
pub fn cast<T: 'static>(&self) -> Result<&T, MachineError> {
|
||||
todo!()
|
||||
// self.inner
|
||||
// .downcast_ref()
|
||||
// .ok_or(MachineError::InvalidArgument)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,17 +71,27 @@ impl fmt::Display for OpaqueValue {
|
||||
}
|
||||
|
||||
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>>,
|
||||
S: Into<IdentifierValue>,
|
||||
D: Into<StringValue>,
|
||||
F: Fn(&mut Machine, &mut 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,
|
||||
@@ -112,6 +128,11 @@ 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
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 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 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,7 +23,7 @@ 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)
|
||||
}
|
||||
|
||||
+69
-73
@@ -1,12 +1,13 @@
|
||||
#![allow(irrefutable_let_patterns)]
|
||||
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
use crate::vm::value::Value;
|
||||
use crate::vm::value::{NumberValue, Value};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum VectorStorage {
|
||||
I8(Vec<i8>),
|
||||
I16(Vec<i16>),
|
||||
@@ -15,7 +16,7 @@ pub enum VectorStorage {
|
||||
Any(Vec<Value>),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Vector(RefCell<VectorStorage>);
|
||||
|
||||
impl Vector {
|
||||
@@ -39,12 +40,6 @@ impl Vector {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Vector {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.borrow().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Vector {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self.0.borrow(), f)
|
||||
@@ -118,7 +113,7 @@ macro_rules! dispatch_integer_convert {
|
||||
$f;
|
||||
}
|
||||
Self::Any($storage) => {
|
||||
let $value = Value::Integer($value);
|
||||
let $value = Value::Number(NumberValue::Int($value));
|
||||
$f;
|
||||
}
|
||||
}
|
||||
@@ -133,7 +128,7 @@ macro_rules! dispatch_any_convert {
|
||||
.iter()
|
||||
.copied()
|
||||
.map(Into::into)
|
||||
.map(Value::Integer)
|
||||
.map(Value::Number)
|
||||
.collect::<Vec<_>>();
|
||||
$f;
|
||||
*$self = Self::Any($storage);
|
||||
@@ -143,7 +138,7 @@ macro_rules! dispatch_any_convert {
|
||||
.iter()
|
||||
.copied()
|
||||
.map(Into::into)
|
||||
.map(Value::Integer)
|
||||
.map(Value::Number)
|
||||
.collect::<Vec<_>>();
|
||||
$f;
|
||||
*$self = Self::Any($storage);
|
||||
@@ -153,7 +148,7 @@ macro_rules! dispatch_any_convert {
|
||||
.iter()
|
||||
.copied()
|
||||
.map(Into::into)
|
||||
.map(Value::Integer)
|
||||
.map(Value::Number)
|
||||
.collect::<Vec<_>>();
|
||||
$f;
|
||||
*$self = Self::Any($storage);
|
||||
@@ -162,7 +157,8 @@ macro_rules! dispatch_any_convert {
|
||||
let mut $storage = $storage
|
||||
.iter()
|
||||
.copied()
|
||||
.map(Value::Integer)
|
||||
.map(Into::into)
|
||||
.map(Value::Number)
|
||||
.collect::<Vec<_>>();
|
||||
$f;
|
||||
*$self = Self::Any($storage);
|
||||
@@ -178,16 +174,16 @@ 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::Integer(value.into()))
|
||||
Some(Value::Number(value.into()))
|
||||
}
|
||||
Self::I16(values) if let Some(value) = values.get(index).copied() => {
|
||||
Some(Value::Integer(value.into()))
|
||||
Some(Value::Number(value.into()))
|
||||
}
|
||||
Self::I32(values) if let Some(value) = values.get(index).copied() => {
|
||||
Some(Value::Integer(value.into()))
|
||||
Some(Value::Number(value.into()))
|
||||
}
|
||||
Self::I64(values) if let Some(value) = values.get(index).copied() => {
|
||||
Some(Value::Integer(value))
|
||||
Some(Value::Number(value.into()))
|
||||
}
|
||||
Self::Any(values) if let Some(value) = values.get(index).cloned() => Some(value),
|
||||
_ => None,
|
||||
@@ -204,7 +200,7 @@ impl VectorStorage {
|
||||
|
||||
fn push(&mut self, value: Value) {
|
||||
match value {
|
||||
Value::Integer(value) => self.push_integer(value),
|
||||
Value::Number(NumberValue::Int(value)) => self.push_integer(value),
|
||||
value => match self {
|
||||
Self::Any(vec) => vec.push(value),
|
||||
_ => self.push_any(value),
|
||||
@@ -217,7 +213,7 @@ impl VectorStorage {
|
||||
return;
|
||||
}
|
||||
match value {
|
||||
Value::Integer(value) => self.set_integer(index, 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),
|
||||
@@ -304,55 +300,55 @@ 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::Integer(1234));
|
||||
assert_eq!(VectorStorage::I16(vec![1, 2, 3, 1234]), v);
|
||||
v.push(Value::Integer(12341234));
|
||||
assert_eq!(VectorStorage::I32(vec![1, 2, 3, 1234, 12341234]), v);
|
||||
v.push(Value::Integer(1234123412341234));
|
||||
assert_eq!(
|
||||
VectorStorage::I64(vec![1, 2, 3, 1234, 12341234, 1234123412341234]),
|
||||
v
|
||||
);
|
||||
v.push(Value::String("a".into()));
|
||||
assert_eq!(
|
||||
VectorStorage::Any(vec![
|
||||
Value::Integer(1),
|
||||
Value::Integer(2),
|
||||
Value::Integer(3),
|
||||
Value::Integer(1234),
|
||||
Value::Integer(12341234),
|
||||
Value::Integer(1234123412341234),
|
||||
Value::String("a".into())
|
||||
]),
|
||||
v
|
||||
);
|
||||
}
|
||||
}
|
||||
// #[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::Integer(1234));
|
||||
// assert_eq!(VectorStorage::I16(vec![1, 2, 3, 1234]), v);
|
||||
// v.push(Value::Integer(12341234));
|
||||
// assert_eq!(VectorStorage::I32(vec![1, 2, 3, 1234, 12341234]), v);
|
||||
// v.push(Value::Integer(1234123412341234));
|
||||
// assert_eq!(
|
||||
// VectorStorage::I64(vec![1, 2, 3, 1234, 12341234, 1234123412341234]),
|
||||
// v
|
||||
// );
|
||||
// v.push(Value::String("a".into()));
|
||||
// assert_eq!(
|
||||
// VectorStorage::Any(vec![
|
||||
// Value::Integer(1),
|
||||
// Value::Integer(2),
|
||||
// Value::Integer(3),
|
||||
// Value::Integer(1234),
|
||||
// Value::Integer(12341234),
|
||||
// Value::Integer(1234123412341234),
|
||||
// Value::String("a".into())
|
||||
// ]),
|
||||
// v
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
+174
-174
@@ -1,174 +1,174 @@
|
||||
use std::io::{self, BufReader, Read};
|
||||
|
||||
use lysp::{
|
||||
error::{EvalError, MachineError, MachineErrorKind},
|
||||
read::{FileReader, read},
|
||||
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> {
|
||||
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, false));
|
||||
}
|
||||
|
||||
last_value.expect("no expressions evaluated")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
#[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));
|
||||
// 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));
|
||||
// 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));
|
||||
}
|
||||
|
||||
#[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("(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) (+ ((lambda () 2)) 1)) 1)"),
|
||||
Value::Integer(3)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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 * '(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)"));
|
||||
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)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_setq() {
|
||||
assert_eq!(eval_str("(setq a 1234) a\n"), Value::Integer(1234));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_let() {
|
||||
// Should fail
|
||||
eval_str_err("(let (a 1234 b (+ a 1)) NIL)");
|
||||
|
||||
assert_eq!(
|
||||
eval_str("(let (a 1234 b 4321) (+ a b))"),
|
||||
Value::Integer(5555)
|
||||
);
|
||||
assert_eq!(
|
||||
eval_str("(let* (a 1234 b (+ a 4321)) (+ b 4444))"),
|
||||
Value::Integer(9999)
|
||||
);
|
||||
|
||||
// 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)
|
||||
);
|
||||
// Does shadow
|
||||
assert_eq!(
|
||||
eval_str("(let (a 1234) (let* (a 9999 b (+ a 4321)) b))"),
|
||||
Value::Integer(14320)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
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)
|
||||
])
|
||||
);
|
||||
}
|
||||
// use std::io::{self, BufReader, Read};
|
||||
//
|
||||
// use lysp::{
|
||||
// error::{EvalError, MachineError, MachineErrorKind},
|
||||
// read::{FileReader, read},
|
||||
// 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> {
|
||||
// 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, false));
|
||||
// }
|
||||
//
|
||||
// last_value.expect("no expressions evaluated")
|
||||
// }
|
||||
//
|
||||
// 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")
|
||||
// }
|
||||
//
|
||||
// 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")
|
||||
// }
|
||||
//
|
||||
// #[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));
|
||||
// // 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));
|
||||
// // 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));
|
||||
// }
|
||||
//
|
||||
// #[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("(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) (+ ((lambda () 2)) 1)) 1)"),
|
||||
// Value::Integer(3)
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// 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 * '(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)"));
|
||||
// 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)"));
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_setq() {
|
||||
// assert_eq!(eval_str("(setq a 1234) a\n"), Value::Integer(1234));
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_let() {
|
||||
// // Should fail
|
||||
// eval_str_err("(let (a 1234 b (+ a 1)) NIL)");
|
||||
//
|
||||
// assert_eq!(
|
||||
// eval_str("(let (a 1234 b 4321) (+ a b))"),
|
||||
// Value::Integer(5555)
|
||||
// );
|
||||
// assert_eq!(
|
||||
// eval_str("(let* (a 1234 b (+ a 4321)) (+ b 4444))"),
|
||||
// Value::Integer(9999)
|
||||
// );
|
||||
//
|
||||
// // 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)
|
||||
// );
|
||||
// // Does shadow
|
||||
// assert_eq!(
|
||||
// eval_str("(let (a 1234) (let* (a 9999 b (+ a 4321)) b))"),
|
||||
// Value::Integer(14320)
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// 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)
|
||||
// ])
|
||||
// );
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user