Compare commits

...

4 Commits

Author SHA1 Message Date
alnyan db5b1dba7b WIP: I/O 2026-05-09 00:30:35 +03:00
alnyan 01012ae211 Implement unwinding mechanism, repl example 2026-05-08 23:19:17 +03:00
alnyan aa7e371747 Implement loop/return forms 2026-05-08 17:43:12 +03:00
alnyan aa0026fa45 Tracing options, while loop 2026-05-08 17:16:32 +03:00
29 changed files with 964 additions and 142 deletions
+4
View File
@@ -0,0 +1,4 @@
;; vi:ft=lisp:sw=2:ts=2
(print (length *args*))
(print *args*)
+17 -15
View File
@@ -7,18 +7,20 @@
)
)
(print 1 "\t" (factorial 1))
(print 2 "\t" (factorial 2))
(print 3 "\t" (factorial 3))
(print 4 "\t" (factorial 4))
(print 5 "\t" (factorial 5))
(print 6 "\t" (factorial 6))
(print 7 "\t" (factorial 7))
(print 8 "\t" (factorial 8))
(print 9 "\t" (factorial 9))
(print 10 "\t" (factorial 10))
(print 11 "\t" (factorial 11))
(print 12 "\t" (factorial 12))
(print 13 "\t" (factorial 13))
(print 14 "\t" (factorial 14))
(print 15 "\t" (factorial 15))
(defun loop-factorial (x)
(let (i 0 acc 1)
(while (< i x)
(setq i (+ i 1))
(setq acc (* acc i))
)
acc
)
)
;; TODO for loops?
(let (i 0)
(while (<= i 15)
(print i "\t" (factorial i) "\t" (loop-factorial i))
(setq i (+ i 1))
)
)
+5
View File
@@ -0,0 +1,5 @@
;; vi:ft=lisp:sw=2:ts=2
(let (f (fopen (car *args*)))
(fwrite (fread f))
)
+32
View File
@@ -0,0 +1,32 @@
;; 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)
)
)
i
))
;; while, broken prematurely
(setq looped-while (let (i 0)
(while (< i 20)
(setq i (+ i 1))
(if (= i 10) (return))
)
i
))
;; while, broken by condition
(setq looped-while-full (let (i 0)
(while (< i 10)
(setq i (+ i 1))
)
i
))
;; All loops execute the same count of times
(assert (= looped-loop looped-while looped-while-full))
+36
View File
@@ -0,0 +1,36 @@
;; vi:ft=lisp:sw=2:ts=2
(defun cadr (x) (car (cdr x)))
(defun map-ok-err (f-ok f-err result)
(if (= (car result) 'ok)
`(ok ,(f-ok (cadr result)))
`(err ,(f-err (cadr result)))
)
)
(defun map-ok (f-ok result) (map-ok-err f-ok identity result))
(defun map-err (f-err result) (map-ok-err identity f-err result))
(defun repl-print (value)
(print "==>" value)
)
(defun repl-eval-print (expression)
(map-ok-err repl-print repl-eval-error (unquote (eval expression)))
)
(defun repl-eval-error (error)
(print "Evaluation error:")
(print error)
)
(defun repl-read-error (error)
(print "Parse error:")
(print error)
)
(loop
(let (expression (read))
(if expression NIL (return))
(setq expression (unquote expression))
(map-ok-err repl-eval-print repl-read-error expression)
)
)
+25
View File
@@ -0,0 +1,25 @@
;; vi:ft=lisp:sw=2:ts=2
(defun replace-argument (x) (let (x 1) x))
(defun replace-let ()
(let (x 1)
(setq x 2) ;; mutate local x
x
)
)
(defun reassignment-in-a-loop (iterations)
(let (x 0 y 1)
(while (< x iterations)
(setq y (* y 2))
(setq x (+ x 1))
)
y
)
)
(assert (= (replace-argument 2) 1))
(assert (= (replace-argument 3) 1))
(assert (= (replace-let) 2))
(assert (= (reassignment-in-a-loop 4) 16))
+192 -46
View File
@@ -8,7 +8,8 @@ use crate::{
module::CompilationModule,
syntax::{
CallExpression, CondExpression, DefmacroExpression, DefunExpression, Expression,
FunctionBody, IfExpression, LambdaExpression, LetExpression, SetqExpression,
FunctionBody, IfExpression, LambdaExpression, LetExpression, LoopExpression,
PrognExpression, SetqExpression, VectorExpression, WhileExpression,
},
value::{BuiltinFunction, CompileConstant, CompileValue},
},
@@ -136,13 +137,19 @@ impl FunctionBlock {
self.labels.insert(label, self.instructions.len());
}
pub fn resolve_labels(self) -> CompiledFunction {
pub fn resolve_labels(self, trace: bool) -> CompiledFunction {
let mut instructions = vec![];
// eprintln!("RESOLVE LABELS");
if trace {
eprintln!("Instruction resolution:");
}
for (function_offset, emitted) in self.instructions.into_iter().enumerate() {
match emitted {
Emitted::Instruction(instruction) => {
// eprintln!("{function_offset}: {instruction:?}");
if trace {
eprintln!("{function_offset}: {instruction:?}");
}
instructions.push(instruction);
}
Emitted::Branch(label) => {
@@ -151,9 +158,11 @@ impl FunctionBlock {
todo!()
}
let diff = address.checked_signed_diff(function_offset).expect("TODO");
// eprintln!(
// "{function_offset}: branch to label {label} (@ {address}) -> {diff:+}"
// );
if trace {
eprintln!(
"{function_offset}: Branch: label {label} (@ {address}) -> {diff:+}"
);
}
let offset = U::from_signed(diff as i64).expect("TODO");
instructions.push(Instruction::Branch(offset));
}
@@ -163,7 +172,11 @@ impl FunctionBlock {
todo!()
}
let diff = address.checked_signed_diff(function_offset).expect("TODO");
// eprintln!("{function_offset}: jump to label {label} (@ {address}) -> {diff:+}");
if trace {
eprintln!(
"{function_offset}: jump to label {label} (@ {address}) -> {diff:+}"
);
}
let offset = U::from_signed(diff as i64).expect("TODO");
instructions.push(Instruction::Jump(offset));
}
@@ -185,9 +198,9 @@ impl FunctionBlock {
) -> Result<(), CompileError> {
let mut local = LocalBlock::root(self, module);
for statement in &body.head {
local.compile_statement(statement)?;
local.compile_statement(statement, None)?;
}
let value = local.compile_expression(&body.tail)?;
let value = local.compile_expression(&body.tail, None)?;
local.compile_return(value)?;
Ok(())
}
@@ -311,8 +324,12 @@ impl<'a> LocalBlock<'a> {
Ok(())
}
pub fn compile_statement(&mut self, expression: &Rc<Expression>) -> Result<(), CompileError> {
let value = self.compile_expression(expression)?;
pub fn compile_statement(
&mut self,
expression: &Rc<Expression>,
break_label: Option<u32>,
) -> Result<(), CompileError> {
let value = self.compile_expression(expression, break_label)?;
self.compile_drop(value)?;
Ok(())
}
@@ -366,10 +383,11 @@ impl<'a> LocalBlock<'a> {
&mut self,
math: MathInstruction,
args: &[Rc<Expression>],
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
// TODO optimize
for arg in args.iter().rev() {
let arg = self.compile_expression(arg)?;
let arg = self.compile_expression(arg, break_label)?;
self.compile_push(arg)?;
}
let Some(count) = U::new(args.len() as u32) else {
@@ -383,18 +401,25 @@ impl<'a> LocalBlock<'a> {
&mut self,
builtin: BuiltinFunction,
args: &[Rc<Expression>],
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
match builtin {
BuiltinFunction::Math(math) => self.compile_builtin_math_generic(math, args),
BuiltinFunction::Math(math) => {
self.compile_builtin_math_generic(math, args, break_label)
}
}
}
fn compile_call(&mut self, call: &CallExpression) -> Result<CompileValue, CompileError> {
fn compile_call(
&mut self,
call: &CallExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
match call.callee.as_ref() {
Expression::Identifier(identifier)
if let Some(builtin) = BuiltinFunction::from_identifier(identifier.as_ref()) =>
{
self.compile_call_builtin(builtin, &call.arguments)
self.compile_call_builtin(builtin, &call.arguments, break_label)
}
_ => {
// Push arguments in reverse order
@@ -402,10 +427,10 @@ impl<'a> LocalBlock<'a> {
todo!();
};
for arg in call.arguments.iter().rev() {
let arg = self.compile_expression(arg)?;
let arg = self.compile_expression(arg, break_label)?;
self.compile_push(arg)?;
}
let callee = self.compile_expression(&call.callee)?;
let callee = self.compile_expression(&call.callee, break_label)?;
self.compile_push(callee)?;
self.function.emit(Instruction::Call(count));
Ok(CompileValue::Stack)
@@ -413,21 +438,25 @@ impl<'a> LocalBlock<'a> {
}
}
fn compile_if(&mut self, condition: &IfExpression) -> Result<CompileValue, CompileError> {
fn compile_if(
&mut self,
condition: &IfExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
let label_false = self.function.new_label();
let label_end = self.function.new_label();
// Emit condition
let condition_value = self.compile_expression(&condition.condition)?;
let condition_value = self.compile_expression(&condition.condition, break_label)?;
self.compile_push(condition_value)?;
self.function.emit(Emitted::Branch(label_false));
// True branch
let true_value = self.compile_expression(&condition.if_true)?;
let true_value = self.compile_expression(&condition.if_true, break_label)?;
self.compile_push(true_value)?;
self.function.emit(Emitted::Jump(label_end));
// False branch
self.function.adjust_label(label_false);
let false_value = if let Some(if_false) = condition.if_false.as_ref() {
self.compile_expression(if_false)?
self.compile_expression(if_false, break_label)?
} else {
CompileValue::Nil
};
@@ -436,16 +465,20 @@ impl<'a> LocalBlock<'a> {
Ok(CompileValue::Stack)
}
fn compile_cond(&mut self, condition: &CondExpression) -> Result<CompileValue, CompileError> {
fn compile_cond(
&mut self,
condition: &CondExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
let label_end = self.function.new_label();
for arm in condition.arms.iter() {
let label_condition_end = self.function.new_label();
let condition_value = self.compile_expression(&arm.condition)?;
let condition_value = self.compile_expression(&arm.condition, break_label)?;
self.compile_push(condition_value)?;
self.function.emit(Emitted::Branch(label_condition_end));
// Condition true
let then_value = self.compile_expression(&arm.then)?;
let then_value = self.compile_expression(&arm.then, break_label)?;
self.compile_push(then_value)?;
self.function.emit(Emitted::Jump(label_end));
// Condition false
@@ -454,7 +487,7 @@ impl<'a> LocalBlock<'a> {
// Otherwise branch
let otherwise_value = if let Some(othwerise_arm) = condition.otherwise_arm.as_ref() {
self.compile_expression(othwerise_arm)?
self.compile_expression(othwerise_arm, break_label)?
} else {
CompileValue::Nil
};
@@ -464,11 +497,61 @@ impl<'a> LocalBlock<'a> {
Ok(CompileValue::Stack)
}
fn compile_let(&mut self, binding: &LetExpression) -> Result<CompileValue, CompileError> {
fn compile_while(
&mut self,
cloop: &WhileExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
let label_start = self.function.new_label();
let label_end = self.function.new_label();
self.function.adjust_label(label_start);
// Condition
let condition_value = self.compile_expression(&cloop.condition, break_label)?;
self.compile_push(condition_value)?;
self.function.emit(Emitted::Branch(label_end));
// Body
for expression in &cloop.body.head {
self.compile_statement(expression, Some(label_end))?;
}
self.compile_statement(&cloop.body.tail, Some(label_end))?;
// Jump back
self.function.emit(Emitted::Jump(label_start));
self.function.adjust_label(label_end);
Ok(CompileValue::Nil)
}
fn compile_loop(&mut self, cloop: &LoopExpression) -> Result<CompileValue, CompileError> {
let label_start = self.function.new_label();
let label_end = self.function.new_label();
self.function.adjust_label(label_start);
for expression in &cloop.body.head {
self.compile_statement(expression, Some(label_end))?;
}
self.compile_statement(&cloop.body.tail, Some(label_end))?;
self.function.emit(Emitted::Jump(label_start));
self.function.adjust_label(label_end);
Ok(CompileValue::Nil)
}
fn compile_let(
&mut self,
binding: &LetExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
self.function.push_local_scope();
let mut indices = vec![];
for pair in &binding.bindings {
let value = self.compile_expression(&pair.value)?;
let value = self.compile_expression(&pair.value, break_label)?;
let index = self.compile_set_local(&pair.identifier, value, binding.sequential)?;
indices.push(index);
}
@@ -480,9 +563,9 @@ impl<'a> LocalBlock<'a> {
}
}
for expr in &binding.body.head {
self.compile_statement(expr)?;
self.compile_statement(expr, break_label)?;
}
let value = self.compile_expression(&binding.body.tail)?;
let value = self.compile_expression(&binding.body.tail, break_label)?;
if let CompileValue::Local(_) = value {
self.compile_push(value)?;
self.function.pop_local_scope();
@@ -493,10 +576,23 @@ impl<'a> LocalBlock<'a> {
}
}
fn compile_setq(&mut self, setq: &SetqExpression) -> Result<CompileValue, CompileError> {
fn compile_setq(
&mut self,
setq: &SetqExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
for pair in setq.pairs.iter() {
let value = self.compile_expression(&pair.value)?;
self.compile_set_global(&pair.identifier, value)?;
let value = self.compile_expression(&pair.value, break_label)?;
if let Some(index) = self
.function
.local_scope_ref()
.and_then(|scope| scope.get(&pair.identifier))
{
self.compile_push(value)?;
self.function.emit(Instruction::SetLocal(index));
} else {
self.compile_set_global(&pair.identifier, value)?;
}
}
Ok(CompileValue::Nil)
}
@@ -505,9 +601,51 @@ impl<'a> LocalBlock<'a> {
Ok(CompileValue::Quote(value.clone()))
}
fn compile_progn(
&mut self,
value: &PrognExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
for expression in &value.body.head {
self.compile_statement(expression, break_label)?;
}
self.compile_statement(&value.body.tail, break_label)?;
Ok(CompileValue::Nil)
}
fn compile_call_return(
&mut self,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
if let Some(break_label) = break_label {
self.function.emit(Emitted::Jump(break_label));
} else {
todo!("return outside a loop")
}
Ok(CompileValue::Nil)
}
fn compile_vector(
&mut self,
vector: &VectorExpression,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
for expression in vector.elements.iter().rev() {
let value = self.compile_expression(expression, break_label)?;
self.compile_push(value)?;
}
self.compile_push(CompileValue::Global("vector".into()))?;
let Some(count) = U::new(vector.elements.len() as u32) else {
todo!()
};
self.function.emit(Instruction::Call(count));
Ok(CompileValue::Stack)
}
pub fn compile_expression(
&mut self,
expression: &Rc<Expression>,
break_label: Option<u32>,
) -> Result<CompileValue, CompileError> {
match expression.as_ref() {
Expression::Nil => Ok(CompileValue::Nil),
@@ -517,13 +655,18 @@ impl<'a> LocalBlock<'a> {
Expression::Identifier(identifier) => self.compile_identifier(identifier),
Expression::Lambda(lambda) => self.compile_lambda(lambda),
Expression::Defun(defun) => self.compile_defun(defun),
Expression::Call(call) => self.compile_call(call),
Expression::If(condition) => self.compile_if(condition),
Expression::Cond(condition) => self.compile_cond(condition),
Expression::Let(binding) => self.compile_let(binding),
Expression::Setq(setq) => self.compile_setq(setq),
Expression::Vector(vector) => self.compile_vector(vector, break_label),
Expression::Call(call) => self.compile_call(call, break_label),
Expression::If(condition) => self.compile_if(condition, break_label),
Expression::Cond(condition) => self.compile_cond(condition, break_label),
Expression::Let(binding) => self.compile_let(binding, break_label),
Expression::Setq(setq) => self.compile_setq(setq, break_label),
Expression::Defmacro(defmacro) => self.compile_defmacro(defmacro),
Expression::Quote(quote) => self.compile_quote(quote),
Expression::While(cloop) => self.compile_while(cloop, break_label),
Expression::Loop(cloop) => self.compile_loop(cloop),
Expression::Progn(progn) => self.compile_progn(progn, break_label),
Expression::Return => self.compile_call_return(break_label),
Expression::SyntaxError(_) => unreachable!(),
}
@@ -599,8 +742,11 @@ mod tests {
max_local_index: 0,
};
let mut local = LocalBlock::root(&mut function, &mut module);
let value = local.compile_expression(&Rc::new(expression)).unwrap();
(module, function.resolve_labels(), value)
let value = local
.compile_expression(&Rc::new(expression), None)
.unwrap();
let trace = module.options.trace_compile;
(module, function.resolve_labels(trace), value)
}
#[test]
@@ -647,9 +793,9 @@ mod tests {
&f.instructions,
&[
Instruction::PushBool(false), // 0
Instruction::Branch(U::truncate(4)), // 1
Instruction::Branch(U::truncate(3)), // 1
Instruction::PushInteger(U::truncate(1)), // 2
Instruction::Jump(U::truncate(5)), // 3
Instruction::Jump(U::truncate(2)), // 3
Instruction::PushInteger(U::truncate(2)) // 4
// 5
]
@@ -676,13 +822,13 @@ mod tests {
&f.instructions,
&[
Instruction::PushBool(false), // 0
Instruction::Branch(U::truncate(4)), // 1
Instruction::Branch(U::truncate(3)), // 1
Instruction::PushInteger(U::truncate(1)), // 2
Instruction::Jump(U::truncate(9)), // 3
Instruction::Jump(U::truncate(6)), // 3
Instruction::PushBool(true), // 4
Instruction::Branch(U::truncate(8)), // 5
Instruction::Branch(U::truncate(3)), // 5
Instruction::PushInteger(U::truncate(2)), // 6
Instruction::Jump(U::truncate(9)), // 7
Instruction::Jump(U::truncate(2)), // 7
Instruction::PushInteger(U::truncate(3)), // 8
// 9
]
+5
View File
@@ -13,3 +13,8 @@ pub use syntax::{
CallExpression, ExpectedWhat, ExpectedWhere, Expression, FunctionBody, LambdaExpression,
ParseError, ParseErrorKind,
};
#[derive(Default, Clone)]
pub struct CompileOptions {
pub trace_compile: bool,
}
+14 -1
View File
@@ -2,6 +2,7 @@ use std::{collections::HashMap, rc::Rc};
use crate::{
compile::{
CompileOptions,
block::{CompiledFunction, FunctionBlock},
error::CompileError,
function::FunctionSignature,
@@ -17,14 +18,24 @@ use crate::{
#[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);
}
@@ -49,7 +60,7 @@ impl CompilationModule {
self.local_function_index += 1;
let mut function = FunctionBlock::new(signature);
function.compile_body(self, body)?;
let function = function.resolve_labels();
let function = function.resolve_labels(self.options.trace_compile);
self.local_functions.insert(index, function);
if root {
self.root = Some(index);
@@ -61,6 +72,7 @@ impl CompilationModule {
// 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());
@@ -90,6 +102,7 @@ impl CompilationModule {
.collect();
Ok(Module {
name,
constants,
instructions,
entry,
+3 -3
View File
@@ -64,9 +64,9 @@ impl IfExpression {
Some(Expression::parse_inner(if_false))
};
Ok(Self {
condition: condition.into(),
if_true: if_true.into(),
if_false: if_false.map(Into::into),
condition,
if_true,
if_false,
})
}
}
+18
View File
@@ -21,6 +21,11 @@ pub struct DefunExpression {
pub body: FunctionBody,
}
#[derive(Debug, PartialEq)]
pub struct PrognExpression {
pub body: FunctionBody,
}
impl FunctionSignature {
pub(super) fn parse(mut value: &Value, input: &Value) -> Result<Self, ParseError> {
enum Mode {
@@ -171,6 +176,13 @@ impl FunctionBody {
}
}
impl PrognExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let body = FunctionBody::parse(value, input, Keyword::Progn)?;
Ok(Self { body })
}
}
impl DefunExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(value) = value else {
@@ -232,6 +244,12 @@ impl CollectErrors<ParseError> for DefunExpression {
}
}
impl CollectErrors<ParseError> for PrognExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
}
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
+59
View File
@@ -0,0 +1,59 @@
use std::rc::Rc;
use crate::{
compile::{
ExpectedWhat, ExpectedWhere, Expression, FunctionBody, ParseError, ParseErrorKind,
syntax::CollectErrors,
},
vm::value::{ConsCell, Keyword, Value},
};
#[derive(Debug, PartialEq)]
pub struct WhileExpression {
pub condition: Rc<Expression>,
pub body: FunctionBody,
}
#[derive(Debug, PartialEq)]
pub struct LoopExpression {
pub body: FunctionBody,
}
impl WhileExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let Value::Cons(cons) = value else {
return Err(ParseError {
input: input.clone(),
error: ParseErrorKind::Expected(
ExpectedWhat::ProperList,
ExpectedWhere::AfterKeyword(Keyword::While),
),
});
};
let ConsCell(car, cdr) = cons.as_ref();
let condition = Expression::parse_inner(car);
let body = FunctionBody::parse(cdr, input, Keyword::While)?;
Ok(Self { condition, body })
}
}
impl LoopExpression {
pub(super) fn parse(value: &Value, input: &Value) -> Result<Self, ParseError> {
let body = FunctionBody::parse(value, input, Keyword::Loop)?;
Ok(Self { body })
}
}
impl CollectErrors<ParseError> for WhileExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
let a = self.condition.collect_errors(errors);
let b = self.body.collect_errors(errors);
a | b
}
}
impl CollectErrors<ParseError> for LoopExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
self.body.collect_errors(errors)
}
}
+27 -1
View File
@@ -8,7 +8,9 @@ mod condition;
mod error;
mod function;
mod lambda;
mod loops;
mod macros;
mod vector;
pub use binding::*;
pub use call::*;
@@ -16,7 +18,9 @@ pub use condition::*;
pub use error::*;
pub use function::*;
pub use lambda::*;
pub use loops::*;
pub use macros::*;
pub use vector::*;
#[derive(Debug, PartialEq)]
pub enum Expression {
@@ -35,6 +39,11 @@ pub enum Expression {
Defmacro(DefmacroExpression),
SyntaxError(ParseError),
Quote(Rc<Value>),
While(WhileExpression),
Loop(LoopExpression),
Progn(PrognExpression),
Vector(VectorExpression),
Return,
}
impl Expression {
@@ -61,7 +70,6 @@ impl Expression {
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) => todo!(),
Value::Identifier(value) => Rc::new(Self::Identifier(value.clone())),
Value::Quasi(_value) => todo!("{value}"),
Value::Unquote(_value) => todo!("Unquote {_value}"),
@@ -97,9 +105,22 @@ impl Expression {
};
Rc::new(Self::Quote(value.into()))
}
Value::Keyword(Keyword::Progn) => {
Self::map_or(PrognExpression::parse(cdr, value), Expression::Progn)
}
Value::Keyword(Keyword::Loop) => {
Self::map_or(LoopExpression::parse(cdr, value), Expression::Loop)
}
Value::Keyword(Keyword::While) => {
Self::map_or(WhileExpression::parse(cdr, value), Expression::While)
}
Value::Keyword(Keyword::Return) => Rc::new(Self::Return),
_ => Self::map_or(CallExpression::parse(cons, value), Expression::Call),
}
}
Value::Vector(vector) => {
Rc::new(Self::Vector(VectorExpression::parse(&vector.borrow())))
}
Value::Keyword(_) => todo!(),
Value::NativeFunction(_) | Value::BytecodeFunction(_) | Value::OpaqueValue(_) => {
todo!()
@@ -123,7 +144,12 @@ impl CollectErrors<ParseError> for Expression {
Self::Let(let_) => let_.collect_errors(errors),
Self::Setq(setq) => setq.collect_errors(errors),
Self::Defmacro(defmacro) => defmacro.collect_errors(errors),
Self::While(cloop) => cloop.collect_errors(errors),
Self::Loop(cloop) => cloop.collect_errors(errors),
Self::Progn(progn) => progn.collect_errors(errors),
Self::Vector(vector) => vector.collect_errors(errors),
Self::Nil
| Self::Return
| Self::IntegerLiteral(_)
| Self::Identifier(_)
| Self::BooleanLiteral(_)
+29
View File
@@ -0,0 +1,29 @@
use std::rc::Rc;
use crate::{
compile::{Expression, ParseError, syntax::CollectErrors},
vm::value::Value,
};
#[derive(Debug, PartialEq)]
pub struct VectorExpression {
pub elements: Vec<Rc<Expression>>,
}
impl VectorExpression {
pub fn parse(values: &[Value]) -> Self {
Self {
elements: values.iter().map(Expression::parse_inner).collect(),
}
}
}
impl CollectErrors<ParseError> for VectorExpression {
fn collect_errors(&self, errors: &mut Vec<ParseError>) -> bool {
let mut r = false;
for expr in self.elements.iter() {
r |= expr.collect_errors(errors);
}
r
}
}
+66 -10
View File
@@ -3,11 +3,12 @@ use std::{
io::{self, BufReader},
path::{Path, PathBuf},
process::ExitCode,
str::FromStr,
};
use clap::Parser;
use lysp::{
compile::{CompileError, ParseError},
compile::{CompileError, CompileOptions, ParseError},
error::{EvalError, MachineErrorKind},
read::{InteractiveReader, ModuleReader, read},
util::Either,
@@ -25,9 +26,38 @@ enum Error {
Printed,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Trace {
Compile,
Execute,
Call,
Return,
}
impl FromStr for Trace {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"compile" => Ok(Self::Compile),
"execute" => Ok(Self::Execute),
"call" => Ok(Self::Call),
"return" => Ok(Self::Return),
_ => Err(format!("Unknown trace flag: {s:?}")),
}
}
}
#[derive(Debug, Parser)]
struct Args {
#[clap(
short = 'T',
long = "trace",
help = "Enable tracing of execution/compilation steps"
)]
trace: Vec<Trace>,
module: Option<PathBuf>,
arguments: Vec<String>,
}
fn print_syntax_errors(errors: &[ParseError]) {
@@ -59,7 +89,7 @@ fn handle_eval_error(value: Option<&Value>, input: EvalError) -> Error {
eprintln!(":: {}", error.error);
eprintln!();
if let Some(ip) = error.ip.as_ref() {
ip.module.dump(Some(ip.address), 8);
ip.module.dump(Some(ip.address), 8, 2);
}
}
EvalError::Compile(CompileError::Parse(errors)) => {
@@ -88,8 +118,13 @@ fn handle_module_error(input: Either<EvalError, Vec<ParseError>>) -> Error {
}
}
fn eval(vm: &mut Machine, env: &mut Environment, value: Value) -> Option<Value> {
let result = vm.eval_value(env, value.clone());
fn eval(
options: &CompileOptions,
vm: &mut Machine,
env: &mut Environment,
value: Value,
) -> Option<Value> {
let result = vm.eval_value(options.clone(), env, value.clone(), false);
match result {
Ok(r) => Some(r),
Err(error) => {
@@ -99,7 +134,11 @@ fn eval(vm: &mut Machine, env: &mut Environment, value: Value) -> Option<Value>
}
}
fn run_interactive(vm: &mut Machine, env: &mut Environment) -> Result<(), Error> {
fn run_interactive(
compile_options: &CompileOptions,
vm: &mut Machine,
env: &mut Environment,
) -> Result<(), Error> {
let mut reader = InteractiveReader::new("> ", ">> ");
loop {
@@ -111,7 +150,7 @@ fn run_interactive(vm: &mut Machine, env: &mut Environment) -> Result<(), Error>
continue;
}
};
if let Some(value) = eval(vm, env, value) {
if let Some(value) = eval(compile_options, vm, env, value) {
println!("== {value}");
} else {
reader.reset();
@@ -122,19 +161,21 @@ fn run_interactive(vm: &mut Machine, env: &mut Environment) -> Result<(), Error>
}
fn run_module<P: AsRef<Path>>(
compile_options: &CompileOptions,
vm: &mut Machine,
env: &mut Environment,
path: P,
) -> Result<(), Error> {
let path = path.as_ref();
let name = format!("{}", path.display());
let reader = BufReader::new(File::open(path)?);
let module_reader = ModuleReader::new(reader);
let module = match module_reader.compile(env) {
let module = match module_reader.compile(Some(name.into()), compile_options, env) {
Ok(module) => module,
Err(error) => return Err(handle_module_error(error)),
};
match vm.eval_module(env, module) {
match vm.eval_module(env, module, false) {
Ok(_) => Ok(()),
Err(error) => Err(handle_eval_error(None, error)),
}
@@ -143,11 +184,26 @@ fn run_module<P: AsRef<Path>>(
fn main() -> ExitCode {
let args = Args::parse();
let mut vm = Machine::default();
let compile_options = CompileOptions {
trace_compile: args.trace.contains(&Trace::Compile),
};
vm.trace_instructions = args.trace.contains(&Trace::Execute);
vm.trace_calls = args.trace.contains(&Trace::Call);
vm.trace_returns = args.trace.contains(&Trace::Return);
let mut env = Environment::default();
prelude::load(&mut env);
let mut arguments = vec![];
if let Some(script) = args.module.as_ref() {
arguments.push(format!("{}", script.display()));
}
arguments.extend(args.arguments);
env.set_global_value(
"*args*",
Value::list_or_nil(arguments.into_iter().map(|arg| Value::String(arg.into()))),
);
let result = match args.module.as_ref() {
Some(module) => run_module(&mut vm, &mut env, module),
None => run_interactive(&mut vm, &mut env),
Some(module) => run_module(&compile_options, &mut vm, &mut env, module),
None => run_interactive(&compile_options, &mut vm, &mut env),
};
match result {
Ok(()) => ExitCode::SUCCESS,
+13 -1
View File
@@ -14,7 +14,7 @@ use nom::{
sequence::{delimited, preceded},
};
use crate::vm::value::{Keyword, Value};
use crate::vm::value::{Keyword, Value, Vector};
struct IdentifierHead;
struct IdentifierTail;
@@ -179,6 +179,17 @@ fn parse_list_or_nil(input: &str) -> IResult<&str, Value> {
.parse(input)
}
fn parse_vector(input: &str) -> IResult<&str, Value> {
delimited(
char('['),
many0(preceded(skip_comment_and_whitespace, parse_value)),
preceded(skip_comment_and_whitespace, char(']')),
)
.map(Vector::from)
.map(Value::from)
.parse(input)
}
fn parse_boolean(input: &str) -> IResult<&str, Value> {
map(
alt((
@@ -298,6 +309,7 @@ fn parse_quote(input: &str) -> IResult<&str, Value> {
pub fn parse_value(input: &str) -> IResult<&str, Value> {
alt((
parse_list_or_nil,
parse_vector,
parse_boolean,
parse_quote,
parse_quasi,
+9 -4
View File
@@ -5,7 +5,9 @@ use std::{
};
use crate::{
compile::{CompilationModule, Expression, FunctionBody, FunctionSignature, ParseError},
compile::{
CompilationModule, CompileOptions, Expression, FunctionBody, FunctionSignature, ParseError,
},
error::EvalError,
parse::{self, parse_value},
util::Either,
@@ -106,6 +108,7 @@ impl<R: BufRead> ModuleReader<R> {
pub fn read_expression(
&mut self,
options: &CompileOptions,
env: &mut Environment,
) -> Result<Option<Rc<Expression>>, Either<EvalError, Vec<ParseError>>> {
loop {
@@ -117,7 +120,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(env, value)
.eval_value(options.clone(), env, value, false)
.map_err(Either::Left)?;
continue;
}
@@ -127,9 +130,11 @@ impl<R: BufRead> ModuleReader<R> {
pub fn compile(
mut self,
module_name: Option<Rc<str>>,
options: &CompileOptions,
env: &mut Environment,
) -> Result<ModuleRef, Either<EvalError, Vec<ParseError>>> {
let mut module = CompilationModule::default();
let mut module = CompilationModule::new(module_name, options.clone());
let mut body = FunctionBody {
head: vec![],
tail: Rc::new(Expression::Nil),
@@ -138,7 +143,7 @@ impl<R: BufRead> ModuleReader<R> {
let mut syntax_errors = vec![];
loop {
let expression = match self.read_expression(env) {
let expression = match self.read_expression(options, env) {
Ok(Some(expression)) => expression,
Ok(None) => break,
Err(Either::Left(error)) => return Err(Either::Left(error)),
+8 -2
View File
@@ -91,7 +91,7 @@ pub enum Instruction {
}
pub type ConstantId = U<12>;
pub type FunctionOffset = U<12>;
pub type FunctionOffset = U<10>;
pub type LocalId = U<8>;
pub type ArgumentId = U<6>;
pub type ArgumentCount = U<6>;
@@ -222,7 +222,7 @@ impl TryFrom<u32> for Instruction {
#[cfg(test)]
mod tests {
use crate::vm::instruction::U;
use crate::vm::instruction::{FunctionOffset, U};
#[test]
fn test_u_convert() {
@@ -238,4 +238,10 @@ mod tests {
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);
}
}
+105 -19
View File
@@ -1,6 +1,7 @@
use std::{collections::HashMap, fmt};
use crate::{
compile::CompileOptions,
error::{EvalError, MachineError, MachineErrorKind},
vm::{
env::Environment,
@@ -32,6 +33,8 @@ pub struct Machine {
value_stack: Stack<Value>,
pub call_stack: Stack<CallFrame>,
pub trace_instructions: bool,
pub trace_calls: bool,
pub trace_returns: bool,
// Top-level locals
locals: HashMap<u32, Value>,
}
@@ -52,6 +55,8 @@ impl Default for Machine {
locals: HashMap::new(),
trace_instructions: false,
trace_calls: false,
trace_returns: false,
}
}
}
@@ -122,10 +127,20 @@ impl Machine {
}),
locals: HashMap::new(),
};
let entry_ip = InstructionPointer { module, address };
if self.trace_calls {
eprintln!("TRACE: Call bytecode function");
if let Some(source_ip) = self.ip.as_ref() {
eprintln!("TRACE: From {source_ip}");
} else {
eprintln!("TRACE: From <undefined>");
}
eprintln!("TRACE: To {entry_ip}");
}
if self.call_stack.push(frame).is_err() {
return Err(self.error_at_ip(MachineErrorKind::CallStackOverflow));
}
self.ip = Some(InstructionPointer { module, address });
self.ip = Some(entry_ip);
Ok(())
}
@@ -186,10 +201,26 @@ impl Machine {
fn execute_return(&mut self) -> Result<ExecutionEvent, MachineError> {
let ip = self.ip.clone().unwrap();
if self.trace_returns {
eprintln!("TRACE: Return");
eprintln!("TRACE: From {ip}");
ip.module.dump(Some(ip.address), 4, 0);
}
if let Some(frame) = self.call_stack.pop() {
if self.trace_returns {
if let Some(target_ip) = frame.return_address.as_ref() {
eprintln!("TRACE: To {target_ip}");
} else {
eprintln!("TRACE: To <undefined>");
}
}
self.ip = frame.return_address;
Ok(frame.event)
} else {
if self.trace_returns {
eprintln!("TRACE: To <undefined>");
}
self.ip = None;
Ok(ExecutionEvent::ModuleExit(ip.module))
}
@@ -369,6 +400,36 @@ impl Machine {
eprintln!();
}
fn unwind(&mut self, until: ExecutionEvent) {
if self.trace_returns {
eprintln!("TRACE: Begin unwind");
if let Some(ip) = self.ip.as_ref() {
eprintln!("TRACE: <- {ip}");
} else {
eprintln!("TRACE: <- <undefined>");
}
}
let mut ip = self.ip.clone();
while let Some(frame) = self.call_stack.pop() {
if self.trace_returns {
eprintln!("TRACE: Unwind frame:");
if let Some(ip) = frame.return_address.as_ref() {
eprintln!("TRACE: -> {ip}");
} else {
eprintln!("TRACE: -> <undefined>");
}
}
ip = frame.return_address;
if frame.event == until {
break;
}
}
self.ip = ip;
if self.trace_returns {
eprintln!("TRACE: Finished unwind");
}
}
pub fn execute_next(
&mut self,
environment: &mut Environment,
@@ -468,23 +529,38 @@ impl Machine {
Ok(value)
}
pub fn load_module(&mut self, module: ModuleRef) -> Result<ModuleRef, MachineError> {
pub fn load_module(
&mut self,
module: ModuleRef,
advance_on_return: bool,
) -> Result<ModuleRef, MachineError> {
let entry = module.entry();
let entry_ip = InstructionPointer {
module: module.clone(),
address: entry,
};
if let Some(ip) = self.ip.clone()
&& self
.call_stack
.push(CallFrame {
arguments: vec![],
return_address: Some(ip),
event: ExecutionEvent::ModuleExit(module.clone()),
locals: HashMap::new(),
})
.is_err()
{
let entry_frame = CallFrame {
arguments: vec![],
event: ExecutionEvent::ModuleExit(module.clone()),
locals: HashMap::new(),
return_address: self.ip.clone().map(|ip| InstructionPointer {
module: ip.module,
address: ip.address + advance_on_return as usize,
}),
};
if self.trace_calls {
eprintln!("TRACE: Enter module");
if let Some(source_ip) = self.ip.as_ref() {
eprintln!("TRACE: From {source_ip}");
} else {
eprintln!("TRACE: From <undefined>");
}
eprintln!("TRACE: To {entry_ip}");
}
if self.call_stack.push(entry_frame).is_err() {
return Err(self.error_at_ip(MachineErrorKind::CallStackOverflow));
}
self.ip = Some(entry_ip);
@@ -495,8 +571,9 @@ impl Machine {
&mut self,
environment: &mut Environment,
module: ModuleRef,
advance_on_return: bool,
) -> Result<Value, EvalError> {
let module = match self.load_module(module) {
let module = match self.load_module(module, advance_on_return) {
Ok(module) => module,
Err(error) => return Err(EvalError::Machine(error)),
};
@@ -504,7 +581,10 @@ impl Machine {
loop {
let event = match self.execute_next(environment) {
Ok(event) => event,
Err(error) => return Err(EvalError::Machine(error)),
Err(error) => {
self.unwind(expect);
return Err(EvalError::Machine(error));
}
};
if event == expect {
break;
@@ -516,19 +596,25 @@ impl Machine {
pub fn eval_value(
&mut self,
compile_options: CompileOptions,
environment: &mut Environment,
value: Value,
advance_on_return: bool,
) -> Result<Value, EvalError> {
let value = value.macro_expand(self, environment, false)?;
let module = Module::compile_value(&value)?;
let module = Module::compile_value(compile_options, &value)?;
let module = ModuleRef::from(module);
self.eval_module(environment, module)
self.eval_module(environment, module, advance_on_return)
}
}
impl fmt::Display for InstructionPointer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:p}:{}", self.module, self.address)
if let Some(name) = self.module.name.as_ref() {
write!(f, "<{} {:p}>:{}", name, self.module, self.address)
} else {
write!(f, "<unnamed {:p}>:{}", self.module, self.address)
}
}
}
@@ -566,7 +652,7 @@ mod tests {
build(i as u32, &mut builder);
builder.add(Instruction::Return);
let module = builder.build();
values.push(machine.eval_module(&mut env, module.into()).unwrap());
values.push(machine.eval_module(&mut env, module.into(), false).unwrap());
}
(machine, values)
}
+5 -1
View File
@@ -75,7 +75,11 @@ impl MacroExpand for Value {
let value = expand_quasiquote(value);
value.macro_expand(vm, env, false)
}
Self::Vector(_) => todo!(),
Self::Vector(vector) => Ok(Value::Vector(
vector
.try_map(|value| value.macro_expand(vm, env, false))?
.into(),
)),
}
}
}
+19 -10
View File
@@ -7,7 +7,10 @@ use std::{
};
use crate::{
compile::{CompilationModule, CompileError, Expression, FunctionBody, FunctionSignature},
compile::{
CompilationModule, CompileError, CompileOptions, Expression, FunctionBody,
FunctionSignature,
},
vm::{
instruction::{ConstantId, Instruction},
pool::Pool,
@@ -38,6 +41,7 @@ impl Hash for ModuleRef {
impl Eq for ModuleRef {}
pub struct Module {
pub name: Option<Rc<str>>,
pub constants: HashMap<ConstantId, ModuleConstant>,
pub instructions: Vec<u32>,
pub entry: usize,
@@ -94,6 +98,7 @@ impl Deref for ModuleRef {
impl Module {
pub fn dummy() -> Self {
Self {
name: None,
constants: HashMap::new(),
instructions: vec![0],
entry: 0,
@@ -122,9 +127,9 @@ impl Module {
self.entry
}
pub fn compile_value(value: &Value) -> Result<Self, CompileError> {
pub fn compile_value(options: CompileOptions, value: &Value) -> Result<Self, CompileError> {
let expression = Expression::parse(value).map_err(CompileError::Parse)?;
let mut module = CompilationModule::default();
let mut module = CompilationModule::new(None, options);
module.compile_function(
FunctionSignature::EMPTY,
&FunctionBody {
@@ -136,10 +141,10 @@ impl Module {
module.compile_module()
}
pub fn dump(&self, highlight: Option<usize>, context: usize) {
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)..end)
.map(|end| end.saturating_sub(context_backward)..end.saturating_add(context_forward))
.unwrap_or(0..self.instructions.len());
let start = window.start;
@@ -175,6 +180,7 @@ impl ModuleBuilder {
pub fn build(self) -> Module {
Module {
name: None,
constants: self.constants.into_map(),
instructions: self.instructions,
entry: self.entry.unwrap(),
@@ -215,10 +221,13 @@ impl fmt::Display for ModuleConstant {
#[cfg(test)]
mod tests {
use crate::vm::{
instruction::{Instruction, MathInstruction, U},
module::Module,
value::Value,
use crate::{
compile::CompileOptions,
vm::{
instruction::{Instruction, MathInstruction, U},
module::Module,
value::Value,
},
};
#[test]
@@ -228,7 +237,7 @@ mod tests {
Value::Integer(1),
Value::Integer(2),
]);
let m = Module::compile_value(&v).unwrap();
let m = Module::compile_value(CompileOptions::default(), &v).unwrap();
assert!(m.constants.is_empty());
let is = [
Instruction::PushInteger(U::truncate(2)),
+119
View File
@@ -0,0 +1,119 @@
use std::{
cell::RefCell,
fs::File,
io::{Read, Write, stdin, stdout},
};
use crate::vm::{
env::Environment,
value::{Value, Vector},
};
enum FileStream {
Closed,
File(File),
}
enum Stream {
Stdin,
Stdout,
File(RefCell<FileStream>),
}
impl Stream {
fn read_bytes(&self, data: &mut [u8]) -> Option<usize> {
let result = match self {
Self::Stdin => stdin().read(data),
Self::Stdout => return None,
Self::File(file) => match &mut *file.borrow_mut() {
FileStream::File(file) => file.read(data),
FileStream::Closed => return None,
},
};
result.ok()
}
fn write_bytes(&self, data: &[u8]) -> Option<usize> {
match self {
Self::Stdin => None,
Self::Stdout => stdout().write(data).ok(),
Self::File(file) => match &mut *file.borrow_mut() {
FileStream::File(file) => file.write(data).ok(),
FileStream::Closed => None,
},
}
}
}
pub(super) fn load(env: &mut Environment) {
env.set_global_value("*stdin*", Value::opaque(Stream::Stdin));
env.set_global_value("*stdout*", Value::opaque(Stream::Stdout));
env.defun_native("fopen", |_, _, args| {
let [path] = args else {
todo!();
};
let Value::String(path) = path else { todo!() };
match File::open(&**path) {
Ok(file) => Ok(Value::opaque(Stream::File(RefCell::new(FileStream::File(
file,
))))),
Err(_) => Ok(Value::Nil),
}
});
env.defun_native("fclose", |vm, _, args| {
let [stream] = args else { todo!() };
let stream = stream
.as_opaque::<Stream>()
.map_err(|e| vm.error_at_ip(e))?;
if let Stream::File(file) = stream {
*file.borrow_mut() = FileStream::Closed;
}
Ok(Value::Nil)
});
env.defun_native("fread", |vm, _, args| {
let stream = match args {
[] => &Stream::Stdin,
[stream] => stream
.as_opaque::<Stream>()
.map_err(|e| vm.error_at_ip(e))?,
_ => todo!(),
};
let mut buffer = [0; 256];
let count = stream.read_bytes(&mut buffer);
match count {
Some(len) => {
let vector = Vector::from_iter(&buffer[..len]);
Ok(Value::Vector(vector.into()))
}
None => Ok(Value::Nil),
}
});
env.defun_native("fwrite", |vm, _, args| {
let (stream, data) = match args {
[data] => (&Stream::Stdout, data),
[stream, data] => (
stream
.as_opaque::<Stream>()
.map_err(|e| vm.error_at_ip(e))?,
data,
),
_ => todo!(),
};
match data {
Value::Vector(vector) => {
let bytes = vector.to_bytes().unwrap();
match stream.write_bytes(&bytes) {
Some(count) => Ok(Value::Integer(count as i64)),
None => Ok(Value::Nil),
}
}
Value::String(value) => match stream.write_bytes(value.as_bytes()) {
Some(count) => Ok(Value::Integer(count as i64)),
None => Ok(Value::Nil),
},
_ => todo!(),
}
});
}
+23
View File
@@ -200,6 +200,7 @@ pub(crate) fn builtin_cmp(
(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,
}
}
@@ -266,3 +267,25 @@ pub(crate) fn builtin_cmp_le(
) -> Result<Value, MachineError> {
builtin_cmp(vm, args, CompareOperation::Le)
}
pub(super) 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);
}
+79 -23
View File
@@ -2,36 +2,22 @@ use std::{rc::Rc, slice};
use crate::{
error::MachineErrorKind,
read::{self, InteractiveReader},
util::IteratorExt,
vm::{
env::Environment,
value::{AnyFunction, ConsCell, Keyword, TryFromValue, Value, ValueString},
value::{AnyFunction, ConsCell, Keyword, TryFromValue, Value, ValueString, Vector},
},
};
mod io;
mod math;
pub(crate) use math::*;
pub fn load(env: &mut Environment) {
// math
env.defun_native("+", builtin_add);
env.defun_native("-", builtin_sub);
env.defun_native("*", builtin_mul);
env.defun_native("%", builtin_mod);
env.defun_native("/", builtin_div);
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);
math::load(env);
io::load(env);
// conversion
env.defun_native("string->int", |vm, _, args| {
@@ -50,8 +36,30 @@ pub fn load(env: &mut Environment) {
let result = Value::String(format!("{arg}").into());
Ok(result)
});
env.defun_native("vector", |_, _, args| {
let vector = Vector::from_iter(args.iter().cloned());
Ok(Value::Vector(vector.into()))
});
// 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));
@@ -85,6 +93,23 @@ pub fn load(env: &mut Environment) {
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| {
@@ -102,9 +127,15 @@ pub fn load(env: &mut Environment) {
[_, _] => todo!(),
_ => todo!(),
};
let value = match vm.eval_value(env, value.clone()) {
Ok(result) => result,
_ => 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)
});
@@ -121,6 +152,31 @@ pub fn load(env: &mut Environment) {
});
// 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 {
+6
View File
@@ -39,6 +39,12 @@ macro_rules! impl_primitive_value {
}
}
impl From<&$t> for Value {
fn from(value: &$t) -> Self {
Self::Integer(*value as i64)
}
}
impl TryFromValue<'_> for $t {
fn try_from_value(value: &Value) -> Result<Self, MachineErrorKind> {
match value {
+3
View File
@@ -47,6 +47,9 @@ impl_keyword! {
LetStar => "let*",
Defmacro => "defmacro",
Quote => "quote",
Progn => "progn",
Loop => "loop",
Return => "return",
// Cons => "cons",
}
}
+13 -1
View File
@@ -1,4 +1,4 @@
use std::{fmt, hash::Hash, rc::Rc};
use std::{any::Any, fmt, hash::Hash, rc::Rc};
use crate::{
compile::{ExpectedWhat, ExpectedWhere, ParseError, ParseErrorKind},
@@ -78,6 +78,12 @@ impl Value {
matches!(self, Self::Nil)
}
pub fn opaque<T: 'static>(value: T) -> Self {
let value: Rc<dyn Any> = Rc::new(value);
let opaque = OpaqueValue::from(value);
Self::OpaqueValue(opaque)
}
pub fn as_opaque<T: 'static>(&self) -> Result<&T, MachineErrorKind> {
match self {
Self::OpaqueValue(opaque) => opaque.cast(),
@@ -135,6 +141,12 @@ impl Value {
}
}
impl From<Vector> for Value {
fn from(value: Vector) -> Self {
Self::Vector(Rc::new(value))
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
+29 -4
View File
@@ -5,7 +5,10 @@ use std::{
ops::Deref,
};
use crate::vm::value::Value;
use crate::{
error::MachineErrorKind,
vm::value::{TryFromValue, Value},
};
#[derive(Debug, PartialEq, Eq)]
pub struct Vector(RefCell<Vec<Value>>);
@@ -16,7 +19,21 @@ impl Hash for Vector {
}
}
impl From<Vec<Value>> for Vector {
fn from(value: Vec<Value>) -> Self {
Self(RefCell::new(value))
}
}
impl Vector {
pub fn try_map<E, F: FnMut(&Value) -> Result<Value, E>>(&self, map: F) -> Result<Vector, E> {
self.0.borrow().iter().map(map).collect()
}
pub fn to_bytes(&self) -> Result<Vec<u8>, MachineErrorKind> {
self.0.borrow().iter().map(u8::try_from_value).collect()
}
pub fn is_empty(&self) -> bool {
self.0.borrow().is_empty()
}
@@ -26,9 +43,17 @@ impl Vector {
}
}
impl FromIterator<Value> for Vector {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
Self(RefCell::new(Vec::from_iter(iter)))
// impl FromIterator<Value> for Vector {
// fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
// Self(RefCell::new(Vec::from_iter(iter)))
// }
// }
impl<T: Into<Value>> FromIterator<T> for Vector {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self(RefCell::new(Vec::from_iter(
iter.into_iter().map(Into::into),
)))
}
}
+1 -1
View File
@@ -30,7 +30,7 @@ fn eval_str_in(code: &str, env: &mut Environment) -> Result<Value, EvalError> {
Err(error) => panic!("{error}"),
};
last_value = Some(machine.eval_value(env, value));
last_value = Some(machine.eval_value(Default::default(), env, value, false));
}
last_value.expect("no expressions evaluated")