Implement closures+upvalues, rework virtual machine

This commit is contained in:
2026-05-18 17:02:58 +03:00
parent 4131cb121b
commit 92d0a80fb1
64 changed files with 4469 additions and 3517 deletions
+11
View File
@@ -0,0 +1,11 @@
(assert (= (->string 1234) "1234"))
(assert (= (->string '(1 2)) "(1 2)"))
(assert (= (->string #t) "#T"))
(assert (= 1234 (string->number "1234")))
(assert (= -1234 (string->number "-1234")))
(assert (= 0x1234 (string->number "1234" 16)))
(assert (= 0o1234 (string->number "1234" 8)))
(assert (= (/ 1 2) (string->number "0.5")))
(assert (= (/ -1 2) (string->number "-0.5")))
(assert (= (/ -1 2) (string->number "-5e-1")))
+1
View File
@@ -0,0 +1 @@
(print (explain explain))
+11 -3
View File
@@ -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))
)
)
-2
View File
@@ -1,5 +1,3 @@
;; vi:ft=lisp:sw=2:ts=2
(defun factorial (x)
(if (= x 0)
1
+28 -20
View File
@@ -1,32 +1,40 @@
;; vi:ft=lisp:sw=2:ts=2
;; loop, broken by (return)
(setq looped-loop (let (i 0)
(loop
(if (< i 10)
(setq i (+ i 1))
(return)
(setq
looped-loop
(let (i 0)
(loop
(if (< i 10)
(setq i (+ i 1))
(break)
)
)
i
)
i
))
)
;; while, broken prematurely
(setq looped-while (let (i 0)
(while (< i 20)
(setq i (+ i 1))
(if (= i 10) (return))
(setq
looped-while
(let (i 0)
(while (< i 20)
(setq i (+ i 1))
(if (= i 10) (break))
)
i
)
i
))
)
;; while, broken by condition
(setq looped-while-full (let (i 0)
(while (< i 10)
(setq i (+ i 1))
(setq
looped-while-full
(let (i 0)
(while (< i 10)
(setq i (+ i 1))
)
i
)
i
))
)
;; All loops execute the same count of times
(assert (= looped-loop looped-while looped-while-full))
(print "Test succeeded")
+45
View File
@@ -0,0 +1,45 @@
; add
(assert (= (+ 1 2 3) 6))
(assert (= (+) 0))
(assert (= (+ 1) 1))
; sub
(assert (= (- 1) -1))
(assert (= (- 1 2) -1))
; mul
(assert (= (*) 1))
(assert (= (* 2) 2))
(assert (= (* 2 3 4) 24))
; div
(assert (= (/ 1 0) #inf))
(assert (= (/ -1 0) -#inf))
(assert (= (/ 6 2) 3))
(assert (= (/ 12 3 2) 2))
(assert (= (/ 6 4) (/ 3 2)))
(assert (= (/ 4) (/ 25 100))) ; reciprocal
; rem
(assert (= (% 6 2) 0))
(assert (= (% 6 4) 2))
;;;; NaN/infinity handling
(assert (≠ #nan #nan))
(assert (= #inf #inf))
(assert (≠ -#inf #inf))
(assert (> #inf 0 -#inf))
;;;; Ordering
(assert (< 1 2 3))
(assert (not (< 1 1 2 3)))
(assert (<= 1 1 2 3 3))
(assert (> 3 2 1))
(assert (not (> 3 3 2 1)))
(assert (>= 3 3 2 1))
(assert (= 1 1 1 1))
(assert (not (= 1 1 2 1)))
(assert (≠ 1 2 3 4))
(assert (not (≠ 1 2 2 3)))
+1 -3
View File
@@ -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
View File
@@ -1,5 +1,3 @@
;; vi:ft=lisp:sw=2:ts=2
(defun replace-argument (x) (let (x 1) x))
(defun replace-let ()
@@ -23,3 +21,26 @@
(assert (= (replace-argument 3) 1))
(assert (= (replace-let) 2))
(assert (= (reassignment-in-a-loop 4) 16))
;; capture upvalue
(assert
(=
123
(let
(loc0 123)
((lambda () loc0))
)
)
)
;; mutate upvalue
(assert
(=
321
(let
(loc0 123)
((lambda () (setq loc0 321)))
loc0
)
)
)
+49
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -0,0 +1,44 @@
use crate::compile::{
block::{Compile, CompileContext, CompileValue},
error::CompileError,
syntax::{LetExpression, SetqExpression},
};
impl Compile for LetExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
cx.push_scope();
let mut binding_indices = vec![];
for binding in self.bindings.iter() {
let index = cx.bind_local(binding.identifier.clone())?;
let value = binding.value.compile(cx)?;
cx.push(value)?;
if self.sequential {
// Mark initialized
cx.init_local(index);
} else {
binding_indices.push(index);
}
}
if !self.sequential {
for index in binding_indices {
// Mark initialized
cx.init_local(index);
}
}
let value = self.body.compile(cx)?;
cx.pop_scope(Some(value))?;
Ok(CompileValue::Stack)
}
}
impl Compile for SetqExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
for assignment in self.pairs.iter() {
let value = assignment.value.compile(cx)?;
cx.compile_assign(assignment.identifier.clone(), value)?;
}
Ok(CompileValue::Nil)
}
}
+119
View File
@@ -0,0 +1,119 @@
use crate::compile::{
block::{Compile, CompileContext, CompileValue},
error::CompileError,
instruction::Emitted,
syntax::{CondExpression, IfExpression, LoopExpression, PrognExpression, WhileExpression},
};
impl Compile for WhileExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_entry = cx.new_label();
let label_exit = cx.new_label();
// Loop condition
cx.adjust_label(label_entry);
let condition_value = self.condition.compile(cx)?;
cx.push(condition_value)?;
cx.emit(Emitted::Branch(label_exit));
cx.enter_loop(label_entry, label_exit);
let body_value = self.body.compile(cx)?;
cx.discard(body_value);
cx.leave_loop();
cx.emit(Emitted::Jump(label_entry));
cx.adjust_label(label_exit);
Ok(CompileValue::Nil)
}
}
impl Compile for LoopExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_entry = cx.new_label();
let label_exit = cx.new_label();
cx.adjust_label(label_entry);
cx.enter_loop(label_entry, label_exit);
let body_value = self.body.compile(cx)?;
cx.discard(body_value);
cx.leave_loop();
cx.emit(Emitted::Jump(label_entry));
cx.adjust_label(label_exit);
Ok(CompileValue::Nil)
}
}
impl Compile for IfExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_false = cx.new_label();
let label_end = cx.new_label();
// Emit condition
let condition_value = self.condition.compile(cx)?;
cx.push(condition_value)?;
cx.emit(Emitted::Branch(label_false));
// True branch
let true_value = self.if_true.compile(cx)?;
cx.push(true_value)?;
cx.emit(Emitted::Jump(label_end));
// False branch
cx.adjust_label(label_false);
let false_value = match self.if_false.as_ref() {
Some(expr) => expr.compile(cx)?,
None => CompileValue::Nil,
};
cx.push(false_value)?;
cx.adjust_label(label_end);
Ok(CompileValue::Stack)
}
}
impl Compile for CondExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let label_end = cx.new_label();
for condition in self.arms.iter() {
let label_otherwise = cx.new_label();
let condition_value = condition.condition.compile(cx)?;
cx.push(condition_value)?;
cx.emit(Emitted::Branch(label_otherwise));
// Condition true
let then_value = condition.then.compile(cx)?;
cx.push(then_value)?;
cx.emit(Emitted::Jump(label_end));
// Condition false
cx.adjust_label(label_otherwise);
}
let otherwise_value = if let Some(otherwise_arm) = self.otherwise_arm.as_ref() {
otherwise_arm.compile(cx)?
} else {
CompileValue::Nil
};
cx.push(otherwise_value)?;
cx.adjust_label(label_end);
Ok(CompileValue::Stack)
}
}
impl Compile for PrognExpression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
let value = self.body.compile(cx)?;
cx.discard(value);
Ok(CompileValue::Nil)
}
}
pub(super) fn compile_break(cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
cx.emit_break()?;
Ok(CompileValue::Nil)
}
pub(super) fn compile_continue(cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
cx.emit_continue()?;
Ok(CompileValue::Nil)
}
+278
View File
@@ -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(),
]
);
}
}
+63
View File
@@ -0,0 +1,63 @@
use crate::compile::{
block::{Compile, CompileContext, CompileValue},
error::CompileError,
syntax::Expression,
};
mod binding;
mod flow;
mod function;
impl Compile for Expression {
fn compile(&self, cx: &mut CompileContext) -> Result<CompileValue, CompileError> {
match self {
Self::Nil => Ok(CompileValue::Nil),
Self::Break => flow::compile_break(cx),
Self::Continue => flow::compile_continue(cx),
Self::Call(call) => call.compile(cx),
Self::If(if_) => if_.compile(cx),
Self::Cond(cond) => cond.compile(cx),
Self::Let(let_) => let_.compile(cx),
Self::Setq(setq) => setq.compile(cx),
Self::While(while_) => while_.compile(cx),
Self::Loop(loop_) => loop_.compile(cx),
Self::Defun(defun) => defun.compile(cx),
Self::Lambda(lambda) => lambda.compile(cx),
Self::Progn(progn) => progn.compile(cx),
Self::Defmacro(defmacro) => defmacro.compile(cx),
Self::Identifier(identifier) => Ok(CompileValue::Identifier(identifier.clone())),
&Self::IntegerLiteral(value) => Ok(CompileValue::Integer(value)),
&Self::BooleanLiteral(value) => Ok(CompileValue::Boolean(value)),
Self::StringLiteral(value) => Ok(CompileValue::String(value.clone())),
Self::Vector(vector) => Ok(CompileValue::Value(vector.clone().into())),
Self::Quote(value) => Ok(CompileValue::Quote(value.clone())),
Self::SyntaxError(_) => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use crate::compile::{
block::{CompileValue, test_compile},
syntax::Expression,
};
#[test]
fn test_nil_compile() {
let e = Expression::Nil;
let (cx, v) = test_compile(&e).unwrap();
assert_eq!(cx.current, 0);
assert!(cx.function_blocks[0].instructions.is_empty());
assert_eq!(v, CompileValue::Nil);
}
#[test]
fn test_integer_literal_compile() {
let e = Expression::IntegerLiteral(1.into());
let (cx, v) = test_compile(&e).unwrap();
assert_eq!(cx.current, 0);
assert!(cx.function_blocks[0].instructions.is_empty());
assert_eq!(v, CompileValue::Integer(1.into()));
}
}
+13 -1
View File
@@ -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 {
+8 -4
View File
@@ -1,10 +1,10 @@
use std::rc::Rc;
use crate::vm::value::IdentifierValue;
#[derive(Debug, Clone, PartialEq)]
pub struct FunctionSignature {
pub required_arguments: Vec<Rc<str>>,
pub optional_arguments: Vec<Rc<str>>,
pub rest_argument: Option<Rc<str>>,
pub required_arguments: Vec<IdentifierValue>,
pub optional_arguments: Vec<IdentifierValue>,
pub rest_argument: Option<IdentifierValue>,
}
impl FunctionSignature {
@@ -37,4 +37,8 @@ impl FunctionSignature {
None
}
}
pub fn arity(&self) -> usize {
self.required_arguments.len()
}
}
+35 -3
View File
@@ -1,10 +1,16 @@
use crate::vm::instruction::Instruction;
use crate::vm::instruction::{ArgumentCount, ConstantId, ImmediateInteger, Instruction, LocalId};
#[derive(Debug, Clone, Copy)]
pub enum Emitted {
Branch(u32),
Jump(u32),
Branch(usize),
Jump(usize),
Instruction(Instruction),
LocalId(LocalId),
ConstantId(ConstantId),
ArgumentCount(ArgumentCount),
ImmediateInteger(ImmediateInteger),
Bool(bool),
// Instruction(Instruction),
}
impl From<Instruction> for Emitted {
@@ -12,3 +18,29 @@ impl From<Instruction> for Emitted {
Self::Instruction(value)
}
}
impl From<LocalId> for Emitted {
fn from(value: LocalId) -> Self {
Self::LocalId(value)
}
}
impl From<ConstantId> for Emitted {
fn from(value: ConstantId) -> Self {
Self::ConstantId(value)
}
}
impl From<ArgumentCount> for Emitted {
fn from(value: ArgumentCount) -> Self {
Self::ArgumentCount(value)
}
}
impl From<ImmediateInteger> for Emitted {
fn from(value: ImmediateInteger) -> Self {
Self::ImmediateInteger(value)
}
}
impl From<bool> for Emitted {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
+5 -11
View File
@@ -1,20 +1,14 @@
mod block;
mod codegen;
mod error;
mod function;
mod instruction;
mod module;
mod syntax;
mod value;
pub use error::CompileError;
pub use function::FunctionSignature;
pub use module::CompilationModule;
pub use syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody, LambdaExpression,
ParseError, ParseErrorKind,
};
pub mod syntax;
#[derive(Default, Clone)]
pub struct CompileOptions {
pub trace_compile: bool,
}
pub use block::{Compile, CompileContext, UpvalueDef};
pub use error::CompileError;
-111
View File
@@ -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,
})
}
}
+16 -16
View File
@@ -1,16 +1,16 @@
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::CollectErrors,
compile::syntax::{
CollectErrors, Expression, FunctionBody,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
},
vm::value::{ConsCell, Keyword, Value},
vm::value::{ConsCell, IdentifierValue, Keyword, Value},
};
#[derive(Debug, PartialEq)]
pub struct Assignment {
pub identifier: Rc<str>,
pub identifier: IdentifierValue,
pub value: Rc<Expression>,
}
@@ -138,9 +138,9 @@ mod tests {
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::{Assignment, LetExpression, SetqExpression},
compile::syntax::{
Assignment, Expression, FunctionBody, LetExpression, SetqExpression,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
},
vm::value::{ConsCell, Keyword, Value},
};
@@ -149,7 +149,7 @@ mod tests {
fn test_parse_let() {
let v = Value::list_or_nil([
Value::Keyword(Keyword::Let),
Value::list_or_nil([Value::Identifier("a".into()), Value::Integer(1)]),
Value::list_or_nil([Value::Identifier("a".into()), Value::Number(1.into())]),
Value::Nil,
]);
let e = Expression::parse(&v).unwrap();
@@ -159,7 +159,7 @@ mod tests {
sequential: false,
bindings: vec![Assignment {
identifier: "a".into(),
value: Rc::new(Expression::IntegerLiteral(1))
value: Rc::new(Expression::IntegerLiteral(1.into()))
},],
body: FunctionBody {
head: vec![],
@@ -171,9 +171,9 @@ mod tests {
Value::Keyword(Keyword::LetStar),
Value::list_or_nil([
Value::Identifier("a".into()),
Value::Integer(1),
Value::Number(1.into()),
Value::Identifier("b".into()),
Value::Integer(2),
Value::Number(2.into()),
]),
Value::Nil,
]);
@@ -185,11 +185,11 @@ mod tests {
bindings: vec![
Assignment {
identifier: "a".into(),
value: Rc::new(Expression::IntegerLiteral(1))
value: Rc::new(Expression::IntegerLiteral(1.into()))
},
Assignment {
identifier: "b".into(),
value: Rc::new(Expression::IntegerLiteral(2))
value: Rc::new(Expression::IntegerLiteral(2.into()))
}
],
body: FunctionBody {
@@ -259,7 +259,7 @@ mod tests {
let v = Value::list_or_nil([
Value::Keyword(Keyword::Setq),
Value::Identifier("a".into()),
Value::Integer(1),
Value::Number(1.into()),
Value::Identifier("b".into()),
]);
let e = Expression::parse(&v).unwrap_err();
@@ -275,7 +275,7 @@ mod tests {
);
let v = Value::list_or_nil([
Value::Keyword(Keyword::Setq),
Value::Integer(1),
Value::Number(1.into()),
Value::Identifier("a".into()),
]);
let e = Expression::parse(&v).unwrap_err();
+5 -7
View File
@@ -1,9 +1,8 @@
use std::rc::Rc;
use crate::{
compile::{
Expression, ParseError,
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
compile::syntax::{
CollectErrors, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
},
vm::value::{ConsCell, Value},
};
@@ -58,9 +57,8 @@ mod tests {
use std::rc::Rc;
use crate::{
compile::{
CallExpression, Expression, ParseError,
syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind},
compile::syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
},
vm::value::Value,
};
@@ -78,7 +76,7 @@ mod tests {
);
// Syntax errors
let v = Value::Identifier("a".into()).cons(Value::Integer(1));
let v = Value::Identifier("a".into()).cons(Value::Number(1.into()));
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
+52 -52
View File
@@ -1,9 +1,8 @@
use std::rc::Rc;
use crate::{
compile::{
Expression, ParseError,
syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind},
compile::syntax::{
CollectErrors, ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind,
},
vm::value::{ConsCell, Keyword, Value},
};
@@ -217,12 +216,9 @@ mod tests {
use std::rc::Rc;
use crate::{
compile::{
Expression, ParseError,
syntax::{
CondExpression, CondExpressionArm, ExpectedWhat, ExpectedWhere, IfExpression,
ParseErrorKind,
},
compile::syntax::{
CondExpression, CondExpressionArm, ExpectedWhat, ExpectedWhere, Expression,
IfExpression, ParseError, ParseErrorKind,
},
vm::value::{ConsCell, Keyword, Value},
};
@@ -230,8 +226,8 @@ mod tests {
#[test]
fn test_parse_cond() {
// without &otherwise branch
let b0 = Value::list_or_nil([Value::Boolean(true), Value::Identifier("a".into())]);
let b1 = Value::list_or_nil([Value::Boolean(false), Value::Identifier("b".into())]);
let b0 = Value::list_or_nil([Value::Boolean(true.into()), Value::Identifier("a".into())]);
let b1 = Value::list_or_nil([Value::Boolean(false.into()), Value::Identifier("b".into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
@@ -239,11 +235,11 @@ mod tests {
&Expression::Cond(CondExpression {
arms: vec![
CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(true)),
condition: Rc::new(Expression::BooleanLiteral(true.into())),
then: Rc::new(Expression::Identifier("a".into()))
},
CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(false)),
condition: Rc::new(Expression::BooleanLiteral(false.into())),
then: Rc::new(Expression::Identifier("b".into()))
}
],
@@ -252,7 +248,7 @@ mod tests {
);
// with &otherwise branch
let b0 = Value::list_or_nil([Value::Boolean(true), Value::Identifier("a".into())]);
let b0 = Value::list_or_nil([Value::Boolean(true.into()), Value::Identifier("a".into())]);
let b1 = Value::list_or_nil([
Value::Keyword(Keyword::Otherwise),
Value::Identifier("b".into()),
@@ -263,7 +259,7 @@ mod tests {
e.as_ref(),
&Expression::Cond(CondExpression {
arms: vec![CondExpressionArm {
condition: Rc::new(Expression::BooleanLiteral(true)),
condition: Rc::new(Expression::BooleanLiteral(true.into())),
then: Rc::new(Expression::Identifier("a".into()))
},],
otherwise_arm: Some(Rc::new(Expression::Identifier("b".into())))
@@ -297,7 +293,7 @@ mod tests {
}]
);
let b0 = Value::list_or_nil([Value::Boolean(false)]);
let b0 = Value::list_or_nil([Value::Boolean(false.into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -310,7 +306,7 @@ mod tests {
)
}]
);
let b0 = Value::Boolean(false);
let b0 = Value::Boolean(false.into());
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -336,7 +332,7 @@ mod tests {
)
}]
);
let b0 = Value::Keyword(Keyword::Otherwise).cons(Value::Integer(1));
let b0 = Value::Keyword(Keyword::Otherwise).cons(Value::Number(1.into()));
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -349,7 +345,7 @@ mod tests {
)
}]
);
let b0 = Value::Boolean(false).cons(Value::Boolean(false));
let b0 = Value::Boolean(false.into()).cons(Value::Boolean(false.into()));
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -362,20 +358,10 @@ mod tests {
)
}]
);
let b0 = Value::list_or_nil([Value::Boolean(false), Value::Integer(1), Value::Integer(2)]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: b0,
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Integer(2)]))
}]
);
let b0 = Value::list_or_nil([
Value::Keyword(Keyword::Otherwise),
Value::Integer(1),
Value::Integer(2),
Value::Boolean(false.into()),
Value::Number(1.into()),
Value::Number(2.into()),
]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
@@ -383,10 +369,24 @@ mod tests {
e,
vec![ParseError {
input: b0,
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Integer(2)]))
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Number(2.into())]))
}]
);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Integer(1)]);
let b0 = Value::list_or_nil([
Value::Keyword(Keyword::Otherwise),
Value::Number(1.into()),
Value::Number(2.into()),
]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
vec![ParseError {
input: b0,
error: ParseErrorKind::extraneous(&Value::list_or_nil([Value::Number(2.into())]))
}]
);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Number(1.into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone(), b0.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -396,8 +396,8 @@ mod tests {
error: ParseErrorKind::extraneous(&b0)
}]
);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Integer(1)]);
let b1 = Value::list_or_nil([Value::Boolean(false), Value::Integer(1)]);
let b0 = Value::list_or_nil([Value::Keyword(Keyword::Otherwise), Value::Number(1.into())]);
let b1 = Value::list_or_nil([Value::Boolean(false.into()), Value::Number(1.into())]);
let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1.clone()]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -414,15 +414,15 @@ mod tests {
// without false branch
let v = Value::list_or_nil([
Value::Keyword(Keyword::If),
Value::Boolean(true),
Value::Integer(1),
Value::Boolean(true.into()),
Value::Number(1.into()),
]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
e.as_ref(),
&Expression::If(IfExpression {
condition: Rc::new(Expression::BooleanLiteral(true)),
if_true: Rc::new(Expression::IntegerLiteral(1)),
condition: Rc::new(Expression::BooleanLiteral(true.into())),
if_true: Rc::new(Expression::IntegerLiteral(1.into())),
if_false: None
})
);
@@ -430,17 +430,17 @@ mod tests {
// with false branch
let v = Value::list_or_nil([
Value::Keyword(Keyword::If),
Value::Boolean(false),
Value::Integer(1),
Value::Integer(2),
Value::Boolean(false.into()),
Value::Number(1.into()),
Value::Number(2.into()),
]);
let e = Expression::parse(&v).unwrap();
assert_eq!(
e.as_ref(),
&Expression::If(IfExpression {
condition: Rc::new(Expression::BooleanLiteral(false)),
if_true: Rc::new(Expression::IntegerLiteral(1)),
if_false: Some(Rc::new(Expression::IntegerLiteral(2)))
condition: Rc::new(Expression::BooleanLiteral(false.into())),
if_true: Rc::new(Expression::IntegerLiteral(1.into())),
if_false: Some(Rc::new(Expression::IntegerLiteral(2.into())))
})
);
@@ -457,7 +457,7 @@ mod tests {
)
}]
);
let v = Value::list_or_nil([Value::Keyword(Keyword::If), Value::Boolean(true)]);
let v = Value::list_or_nil([Value::Keyword(Keyword::If), Value::Boolean(true.into())]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
e,
@@ -471,10 +471,10 @@ mod tests {
);
let v = Value::list_or_nil([
Value::Keyword(Keyword::If),
Value::Boolean(true),
Value::Integer(1),
Value::Integer(2),
Value::Integer(3),
Value::Boolean(true.into()),
Value::Number(1.into()),
Value::Number(2.into()),
Value::Number(3.into()),
]);
let e = Expression::parse(&v).unwrap_err();
assert_eq!(
@@ -482,7 +482,7 @@ mod tests {
vec![ParseError {
input: v,
error: ParseErrorKind::ExtraneousExpressions(Rc::new(ConsCell::end(
Value::Integer(3)
Value::Number(3.into())
)))
}]
);
+21 -13
View File
@@ -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,
+11 -7
View File
@@ -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()
+3 -3
View File
@@ -1,9 +1,9 @@
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::CollectErrors,
compile::syntax::{
CollectErrors, Expression, FunctionBody,
error::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
},
vm::value::{ConsCell, Keyword, Value},
};
+7 -6
View File
@@ -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
View File
@@ -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!(
+8 -8
View File
@@ -19,14 +19,14 @@ pub enum CompileValue {
Nil,
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub enum CompileConstant {
Integer(i64),
LocalFunction(u32, usize),
Identifier(Rc<str>),
String(ValueString),
Value(Rc<Value>),
}
// #[derive(Debug, Hash, PartialEq, Eq)]
// pub enum CompileConstant {
// Integer(i64),
// LocalFunction(u32, usize),
// Identifier(Rc<str>),
// String(ValueString),
// Value(Rc<Value>),
// }
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum BuiltinFunction {
+120 -40
View File
@@ -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
View File
@@ -1,14 +1,19 @@
#![feature(
coverage_attribute,
debug_closure_helpers,
unboxed_closures,
iter_next_chunk,
exact_size_is_empty
exact_size_is_empty,
exact_div
)]
//
// pub mod error;
pub mod parse;
pub mod read;
#[macro_use]
pub mod util;
pub mod compile;
pub mod error;
pub mod parse;
pub mod read;
pub mod util;
pub mod vm;
+38 -33
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -22,6 +22,39 @@ pub trait IteratorExt<I, E1, T> {
E2: From<E1>;
}
#[macro_export]
macro_rules! primitive_enum {
(
$(#[$meta:meta])*
$vis:vis enum $ident:ident: $repr:ty [$conversion_err:ident] {
$($variant:ident = $discriminant:literal),+ $(,)?
}
) => {
$(#[$meta])*
#[repr($repr)]
$vis enum $ident {
$($variant = $discriminant),+
}
impl From<$ident> for $repr {
fn from(value: $ident) -> $repr {
value as $repr
}
}
impl TryFrom<$repr> for $ident {
type Error = $conversion_err;
fn try_from(value: $repr) -> Result<$ident, $conversion_err> {
match value {
$($discriminant => Ok($ident::$variant),)+
_ => Err($conversion_err(value))
}
}
}
};
}
impl<A: fmt::Debug, B: fmt::Debug> fmt::Debug for Either<A, B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
+42 -16
View File
@@ -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
View File
@@ -1,247 +1,229 @@
use std::fmt;
use bitmatch::bitmatch;
use crate::{
error::MachineError,
vm::{machine::Machine, value::NumberValue},
};
#[derive(Debug, thiserror::Error)]
pub enum InstructionError {
#[error("invalid instruction")]
Invalid,
#[derive(Debug, PartialEq, thiserror::Error)]
#[error("unrecognized instruction: {0}")]
pub struct InstructionDecodeError(u8);
primitive_enum! {
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum Instruction: u8 [InstructionDecodeError] {
// Stack manipulation
PushNil = 0,
PushInteger = 1,
PushTrue = 2,
PushFalse = 3,
PushConstant = 4,
CloseUpvalue = 5,
Drop = 6,
SetTemp = 7,
GetTemp = 8,
// Binding manupulation
GetLocal = 10,
SetLocal = 11,
GetUpvalue = 12,
SetUpvalue = 13,
GetGlobal = 14,
SetGlobal = 15,
DeclareGlobal = 16,
DeclareMacro = 17,
// Arithmetic
Add = 20,
Sub = 21,
Mul = 22,
Div = 23,
Mod = 24,
Gt = 25,
Lt = 26,
Eq = 27,
Ge = 28,
Le = 29,
Ne = 30,
Not = 31,
Negate = 32,
// Branching
Branch = 40,
Jump = 41,
// Functions and closures
Call = 50,
Return = 51,
MakeClosure = 52,
}
}
#[derive(Debug, thiserror::Error)]
pub enum InstructionEncodeError {}
macro_rules! impl_fixed_unsigned {
($vis:vis $ident:ident : $repr:ty, $bits:literal) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
$vis struct $ident($repr);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct U<const N: usize>(u32);
impl $ident {
pub const BITS: usize = $bits;
macro_rules! primitive_enum {
(
$(#[$meta:meta])*
$vis:vis enum $ident:ident: $repr:ty {
$($variant:ident = $value:literal),* $(,)?
}
) => {
$(#[$meta])*
#[repr($repr)]
$vis enum $ident {
$($variant = $value),*
pub fn to_bytes(&self) -> [u8; $bits / 8] {
self.0.to_le_bytes()
}
}
impl TryFrom<$repr> for $ident {
type Error = InstructionError;
impl TryFrom<usize> for $ident {
type Error = FixedConvertError<usize, $bits>;
fn try_from(value: $repr) -> Result<Self, Self::Error> {
match value {
$($value => Ok(Self::$variant),)*
_ => Err(InstructionError::Invalid)
fn try_from(value: usize) -> Result<Self, Self::Error> {
if let Ok(value) = <$repr>::try_from(value) {
Ok(Self(value))
} else {
Err(FixedConvertError(value))
}
}
}
impl From<$repr> for $ident {
fn from(value: $repr) -> $ident {
$ident(value)
}
}
impl From<$ident> for usize {
fn from(value: $ident) -> usize {
value.0.into()
}
}
impl From<$ident> for $repr {
fn from(value: $ident) -> $repr {
match value {
$($ident::$variant => $value,)*
}
value.0
}
}
};
}
macro_rules! impl_fixed {
($vis:vis $ident:ident: u8) => {
impl_fixed_unsigned!($vis $ident : u8, 8);
impl ReadEncoded for $ident {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError> {
let b0 = stream.fetch_byte()?;
Ok(Self(b0))
}
}
};
($vis:vis $ident:ident: u16) => {
impl_fixed_unsigned!($vis $ident : u16, 16);
impl ReadEncoded for $ident {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError> {
let b0 = stream.fetch_byte()?;
let b1 = stream.fetch_byte()?;
Ok(Self(u16::from_le_bytes([b0, b1])))
}
}
};
}
primitive_enum! {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum MathInstruction: u32 {
Add = 0,
Sub = 1,
Mul = 2,
Div = 3,
Mod = 4,
And = 5,
Or = 6,
BitwiseAnd = 7,
BitwiseOr = 8,
BitwiseXor = 9,
Gt = 10,
Lt = 11,
Eq = 12,
Ne = 13,
Ge = 14,
Le = 15,
#[derive(Debug, thiserror::Error)]
#[error("cannot fit {0} into {N} bits")]
pub struct FixedConvertError<T: fmt::Display, const N: usize>(T);
impl_fixed!(pub LocalId: u8);
impl_fixed!(pub ConstantId: u16);
impl_fixed!(pub ArgumentCount: u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ImmediateInteger(i16);
impl ImmediateInteger {
pub fn sign_extend_i64(self) -> i64 {
self.0 as i64
}
pub fn to_bytes(&self) -> [u8; 2] {
self.0.to_le_bytes()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Instruction {
PushNil,
PushInteger(PackedInteger),
PushBool(bool),
PushConstant(ConstantId),
PushArgument(ArgumentId),
Drop,
SetGlobal,
GetGlobal,
SetLocal(LocalId),
GetLocal(LocalId),
ExportMacro(ConstantId),
Call(ArgumentCount),
Return,
Math(MathInstruction, ArgumentCount),
Branch(FunctionOffset),
Jump(FunctionOffset),
}
pub type ConstantId = U<12>;
pub type FunctionOffset = U<10>;
pub type LocalId = U<8>;
pub type ArgumentId = U<6>;
pub type ArgumentCount = U<6>;
pub type PackedInteger = U<12>;
impl<const N: usize> U<N> {
pub const BITS: usize = N;
pub const ZERO: Self = Self(0);
const UNSIGNED_MASK: i64 = (1 << (N - 1)) - 1;
const SIGNED_MASK: i64 = (1 << N) - 1;
pub fn sign_extend_i64(&self) -> i64 {
if self.0 & (1 << (N - 1)) != 0 {
self.0 as i64 | !Self::SIGNED_MASK
} else {
self.0 as i64
}
}
pub const fn new(value: u32) -> Option<Self> {
if value >= (1u32 << N) {
None
} else {
Some(Self(value))
}
}
pub fn from_signed(value: i64) -> Option<Self> {
if value > 0 && value & !Self::UNSIGNED_MASK == 0 {
Some(Self(value as u32))
} else if value < 0 && value & !Self::SIGNED_MASK == !Self::SIGNED_MASK {
Some(Self((value & Self::SIGNED_MASK) as u32))
} else {
None
}
}
pub const fn truncate(value: u32) -> Self {
Self(value & ((1 << N) - 1))
}
pub const unsafe fn new_unchecked(value: u32) -> Self {
impl From<i16> for ImmediateInteger {
fn from(value: i16) -> Self {
Self(value)
}
}
impl<const N: usize> fmt::Debug for U<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
impl ReadEncoded for ImmediateInteger {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError> {
let b0 = stream.fetch_byte()?;
let b1 = stream.fetch_byte()?;
Ok(Self(i16::from_le_bytes([b0, b1])))
}
}
impl<const N: usize> From<U<N>> for usize {
fn from(value: U<N>) -> Self {
value.0 as usize
}
}
impl TryFrom<NumberValue> for ImmediateInteger {
type Error = FixedConvertError<NumberValue, 16>;
impl<const N: usize> From<U<N>> for u32 {
fn from(value: U<N>) -> Self {
value.0
}
}
impl From<Instruction> for u32 {
fn from(instruction: Instruction) -> u32 {
match instruction {
Instruction::Drop => 0b00000000_00000000,
Instruction::PushNil => 0b00000000_00000001,
Instruction::PushBool(value) => 0b00000000_00000010 | (value as u32),
Instruction::Return => 0b00000000_00000100,
Instruction::SetGlobal => 0b00000000_00000101,
Instruction::GetGlobal => 0b00000000_00000110,
Instruction::GetLocal(value) => 0b00000010_00000000 | value.0,
Instruction::SetLocal(value) => 0b00000011_00000000 | value.0,
Instruction::Call(count) => 0b00000000_01000000 | count.0,
Instruction::PushInteger(value) => 0b00010000_00000000 | value.0,
Instruction::PushConstant(index) => 0b00100000_00000000 | index.0,
Instruction::PushArgument(index) => 0b00000000_10000000 | index.0,
Instruction::ExportMacro(index) => 0b00110000_00000000 | index.0,
Instruction::Branch(offset) => 0b00001000_00000000 | offset.0,
Instruction::Jump(offset) => 0b00001100_00000000 | offset.0,
Instruction::Math(math, count) => {
0b00000100_00000000 | (u32::from(math) << 6) | count.0
}
}
}
}
impl TryFrom<u32> for Instruction {
type Error = InstructionError;
#[bitmatch]
fn try_from(value: u32) -> Result<Self, Self::Error> {
#[bitmatch]
fn try_from(value: NumberValue) -> Result<Self, Self::Error> {
match value {
"00000000_00000000" => Ok(Instruction::Drop),
"00000000_00000001" => Ok(Instruction::PushNil),
"00000000_0000001x" => Ok(Instruction::PushBool(x != 0)),
"00000000_00000100" => Ok(Instruction::Return),
"00000000_00000101" => Ok(Instruction::SetGlobal),
"00000000_00000110" => Ok(Instruction::GetGlobal),
"00000000_00000111" => todo!(),
"00000000_00001???" => todo!(),
NumberValue::Int(value) if let Ok(value) = value.try_into() => Ok(Self(value)),
_ => Err(FixedConvertError(value)),
}
}
}
impl TryFrom<i64> for ImmediateInteger {
type Error = FixedConvertError<i64, 16>;
"00000000_0001????" => todo!(),
"00000000_001?????" => todo!(),
"00000000_01xxxxxx" => Ok(Instruction::Call(U(x))),
"00000000_10xxxxxx" => Ok(Instruction::PushArgument(U(x))),
"00000000_11??????" => todo!(),
"00000001_????????" => todo!(),
"00000010_xxxxxxxx" => Ok(Instruction::GetLocal(U(x))),
"00000011_xxxxxxxx" => Ok(Instruction::SetLocal(U(x))),
"000001xx_xxyyyyyy" => Ok(Instruction::Math(x.try_into()?, U(y))),
"000010xx_xxxxxxxx" => Ok(Instruction::Branch(U(x))),
"000011xx_xxxxxxxx" => Ok(Instruction::Jump(U(x))),
"0001xxxx_xxxxxxxx" => Ok(Instruction::PushInteger(U(x))),
"0010xxxx_xxxxxxxx" => Ok(Instruction::PushConstant(U(x))),
"0011xxxx_xxxxxxxx" => Ok(Instruction::ExportMacro(U(x))),
"01??????_????????" => todo!(),
"1???????_????????" => todo!("{value:032b}"),
fn try_from(value: i64) -> Result<Self, Self::Error> {
if let Ok(value) = value.try_into() {
Ok(Self(value))
} else {
Err(FixedConvertError(value))
}
}
}
#[cfg(test)]
mod tests {
use crate::vm::instruction::{FunctionOffset, U};
pub trait ReadEncoded: Sized {
fn read_encoded(stream: &mut Machine) -> Result<Self, MachineError>;
}
#[test]
fn test_u_convert() {
type T = U<24>;
let t = T::from_signed(-1234).unwrap();
assert_eq!(t.0, (-1234i64 & 0xFFFFFF) as u32);
assert_eq!(t.sign_extend_i64(), -1234);
let t = T::from_signed(1234).unwrap();
assert_eq!(t.0, 1234);
let t = T::from_signed(0x800000);
assert!(t.is_none());
let t = T::from_signed(-0x1000001);
assert!(t.is_none());
}
#[test]
fn test_branch_target_extend() {
let target = FunctionOffset::truncate(1022);
assert_eq!(target.sign_extend_i64(), -2);
impl fmt::Display for Instruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
Self::PushNil => "PUSH_NIL",
Self::PushInteger => "PUSH_INTEGER",
Self::PushTrue => "PUSH_TRUE",
Self::PushFalse => "PUSH_FALSE",
Self::PushConstant => "PUSH_CONSTANT",
Self::CloseUpvalue => "CLOSE_UPVALUE",
Self::Drop => "DROP",
Self::SetTemp => "SET_TEMP",
Self::GetTemp => "GET_TEMP",
Self::GetLocal => "GET_LOCAL",
Self::SetLocal => "SET_LOCAL",
Self::GetUpvalue => "GET_UPVALUE",
Self::SetUpvalue => "SET_UPVALUE",
Self::GetGlobal => "GET_GLOBAL",
Self::SetGlobal => "SET_GLOBAL",
Self::DeclareGlobal => "DECLARE_GLOBAL",
Self::DeclareMacro => "DECLARE_MACRO",
Self::Add => "ADD",
Self::Sub => "SUB",
Self::Mul => "MUL",
Self::Div => "DIV",
Self::Mod => "MOD",
Self::Gt => "GT",
Self::Lt => "LT",
Self::Eq => "EQ",
Self::Ge => "GE",
Self::Le => "LE",
Self::Ne => "NE",
Self::Not => "NOT",
Self::Negate => "NEGATE",
Self::Branch => "BRANCH",
Self::Jump => "JUMP",
Self::Call => "CALL",
Self::Return => "RETURN",
Self::MakeClosure => "MAKE_CLOSURE",
};
f.write_str(name)
}
}
-1
View File
@@ -1 +0,0 @@
+688 -679
View File
File diff suppressed because it is too large Load Diff
+26 -24
View File
@@ -1,11 +1,11 @@
use std::rc::Rc;
use crate::{
error::EvalError,
error::MachineErrorAt,
vm::{
env::Environment,
env::{Environment, Macro},
machine::Machine,
value::{ConsCell, Keyword, Macro, Value},
value::{ClosureValue, ConsCell, 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
View File
@@ -1,10 +1,9 @@
pub mod env;
pub mod instruction;
pub mod loader;
pub mod machine;
pub mod macros;
pub mod module;
pub mod pool;
pub mod prelude;
pub mod stack;
pub mod value;
pub use value::Value;
-254
View File
@@ -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);
}
}
-50
View File
@@ -1,50 +0,0 @@
use std::{
collections::{HashMap, hash_map},
hash::Hash,
};
use crate::vm::instruction::U;
pub struct Pool<T, const N: usize> {
map: HashMap<T, U<N>>,
index: u32,
}
impl<T: Hash + Eq, const N: usize> Pool<T, N> {
pub fn key(&mut self, value: T) -> Option<U<N>> {
match self.map.entry(value) {
hash_map::Entry::Vacant(slot) => {
let index = U::new(self.index + 1)?;
self.index += 1;
Some(*slot.insert(index))
}
hash_map::Entry::Occupied(slot) => Some(*slot.get()),
}
}
pub fn into_map(mut self) -> HashMap<U<N>, T> {
let mut result = HashMap::new();
for (value, key) in self.map.drain() {
result.insert(key, value);
}
result
}
}
impl<T, const N: usize> IntoIterator for Pool<T, N> {
type Item = (T, U<N>);
type IntoIter = <HashMap<T, U<N>> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.map.into_iter()
}
}
impl<T, const N: usize> Default for Pool<T, N> {
fn default() -> Self {
Self {
map: HashMap::new(),
index: 0,
}
}
}
+107
View File
@@ -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())
},
);
}
+67
View File
@@ -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())
// });
}
+95
View File
@@ -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()))
},
);
}
+80
View File
@@ -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)
},
);
}
+33
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,71 +1,220 @@
use std::{fmt, mem::MaybeUninit, ops::Deref};
use std::{
mem::MaybeUninit,
ops::{Bound, Index, RangeBounds},
};
pub struct Stack<T> {
data: Box<[MaybeUninit<T>]>,
pointer: usize,
}
impl<T: fmt::Debug> fmt::Debug for Stack<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let data = &self[..];
f.debug_struct("Stack")
.field("pointer", &self.pointer)
.field("data", &data)
.finish()
}
}
impl<T> Deref for Stack<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
let slice = &self.data[self.pointer..];
unsafe { slice.assume_init_ref() }
}
}
impl<T> Stack<T> {
pub fn new(limit: usize) -> Self {
assert_ne!(limit, 0);
Self {
data: Box::new_uninit_slice(limit),
pointer: limit,
pointer: 0,
}
}
pub fn current(&self) -> Option<&T> {
if self.pointer >= self.data.len() {
None
pub fn is_empty(&self) -> bool {
self.pointer == 0
}
pub fn checked_slice<I: RangeBounds<usize>>(&self, range: I) -> Option<&[T]> {
let start = match range.start_bound() {
Bound::Unbounded => 0,
Bound::Included(start) | Bound::Excluded(start) => *start,
};
let end = match range.end_bound() {
Bound::Unbounded => self.pointer,
Bound::Excluded(end) => *end,
Bound::Included(end) => *end + 1,
};
if start <= self.pointer && end <= self.pointer {
Some(unsafe { self.data[start..end].assume_init_ref() })
} else {
Some(unsafe { self.data[self.pointer].assume_init_ref() })
None
}
}
pub fn current_mut(&mut self) -> Option<&mut T> {
if self.pointer >= self.data.len() {
None
pub fn checked_last(&self, n: usize) -> Option<&[T]> {
if n < self.pointer {
Some(unsafe { self.data[self.pointer - n..self.pointer].assume_init_ref() })
} else {
Some(unsafe { self.data[self.pointer].assume_init_mut() })
None
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
if index < self.pointer {
Some(unsafe { self.data[index].assume_init_mut() })
} else {
None
}
}
pub fn get(&self, index: usize) -> Option<&T> {
if index < self.pointer {
Some(unsafe { self.data[index].assume_init_ref() })
} else {
None
}
}
pub fn head(&self) -> Option<&T> {
if self.pointer > 0 {
Some(unsafe { self.data[self.pointer - 1].assume_init_ref() })
} else {
None
}
}
pub fn head_mut(&mut self) -> Option<&mut T> {
if self.pointer > 0 {
Some(unsafe { self.data[self.pointer - 1].assume_init_mut() })
} else {
None
}
}
pub fn pointer(&self) -> usize {
self.pointer
}
pub fn set_pointer(&mut self, pointer: usize) {
if pointer < self.pointer {
// Call drop for values between
for slot in &mut self.data[pointer..self.pointer] {
unsafe { slot.assume_init_drop() };
}
} else if pointer > self.pointer {
panic!("Cannot set stack pointer higher than it currently is");
}
self.pointer = pointer;
}
pub fn pop(&mut self) -> Option<T> {
if self.pointer > 0 {
self.pointer -= 1;
let value = unsafe { self.data[self.pointer].assume_init_read() };
Some(value)
} else {
None
}
}
pub fn push(&mut self, value: T) -> Result<(), T> {
if self.pointer > 0 {
self.pointer -= 1;
if self.pointer < self.data.len() {
self.data[self.pointer].write(value);
self.pointer += 1;
Ok(())
} else {
Err(value)
}
}
}
pub fn pop(&mut self) -> Option<T> {
if self.pointer >= self.data.len() {
None
} else {
let value = unsafe { self.data[self.pointer].assume_init_read() };
self.pointer += 1;
Some(value)
}
impl<T> Index<usize> for Stack<T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
self.get(index).expect("stack index out of bounds")
}
}
#[cfg(test)]
mod tests {
use std::sync::atomic::{AtomicBool, Ordering};
use crate::vm::stack::Stack;
#[derive(Debug)]
struct DropTracker(&'static AtomicBool);
impl Drop for DropTracker {
fn drop(&mut self) {
self.0.store(true, Ordering::Release);
}
}
#[test]
fn test_checked_slice() {
let mut s = Stack::<u32>::new(32);
s.push(1).unwrap();
s.push(2).unwrap();
s.push(3).unwrap();
s.push(4).unwrap();
s.push(5).unwrap();
// unbound..unbound
assert_eq!(s.checked_slice(..).unwrap(), &[1, 2, 3, 4, 5]);
// unbound..included
assert_eq!(s.checked_slice(..=3).unwrap(), &[1, 2, 3, 4]);
// unbound..excluded
assert_eq!(s.checked_slice(..3).unwrap(), &[1, 2, 3]);
// included..unbound
assert_eq!(s.checked_slice(2..).unwrap(), &[3, 4, 5]);
// included..included
assert_eq!(s.checked_slice(2..=3).unwrap(), &[3, 4]);
// included..excluded
assert_eq!(s.checked_slice(2..3).unwrap(), &[3]);
// Invalid
assert!(s.checked_slice(..10).is_none());
assert!(s.checked_slice(10..).is_none());
assert!(s.checked_slice(10..9).is_none());
}
#[test]
fn test_underflow() {
let mut s = Stack::<u32>::new(128);
assert!(s.pop().is_none());
}
#[test]
fn test_overflow() {
let mut s = Stack::<u32>::new(4);
for i in 0..4 {
s.push(i).unwrap();
}
assert_eq!(s.push(1234).unwrap_err(), 1234);
}
#[test]
fn test_no_drop_on_pop() {
static DROP0: AtomicBool = AtomicBool::new(false);
let mut s = Stack::<DropTracker>::new(4);
s.push(DropTracker(&DROP0)).unwrap();
assert!(!DROP0.load(Ordering::Acquire));
let v = s.pop().unwrap();
assert!(!DROP0.load(Ordering::Acquire));
drop(v);
assert!(DROP0.load(Ordering::Acquire));
}
#[test]
fn test_drop_on_set_pointer_lower() {
static DROP0: AtomicBool = AtomicBool::new(false);
static DROP1: AtomicBool = AtomicBool::new(false);
let mut s = Stack::<DropTracker>::new(4);
s.push(DropTracker(&DROP0)).unwrap();
s.push(DropTracker(&DROP1)).unwrap();
s.set_pointer(1);
assert!(!DROP0.load(Ordering::Acquire));
assert!(DROP1.load(Ordering::Acquire));
}
#[test]
fn test_stack_head() {
let mut s = Stack::<u32>::new(4);
assert!(s.head().is_none());
s.push(1234).unwrap();
assert_eq!(s.head().unwrap(), &1234);
}
}
+40
View File
@@ -0,0 +1,40 @@
use std::{
fmt,
ops::{Add, Not},
};
use crate::vm::value::NumberValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BooleanValue(pub bool);
impl From<bool> for BooleanValue {
fn from(value: bool) -> Self {
Self(value)
}
}
impl Not for BooleanValue {
type Output = Self;
fn not(self) -> Self::Output {
Self(!self.0)
}
}
impl Add for BooleanValue {
type Output = NumberValue;
fn add(self, rhs: Self) -> Self::Output {
NumberValue::Int(self.0 as i64 + rhs.0 as i64)
}
}
impl fmt::Display for BooleanValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
true => write!(f, "#T"),
false => write!(f, "#F"),
}
}
}
-16
View File
@@ -1,16 +0,0 @@
use std::fmt;
use crate::vm::module::ModuleRef;
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct BytecodeFunction {
pub module: ModuleRef,
pub required_count: usize,
pub address: usize,
}
impl fmt::Display for BytecodeFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<bytecode {:p}:{}>", self.module, self.address)
}
}
+35
View File
@@ -0,0 +1,35 @@
use std::{fmt, rc::Rc};
use crate::vm::{Value, value::BytecodeFunction};
#[derive(Debug, Clone, PartialEq)]
pub enum UpvalueValue {
Open(usize),
Closed(Box<Value>),
}
#[derive(Clone, PartialEq)]
pub struct ClosureValue {
pub function: Rc<BytecodeFunction>,
pub upvalues: Vec<usize>,
}
impl ClosureValue {
pub fn instruction_byte(&self, address: usize) -> Option<u8> {
self.function.instructions.get(address).copied()
}
}
impl fmt::Debug for ClosureValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ClosureValue")
.field("function", &self.function)
.finish_non_exhaustive()
}
}
impl fmt::Display for ClosureValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<closure {}@{:p}>", self.function.name(), self.function)
}
}
+1 -1
View File
@@ -2,7 +2,7 @@ use std::fmt;
use crate::vm::value::Value;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq)]
pub struct ConsCell(pub Value, pub Value);
impl ConsCell {
+77 -117
View File
@@ -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
);
+194
View File
@@ -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))
);
}
}
}
}
}
+28
View File
@@ -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)
}
}
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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()
)
}
}
+246
View File
@@ -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),
}
}
}
+6 -6
View File
@@ -1,21 +1,21 @@
use std::{fmt, ops::Deref, rc::Rc};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ValueString(Rc<str>);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct StringValue(Rc<str>);
impl From<&str> for ValueString {
impl From<&str> for StringValue {
fn from(value: &str) -> Self {
Self(value.into())
}
}
impl From<String> for ValueString {
impl From<String> for StringValue {
fn from(value: String) -> Self {
Self(value.into())
}
}
impl Deref for ValueString {
impl Deref for StringValue {
type Target = str;
fn deref(&self) -> &Self::Target {
@@ -23,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
View File
@@ -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
View File
@@ -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)
// ])
// );
// }