From 58cc01c11403abb7e1102b0d5dd1dd1b935199a5 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 1 May 2026 14:05:10 +0300 Subject: [PATCH] Implement missing parser functions --- src/compile/block.rs | 267 +++++++++++++++-- src/compile/error.rs | 10 +- src/compile/function.rs | 2 +- src/compile/instruction.rs | 14 + src/compile/mod.rs | 3 +- src/compile/module.rs | 15 +- src/compile/syntax.rs | 264 ----------------- src/compile/syntax/call.rs | 97 +++++++ src/compile/syntax/condition.rs | 490 ++++++++++++++++++++++++++++++++ src/compile/syntax/error.rs | 87 ++++++ src/compile/syntax/function.rs | 373 ++++++++++++++++++++++++ src/compile/syntax/lambda.rs | 115 ++++++++ src/compile/syntax/mod.rs | 150 ++++++++++ src/compile/value.rs | 13 + src/main.rs | 42 ++- src/parse.rs | 5 +- src/vm/instruction.rs | 35 +++ src/vm/machine.rs | 146 +++++++++- src/vm/module.rs | 13 +- src/vm/value.rs | 73 +++-- 20 files changed, 1879 insertions(+), 335 deletions(-) create mode 100644 src/compile/instruction.rs delete mode 100644 src/compile/syntax.rs create mode 100644 src/compile/syntax/call.rs create mode 100644 src/compile/syntax/condition.rs create mode 100644 src/compile/syntax/error.rs create mode 100644 src/compile/syntax/function.rs create mode 100644 src/compile/syntax/lambda.rs create mode 100644 src/compile/syntax/mod.rs diff --git a/src/compile/block.rs b/src/compile/block.rs index 1c0bfd0..751d83d 100644 --- a/src/compile/block.rs +++ b/src/compile/block.rs @@ -1,18 +1,28 @@ -use std::rc::Rc; +use std::{collections::HashMap, rc::Rc}; use crate::{ compile::{ error::CompileError, function::FunctionSignature, + instruction::Emitted, module::CompilationModule, - syntax::{CallExpression, Expression, FunctionBody, LambdaExpression}, + syntax::{ + CallExpression, CondExpression, Expression, FunctionBody, IfExpression, + LambdaExpression, + }, value::{BuiltinFunction, CompileConstant, CompileValue}, }, - vm::instruction::{Instruction, U}, + vm::instruction::{Comparison, Instruction, U}, }; -pub struct FunctionBlock { +pub struct CompiledFunction { pub(crate) instructions: Vec, +} + +pub struct FunctionBlock { + pub(crate) instructions: Vec, + labels: HashMap, + last_label: u32, signature: FunctionSignature, } @@ -27,13 +37,55 @@ impl FunctionBlock { pub fn new(signature: FunctionSignature) -> Self { Self { instructions: vec![], + labels: HashMap::new(), + last_label: 0, signature, } } - pub fn emit(&mut self, instruction: Instruction) { - eprintln!("emit {instruction:?}"); - self.instructions.push(instruction); + pub fn new_label(&mut self) -> u32 { + let id = self.last_label; + self.last_label += 1; + self.labels.insert(id, usize::MAX); + id + } + + pub fn adjust_label(&mut self, label: u32) { + self.labels.insert(label, self.instructions.len()); + } + + pub fn resolve_labels(self) -> CompiledFunction { + let mut instructions = vec![]; + for emitted in self.instructions { + match emitted { + Emitted::Instruction(instruction) => { + instructions.push(instruction); + } + Emitted::Branch(label) => { + let address = self.labels.get(&label).copied().unwrap_or(usize::MAX); + if address == usize::MAX { + todo!() + } + let offset = U::new(address as u32).unwrap(); + instructions.push(Instruction::Branch(offset)); + } + Emitted::Jump(label) => { + let address = self.labels.get(&label).copied().unwrap_or(usize::MAX); + if address == usize::MAX { + todo!() + } + let offset = U::new(address as u32).unwrap(); + instructions.push(Instruction::Jump(offset)); + } + } + } + CompiledFunction { instructions } + } + + pub fn emit>(&mut self, instruction: E) { + let emitted = instruction.into(); + // eprintln!("emit {emitted:?}"); + self.instructions.push(emitted); } pub fn compile_body( @@ -62,8 +114,18 @@ impl<'a> LocalBlock<'a> { fn compile_push(&mut self, value: CompileValue) -> Result<(), CompileError> { match value { - CompileValue::Nil => todo!(), - CompileValue::Boolean(_) => todo!(), + CompileValue::Nil => { + self.function.emit(Instruction::PushNil); + } + CompileValue::Global(identifier) => { + let value = self + .module + .constant(CompileConstant::Identifier(identifier))?; + self.function.emit(Instruction::PushConstant(value)); + } + CompileValue::Boolean(value) => { + self.function.emit(Instruction::PushBool(value)); + } CompileValue::Integer(value) => { // TODO signed/unsigned if let Some(value) = U::new(value as u32) { @@ -91,6 +153,13 @@ impl<'a> LocalBlock<'a> { Ok(()) } + fn compile_drop(&mut self, value: CompileValue) -> Result<(), CompileError> { + if matches!(value, CompileValue::Stack) { + self.function.emit(Instruction::Drop); + } + Ok(()) + } + pub fn compile_return(&mut self, value: CompileValue) -> Result<(), CompileError> { self.compile_push(value)?; self.function.emit(Instruction::Return); @@ -98,7 +167,9 @@ impl<'a> LocalBlock<'a> { } pub fn compile_statement(&mut self, expression: &Expression) -> Result<(), CompileError> { - todo!() + let value = self.compile_expression(expression)?; + self.compile_drop(value)?; + Ok(()) } fn compile_identifier(&mut self, identifier: &Rc) -> Result { @@ -109,7 +180,7 @@ impl<'a> LocalBlock<'a> { return Ok(CompileValue::Argument(argument)); } // TODO local bindings - todo!() + Ok(CompileValue::Global(identifier.clone())) } fn compile_lambda(&mut self, lambda: &LambdaExpression) -> Result { @@ -136,6 +207,24 @@ impl<'a> LocalBlock<'a> { todo!() } + fn compile_builtin_cmp( + &mut self, + not: bool, + cmp: Comparison, + args: &[Expression], + ) -> Result { + // TODO optimize literals + for arg in args.iter().rev() { + let arg = self.compile_expression(arg)?; + self.compile_push(arg)?; + } + let Some(count) = U::new(args.len() as u32) else { + todo!() + }; + self.function.emit(Instruction::Compare(not, cmp, count)); + Ok(CompileValue::Stack) + } + fn compile_call_builtin( &mut self, builtin: BuiltinFunction, @@ -144,6 +233,7 @@ impl<'a> LocalBlock<'a> { match builtin { BuiltinFunction::Add => self.compile_builtin_add(args), BuiltinFunction::Sub => self.compile_builtin_sub(args), + BuiltinFunction::Cmp(not, cmp) => self.compile_builtin_cmp(not, cmp, args), } } @@ -172,6 +262,57 @@ impl<'a> LocalBlock<'a> { } } + fn compile_if(&mut self, condition: &IfExpression) -> Result { + let label_false = self.function.new_label(); + let label_end = self.function.new_label(); + // Emit condition + let condition_value = self.compile_expression(&condition.condition)?; + self.compile_push(condition_value)?; + self.function.emit(Emitted::Branch(label_false)); + // True branch + let true_value = self.compile_expression(&condition.if_true)?; + 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)? + } else { + CompileValue::Nil + }; + self.compile_push(false_value)?; + self.function.adjust_label(label_end); + Ok(CompileValue::Stack) + } + + fn compile_cond(&mut self, condition: &CondExpression) -> Result { + 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)?; + self.compile_push(condition_value)?; + self.function.emit(Emitted::Branch(label_condition_end)); + // Condition true + let then_value = self.compile_expression(&arm.then)?; + self.compile_push(then_value)?; + self.function.emit(Emitted::Jump(label_end)); + // Condition false + self.function.adjust_label(label_condition_end); + } + + // Otherwise branch + let otherwise_value = if let Some(othwerise_arm) = condition.otherwise_arm.as_ref() { + self.compile_expression(othwerise_arm)? + } else { + CompileValue::Nil + }; + self.compile_push(otherwise_value)?; + self.function.adjust_label(label_end); + + Ok(CompileValue::Stack) + } + pub fn compile_expression( &mut self, expression: &Expression, @@ -183,26 +324,35 @@ impl<'a> LocalBlock<'a> { Expression::Identifier(identifier) => self.compile_identifier(identifier), Expression::Lambda(lambda) => self.compile_lambda(lambda), Expression::Call(call) => self.compile_call(call), + Expression::If(condition) => self.compile_if(condition), + Expression::Cond(condition) => self.compile_cond(condition), + + Expression::SyntaxError(_) => unreachable!(), } } } #[cfg(test)] mod tests { - use std::rc::Rc; + use std::{collections::HashMap, rc::Rc}; use crate::{ compile::{ - block::{FunctionBlock, LocalBlock}, + block::{CompiledFunction, FunctionBlock, LocalBlock}, function::FunctionSignature, module::CompilationModule, - syntax::{CallExpression, Expression, FunctionBody, LambdaExpression}, - value::CompileValue, + syntax::{ + CallExpression, CondExpression, CondExpressionArm, Expression, FunctionBody, + IfExpression, LambdaExpression, + }, + value::{CompileConstant, CompileValue}, }, - vm::instruction::{Instruction, U}, + vm::instruction::{Comparison, Instruction, U}, }; - fn test_compile(expression: &Expression) -> (CompilationModule, FunctionBlock, CompileValue) { + fn test_compile( + expression: &Expression, + ) -> (CompilationModule, CompiledFunction, CompileValue) { let mut module = CompilationModule::default(); let mut function = FunctionBlock { signature: FunctionSignature { @@ -210,11 +360,13 @@ mod tests { optional_arguments: vec!["arg1".into()], rest_argument: Some("arg2".into()), }, + labels: HashMap::new(), + last_label: 0, instructions: vec![], }; let mut local = LocalBlock::root(&mut function, &mut module); let value = local.compile_expression(expression).unwrap(); - (module, function, value) + (module, function.resolve_labels(), value) } #[test] @@ -224,6 +376,85 @@ mod tests { assert_eq!(v, CompileValue::Integer(1)); } + #[test] + fn test_comparison_compile() { + let (m, f, v) = test_compile(&Expression::Call(CallExpression { + callee: Rc::new(Expression::Identifier(">".into())), + arguments: vec![ + Expression::Identifier("a".into()), + Expression::IntegerLiteral(1), + ], + })); + let cs = m.constant_pool.into_map(); + assert_eq!( + cs, + [(U::truncate(1), CompileConstant::Identifier("a".into()))].into() + ); + assert_eq!( + &f.instructions, + &[ + Instruction::PushInteger(U::truncate(1)), + Instruction::PushConstant(U::truncate(1)), + Instruction::Compare(false, Comparison::Gt, U::truncate(2)), + ] + ); + assert_eq!(v, CompileValue::Stack); + } + + #[test] + fn test_if_compile() { + 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))), + })); + assert_eq!( + &f.instructions, + &[ + Instruction::PushBool(false), // 0 + Instruction::Branch(U::truncate(4)), // 1 + Instruction::PushInteger(U::truncate(1)), // 2 + Instruction::Jump(U::truncate(5)), // 3 + Instruction::PushInteger(U::truncate(2)) // 4 + // 5 + ] + ); + assert_eq!(v, CompileValue::Stack); + } + + #[test] + fn test_cond_compile() { + let (_, f, v) = test_compile(&Expression::Cond(CondExpression { + arms: vec![ + CondExpressionArm { + condition: Expression::BooleanLiteral(false), + then: Expression::IntegerLiteral(1), + }, + CondExpressionArm { + condition: Expression::BooleanLiteral(true), + then: Expression::IntegerLiteral(2), + }, + ], + otherwise_arm: Some(Rc::new(Expression::IntegerLiteral(3))), + })); + assert_eq!( + &f.instructions, + &[ + Instruction::PushBool(false), // 0 + Instruction::Branch(U::truncate(4)), // 1 + Instruction::PushInteger(U::truncate(1)), // 2 + Instruction::Jump(U::truncate(9)), // 3 + Instruction::PushBool(true), // 4 + Instruction::Branch(U::truncate(8)), // 5 + Instruction::PushInteger(U::truncate(2)), // 6 + Instruction::Jump(U::truncate(9)), // 7 + Instruction::PushInteger(U::truncate(3)), // 8 + // 9 + ] + ); + assert_eq!(v, CompileValue::Stack); + } + #[test] fn test_compile_lambda_returning_lambda() { // ( ((lambda () (lambda (a b) (+ a b)))) 1 2 ) diff --git a/src/compile/error.rs b/src/compile/error.rs index b1e56bd..c26a2b7 100644 --- a/src/compile/error.rs +++ b/src/compile/error.rs @@ -2,6 +2,12 @@ use crate::compile::syntax::ParseError; #[derive(Debug, thiserror::Error)] pub enum CompileError { - #[error("parse error: {0}")] - Parse(#[from] ParseError), + #[error("parse error: {0:?}")] + Parse(Vec), +} + +impl From> for CompileError { + fn from(value: Vec) -> Self { + Self::Parse(value) + } } diff --git a/src/compile/function.rs b/src/compile/function.rs index 058f008..e9df346 100644 --- a/src/compile/function.rs +++ b/src/compile/function.rs @@ -34,7 +34,7 @@ impl FunctionSignature { { Some(self.required_arguments.len() + self.optional_arguments.len()) } else { - todo!() + None } } } diff --git a/src/compile/instruction.rs b/src/compile/instruction.rs new file mode 100644 index 0000000..1e7c052 --- /dev/null +++ b/src/compile/instruction.rs @@ -0,0 +1,14 @@ +use crate::vm::instruction::Instruction; + +#[derive(Debug, Clone, Copy)] +pub enum Emitted { + Branch(u32), + Jump(u32), + Instruction(Instruction), +} + +impl From for Emitted { + fn from(value: Instruction) -> Self { + Self::Instruction(value) + } +} diff --git a/src/compile/mod.rs b/src/compile/mod.rs index 12dddd8..b6661ef 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -1,6 +1,7 @@ mod block; mod error; mod function; +mod instruction; mod module; mod syntax; mod value; @@ -8,4 +9,4 @@ mod value; pub use error::CompileError; pub use function::FunctionSignature; pub use module::CompilationModule; -pub use syntax::{CallExpression, Expression, FunctionBody, LambdaExpression}; +pub use syntax::{CallExpression, Expression, FunctionBody, LambdaExpression, ParseError}; diff --git a/src/compile/module.rs b/src/compile/module.rs index 6e70a7a..f84d3db 100644 --- a/src/compile/module.rs +++ b/src/compile/module.rs @@ -2,8 +2,11 @@ use std::collections::HashMap; use crate::{ compile::{ - block::FunctionBlock, error::CompileError, function::FunctionSignature, - syntax::FunctionBody, value::CompileConstant, + block::{CompiledFunction, FunctionBlock}, + error::CompileError, + function::FunctionSignature, + syntax::FunctionBody, + value::CompileConstant, }, vm::{ instruction::ConstantId, @@ -14,8 +17,8 @@ use crate::{ #[derive(Default)] pub struct CompilationModule { - constant_pool: Pool, - pub(crate) local_functions: HashMap, + pub(crate) constant_pool: Pool, + pub(crate) local_functions: HashMap, local_function_index: u32, root: Option, } @@ -41,6 +44,7 @@ impl CompilationModule { self.local_function_index += 1; let mut function = FunctionBlock::new(signature); function.compile_body(self, body)?; + let function = function.resolve_labels(); self.local_functions.insert(index, function); if root { self.root = Some(index); @@ -66,6 +70,9 @@ impl CompilationModule { key, match value { CompileConstant::Integer(value) => ModuleConstant::Integer(value), + CompileConstant::Identifier(identifier) => { + ModuleConstant::Identifier(identifier) + } CompileConstant::LocalFunction(index) => { let address = *function_offsets.get(&index).unwrap(); ModuleConstant::LocalFunction(address) diff --git a/src/compile/syntax.rs b/src/compile/syntax.rs deleted file mode 100644 index 1b1e971..0000000 --- a/src/compile/syntax.rs +++ /dev/null @@ -1,264 +0,0 @@ -use std::rc::Rc; - -use crate::{ - compile::function::FunctionSignature, - vm::value::{ConsCell, Keyword, Value}, -}; - -#[derive(Debug, thiserror::Error)] -pub enum ParseError { - #[error("Non-expression value")] - NonExpressionValue, - #[error("Expected argument list: {0}")] - ExpectedArgumentList(Value), -} - -#[derive(Debug, PartialEq)] -pub struct FunctionBody { - pub head: Vec, - pub tail: Rc, -} - -#[derive(Debug, PartialEq)] -pub struct LambdaExpression { - pub signature: FunctionSignature, - pub body: FunctionBody, -} - -#[derive(Debug, PartialEq)] -pub struct CallExpression { - pub callee: Rc, - pub arguments: Vec, -} - -#[derive(Debug, PartialEq)] -pub enum Expression { - Nil, - BooleanLiteral(bool), - IntegerLiteral(i64), - Identifier(Rc), - Lambda(LambdaExpression), - Call(CallExpression), -} - -impl FunctionSignature { - fn parse(mut value: &Value) -> Result { - enum Mode { - Required, - Optional, - Rest, - } - - let mut required_arguments = vec![]; - let mut optional_arguments = vec![]; - let mut rest_argument = None; - let mut mode = Mode::Required; - - while !value.is_nil() { - let Value::Cons(cons) = value else { - todo!(); - }; - let ConsCell(car, cdr) = cons.as_ref(); - - match (&mode, car) { - (Mode::Required, Value::Identifier(arg)) => { - required_arguments.push(arg.clone()); - } - (Mode::Optional, Value::Identifier(arg)) => { - optional_arguments.push(arg.clone()); - } - (Mode::Rest, Value::Identifier(arg)) => { - if rest_argument.is_some() { - todo!(); - } - rest_argument = Some(arg.clone()); - } - (Mode::Required, Value::Keyword(Keyword::Optional)) => { - mode = Mode::Optional; - } - (Mode::Required, Value::Keyword(Keyword::Rest)) => { - mode = Mode::Rest; - } - (Mode::Optional, Value::Keyword(Keyword::Rest)) => { - if optional_arguments.is_empty() { - todo!(); - } - mode = Mode::Rest; - } - _ => todo!(), - } - - value = cdr; - } - - match mode { - Mode::Required => (), - Mode::Optional => { - if optional_arguments.is_empty() { - todo!() - } - } - Mode::Rest => { - if rest_argument.is_none() { - todo!() - } - } - } - - Ok(Self { - required_arguments, - optional_arguments, - rest_argument, - }) - } -} - -impl FunctionBody { - fn parse(mut value: &Value) -> Result { - let mut expressions = vec![]; - while !value.is_nil() { - let Value::Cons(cons) = value else { todo!() }; - let ConsCell(car, cdr) = cons.as_ref(); - - let expression = Expression::parse(car)?; - expressions.push(expression); - - value = cdr; - } - let Some(tail) = expressions.pop() else { - todo!() - }; - Ok(Self { - head: expressions, - tail: tail.into(), - }) - } -} - -impl LambdaExpression { - fn parse(value: &ConsCell) -> Result { - let ConsCell(car, cdr) = value; - let signature = FunctionSignature::parse(car)?; - let body = FunctionBody::parse(cdr)?; - Ok(Self { signature, body }) - } -} - -impl Expression { - fn parse_cons(value: &ConsCell) -> Result { - let ConsCell(car, cdr) = value; - match car { - Value::Keyword(Keyword::Lambda) => { - let Value::Cons(cdr) = cdr else { - todo!(); - }; - LambdaExpression::parse(cdr).map(Self::Lambda) - } - _ => { - let callee = Expression::parse(car)?; - - let mut arguments = vec![]; - let mut list = cdr; - while !list.is_nil() { - let Value::Cons(cons) = list else { - todo!(); - }; - let ConsCell(car, cdr) = cons.as_ref(); - - let expression = Expression::parse(car)?; - arguments.push(expression); - - list = cdr; - } - - Ok(Self::Call(CallExpression { - callee: callee.into(), - arguments, - })) - } - } - } - - pub fn parse(value: &Value) -> Result { - match value { - Value::Nil => Ok(Self::Nil), - Value::Boolean(value) => Ok(Self::BooleanLiteral(*value)), - Value::Integer(value) => Ok(Self::IntegerLiteral(*value)), - Value::Identifier(value) => Ok(Self::Identifier(value.clone())), - Value::Cons(cons) => Self::parse_cons(cons), - _ => Err(ParseError::NonExpressionValue), - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - compile::{ - function::FunctionSignature, - syntax::{CallExpression, Expression, FunctionBody, LambdaExpression}, - }, - vm::value::{Keyword, Value}, - }; - - #[test] - fn test_parse_basic() { - let v = Value::Nil; - let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::Nil); - - let v = Value::Integer(1234); - let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::IntegerLiteral(1234)); - - let v = Value::Boolean(false); - let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::BooleanLiteral(false)); - - let v = Value::Identifier("a".into()); - let e = Expression::parse(&v).unwrap(); - assert_eq!(e, Expression::Identifier("a".into())); - } - - #[test] - fn test_parse_lambda() { - let args = Value::list_or_nil([ - Value::Identifier("a".into()), - Value::Keyword(Keyword::Optional), - Value::Identifier("b".into()), - Value::Keyword(Keyword::Rest), - Value::Identifier("c".into()), - ]); - let body = Value::list_or_nil([ - Value::Identifier("+".into()), - Value::Identifier("a".into()), - Value::Integer(1), - ]); - let lambda = Value::Keyword(Keyword::Lambda).cons(args.cons(body.cons(Value::Nil))); - let expr = Expression::parse(&lambda).unwrap(); - - assert_eq!( - expr, - Expression::Lambda(LambdaExpression { - signature: FunctionSignature { - required_arguments: vec!["a".into()], - optional_arguments: vec!["b".into()], - rest_argument: Some("c".into()) - }, - body: FunctionBody { - head: vec![], - tail: Expression::Call(CallExpression { - callee: Expression::Identifier("+".into()).into(), - arguments: vec![ - Expression::Identifier("a".into()), - Expression::IntegerLiteral(1) - ] - }) - .into() - } - }) - ); - - let lambda = Value::list_or_nil([Value::Keyword(())]); - } -} diff --git a/src/compile/syntax/call.rs b/src/compile/syntax/call.rs new file mode 100644 index 0000000..d350a8e --- /dev/null +++ b/src/compile/syntax/call.rs @@ -0,0 +1,97 @@ +use std::rc::Rc; + +use crate::{ + compile::{ + Expression, ParseError, + syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind}, + }, + vm::value::{ConsCell, Value}, +}; + +#[derive(Debug, PartialEq)] +pub struct CallExpression { + pub callee: Rc, + pub arguments: Vec, +} + +impl CallExpression { + pub(super) fn parse(cons: &ConsCell, input: &Value) -> Result { + let ConsCell(car, cdr) = cons; + let callee = Expression::parse_inner(car); + + let mut arguments = vec![]; + let mut list = cdr; + while !list.is_nil() { + let Value::Cons(cons) = list else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ProperList, + ExpectedWhere::InCallExpression, + ), + }); + }; + let ConsCell(car, cdr) = cons.as_ref(); + + let expression = Expression::parse_inner(car); + arguments.push(expression); + + list = cdr; + } + + Ok(Self { + callee: callee.into(), + arguments, + }) + } +} + +impl CollectErrors for CallExpression { + fn collect_errors(&self, errors: &mut Vec) -> bool { + let mut r = self.callee.collect_errors(errors); + for expr in self.arguments.iter() { + r |= expr.collect_errors(errors); + } + r + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use crate::{ + compile::{ + CallExpression, Expression, ParseError, + syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind}, + }, + vm::value::Value, + }; + + #[test] + fn test_parse_call() { + let v = Value::list_or_nil([Value::Identifier("a".into())]); + let e = Expression::parse(&v).unwrap(); + assert_eq!( + e, + Expression::Call(CallExpression { + callee: Rc::new(Expression::Identifier("a".into())), + arguments: vec![] + }) + ); + + // Syntax errors + let v = Value::Identifier("a".into()).cons(Value::Integer(1)); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + error: ParseErrorKind::Expected( + ExpectedWhat::ProperList, + ExpectedWhere::InCallExpression + ), + }] + ); + } +} diff --git a/src/compile/syntax/condition.rs b/src/compile/syntax/condition.rs new file mode 100644 index 0000000..4723fc1 --- /dev/null +++ b/src/compile/syntax/condition.rs @@ -0,0 +1,490 @@ +use std::rc::Rc; + +use crate::{ + compile::{ + Expression, ParseError, + syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind}, + }, + vm::value::{ConsCell, Keyword, Value}, +}; + +#[derive(Debug, PartialEq)] +pub struct IfExpression { + pub condition: Rc, + pub if_true: Rc, + pub if_false: Option>, +} + +#[derive(Debug, PartialEq)] +pub struct CondExpressionArm { + pub condition: Expression, + pub then: Expression, +} +#[derive(Debug, PartialEq)] +pub struct CondExpression { + pub arms: Vec, + pub otherwise_arm: Option>, +} + +impl IfExpression { + pub(super) fn parse(value: &Value, input: &Value) -> Result { + let Value::Cons(value) = value else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::Condition, + ExpectedWhere::AfterKeyword(Keyword::If), + ), + }); + }; + let ConsCell(condition, cdr) = value.as_ref(); + let condition = Expression::parse_inner(condition); + let Value::Cons(cdr) = cdr else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::Expression, + ExpectedWhere::AfterCondition, + ), + }); + }; + let ConsCell(if_true, cdr) = cdr.as_ref(); + let if_true = Expression::parse_inner(if_true); + let if_false = if cdr.is_nil() { + None + } else { + let Value::Cons(cdr) = cdr else { todo!() }; + let ConsCell(if_false, cdr) = cdr.as_ref(); + if !cdr.is_nil() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::extraneous(cdr), + }); + } + Some(Expression::parse_inner(if_false)) + }; + Ok(Self { + condition: condition.into(), + if_true: if_true.into(), + if_false: if_false.map(Into::into), + }) + } +} + +impl CondExpressionArm { + fn parse(cons: &ConsCell, input: &Value) -> Result { + let ConsCell(condition, cdr) = cons; + let condition = Expression::parse_inner(condition); + let Value::Cons(cons) = cdr else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::Expression, + ExpectedWhere::InConditionArm, + ), + }); + }; + let ConsCell(then, cdr) = cons.as_ref(); + if !cdr.is_nil() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::extraneous(cdr), + }); + } + let then = Expression::parse_inner(then); + + Ok(Self { condition, then }) + } +} + +impl CondExpression { + pub(super) fn parse(mut value: &Value, input: &Value) -> Result { + let mut arms = vec![]; + let mut otherwise_arm = None; + + while !value.is_nil() { + let Value::Cons(cons) = value else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ConditionList, + ExpectedWhere::AfterKeyword(Keyword::Cond), + ), + }); + }; + let ConsCell(arm, cdr) = cons.as_ref(); + + // Arm must be a list + let Value::Cons(arm_cons) = arm else { + return Err(ParseError { + input: arm.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::Condition, + ExpectedWhere::InConditionList, + ), + }); + }; + let ConsCell(arm_condition, arm_condition_cdr) = arm_cons.as_ref(); + + match arm_condition { + Value::Keyword(Keyword::Otherwise) => { + if otherwise_arm.is_some() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::extraneous(arm), + }); + } + let Value::Cons(otherwise_cons) = arm_condition_cdr else { + return Err(ParseError { + input: arm.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ProperList, + ExpectedWhere::InConditionList, + ), + }); + }; + let ConsCell(arm_expr, arm_cdr) = otherwise_cons.as_ref(); + if !arm_cdr.is_nil() { + return Err(ParseError { + input: arm.clone(), + error: ParseErrorKind::extraneous(arm_cdr), + }); + } + let arm = Expression::parse_inner(arm_expr); + otherwise_arm = Some(Rc::new(arm)); + } + _ => { + if otherwise_arm.is_some() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::extraneous(arm), + }); + } + let arm = CondExpressionArm::parse(arm_cons, arm)?; + arms.push(arm); + } + } + + value = cdr; + } + + if arms.is_empty() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ConditionList, + ExpectedWhere::AfterKeyword(Keyword::Cond), + ), + }); + } + + Ok(Self { + arms, + otherwise_arm, + }) + } +} + +impl CollectErrors for IfExpression { + fn collect_errors(&self, errors: &mut Vec) -> bool { + let a = self.condition.collect_errors(errors); + let b = self.if_true.collect_errors(errors); + let c = self + .if_false + .as_ref() + .map(|x| x.collect_errors(errors)) + .unwrap_or_default(); + a | b | c + } +} + +impl CollectErrors for CondExpression { + fn collect_errors(&self, errors: &mut Vec) -> bool { + let mut r = false; + for arm in self.arms.iter() { + r |= arm.condition.collect_errors(errors); + r |= arm.then.collect_errors(errors); + } + if let Some(otherwise_arm) = self.otherwise_arm.as_ref() { + r |= otherwise_arm.collect_errors(errors); + } + r + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use crate::{ + compile::{ + Expression, ParseError, + syntax::{ + CondExpression, CondExpressionArm, ExpectedWhat, ExpectedWhere, IfExpression, + ParseErrorKind, + }, + }, + vm::value::{ConsCell, Keyword, Value}, + }; + + #[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 v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1]); + let e = Expression::parse(&v).unwrap(); + assert_eq!( + e, + Expression::Cond(CondExpression { + arms: vec![ + CondExpressionArm { + condition: Expression::BooleanLiteral(true), + then: Expression::Identifier("a".into()) + }, + CondExpressionArm { + condition: Expression::BooleanLiteral(false), + then: Expression::Identifier("b".into()) + } + ], + otherwise_arm: None + }) + ); + + // with &otherwise branch + let b0 = Value::list_or_nil([Value::Boolean(true), Value::Identifier("a".into())]); + let b1 = Value::list_or_nil([ + Value::Keyword(Keyword::Otherwise), + Value::Identifier("b".into()), + ]); + let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1]); + let e = Expression::parse(&v).unwrap(); + assert_eq!( + e, + Expression::Cond(CondExpression { + arms: vec![CondExpressionArm { + condition: Expression::BooleanLiteral(true), + then: Expression::Identifier("a".into()) + },], + otherwise_arm: Some(Rc::new(Expression::Identifier("b".into()))) + }) + ); + + // syntax error + let v = Value::list_or_nil([Value::Keyword(Keyword::Cond)]); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + error: ParseErrorKind::Expected( + ExpectedWhat::ConditionList, + ExpectedWhere::AfterKeyword(Keyword::Cond) + ) + }] + ); + + let v = Value::Keyword(Keyword::Cond).cons(Value::Identifier("a".into())); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + error: ParseErrorKind::Expected( + ExpectedWhat::ConditionList, + ExpectedWhere::AfterKeyword(Keyword::Cond) + ) + }] + ); + + let b0 = Value::list_or_nil([Value::Boolean(false)]); + 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::Expected( + ExpectedWhat::Expression, + ExpectedWhere::InConditionArm + ) + }] + ); + let b0 = Value::Boolean(false); + 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::Expected( + ExpectedWhat::Condition, + ExpectedWhere::InConditionList + ) + }] + ); + let b0 = Value::Keyword(Keyword::Otherwise); + 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::Expected( + ExpectedWhat::Condition, + ExpectedWhere::InConditionList + ) + }] + ); + let b0 = Value::Keyword(Keyword::Otherwise).cons(Value::Integer(1)); + 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::Expected( + ExpectedWhat::ProperList, + ExpectedWhere::InConditionList + ) + }] + ); + let b0 = Value::Boolean(false).cons(Value::Boolean(false)); + 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::Expected( + ExpectedWhat::Expression, + ExpectedWhere::InConditionArm + ) + }] + ); + 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), + ]); + 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)]); + let v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0.clone(), b0.clone()]); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + 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 v = Value::list_or_nil([Value::Keyword(Keyword::Cond), b0, b1.clone()]); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + error: ParseErrorKind::extraneous(&b1) + }] + ); + } + + #[test] + fn test_parse_if() { + // without false branch + let v = Value::list_or_nil([ + Value::Keyword(Keyword::If), + Value::Boolean(true), + Value::Integer(1), + ]); + let e = Expression::parse(&v).unwrap(); + assert_eq!( + e, + Expression::If(IfExpression { + condition: Rc::new(Expression::BooleanLiteral(true)), + if_true: Rc::new(Expression::IntegerLiteral(1)), + if_false: None + }) + ); + + // with false branch + let v = Value::list_or_nil([ + Value::Keyword(Keyword::If), + Value::Boolean(false), + Value::Integer(1), + Value::Integer(2), + ]); + let e = Expression::parse(&v).unwrap(); + assert_eq!( + e, + Expression::If(IfExpression { + condition: Rc::new(Expression::BooleanLiteral(false)), + if_true: Rc::new(Expression::IntegerLiteral(1)), + if_false: Some(Rc::new(Expression::IntegerLiteral(2))) + }) + ); + + // invalid syntax + let v = Value::list_or_nil([Value::Keyword(Keyword::If)]); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + error: ParseErrorKind::Expected( + ExpectedWhat::Condition, + ExpectedWhere::AfterKeyword(Keyword::If) + ) + }] + ); + let v = Value::list_or_nil([Value::Keyword(Keyword::If), Value::Boolean(true)]); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + error: ParseErrorKind::Expected( + ExpectedWhat::Expression, + ExpectedWhere::AfterCondition + ) + }] + ); + let v = Value::list_or_nil([ + Value::Keyword(Keyword::If), + Value::Boolean(true), + Value::Integer(1), + Value::Integer(2), + Value::Integer(3), + ]); + let e = Expression::parse(&v).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: v, + error: ParseErrorKind::ExtraneousExpressionList(Rc::new(ConsCell::end( + Value::Integer(3) + ))) + }] + ); + } +} diff --git a/src/compile/syntax/error.rs b/src/compile/syntax/error.rs new file mode 100644 index 0000000..c805634 --- /dev/null +++ b/src/compile/syntax/error.rs @@ -0,0 +1,87 @@ +#![coverage(off)] + +use std::{error::Error as StdError, fmt, rc::Rc}; + +use crate::vm::value::{ConsCell, Keyword, Value}; + +#[derive(Debug, Clone, PartialEq)] +pub struct ParseError { + pub input: Value, + pub error: ParseErrorKind, +} + +#[derive(Debug, Clone, PartialEq, thiserror::Error)] +pub enum ParseErrorKind { + #[error("Not an expression")] + NotAnExpression, + #[error("Expected {0} {1}")] + Expected(ExpectedWhat, ExpectedWhere), + #[error("Extraneous expression: {0}")] + ExtraneousExpression(Value), + #[error("Extraneous expression(s): {0}")] + ExtraneousExpressionList(Rc), +} + +#[derive(Debug, Clone, PartialEq, thiserror::Error)] +pub enum ExpectedWhat { + #[error("an expression")] + Expression, + #[error("an argument list")] + ArgumentList, + #[error("a condition")] + Condition, + #[error("a condition list")] + ConditionList, + #[error("at least one argument")] + AtLeastOneArgument, + #[error("exactly one argument")] + ExactlyOneArgument, + #[error("an argument, &optional or &rest keywords")] + ArgumentSpec, + #[error("a proper list")] + ProperList, +} + +#[derive(Debug, Clone, PartialEq, thiserror::Error)] +pub enum ExpectedWhere { + #[error("after a condition")] + AfterCondition, + #[error("after the \"{0}\" keyword")] + AfterKeyword(Keyword), + #[error("after the argument list in a {0}")] + AfterArgumentList(Keyword), + #[error("in the argument list")] + InArgumentList, + #[error("in the call expression")] + InCallExpression, + #[error("in the condition list")] + InConditionList, + #[error("in the condition arm")] + InConditionArm, +} + +pub(super) trait CollectErrors { + fn collect_errors(&self, errors: &mut Vec) -> bool; +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.error, f) + } +} + +impl StdError for ParseError { + fn description(&self) -> &str { + "Parse error" + } +} + +impl ParseErrorKind { + pub fn extraneous(value: &Value) -> Self { + match value { + Value::Nil => unreachable!(), + Value::Cons(cons) => Self::ExtraneousExpressionList(cons.clone()), + _ => Self::ExtraneousExpression(value.clone()), + } + } +} diff --git a/src/compile/syntax/function.rs b/src/compile/syntax/function.rs new file mode 100644 index 0000000..a4e3d2d --- /dev/null +++ b/src/compile/syntax/function.rs @@ -0,0 +1,373 @@ +use std::rc::Rc; + +use crate::{ + compile::{ + Expression, FunctionSignature, ParseError, + syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind}, + }, + vm::value::{ConsCell, Keyword, Value}, +}; + +#[derive(Debug, PartialEq)] +pub struct FunctionBody { + pub head: Vec, + pub tail: Rc, +} + +impl FunctionSignature { + pub(super) fn parse(mut value: &Value, input: &Value) -> Result { + enum Mode { + Required, + Optional, + Rest, + } + + let mut required_arguments = vec![]; + let mut optional_arguments = vec![]; + let mut rest_argument = None; + let mut mode = Mode::Required; + + while !value.is_nil() { + let Value::Cons(cons) = value else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ArgumentSpec, + ExpectedWhere::InArgumentList, + ), + }); + }; + let ConsCell(car, cdr) = cons.as_ref(); + + match (&mode, car) { + (Mode::Required, Value::Identifier(arg)) => { + required_arguments.push(arg.clone()); + } + (Mode::Optional, Value::Identifier(arg)) => { + optional_arguments.push(arg.clone()); + } + (Mode::Rest, Value::Identifier(arg)) => { + if rest_argument.is_some() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ExactlyOneArgument, + ExpectedWhere::AfterKeyword(Keyword::Rest), + ), + }); + } + rest_argument = Some(arg.clone()); + } + (Mode::Required, Value::Keyword(Keyword::Optional)) => { + mode = Mode::Optional; + } + (Mode::Required, Value::Keyword(Keyword::Rest)) => { + mode = Mode::Rest; + } + (Mode::Optional, Value::Keyword(Keyword::Rest)) => { + if optional_arguments.is_empty() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::AtLeastOneArgument, + ExpectedWhere::AfterKeyword(Keyword::Optional), + ), + }); + } + mode = Mode::Rest; + } + _ => { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ArgumentSpec, + ExpectedWhere::InArgumentList, + ), + }); + } + } + + value = cdr; + } + + match mode { + Mode::Required => (), + Mode::Optional => { + if optional_arguments.is_empty() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::AtLeastOneArgument, + ExpectedWhere::AfterKeyword(Keyword::Optional), + ), + }); + } + } + Mode::Rest => { + if rest_argument.is_none() { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ExactlyOneArgument, + ExpectedWhere::AfterKeyword(Keyword::Rest), + ), + }); + } + } + } + + Ok(Self { + required_arguments, + optional_arguments, + rest_argument, + }) + } +} + +impl FunctionBody { + pub(super) fn parse( + mut value: &Value, + input: &Value, + inside: Keyword, + ) -> Result { + let mut expressions = vec![]; + while !value.is_nil() { + let Value::Cons(cons) = value else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ProperList, + ExpectedWhere::AfterArgumentList(inside), + ), + }); + }; + let ConsCell(car, cdr) = cons.as_ref(); + + let expression = Expression::parse_inner(car); + expressions.push(expression); + + value = cdr; + } + let Some(tail) = expressions.pop() else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::Expression, + ExpectedWhere::AfterArgumentList(inside), + ), + }); + }; + Ok(Self { + head: expressions, + tail: tail.into(), + }) + } +} + +impl CollectErrors for FunctionBody { + fn collect_errors(&self, errors: &mut Vec) -> bool { + let mut r = false; + for expr in self.head.iter() { + r |= expr.collect_errors(errors); + } + r |= self.tail.collect_errors(errors); + r + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use crate::{ + compile::{ + Expression, FunctionBody, FunctionSignature, ParseError, + syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind}, + }, + vm::value::{Keyword, Value}, + }; + + #[test] + fn test_parse_function_body() { + let v = Value::list_or_nil([Value::Integer(1)]); + let e = FunctionBody::parse(&v, &v, Keyword::Lambda).unwrap(); + assert_eq!( + e, + FunctionBody { + head: vec![], + tail: Rc::new(Expression::IntegerLiteral(1)) + } + ); + + let v = Value::list_or_nil([Value::Integer(1), Value::Integer(2), Value::Integer(3)]); + let e = FunctionBody::parse(&v, &v, Keyword::Lambda).unwrap(); + assert_eq!( + e, + FunctionBody { + head: vec![Expression::IntegerLiteral(1), Expression::IntegerLiteral(2)], + tail: Rc::new(Expression::IntegerLiteral(3)) + } + ); + + // syntax error + let v = Value::list_or_nil([]); + let e = FunctionBody::parse(&v, &v, Keyword::Lambda).unwrap_err(); + assert_eq!( + e, + ParseError { + input: v, + error: ParseErrorKind::Expected( + ExpectedWhat::Expression, + ExpectedWhere::AfterArgumentList(Keyword::Lambda) + ), + } + ); + let v = Value::Identifier("a".into()).cons(Value::Identifier("b".into())); + let e = FunctionBody::parse(&v, &v, Keyword::Lambda).unwrap_err(); + assert_eq!( + e, + ParseError { + input: v, + error: ParseErrorKind::Expected( + ExpectedWhat::ProperList, + ExpectedWhere::AfterArgumentList(Keyword::Lambda) + ), + } + ); + } + + #[test] + fn test_parse_function_signature() { + let args = Value::list_or_nil([]); + let v = FunctionSignature::parse(&args, &args).unwrap(); + assert_eq!( + v, + FunctionSignature { + required_arguments: vec![], + optional_arguments: vec![], + rest_argument: None + } + ); + let args = + Value::list_or_nil([Value::Identifier("a".into()), Value::Identifier("b".into())]); + let v = FunctionSignature::parse(&args, &args).unwrap(); + assert_eq!( + v, + FunctionSignature { + required_arguments: vec!["a".into(), "b".into()], + optional_arguments: vec![], + rest_argument: None + } + ); + let args = Value::list_or_nil([ + Value::Identifier("a".into()), + Value::Keyword(Keyword::Optional), + Value::Identifier("b".into()), + ]); + let v = FunctionSignature::parse(&args, &args).unwrap(); + assert_eq!( + v, + FunctionSignature { + required_arguments: vec!["a".into()], + optional_arguments: vec!["b".into()], + rest_argument: None + } + ); + let args = Value::list_or_nil([ + Value::Identifier("a".into()), + Value::Keyword(Keyword::Rest), + Value::Identifier("b".into()), + ]); + let v = FunctionSignature::parse(&args, &args).unwrap(); + assert_eq!( + v, + FunctionSignature { + required_arguments: vec!["a".into()], + optional_arguments: vec![], + rest_argument: Some("b".into()) + } + ); + let args = + Value::list_or_nil([Value::Keyword(Keyword::Rest), Value::Identifier("c".into())]); + let v = FunctionSignature::parse(&args, &args).unwrap(); + assert_eq!( + v, + FunctionSignature { + required_arguments: vec![], + optional_arguments: vec![], + rest_argument: Some("c".into()) + } + ); + let args = Value::list_or_nil([ + Value::Keyword(Keyword::Optional), + Value::Identifier("b".into()), + Value::Keyword(Keyword::Rest), + Value::Identifier("c".into()), + ]); + let v = FunctionSignature::parse(&args, &args).unwrap(); + assert_eq!( + v, + FunctionSignature { + required_arguments: vec![], + optional_arguments: vec!["b".into()], + rest_argument: Some("c".into()) + } + ); + let args = Value::list_or_nil([ + Value::Identifier("a".into()), + Value::Keyword(Keyword::Rest), + Value::Identifier("c".into()), + ]); + let v = FunctionSignature::parse(&args, &args).unwrap(); + assert_eq!( + v, + FunctionSignature { + required_arguments: vec!["a".into()], + optional_arguments: vec![], + rest_argument: Some("c".into()) + } + ); + + // syntax errors + let args = Value::list_or_nil([Value::Keyword(Keyword::Optional)]); + let e = FunctionSignature::parse(&args, &args).unwrap_err(); + assert_eq!( + e, + ParseError { + input: args, + error: ParseErrorKind::Expected( + ExpectedWhat::AtLeastOneArgument, + ExpectedWhere::AfterKeyword(Keyword::Optional) + ) + } + ); + let args = Value::list_or_nil([ + Value::Keyword(Keyword::Rest), + Value::Identifier("a".into()), + Value::Identifier("b".into()), + ]); + let e = FunctionSignature::parse(&args, &args).unwrap_err(); + assert_eq!( + e, + ParseError { + input: args, + error: ParseErrorKind::Expected( + ExpectedWhat::ExactlyOneArgument, + ExpectedWhere::AfterKeyword(Keyword::Rest) + ) + } + ); + let args = Value::list_or_nil([Value::Boolean(false)]); + let e = FunctionSignature::parse(&args, &args).unwrap_err(); + assert_eq!( + e, + ParseError { + input: args, + error: ParseErrorKind::Expected( + ExpectedWhat::ArgumentSpec, + ExpectedWhere::InArgumentList + ) + } + ); + } +} diff --git a/src/compile/syntax/lambda.rs b/src/compile/syntax/lambda.rs new file mode 100644 index 0000000..af4f6ed --- /dev/null +++ b/src/compile/syntax/lambda.rs @@ -0,0 +1,115 @@ +use crate::{ + compile::{ + FunctionBody, FunctionSignature, ParseError, + syntax::{CollectErrors, ExpectedWhat, ExpectedWhere, ParseErrorKind}, + }, + vm::value::{ConsCell, Keyword, Value}, +}; + +#[derive(Debug, PartialEq)] +pub struct LambdaExpression { + pub signature: FunctionSignature, + pub body: FunctionBody, +} + +impl LambdaExpression { + pub(super) fn parse(value: &Value, input: &Value) -> Result { + let Value::Cons(value) = value else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ArgumentList, + ExpectedWhere::AfterKeyword(Keyword::Lambda), + ), + }); + }; + let ConsCell(car, cdr) = value.as_ref(); + let signature = FunctionSignature::parse(car, input)?; + let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?; + Ok(Self { signature, body }) + } +} + +impl CollectErrors for LambdaExpression { + fn collect_errors(&self, errors: &mut Vec) -> bool { + self.body.collect_errors(errors) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + compile::{ + CallExpression, Expression, FunctionBody, FunctionSignature, LambdaExpression, + ParseError, + syntax::{ExpectedWhat, ExpectedWhere, ParseErrorKind}, + }, + vm::value::{Keyword, Value}, + }; + + #[test] + fn test_parse_lambda() { + let args = Value::list_or_nil([ + Value::Identifier("a".into()), + Value::Keyword(Keyword::Optional), + Value::Identifier("b".into()), + Value::Keyword(Keyword::Rest), + Value::Identifier("c".into()), + ]); + let body = Value::list_or_nil([ + Value::Identifier("+".into()), + Value::Identifier("a".into()), + Value::Integer(1), + ]); + let lambda = Value::Keyword(Keyword::Lambda).cons(args.cons(body.cons(Value::Nil))); + let expr = Expression::parse(&lambda).unwrap(); + + assert_eq!( + expr, + Expression::Lambda(LambdaExpression { + signature: FunctionSignature { + required_arguments: vec!["a".into()], + optional_arguments: vec!["b".into()], + rest_argument: Some("c".into()) + }, + body: FunctionBody { + head: vec![], + tail: Expression::Call(CallExpression { + callee: Expression::Identifier("+".into()).into(), + arguments: vec![ + Expression::Identifier("a".into()), + Expression::IntegerLiteral(1) + ] + }) + .into() + } + }) + ); + + // syntax errors + let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda)]); + let e = Expression::parse(&lambda).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: lambda, + error: ParseErrorKind::Expected( + ExpectedWhat::ArgumentList, + ExpectedWhere::AfterKeyword(Keyword::Lambda) + ) + }] + ); + let lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda), Value::Nil]); + let e = Expression::parse(&lambda).unwrap_err(); + assert_eq!( + e, + vec![ParseError { + input: lambda, + error: ParseErrorKind::Expected( + ExpectedWhat::Expression, + ExpectedWhere::AfterArgumentList(Keyword::Lambda) + ) + }] + ); + } +} diff --git a/src/compile/syntax/mod.rs b/src/compile/syntax/mod.rs new file mode 100644 index 0000000..137efdd --- /dev/null +++ b/src/compile/syntax/mod.rs @@ -0,0 +1,150 @@ +use std::{error::Error as StdError, fmt, rc::Rc}; + +use crate::vm::value::{ConsCell, Keyword, Value}; + +mod call; +mod condition; +mod error; +mod function; +mod lambda; + +pub use call::*; +pub use condition::*; +pub use error::*; +pub use function::*; +pub use lambda::*; + +#[derive(Debug, PartialEq)] +pub enum Expression { + Nil, + BooleanLiteral(bool), + IntegerLiteral(i64), + Identifier(Rc), + Lambda(LambdaExpression), + Call(CallExpression), + If(IfExpression), + Cond(CondExpression), + SyntaxError(ParseError), +} + +impl Expression { + fn map_or Self>(result: Result, map: F) -> Self { + match result { + Ok(r) => map(r), + Err(error) => Self::SyntaxError(error), + } + } + + pub fn parse(value: &Value) -> Result> { + let inner = Self::parse_inner(value); + let mut errors = vec![]; + if inner.collect_errors(&mut errors) { + Err(errors) + } else { + Ok(inner) + } + } + + fn parse_inner(value: &Value) -> Self { + match value { + Value::Nil => Self::Nil, + Value::Boolean(value) => Self::BooleanLiteral(*value), + Value::Integer(value) => Self::IntegerLiteral(*value), + Value::Identifier(value) => Self::Identifier(value.clone()), + Value::Cons(cons) => { + let ConsCell(car, cdr) = cons.as_ref(); + match car { + Value::Keyword(Keyword::Lambda) => { + Self::map_or(LambdaExpression::parse(cdr, value), Expression::Lambda) + } + Value::Keyword(Keyword::If) => { + Self::map_or(IfExpression::parse(cdr, value), Expression::If) + } + Value::Keyword(Keyword::Cond) => { + Self::map_or(CondExpression::parse(cdr, value), Expression::Cond) + } + _ => Self::map_or(CallExpression::parse(cons, value), Expression::Call), + } + } + Value::Keyword(_) => todo!(), + Value::NativeFunction(_) | Value::BytecodeFunction(_) => todo!(), + } + } +} + +impl CollectErrors for Expression { + fn collect_errors(&self, errors: &mut Vec) -> bool { + match self { + Self::SyntaxError(error) => { + errors.push(error.clone()); + true + } + Self::If(condition) => condition.collect_errors(errors), + Self::Cond(condition) => condition.collect_errors(errors), + Self::Lambda(condition) => condition.collect_errors(errors), + Self::Call(call) => call.collect_errors(errors), + Self::Nil | Self::IntegerLiteral(_) | Self::Identifier(_) | Self::BooleanLiteral(_) => { + false + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + compile::syntax::{ExpectedWhat, ExpectedWhere, Expression, ParseError, ParseErrorKind}, + vm::value::{Keyword, Value}, + }; + + #[test] + fn test_parse_basic() { + let v = Value::Nil; + let e = Expression::parse(&v).unwrap(); + assert_eq!(e, Expression::Nil); + + let v = Value::Integer(1234); + let e = Expression::parse(&v).unwrap(); + assert_eq!(e, Expression::IntegerLiteral(1234)); + + let v = Value::Boolean(false); + let e = Expression::parse(&v).unwrap(); + assert_eq!(e, Expression::BooleanLiteral(false)); + + let v = Value::Identifier("a".into()); + let e = Expression::parse(&v).unwrap(); + assert_eq!(e, Expression::Identifier("a".into())); + } + + #[test] + fn test_nested_syntax_error() { + let inner_if = Value::list_or_nil([Value::Keyword(Keyword::If)]); + let inner_lambda = Value::list_or_nil([Value::Keyword(Keyword::Lambda)]); + let outer_if = Value::list_or_nil([ + Value::Keyword(Keyword::If), + inner_if.clone(), + inner_lambda.clone(), + Value::Integer(3), + ]); + let e = Expression::parse(&outer_if).unwrap_err(); + assert_eq!( + e, + vec![ + ParseError { + input: inner_if, + error: ParseErrorKind::Expected( + ExpectedWhat::Condition, + ExpectedWhere::AfterKeyword(Keyword::If) + ) + }, + ParseError { + input: inner_lambda, + error: ParseErrorKind::Expected( + ExpectedWhat::ArgumentList, + ExpectedWhere::AfterKeyword(Keyword::Lambda) + ) + } + ] + ); + } +} diff --git a/src/compile/value.rs b/src/compile/value.rs index 95b6cc9..c33cfe0 100644 --- a/src/compile/value.rs +++ b/src/compile/value.rs @@ -1,9 +1,14 @@ +use std::rc::Rc; + +use crate::vm::instruction::Comparison; + #[derive(Debug, PartialEq)] pub enum CompileValue { Integer(i64), Boolean(bool), LocalFunction(u32), Argument(usize), + Global(Rc), Stack, Nil, } @@ -12,12 +17,14 @@ pub enum CompileValue { pub enum CompileConstant { Integer(i64), LocalFunction(u32), + Identifier(Rc), } #[derive(Debug, PartialEq, Clone, Copy)] pub enum BuiltinFunction { Add, Sub, + Cmp(bool, Comparison), } impl BuiltinFunction { @@ -25,6 +32,12 @@ impl BuiltinFunction { match identifier { "+" => Some(Self::Add), "-" => Some(Self::Sub), + ">" => Some(Self::Cmp(false, Comparison::Gt)), + ">=" => Some(Self::Cmp(true, Comparison::Lt)), + "=" => Some(Self::Cmp(false, Comparison::Eq)), + "/=" => Some(Self::Cmp(true, Comparison::Eq)), + "<=" => Some(Self::Cmp(true, Comparison::Gt)), + "<" => Some(Self::Cmp(false, Comparison::Lt)), _ => None, } } diff --git a/src/main.rs b/src/main.rs index 0441290..6e99c2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,11 @@ use std::io::stdin; -use lysp::{parse::parse_value, vm::machine::Machine}; +use lysp::{ + compile::{CompileError, ParseError}, + error::EvalError, + parse::parse_value, + vm::machine::{EvalResult, Machine}, +}; fn main() { let mut vm = Machine::default(); @@ -30,13 +35,44 @@ fn main() { let result = vm.eval_value(&value); let result = match result { - Ok(r) => r, - Err(error) => { + EvalResult::Ok(r) => r, + EvalResult::Err(module, error) => { eprintln!("Error in expression:"); eprintln!(); eprintln!(" {value}:"); eprintln!(); eprintln!(":: {error}"); + eprintln!(); + module.dump(); + i = ""; + break; + } + EvalResult::LoadErr(error) => { + match error { + EvalError::Compile(CompileError::Parse(errors)) => { + if errors.len() > 1 { + eprintln!("Syntax errors:"); + } else { + eprintln!("Syntax error:"); + } + eprintln!(); + for error in errors { + let ParseError { input, error } = error; + eprintln!(" * In (sub)expression:"); + eprintln!(); + eprintln!(" {input}"); + eprintln!(); + eprintln!(" :: {error}"); + } + } + _ => { + eprintln!("Error in expression:"); + eprintln!(); + eprintln!(" {value}:"); + eprintln!(); + eprintln!(":: {error}") + } + } i = ""; break; } diff --git a/src/parse.rs b/src/parse.rs index 9338a1f..bdade73 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -159,10 +159,7 @@ fn parse_identifier(input: &str) -> IResult<&str, &str> { fn parse_identifier_or_keyword_or_nil(input: &str) -> IResult<&str, Value> { map(parse_identifier, |ident| match ident { "NIL" | "nil" => Value::Nil, - "lambda" => Value::Keyword(Keyword::Lambda), - "defun" => Value::Keyword(Keyword::Defun), - "&optional" => Value::Keyword(Keyword::Optional), - "&rest" => Value::Keyword(Keyword::Rest), + _ if let Some(keyword) = Keyword::from_str(ident) => Value::Keyword(keyword), _ => Value::Identifier(ident.into()), }) .parse(input) diff --git a/src/vm/instruction.rs b/src/vm/instruction.rs index b141b33..aeda002 100644 --- a/src/vm/instruction.rs +++ b/src/vm/instruction.rs @@ -14,6 +14,27 @@ pub enum InstructionEncodeError {} #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct U(u32); +#[derive(Debug, Clone, Copy, PartialEq)] +#[repr(u32)] +pub enum Comparison { + Eq = 0, + Lt = 1, + Gt = 2, +} + +impl TryFrom for Comparison { + type Error = InstructionError; + + fn try_from(value: u32) -> Result { + match value { + 0 => Ok(Self::Eq), + 1 => Ok(Self::Lt), + 2 => Ok(Self::Gt), + _ => Err(InstructionError::Invalid), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq)] pub enum Instruction { PushNil, @@ -21,12 +42,16 @@ pub enum Instruction { PushBool(bool), PushConstant(ConstantId), PushArgument(U<6>), + Drop, SetGlobal, GetGlobal, Call(U<6>), Return, Add(U<6>), Sub(U<6>), + Branch(U<12>), + Jump(U<12>), + Compare(bool, Comparison, U<6>), } pub type ConstantId = U<24>; @@ -81,6 +106,7 @@ impl Into for U { impl From for u32 { fn from(instruction: Instruction) -> u32 { match instruction { + Instruction::Drop => 0b0000_0000_0000_0000, Instruction::PushNil => 0b0000_0000_0000_0001, Instruction::PushBool(value) => 0b0000_0000_0000_0010 | (value as u32), Instruction::Return => 0b0000_0000_0000_0100, @@ -92,6 +118,11 @@ impl From for u32 { Instruction::PushInteger(value) => 0b0001_0000_0000_0000 | value.0, Instruction::PushConstant(index) => 0b0010_0000_0000_0000 | index.0, Instruction::PushArgument(index) => 0b0000_0000_1000_0000 | index.0, + Instruction::Branch(offset) => 0b0000_1000_0000_0000 | offset.0, + Instruction::Jump(offset) => 0b0000_1100_0000_0000 | offset.0, + Instruction::Compare(not, cmp, count) => { + 0b0000_0010_0000_0000 | ((not as u32) << 8) | ((cmp as u32) << 6) | count.0 + } } } } @@ -103,6 +134,7 @@ impl TryFrom for Instruction { fn try_from(value: u32) -> Result { #[bitmatch] match value { + "0000_0000_0000_0000" => Ok(Instruction::Drop), "0000_0000_0000_0001" => Ok(Instruction::PushNil), "0000_0000_0000_001x" => Ok(Instruction::PushBool(x != 0)), "0000_0000_0000_0100" => Ok(Instruction::Return), @@ -112,6 +144,9 @@ impl TryFrom for Instruction { "0000_0000_10xx_xxxx" => Ok(Instruction::PushArgument(U(x))), "0000_0001_00xx_xxxx" => Ok(Instruction::Add(U(x))), "0000_0001_01xx_xxxx" => Ok(Instruction::Sub(U(x))), + "0000_001x_yyzz_zzzz" => Ok(Instruction::Compare(x != 0, y.try_into()?, U(z))), + "0000_10xx_xxxx_xxxx" => Ok(Instruction::Branch(U(x))), + "0000_11xx_xxxx_xxxx" => Ok(Instruction::Jump(U(x))), "0001_xxxx_xxxx_xxxx" => Ok(Instruction::PushInteger(U(x))), "0010_xxxx_xxxx_xxxx" => Ok(Instruction::PushConstant(U(x))), _ => todo!(), diff --git a/src/vm/machine.rs b/src/vm/machine.rs index f7531df..ad30a1f 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -1,9 +1,9 @@ -use std::{collections::HashMap, fmt, rc::Rc}; +use std::{cmp::Ordering, collections::HashMap, fmt, rc::Rc}; use crate::{ error::EvalError, vm::{ - instruction::{ConstantId, Instruction, InstructionError}, + instruction::{Comparison, ConstantId, Instruction, InstructionError}, module::{Module, ModuleConstant, ModuleRef}, stack::Stack, value::{BytecodeFunction, NativeFunction, Value}, @@ -14,6 +14,8 @@ use crate::{ pub enum MachineError { #[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")] @@ -26,6 +28,13 @@ pub enum MachineError { CallStackOverflow, } +#[derive(Debug)] +pub enum EvalResult { + Ok(T), + Err(ModuleRef, E), + LoadErr(E), +} + #[derive(Debug, Clone, PartialEq)] pub struct InstructionPointer { pub module: ModuleRef, @@ -152,6 +161,65 @@ impl Machine { todo!() } + fn execute_compare( + &mut self, + not: bool, + cmp: Comparison, + count: usize, + ) -> Result<(), MachineError> { + fn ordering(a: &Value, b: &Value) -> Ordering { + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => Ord::cmp(a, b), + _ => todo!(), + } + } + + // TODO checks + let values = (0..count) + .map(|_| self.pop()) + .collect::, _>>()?; + let mut accumulator = true; + for i in 0..values.len() - 1 { + let a = &values[i]; + let b = &values[i + 1]; + let ord = ordering(a, b); + let check = match (not, cmp) { + (true, Comparison::Gt) => ord.is_le(), + (false, Comparison::Gt) => ord.is_gt(), + (true, Comparison::Eq) => ord.is_ne(), + (false, Comparison::Eq) => ord.is_eq(), + (true, Comparison::Lt) => ord.is_ge(), + (false, Comparison::Lt) => ord.is_lt(), + }; + accumulator &= check; + } + self.push(Value::Boolean(accumulator))?; + Ok(()) + } + + fn execute_branch(&mut self, offset: usize) -> Result { + let value = self.pop()?; + let do_branch = match value { + Value::Boolean(true) => false, + Value::Boolean(false) => true, + Value::Nil => todo!(), + _ => todo!(), + }; + if do_branch { + self.execute_jump(offset)?; + } + Ok(!do_branch) + } + + fn execute_jump(&mut self, offset: usize) -> Result<(), MachineError> { + let ip = self.ip.clone().unwrap(); + self.ip = Some(InstructionPointer { + module: ip.module, + address: offset, + }); + Ok(()) + } + fn execute_push_constant(&mut self, index: ConstantId) -> Result<(), MachineError> { let ip = self.ip.as_ref().unwrap(); let constant = ip.module.constant(index).expect("TODO"); @@ -204,7 +272,10 @@ impl Machine { pub fn execute_next(&mut self) -> Result { let ip = self.ip.clone().unwrap(); - let instruction = ip.module.instruction(ip.address).expect("TODO"); + let instruction = ip + .module + .instruction(ip.address) + .ok_or_else(|| MachineError::InstructionOutOfBounds(ip.clone()))?; let instruction = Instruction::try_from(instruction)?; eprintln!("{ip}: {instruction:?}"); let mut advance = true; @@ -225,6 +296,9 @@ impl Machine { Instruction::PushArgument(index) => { self.execute_push_argument(index.into())?; } + Instruction::Drop => { + self.pop()?; + } Instruction::GetGlobal => { self.execute_get_global()?; } @@ -245,6 +319,16 @@ impl Machine { Instruction::Sub(count) => { self.execute_sub(count.into())?; } + Instruction::Branch(offset) => { + advance = self.execute_branch(offset.into())?; + } + Instruction::Jump(offset) => { + advance = false; + self.execute_jump(offset.into())?; + } + Instruction::Compare(not, cmp, count) => { + self.execute_compare(not, cmp, count.into())?; + } } if advance { self.ip = Some(InstructionPointer { @@ -255,8 +339,7 @@ impl Machine { Ok(event) } - pub fn load_module(&mut self, module: Module) -> Result { - let module = ModuleRef::from(module); + pub fn load_module(&mut self, module: ModuleRef) -> Result { let entry = module.entry(); let entry_ip = InstructionPointer { module: module.clone(), @@ -278,22 +361,55 @@ impl Machine { Ok(module) } - pub fn eval_module(&mut self, module: Module) -> Result { - let module = self.load_module(module)?; - let expect = ExecutionEvent::ModuleEntry(module); + pub fn eval_module(&mut self, module: ModuleRef) -> EvalResult { + let module = match self.load_module(module) { + Ok(module) => module, + Err(error) => return EvalResult::LoadErr(error), + }; + let expect = ExecutionEvent::ModuleEntry(module.clone()); loop { - let event = self.execute_next()?; + let event = match self.execute_next() { + Ok(event) => event, + Err(error) => return EvalResult::Err(module, error), + }; if event == expect { break; } } - let value = self.pop()?; - Ok(value) + match self.pop() { + Ok(value) => EvalResult::Ok(value), + Err(error) => EvalResult::Err(module, error), + } } - pub fn eval_value(&mut self, value: &Value) -> Result { - let module = Module::compile_value(value)?; - self.eval_module(module).map_err(EvalError::from) + pub fn eval_value(&mut self, value: &Value) -> EvalResult { + let module = match Module::compile_value(value) { + Ok(module) => module, + Err(error) => return EvalResult::LoadErr(error.into()), + }; + let module = ModuleRef::from(module); + match self.eval_module(module) { + EvalResult::Ok(value) => EvalResult::Ok(value), + EvalResult::Err(module, error) => EvalResult::Err(module, error.into()), + EvalResult::LoadErr(error) => EvalResult::LoadErr(error.into()), + } + } +} + +impl EvalResult { + pub fn unwrap(self) -> T + where + E: fmt::Display, + { + match self { + Self::Ok(value) => value, + Self::Err(module, error) => { + panic!("Unwrap called on module {module:p} error: {error}"); + } + Self::LoadErr(error) => { + panic!("Unwrap called on error: {error}"); + } + } } } @@ -335,7 +451,7 @@ mod tests { build(i as u32, &mut builder); builder.add(Instruction::Return); let module = builder.build(); - values.push(machine.eval_module(module).unwrap()); + values.push(machine.eval_module(module.into()).unwrap()); } (machine, values) } diff --git a/src/vm/module.rs b/src/vm/module.rs index c3bd1b5..b0c84b3 100644 --- a/src/vm/module.rs +++ b/src/vm/module.rs @@ -107,7 +107,7 @@ impl Module { } pub fn compile_value(value: &Value) -> Result { - let expression = Expression::parse(value)?; + let expression = Expression::parse(value).map_err(CompileError::Parse)?; let mut module = CompilationModule::default(); module.compile_function( FunctionSignature::EMPTY, @@ -119,6 +119,17 @@ impl Module { )?; module.compile_module() } + + pub fn dump(&self) { + for (i, instruction) in self.instructions.iter().enumerate() { + print!(" {i:4}: {instruction:#x}"); + if let Ok(instruction) = Instruction::try_from(*instruction) { + println!(" {instruction:?}"); + } else { + println!(); + } + } + } } impl ModuleBuilder { diff --git a/src/vm/value.rs b/src/vm/value.rs index e23c590..11832bf 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -34,12 +34,51 @@ pub enum Value { NativeFunction(NativeFunction), } -#[derive(Debug, Clone, PartialEq)] -pub enum Keyword { - Lambda, - Defun, - Optional, - Rest, +macro_rules! impl_keyword { + ( + $vis:vis enum $name:ident { + $($variant:ident => $s:literal),* $(,)? + } + ) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + $vis enum $name { + $($variant),* + } + + impl $name { + pub fn from_str(s: &str) -> Option { + match s { + $( + $s => Some(Self::$variant), + )* + _ => None, + } + } + } + + impl fmt::Display for $name { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + $( + Self::$variant => $s + ),* + }) + } + } + }; +} + +impl_keyword! { + pub enum Keyword { + Lambda => "lambda", + Defun => "defun", + If => "if", + Cond => "cond", + While => "while", + Otherwise => "&otherwise", + Optional => "&optional", + Rest => "&rest" + } } #[derive(Debug, Clone, PartialEq)] @@ -67,6 +106,10 @@ impl Value { } impl ConsCell { + pub fn end(value: Value) -> Self { + Self(value, Value::Nil) + } + fn fmt_inner(&self, f: &mut fmt::Formatter<'_>, first: bool) -> fmt::Result { let Self(car, cdr) = self; if !first { @@ -107,9 +150,7 @@ impl PartialEq for NativeFunction { impl fmt::Display for ConsCell { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "(")?; - self.fmt_inner(f, true)?; - write!(f, ")") + self.fmt_inner(f, true) } } @@ -125,18 +166,6 @@ impl fmt::Display for BytecodeFunction { } } -impl fmt::Display for Keyword { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let word = match self { - Self::Lambda => "lambda", - Self::Defun => "defun", - Self::Optional => "&optional", - Self::Rest => "&rest", - }; - write!(f, "{word}") - } -} - impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -146,7 +175,7 @@ impl fmt::Display for Value { 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::Cons(cons) => write!(f, "({cons})"), Self::BytecodeFunction(bytecode) => write!(f, "{bytecode}"), Self::NativeFunction(native) => write!(f, "{native}"), }