From 0c2d6c85bbea08676a2f8766e99916616e4ec19c Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Wed, 6 May 2026 16:17:08 +0300 Subject: [PATCH] Add defmacro stubs --- lysp/example1.lysp | 2 + src/compile/block.rs | 117 +++++++++++++++++++++++--------- src/compile/module.rs | 9 ++- src/compile/syntax/binding.rs | 24 +++---- src/compile/syntax/call.rs | 24 +++++-- src/compile/syntax/condition.rs | 34 +++++----- src/compile/syntax/function.rs | 9 ++- src/compile/syntax/lambda.rs | 10 +-- src/compile/syntax/macros.rs | 52 ++++++++++++++ src/compile/syntax/mod.rs | 64 ++++++++++++----- src/convert.rs | 1 + src/lib.rs | 3 +- src/main.rs | 2 +- src/parse.rs | 10 +++ src/util.rs | 5 ++ src/vm/machine.rs | 40 ++++++++++- src/vm/module.rs | 2 +- src/vm/value.rs | 4 ++ 18 files changed, 314 insertions(+), 98 deletions(-) create mode 100644 src/compile/syntax/macros.rs diff --git a/lysp/example1.lysp b/lysp/example1.lysp index 2c24482..e554101 100644 --- a/lysp/example1.lysp +++ b/lysp/example1.lysp @@ -1,5 +1,7 @@ ;; vi:ft=lisp:sw=2:ts=2 +(defun fun1 (a b) (+ a (* b 3))) + (assert-equal 2 (+ 1 #t #f)) (assert-equal "test1" (+ "test" 1)) (assert-equal "1test2" (+ 1 "test" 2)) diff --git a/src/compile/block.rs b/src/compile/block.rs index 7a8e4f9..bea4b72 100644 --- a/src/compile/block.rs +++ b/src/compile/block.rs @@ -279,7 +279,7 @@ impl<'a> LocalBlock<'a> { Ok(()) } - pub fn compile_statement(&mut self, expression: &Expression) -> Result<(), CompileError> { + pub fn compile_statement(&mut self, expression: &Rc) -> Result<(), CompileError> { let value = self.compile_expression(expression)?; self.compile_drop(value)?; Ok(()) @@ -318,7 +318,7 @@ impl<'a> LocalBlock<'a> { fn compile_builtin_math_generic( &mut self, math: MathInstruction, - args: &[Expression], + args: &[Rc], ) -> Result { // TODO optimize for arg in args.iter().rev() { @@ -335,7 +335,7 @@ impl<'a> LocalBlock<'a> { fn compile_call_builtin( &mut self, builtin: BuiltinFunction, - args: &[Expression], + args: &[Rc], ) -> Result { match builtin { BuiltinFunction::Math(math) => self.compile_builtin_math_generic(math, args), @@ -456,9 +456,9 @@ impl<'a> LocalBlock<'a> { pub fn compile_expression( &mut self, - expression: &Expression, + expression: &Rc, ) -> Result { - match expression { + match expression.as_ref() { Expression::Nil => Ok(CompileValue::Nil), Expression::StringLiteral(value) => Ok(CompileValue::String(value.clone())), Expression::BooleanLiteral(value) => Ok(CompileValue::Boolean(*value)), @@ -471,6 +471,7 @@ impl<'a> LocalBlock<'a> { Expression::Cond(condition) => self.compile_cond(condition), Expression::Let(binding) => self.compile_let(binding), Expression::Setq(setq) => self.compile_setq(setq), + Expression::Defmacro(_defmacro) => todo!(), Expression::SyntaxError(_) => unreachable!(), } @@ -487,18 +488,52 @@ mod tests { function::FunctionSignature, module::CompilationModule, syntax::{ - CallExpression, CondExpression, CondExpressionArm, Expression, FunctionBody, - IfExpression, LambdaExpression, + CallExpression, CondExpression, CondExpressionArm, DefmacroExpression, Expression, + FunctionBody, IfExpression, LambdaExpression, }, value::{CompileConstant, CompileValue}, }, vm::instruction::{Instruction, MathInstruction, U}, }; - fn test_compile( - expression: &Expression, - ) -> (CompilationModule, CompiledFunction, CompileValue) { + fn test_compile(expression: Expression) -> (CompilationModule, CompiledFunction, CompileValue) { let mut module = CompilationModule::default(); + module.define_macro(DefmacroExpression { + identifier: "test-macro-1".into(), + signature: FunctionSignature { + required_arguments: vec!["x".into()], + optional_arguments: vec![], + rest_argument: None, + }, + body: FunctionBody { + head: vec![], + tail: Rc::new(Expression::Call(CallExpression { + callee: Rc::new(Expression::Identifier("test-macro-2".into())), + arguments: vec![ + Rc::new(Expression::Identifier("x".into())), + Rc::new(Expression::IntegerLiteral(1)), + ], + })), + }, + }); + module.define_macro(DefmacroExpression { + identifier: "test-macro-2".into(), + signature: FunctionSignature { + required_arguments: vec!["x".into(), "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())), + ], + })), + }, + }); let mut function = FunctionBlock { signature: FunctionSignature { required_arguments: vec!["arg0".into()], @@ -512,24 +547,41 @@ mod tests { max_local_index: 0, }; let mut local = LocalBlock::root(&mut function, &mut module); - let value = local.compile_expression(expression).unwrap(); + let value = local.compile_expression(&Rc::new(expression)).unwrap(); (module, function.resolve_labels(), value) } #[test] fn test_identity_compile() { - let (_, f, v) = test_compile(&Expression::IntegerLiteral(1)); + let (_, f, v) = test_compile(Expression::IntegerLiteral(1)); assert!(f.instructions.is_empty()); assert_eq!(v, CompileValue::Integer(1)); } + #[test] + fn test_macro_expansion() { + let (_, f, v) = test_compile(Expression::Call(CallExpression { + callee: Rc::new(Expression::Identifier("test-macro-1".into())), + arguments: vec![Rc::new(Expression::IntegerLiteral(2))], + })); + assert_eq!( + &f.instructions, + &[ + Instruction::PushInteger(U::truncate(1)), + Instruction::PushInteger(U::truncate(2)), + Instruction::Math(MathInstruction::Add, U::truncate(2)), + ] + ); + assert_eq!(v, CompileValue::Stack); + } + #[test] fn test_comparison_compile() { - let (m, f, v) = test_compile(&Expression::Call(CallExpression { + let (m, f, v) = test_compile(Expression::Call(CallExpression { callee: Rc::new(Expression::Identifier(">".into())), arguments: vec![ - Expression::Identifier("a".into()), - Expression::IntegerLiteral(1), + Rc::new(Expression::Identifier("a".into())), + Rc::new(Expression::IntegerLiteral(1)), ], })); let cs = m.constant_pool.into_map(); @@ -551,7 +603,7 @@ mod tests { #[test] fn test_if_compile() { - let (_, f, v) = test_compile(&Expression::If(IfExpression { + let (_, f, v) = test_compile(Expression::If(IfExpression { condition: Rc::new(Expression::BooleanLiteral(false)), if_true: Rc::new(Expression::IntegerLiteral(1)), if_false: Some(Rc::new(Expression::IntegerLiteral(2))), @@ -572,15 +624,15 @@ mod tests { #[test] fn test_cond_compile() { - let (_, f, v) = test_compile(&Expression::Cond(CondExpression { + let (_, f, v) = test_compile(Expression::Cond(CondExpression { arms: vec![ CondExpressionArm { - condition: Expression::BooleanLiteral(false), - then: Expression::IntegerLiteral(1), + condition: Rc::new(Expression::BooleanLiteral(false)), + then: Rc::new(Expression::IntegerLiteral(1)), }, CondExpressionArm { - condition: Expression::BooleanLiteral(true), - then: Expression::IntegerLiteral(2), + condition: Rc::new(Expression::BooleanLiteral(true)), + then: Rc::new(Expression::IntegerLiteral(2)), }, ], otherwise_arm: Some(Rc::new(Expression::IntegerLiteral(3))), @@ -606,7 +658,7 @@ mod tests { #[test] fn test_compile_lambda_returning_lambda() { // ( ((lambda () (lambda (a b) (+ a b)))) 1 2 ) - let (m, f, v) = test_compile(&Expression::Call(CallExpression { + let (m, f, v) = test_compile(Expression::Call(CallExpression { callee: Rc::new(Expression::Call(CallExpression { callee: Rc::new(Expression::Lambda(LambdaExpression { signature: FunctionSignature { @@ -627,8 +679,8 @@ mod tests { tail: Rc::new(Expression::Call(CallExpression { callee: Rc::new(Expression::Identifier("+".into())), arguments: vec![ - Expression::Identifier("a".into()), - Expression::Identifier("b".into()), + Rc::new(Expression::Identifier("a".into())), + Rc::new(Expression::Identifier("b".into())), ], })), }, @@ -637,7 +689,10 @@ mod tests { })), arguments: vec![], })), - arguments: vec![Expression::IntegerLiteral(1), Expression::IntegerLiteral(2)], + arguments: vec![ + Rc::new(Expression::IntegerLiteral(1)), + Rc::new(Expression::IntegerLiteral(2)), + ], })); assert_eq!( &f.instructions[..], @@ -671,10 +726,10 @@ mod tests { #[test] fn test_compile_lambda() { // (+ ((lambda (a) (+ a 1))) 2) - let (m, f, v) = test_compile(&Expression::Call(CallExpression { + let (m, f, v) = test_compile(Expression::Call(CallExpression { callee: Rc::new(Expression::Identifier("+".into())), arguments: vec![ - Expression::Call(CallExpression { + Rc::new(Expression::Call(CallExpression { callee: Expression::Lambda(LambdaExpression { signature: FunctionSignature { required_arguments: vec!["a".into()], @@ -686,16 +741,16 @@ mod tests { tail: Rc::new(Expression::Call(CallExpression { callee: Rc::new(Expression::Identifier("+".into())), arguments: vec![ - Expression::Identifier("a".into()), - Expression::IntegerLiteral(1), + Rc::new(Expression::Identifier("a".into())), + Rc::new(Expression::IntegerLiteral(1)), ], })), }, }) .into(), arguments: vec![], - }), - Expression::IntegerLiteral(2), + })), + Rc::new(Expression::IntegerLiteral(2)), ], })); assert_eq!(v, CompileValue::Stack); diff --git a/src/compile/module.rs b/src/compile/module.rs index 6d4bac4..f3ff6b4 100644 --- a/src/compile/module.rs +++ b/src/compile/module.rs @@ -1,11 +1,11 @@ -use std::collections::HashMap; +use std::{collections::HashMap, rc::Rc}; use crate::{ compile::{ block::{CompiledFunction, FunctionBlock}, error::CompileError, function::FunctionSignature, - syntax::FunctionBody, + syntax::{DefmacroExpression, FunctionBody}, value::CompileConstant, }, vm::{ @@ -19,11 +19,16 @@ use crate::{ pub struct CompilationModule { pub(crate) constant_pool: Pool, pub(crate) local_functions: HashMap, + macros: HashMap, DefmacroExpression>, local_function_index: u32, root: Option, } impl CompilationModule { + pub fn define_macro(&mut self, defmacro: DefmacroExpression) { + self.macros.insert(defmacro.identifier.clone(), defmacro); + } + pub fn constant(&mut self, value: CompileConstant) -> Result { match self.constant_pool.key(value) { Some(key) => Ok(key), diff --git a/src/compile/syntax/binding.rs b/src/compile/syntax/binding.rs index 78a7ee7..0d4fd53 100644 --- a/src/compile/syntax/binding.rs +++ b/src/compile/syntax/binding.rs @@ -11,7 +11,7 @@ use crate::{ #[derive(Debug, PartialEq)] pub struct Assignment { pub identifier: Rc, - pub value: Expression, + pub value: Rc, } #[derive(Debug, PartialEq)] @@ -154,12 +154,12 @@ mod tests { ]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::Let(LetExpression { + e.as_ref(), + &Expression::Let(LetExpression { sequential: false, bindings: vec![Assignment { identifier: "a".into(), - value: Expression::IntegerLiteral(1) + value: Rc::new(Expression::IntegerLiteral(1)) },], body: FunctionBody { head: vec![], @@ -179,17 +179,17 @@ mod tests { ]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::Let(LetExpression { + e.as_ref(), + &Expression::Let(LetExpression { sequential: true, bindings: vec![ Assignment { identifier: "a".into(), - value: Expression::IntegerLiteral(1) + value: Rc::new(Expression::IntegerLiteral(1)) }, Assignment { identifier: "b".into(), - value: Expression::IntegerLiteral(2) + value: Rc::new(Expression::IntegerLiteral(2)) } ], body: FunctionBody { @@ -228,16 +228,16 @@ mod tests { ]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::Setq(SetqExpression { + e.as_ref(), + &Expression::Setq(SetqExpression { pairs: vec![ Assignment { identifier: "a".into(), - value: Expression::Identifier("b".into()), + value: Rc::new(Expression::Identifier("b".into())), }, Assignment { identifier: "c".into(), - value: Expression::Identifier("d".into()), + value: Rc::new(Expression::Identifier("d".into())), } ] }) diff --git a/src/compile/syntax/call.rs b/src/compile/syntax/call.rs index d350a8e..49e8f86 100644 --- a/src/compile/syntax/call.rs +++ b/src/compile/syntax/call.rs @@ -2,8 +2,8 @@ use std::rc::Rc; use crate::{ compile::{ - Expression, ParseError, - syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind}, + CompileError, Expression, ParseError, + syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind, SubMap, Substitute}, }, vm::value::{ConsCell, Value}, }; @@ -11,7 +11,7 @@ use crate::{ #[derive(Debug, PartialEq)] pub struct CallExpression { pub callee: Rc, - pub arguments: Vec, + pub arguments: Vec>, } impl CallExpression { @@ -39,9 +39,19 @@ impl CallExpression { list = cdr; } + Ok(Self { callee, arguments }) + } +} + +impl Substitute for CallExpression { + fn substitute(&self, map: &SubMap) -> Result { Ok(Self { - callee: callee.into(), - arguments, + callee: self.callee.substitute(map)?, + arguments: self + .arguments + .iter() + .map(|arg| arg.substitute(map)) + .collect::>()?, }) } } @@ -73,8 +83,8 @@ mod tests { let v = Value::list_or_nil([Value::Identifier("a".into())]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::Call(CallExpression { + e.as_ref(), + &Expression::Call(CallExpression { callee: Rc::new(Expression::Identifier("a".into())), arguments: vec![] }) diff --git a/src/compile/syntax/condition.rs b/src/compile/syntax/condition.rs index 834fb69..7acba7f 100644 --- a/src/compile/syntax/condition.rs +++ b/src/compile/syntax/condition.rs @@ -17,8 +17,8 @@ pub struct IfExpression { #[derive(Debug, PartialEq)] pub struct CondExpressionArm { - pub condition: Expression, - pub then: Expression, + pub condition: Rc, + pub then: Rc, } #[derive(Debug, PartialEq)] pub struct CondExpression { @@ -151,7 +151,7 @@ impl CondExpression { }); } let arm = Expression::parse_inner(arm_expr); - otherwise_arm = Some(Rc::new(arm)); + otherwise_arm = Some(arm); } _ => { if otherwise_arm.is_some() { @@ -235,16 +235,16 @@ mod tests { let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::Cond(CondExpression { + e.as_ref(), + &Expression::Cond(CondExpression { arms: vec![ CondExpressionArm { - condition: Expression::BooleanLiteral(true), - then: Expression::Identifier("a".into()) + condition: Rc::new(Expression::BooleanLiteral(true)), + then: Rc::new(Expression::Identifier("a".into())) }, CondExpressionArm { - condition: Expression::BooleanLiteral(false), - then: Expression::Identifier("b".into()) + condition: Rc::new(Expression::BooleanLiteral(false)), + then: Rc::new(Expression::Identifier("b".into())) } ], otherwise_arm: None @@ -260,11 +260,11 @@ mod tests { let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::Cond(CondExpression { + e.as_ref(), + &Expression::Cond(CondExpression { arms: vec![CondExpressionArm { - condition: Expression::BooleanLiteral(true), - then: Expression::Identifier("a".into()) + condition: Rc::new(Expression::BooleanLiteral(true)), + then: Rc::new(Expression::Identifier("a".into())) },], otherwise_arm: Some(Rc::new(Expression::Identifier("b".into()))) }) @@ -419,8 +419,8 @@ mod tests { ]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::If(IfExpression { + e.as_ref(), + &Expression::If(IfExpression { condition: Rc::new(Expression::BooleanLiteral(true)), if_true: Rc::new(Expression::IntegerLiteral(1)), if_false: None @@ -436,8 +436,8 @@ mod tests { ]); let e = Expression::parse(&v).unwrap(); assert_eq!( - e, - Expression::If(IfExpression { + 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))) diff --git a/src/compile/syntax/function.rs b/src/compile/syntax/function.rs index 8510c2a..a525663 100644 --- a/src/compile/syntax/function.rs +++ b/src/compile/syntax/function.rs @@ -10,7 +10,7 @@ use crate::{ #[derive(Debug, PartialEq)] pub struct FunctionBody { - pub head: Vec, + pub head: Vec>, pub tail: Rc, } @@ -166,7 +166,7 @@ impl FunctionBody { }; Ok(Self { head: expressions, - tail: tail.into(), + tail, }) } } @@ -261,7 +261,10 @@ mod tests { assert_eq!( e, FunctionBody { - head: vec![Expression::IntegerLiteral(1), Expression::IntegerLiteral(2)], + head: vec![ + Rc::new(Expression::IntegerLiteral(1)), + Rc::new(Expression::IntegerLiteral(2)) + ], tail: Rc::new(Expression::IntegerLiteral(3)) } ); diff --git a/src/compile/syntax/lambda.rs b/src/compile/syntax/lambda.rs index af4f6ed..ab45b55 100644 --- a/src/compile/syntax/lambda.rs +++ b/src/compile/syntax/lambda.rs @@ -38,6 +38,8 @@ impl CollectErrors for LambdaExpression { #[cfg(test)] mod tests { + use std::rc::Rc; + use crate::{ compile::{ CallExpression, Expression, FunctionBody, FunctionSignature, LambdaExpression, @@ -65,8 +67,8 @@ mod tests { let expr = Expression::parse(&lambda).unwrap(); assert_eq!( - expr, - Expression::Lambda(LambdaExpression { + expr.as_ref(), + &Expression::Lambda(LambdaExpression { signature: FunctionSignature { required_arguments: vec!["a".into()], optional_arguments: vec!["b".into()], @@ -77,8 +79,8 @@ mod tests { tail: Expression::Call(CallExpression { callee: Expression::Identifier("+".into()).into(), arguments: vec![ - Expression::Identifier("a".into()), - Expression::IntegerLiteral(1) + Rc::new(Expression::Identifier("a".into())), + Rc::new(Expression::IntegerLiteral(1)) ] }) .into() diff --git a/src/compile/syntax/macros.rs b/src/compile/syntax/macros.rs new file mode 100644 index 0000000..b0762e0 --- /dev/null +++ b/src/compile/syntax/macros.rs @@ -0,0 +1,52 @@ +use std::{collections::HashMap, rc::Rc}; + +use crate::{ + compile::{ + CompileError, Expression, FunctionBody, FunctionSignature, ParseError, + syntax::CollectErrors, + }, + vm::value::Value, +}; + +#[derive(Debug, PartialEq)] +pub struct DefmacroExpression { + pub identifier: Rc, + pub signature: FunctionSignature, + pub body: FunctionBody, +} + +impl DefmacroExpression { + pub(super) fn parse(_value: &Value, _input: &Value) -> Result { + todo!() + } + + pub fn expand(&self, args: &[Rc]) -> Result, CompileError> { + // TODO emit progn? + if !self.body.head.is_empty() { + todo!() + } + + let mut substitution_map = HashMap::new(); + for (i, required) in self.signature.required_arguments.iter().enumerate() { + let Some(substitution) = args.get(i) else { + todo!() + }; + substitution_map.insert(required.clone(), substitution.clone()); + } + for (i, optional) in self.signature.optional_arguments.iter().enumerate() { + if let Some(substitution) = args.get(i + self.signature.required_arguments.len()) { + substitution_map.insert(optional.clone(), substitution.clone()); + } else { + substitution_map.insert(optional.clone(), Expression::Nil.into()); + } + } + + self.body.tail.substitute(&substitution_map) + } +} + +impl CollectErrors for DefmacroExpression { + fn collect_errors(&self, _errors: &mut Vec) -> bool { + todo!() + } +} diff --git a/src/compile/syntax/mod.rs b/src/compile/syntax/mod.rs index 805f720..78a73d6 100644 --- a/src/compile/syntax/mod.rs +++ b/src/compile/syntax/mod.rs @@ -1,6 +1,9 @@ -use std::rc::Rc; +use std::{collections::HashMap, rc::Rc}; -use crate::vm::value::{ConsCell, Keyword, Value, ValueString}; +use crate::{ + compile::CompileError, + vm::value::{ConsCell, Keyword, Value, ValueString}, +}; mod binding; mod call; @@ -8,6 +11,7 @@ mod condition; mod error; mod function; mod lambda; +mod macros; pub use binding::*; pub use call::*; @@ -15,6 +19,13 @@ pub use condition::*; pub use error::*; pub use function::*; pub use lambda::*; +pub use macros::*; + +pub type SubMap = HashMap, Rc>; + +pub trait Substitute: Sized { + fn substitute(&self, map: &SubMap) -> Result; +} #[derive(Debug, PartialEq)] pub enum Expression { @@ -30,18 +41,19 @@ pub enum Expression { Cond(CondExpression), Setq(SetqExpression), Let(LetExpression), + Defmacro(DefmacroExpression), SyntaxError(ParseError), } impl Expression { - fn map_or Self>(result: Result, map: F) -> Self { + fn map_or Self>(result: Result, map: F) -> Rc { match result { - Ok(r) => map(r), - Err(error) => Self::SyntaxError(error), + Ok(r) => Rc::new(map(r)), + Err(error) => Rc::new(Self::SyntaxError(error)), } } - pub fn parse(value: &Value) -> Result> { + pub fn parse(value: &Value) -> Result, Vec> { let inner = Self::parse_inner(value); let mut errors = vec![]; if inner.collect_errors(&mut errors) { @@ -51,14 +63,15 @@ impl Expression { } } - fn parse_inner(value: &Value) -> Self { + fn parse_inner(value: &Value) -> Rc { match value { - Value::Nil => Self::Nil, - Value::Boolean(value) => Self::BooleanLiteral(*value), - Value::Integer(value) => Self::IntegerLiteral(*value), - Value::String(value) => Self::StringLiteral(value.clone()), + 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) => todo!(), - Value::Identifier(value) => Self::Identifier(value.clone()), + Value::Identifier(value) => Rc::new(Self::Identifier(value.clone())), + Value::Quasi(_value) => todo!(), Value::Cons(cons) => { let ConsCell(car, cdr) = cons.as_ref(); match car { @@ -80,6 +93,9 @@ impl Expression { Value::Keyword(Keyword::Setq) => { Self::map_or(SetqExpression::parse(cdr, value), Expression::Setq) } + Value::Keyword(Keyword::Defmacro) => { + Self::map_or(DefmacroExpression::parse(cdr, value), Expression::Defmacro) + } _ => Self::map_or(CallExpression::parse(cons, value), Expression::Call), } } @@ -89,6 +105,21 @@ impl Expression { } } } + + fn substitute(self: &Rc, map: &SubMap) -> Result, CompileError> { + match self.as_ref() { + Self::Identifier(value) if let Some(sub) = map.get(value) => Ok(sub.clone()), + Self::Call(call) => call.substitute(map).map(Self::Call).map(Rc::new), + Self::If(_) => todo!(), + Self::Let(_) => todo!(), + Self::Cond(_) => todo!(), + Self::Setq(_) => todo!(), + Self::Defun(_) => todo!(), + Self::Defmacro(_) => todo!(), + Self::Lambda(_) => todo!(), + _ => Ok(self.clone()), + } + } } impl CollectErrors for Expression { @@ -105,6 +136,7 @@ impl CollectErrors for Expression { Self::Call(call) => call.collect_errors(errors), Self::Let(let_) => let_.collect_errors(errors), Self::Setq(setq) => setq.collect_errors(errors), + Self::Defmacro(defmacro) => defmacro.collect_errors(errors), Self::Nil | Self::IntegerLiteral(_) | Self::Identifier(_) @@ -125,19 +157,19 @@ mod tests { fn test_parse_basic() { let v = Value::Nil; let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::Nil); + assert_eq!(e.as_ref(), &Expression::Nil); let v = Value::Integer(1234); let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::IntegerLiteral(1234)); + assert_eq!(e.as_ref(), &Expression::IntegerLiteral(1234)); let v = Value::Boolean(false); let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::BooleanLiteral(false)); + assert_eq!(e.as_ref(), &Expression::BooleanLiteral(false)); let v = Value::Identifier("a".into()); let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::Identifier("a".into())); + assert_eq!(e.as_ref(), &Expression::Identifier("a".into())); } #[test] diff --git a/src/convert.rs b/src/convert.rs index 7a66ee6..e430f50 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -79,6 +79,7 @@ impl TryFromValue<'_> for bool { Value::Boolean(value) => Ok(*value), Value::Integer(value) => Ok(*value != 0), Value::Keyword(_) + | Value::Quasi(_) | Value::Identifier(_) | Value::OpaqueValue(_) | Value::NativeFunction(_) diff --git a/src/lib.rs b/src/lib.rs index 75fd073..f670b2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,8 @@ debug_closure_helpers, unboxed_closures, iter_next_chunk, - exact_size_is_empty + exact_size_is_empty, + try_trait_v2 )] pub mod compile; diff --git a/src/main.rs b/src/main.rs index 5235f23..d1c1b0f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,7 +76,7 @@ fn run_interactive(vm: &mut Machine) -> Result<(), Error> { } }; - let result = vm.eval_value(&value); + let result = vm.eval_value(value.clone()); let result = match result { EvalResult::Ok(r) => r, EvalResult::Err(module, ip, error) => { diff --git a/src/parse.rs b/src/parse.rs index 5fa4ef3..18b9a4b 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use nom::{ AsChar, FindToken, IResult, Input, Parser, branch::alt, @@ -272,10 +274,18 @@ pub fn skip_comment_and_whitespace(mut input: &str) -> IResult<&str, ()> { Ok((input, ())) } +fn parse_quasi(input: &str) -> IResult<&str, Value> { + preceded(char('`'), parse_value) + .map(Rc::new) + .map(Value::Quasi) + .parse(input) +} + pub fn parse_value(input: &str) -> IResult<&str, Value> { alt(( parse_list_or_nil, parse_boolean, + parse_quasi, parse_integer, parse_string, parse_identifier_or_keyword_or_nil, diff --git a/src/util.rs b/src/util.rs index ed7e004..1f3cbe0 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,8 @@ +pub enum Either { + Left(A), + Right(B), +} + pub struct TryFilter where I: Iterator>, diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 8cb14e3..2bdbc67 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -1,4 +1,9 @@ -use std::{collections::HashMap, fmt, rc::Rc}; +use std::{ + collections::HashMap, + fmt, + ops::{ControlFlow, FromResidual, Try}, + rc::Rc, +}; use crate::{ error::EvalError, @@ -429,6 +434,11 @@ impl Machine { Ok(event) } + fn macro_expand(&mut self, value: Value) -> EvalResult { + // TODO + EvalResult::Ok(value) + } + pub fn eval_bytecode_call( &mut self, function: BytecodeFunction, @@ -496,8 +506,9 @@ impl Machine { } } - pub fn eval_value(&mut self, value: &Value) -> EvalResult { - let module = match Module::compile_value(value) { + pub fn eval_value(&mut self, value: Value) -> EvalResult { + let value = self.macro_expand(value)?; + let module = match Module::compile_value(&value) { Ok(module) => module, Err(error) => return EvalResult::LoadErr(error.into()), }; @@ -510,6 +521,29 @@ impl Machine { } } +impl Try for EvalResult { + type Output = T; + type Residual = EvalResult; + + fn branch(self) -> ControlFlow { + match self { + Self::Ok(result) => ControlFlow::Continue(result), + Self::Err(module, ip, error) => ControlFlow::Break(EvalResult::Err(module, ip, error)), + Self::LoadErr(error) => ControlFlow::Break(EvalResult::LoadErr(error)), + } + } + + fn from_output(output: Self::Output) -> Self { + Self::Ok(output) + } +} + +impl FromResidual for EvalResult { + fn from_residual(residual: ::Residual) -> Self { + residual + } +} + impl EvalResult { pub fn unwrap(self) -> T where diff --git a/src/vm/module.rs b/src/vm/module.rs index 6fabdd9..d2e9ccc 100644 --- a/src/vm/module.rs +++ b/src/vm/module.rs @@ -114,7 +114,7 @@ impl Module { FunctionSignature::EMPTY, &FunctionBody { head: vec![], - tail: Rc::new(expression), + tail: expression, }, true, )?; diff --git a/src/vm/value.rs b/src/vm/value.rs index db7ab86..b0bcf46 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -39,6 +39,7 @@ pub enum Value { Keyword(Keyword), String(ValueString), Vector(Rc), + Quasi(Rc), // "Runtime" values BytecodeFunction(BytecodeFunction), NativeFunction(NativeFunction), @@ -143,6 +144,7 @@ impl_keyword! { Setq => "setq", Let => "let", LetStar => "let*", + Defmacro => "defmacro", } } @@ -182,6 +184,7 @@ impl Value { Self::Boolean(false) => Ok("#f".into()), Self::Identifier(value) => Ok(format!("{value}")), Self::String(value) => Ok(format!("{value}")), + Self::Quasi(_) => todo!(), Self::Nil => todo!(), Self::Cons(_) => todo!(), Self::Vector(_) => todo!(), @@ -314,6 +317,7 @@ impl fmt::Display for 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::BytecodeFunction(bytecode) => write!(f, "{bytecode}"), Self::NativeFunction(native) => write!(f, "{native}"), Self::OpaqueValue(opaque) => write!(f, "{opaque}"),