diff --git a/lysp/example0.lysp b/lysp/example0.lysp new file mode 100644 index 0000000..cb86c54 --- /dev/null +++ b/lysp/example0.lysp @@ -0,0 +1,14 @@ +(defun return-1 () 1) +(assert (= 1 (return-1))) +(assert (< 1 2 3 4)) +(assert (<= 1 2 2 3)) +(assert (= 6 (+ 1 2 3) (apply + (list 1 2 3)))) +(assert (= (- 4) (- 1 2 3) (apply - (list 1 2 3)))) +(assert (= 6 (* 1 2 3) (apply * (list 1 2 3)))) +(assert (= 2 (/ 6 3 1) (apply / (list 6 3 1)))) +(assert (= 2 (% 6 4) (apply % (list 6 4)))) +(assert (= 7 (| 1 2 4) (apply | (list 1 2 4)))) +(assert (= 1 (& 1 3 5 7) (apply & (list 1 3 5 7)))) +(assert (= 0 (^ 1 3 5 7) (apply ^ (list 1 3 5 7)))) +(assert (= #t (&& #t #t) (apply && (list #t #t)))) +(assert (= #f (|| #f #f) (apply || (list #f #f)))) diff --git a/src/compile/block.rs b/src/compile/block.rs index e8d124a..7d4a85b 100644 --- a/src/compile/block.rs +++ b/src/compile/block.rs @@ -7,12 +7,12 @@ use crate::{ instruction::Emitted, module::CompilationModule, syntax::{ - CallExpression, CondExpression, Expression, FunctionBody, IfExpression, - LambdaExpression, + CallExpression, CondExpression, DefunExpression, Expression, FunctionBody, + IfExpression, LambdaExpression, }, value::{BuiltinFunction, CompileConstant, CompileValue}, }, - vm::instruction::{Comparison, Instruction, U}, + vm::instruction::{Instruction, MathInstruction, U}, }; pub struct CompiledFunction { @@ -112,6 +112,20 @@ impl<'a> LocalBlock<'a> { } } + fn compile_set_global( + &mut self, + identifier: &Rc, + value: CompileValue, + ) -> Result<(), CompileError> { + self.compile_push(value)?; + let index = self + .module + .constant(CompileConstant::Identifier(identifier.clone()))?; + self.function.emit(Instruction::PushConstant(index)); + self.function.emit(Instruction::SetGlobal); + Ok(()) + } + fn compile_push(&mut self, value: CompileValue) -> Result<(), CompileError> { match value { CompileValue::Nil => { @@ -141,10 +155,10 @@ impl<'a> LocalBlock<'a> { }; self.function.emit(Instruction::PushArgument(index)); } - CompileValue::LocalFunction(index) => { + CompileValue::LocalFunction(index, required_count) => { let value = self .module - .constant(CompileConstant::LocalFunction(index))?; + .constant(CompileConstant::LocalFunction(index, required_count))?; self.function.emit(Instruction::PushConstant(value)); } // Already on stack @@ -174,9 +188,6 @@ impl<'a> LocalBlock<'a> { } fn compile_identifier(&mut self, identifier: &Rc) -> Result { - if let Some(_builtin) = BuiltinFunction::from_identifier(identifier) { - todo!("Illegal"); - } if let Some(argument) = self.function.signature.argument(identifier) { return Ok(CompileValue::Argument(argument)); } @@ -188,33 +199,27 @@ impl<'a> LocalBlock<'a> { let index = self .module .compile_function(lambda.signature.clone(), &lambda.body, false)?; - Ok(CompileValue::LocalFunction(index)) + Ok(CompileValue::LocalFunction( + index, + lambda.signature.required_arguments.len(), + )) } - fn compile_builtin_add(&mut self, 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::Add(count)); - Ok(CompileValue::Stack) + fn compile_defun(&mut self, defun: &DefunExpression) -> Result { + let index = self + .module + .compile_function(defun.signature.clone(), &defun.body, false)?; + let value = CompileValue::LocalFunction(index, defun.signature.required_arguments.len()); + self.compile_set_global(&defun.name, value)?; + Ok(CompileValue::Nil) } - fn compile_builtin_sub(&mut self, _args: &[Expression]) -> Result { - todo!() - } - - fn compile_builtin_cmp( + fn compile_builtin_math_generic( &mut self, - not: bool, - cmp: Comparison, + math: MathInstruction, args: &[Expression], ) -> Result { - // TODO optimize literals + // TODO optimize for arg in args.iter().rev() { let arg = self.compile_expression(arg)?; self.compile_push(arg)?; @@ -222,7 +227,7 @@ impl<'a> LocalBlock<'a> { let Some(count) = U::new(args.len() as u32) else { todo!() }; - self.function.emit(Instruction::Compare(not, cmp, count)); + self.function.emit(Instruction::Math(math, count)); Ok(CompileValue::Stack) } @@ -232,9 +237,7 @@ impl<'a> LocalBlock<'a> { args: &[Expression], ) -> Result { 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), + BuiltinFunction::Math(math) => self.compile_builtin_math_generic(math, args), } } @@ -323,6 +326,7 @@ impl<'a> LocalBlock<'a> { Expression::IntegerLiteral(value) => Ok(CompileValue::Integer(*value)), Expression::Identifier(identifier) => self.compile_identifier(identifier), Expression::Lambda(lambda) => self.compile_lambda(lambda), + Expression::Defun(defun) => self.compile_defun(defun), Expression::Call(call) => self.compile_call(call), Expression::If(condition) => self.compile_if(condition), Expression::Cond(condition) => self.compile_cond(condition), @@ -347,7 +351,7 @@ mod tests { }, value::{CompileConstant, CompileValue}, }, - vm::instruction::{Comparison, Instruction, U}, + vm::instruction::{Instruction, U}, }; fn test_compile( diff --git a/src/compile/module.rs b/src/compile/module.rs index f84d3db..ac7813e 100644 --- a/src/compile/module.rs +++ b/src/compile/module.rs @@ -73,9 +73,9 @@ impl CompilationModule { CompileConstant::Identifier(identifier) => { ModuleConstant::Identifier(identifier) } - CompileConstant::LocalFunction(index) => { + CompileConstant::LocalFunction(index, required_count) => { let address = *function_offsets.get(&index).unwrap(); - ModuleConstant::LocalFunction(address) + ModuleConstant::LocalFunction(address, required_count) } }, ) diff --git a/src/compile/syntax/error.rs b/src/compile/syntax/error.rs index c805634..536d469 100644 --- a/src/compile/syntax/error.rs +++ b/src/compile/syntax/error.rs @@ -40,6 +40,8 @@ pub enum ExpectedWhat { ArgumentSpec, #[error("a proper list")] ProperList, + #[error("an identifier")] + Identifier, } #[derive(Debug, Clone, PartialEq, thiserror::Error)] @@ -58,6 +60,8 @@ pub enum ExpectedWhere { InConditionList, #[error("in the condition arm")] InConditionArm, + #[error("in the function definition")] + InFunctionDefinition, } pub(super) trait CollectErrors { diff --git a/src/compile/syntax/function.rs b/src/compile/syntax/function.rs index a4e3d2d..8510c2a 100644 --- a/src/compile/syntax/function.rs +++ b/src/compile/syntax/function.rs @@ -14,6 +14,13 @@ pub struct FunctionBody { pub tail: Rc, } +#[derive(Debug, PartialEq)] +pub struct DefunExpression { + pub name: Rc, + pub signature: FunctionSignature, + pub body: FunctionBody, +} + impl FunctionSignature { pub(super) fn parse(mut value: &Value, input: &Value) -> Result { enum Mode { @@ -164,6 +171,50 @@ impl FunctionBody { } } +impl DefunExpression { + 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::ProperList, + ExpectedWhere::InFunctionDefinition, + ), + }); + }; + let ConsCell(identifier, cdr) = value.as_ref(); + + let Value::Identifier(identifier) = identifier else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::Identifier, + ExpectedWhere::AfterKeyword(Keyword::Defun), + ), + }); + }; + + let Value::Cons(cdr) = cdr else { + return Err(ParseError { + input: input.clone(), + error: ParseErrorKind::Expected( + ExpectedWhat::ProperList, + ExpectedWhere::InFunctionDefinition, + ), + }); + }; + let ConsCell(car, cdr) = cdr.as_ref(); + + let signature = FunctionSignature::parse(car, input)?; + let body = FunctionBody::parse(cdr, input, Keyword::Lambda)?; + Ok(Self { + name: identifier.clone(), + signature, + body, + }) + } +} + impl CollectErrors for FunctionBody { fn collect_errors(&self, errors: &mut Vec) -> bool { let mut r = false; @@ -175,6 +226,12 @@ impl CollectErrors for FunctionBody { } } +impl CollectErrors for DefunExpression { + fn collect_errors(&self, errors: &mut Vec) -> bool { + self.body.collect_errors(errors) + } +} + #[cfg(test)] mod tests { use std::rc::Rc; diff --git a/src/compile/syntax/mod.rs b/src/compile/syntax/mod.rs index 09a1a5a..754282d 100644 --- a/src/compile/syntax/mod.rs +++ b/src/compile/syntax/mod.rs @@ -21,6 +21,7 @@ pub enum Expression { IntegerLiteral(i64), Identifier(Rc), Lambda(LambdaExpression), + Defun(DefunExpression), Call(CallExpression), If(IfExpression), Cond(CondExpression), @@ -63,6 +64,9 @@ impl Expression { Value::Keyword(Keyword::Cond) => { Self::map_or(CondExpression::parse(cdr, value), Expression::Cond) } + Value::Keyword(Keyword::Defun) => { + Self::map_or(DefunExpression::parse(cdr, value), Expression::Defun) + } _ => Self::map_or(CallExpression::parse(cons, value), Expression::Call), } } @@ -83,7 +87,8 @@ impl CollectErrors for Expression { } Self::If(condition) => condition.collect_errors(errors), Self::Cond(condition) => condition.collect_errors(errors), - Self::Lambda(condition) => condition.collect_errors(errors), + Self::Lambda(lambda) => lambda.collect_errors(errors), + Self::Defun(defun) => defun.collect_errors(errors), Self::Call(call) => call.collect_errors(errors), Self::Nil | Self::IntegerLiteral(_) | Self::Identifier(_) | Self::BooleanLiteral(_) => { false diff --git a/src/compile/value.rs b/src/compile/value.rs index c33cfe0..078a65f 100644 --- a/src/compile/value.rs +++ b/src/compile/value.rs @@ -1,12 +1,12 @@ use std::rc::Rc; -use crate::vm::instruction::Comparison; +use crate::vm::instruction::MathInstruction; #[derive(Debug, PartialEq)] pub enum CompileValue { Integer(i64), Boolean(bool), - LocalFunction(u32), + LocalFunction(u32, usize), Argument(usize), Global(Rc), Stack, @@ -16,28 +16,34 @@ pub enum CompileValue { #[derive(Debug, Hash, PartialEq, Eq)] pub enum CompileConstant { Integer(i64), - LocalFunction(u32), + LocalFunction(u32, usize), Identifier(Rc), } #[derive(Debug, PartialEq, Clone, Copy)] pub enum BuiltinFunction { - Add, - Sub, - Cmp(bool, Comparison), + Math(MathInstruction), } impl BuiltinFunction { pub fn from_identifier(identifier: &str) -> Option { 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)), + "+" => Some(Self::Math(MathInstruction::Add)), + "-" => Some(Self::Math(MathInstruction::Sub)), + "*" => Some(Self::Math(MathInstruction::Mul)), + "/" => Some(Self::Math(MathInstruction::Div)), + "%" => Some(Self::Math(MathInstruction::Mod)), + "&" => Some(Self::Math(MathInstruction::BitwiseAnd)), + "|" => Some(Self::Math(MathInstruction::BitwiseOr)), + "^" => Some(Self::Math(MathInstruction::BitwiseXor)), + "&&" => Some(Self::Math(MathInstruction::And)), + "||" => Some(Self::Math(MathInstruction::Or)), + ">" => Some(Self::Math(MathInstruction::Gt)), + ">=" => Some(Self::Math(MathInstruction::Ge)), + "=" => Some(Self::Math(MathInstruction::Eq)), + "/=" => Some(Self::Math(MathInstruction::Ne)), + "<=" => Some(Self::Math(MathInstruction::Le)), + "<" => Some(Self::Math(MathInstruction::Lt)), _ => None, } } diff --git a/src/lib.rs b/src/lib.rs index e47ae52..b2a9124 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,4 +4,5 @@ pub mod compile; pub mod convert; pub mod error; pub mod parse; +pub mod util; pub mod vm; diff --git a/src/main.rs b/src/main.rs index b0fee7a..7b9d8e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,23 +1,56 @@ -use std::io::stdin; +use std::{ + fs::File, + io::{self, BufRead, BufReader, Write, stdin, stdout}, + path::{Path, PathBuf}, + process::ExitCode, + rc::Rc, +}; +use clap::Parser; use lysp::{ - compile::{CompileError, ParseError}, + compile::{ + CompilationModule, CompileError, Expression, FunctionBody, FunctionSignature, ParseError, + }, error::EvalError, parse::parse_value, vm::{ - machine::{EvalResult, Machine}, + machine::{EvalResult, Machine, MachineError}, prelude, }, }; -fn main() { - let mut vm = Machine::default(); - prelude::load(&mut vm); +#[derive(Debug, thiserror::Error)] +#[error("{0}")] +enum Error { + Machine(#[from] MachineError), + Eval(#[from] EvalError), + Io(#[from] io::Error), + Compile(#[from] CompileError), + #[error("Compilation errors ocurred")] + CompilationErrors, + #[error("Error already printed")] + Printed, +} +#[derive(Debug, Parser)] +struct Args { + module: Option, +} + +fn run_interactive(vm: &mut Machine) -> Result<(), Error> { let mut input = String::new(); let stdin = stdin(); + let mut stdout = stdout(); + loop { - let len = stdin.read_line(&mut input).unwrap(); + if input.is_empty() { + print!("> "); + } else { + print!(">>> "); + } + stdout.flush().ok(); + + let len = stdin.read_line(&mut input)?; if len == 0 { break; } @@ -48,7 +81,7 @@ fn main() { eprintln!(); eprintln!(":: {error}"); eprintln!(); - module.dump(ip_address); + module.dump(ip_address, 8); i = ""; break; } @@ -90,4 +123,121 @@ fn main() { input = i.into(); } + + Ok(()) +} + +fn run_module>(vm: &mut Machine, path: P) -> Result<(), Error> { + let path = path.as_ref(); + let mut input = String::new(); + let mut reader = BufReader::new(File::open(path)?); + + let mut module = CompilationModule::default(); + let mut body = FunctionBody { + head: vec![], + tail: Rc::new(Expression::Nil), + }; + + let mut fail = false; + + loop { + let len = reader.read_line(&mut input)?; + if len == 0 { + break; + } + + let mut i = input.trim_start(); + while !i.is_empty() { + let result = parse_value(i); + let (o, value) = match result { + Ok(r) => r, + Err(nom::Err::Incomplete(_)) => { + break; + } + Err(error) => { + eprintln!("Syntax error: {error}"); + i = ""; + fail = true; + break; + } + }; + + let expression = match Expression::parse(&value) { + Ok(expression) => expression, + Err(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}"); + } + fail = true; + continue; + } + }; + + body.head.push(expression); + + i = o.trim_start(); + } + + input = i.into(); + } + + if fail { + return Err(Error::CompilationErrors); + } + + module.compile_function( + FunctionSignature { + required_arguments: vec![], + optional_arguments: vec![], + rest_argument: None, + }, + &body, + true, + )?; + let module = module.compile_module()?; + + match vm.eval_module(module.into()) { + EvalResult::Ok(_) => Ok(()), + EvalResult::Err(module, ip, error) => { + let ip_address = ip.map(|ip| ip.address); + eprintln!("Error in module"); + eprintln!(); + eprintln!(" {}:", path.display()); + eprintln!(); + eprintln!(":: {error}"); + eprintln!(); + module.dump(ip_address, 8); + Err(Error::Printed) + } + EvalResult::LoadErr(error) => Err(error.into()), + } +} + +fn main() -> ExitCode { + let args = Args::parse(); + let mut vm = Machine::default(); + prelude::load(&mut vm); + let result = match args.module.as_ref() { + Some(module) => run_module(&mut vm, module), + None => run_interactive(&mut vm), + }; + match result { + Ok(()) => ExitCode::SUCCESS, + Err(Error::Printed) => ExitCode::FAILURE, + Err(error) => { + eprintln!("{error}"); + ExitCode::FAILURE + } + } } diff --git a/src/parse.rs b/src/parse.rs index dccf0ec..e110533 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -19,13 +19,13 @@ struct IdentifierTail; impl FindToken for IdentifierHead { fn find_token(&self, token: char) -> bool { - token.is_alphabetic() || "~!@$%^&*-=_+<>?/".contains(token) + token.is_alphabetic() || "~!@$%^&*-=_+<>?/|".contains(token) } } impl FindToken for IdentifierTail { fn find_token(&self, token: char) -> bool { - token.is_alphanumeric() || "~!@$%^&*-=_+<>?/".contains(token) + token.is_alphanumeric() || "~!@$%^&*-=_+<>?/|".contains(token) } } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..ed7e004 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,60 @@ +pub struct TryFilter +where + I: Iterator>, + F: FnMut(&T) -> Result, + E2: From, +{ + iterator: I, + filter: F, +} + +pub trait IteratorExt { + fn try_filter(self, filter: F) -> impl Iterator> + where + F: FnMut(&T) -> Result, + E2: From; +} + +impl IteratorExt for I +where + I: Iterator>, +{ + fn try_filter(self, filter: F) -> impl Iterator> + where + F: FnMut(&T) -> Result, + E2: From, + { + TryFilter { + iterator: self, + filter, + } + } +} + +impl Iterator for TryFilter +where + I: Iterator>, + F: FnMut(&T) -> Result, + E2: From, +{ + type Item = Result; + + fn next(&mut self) -> Option { + loop { + let value = self.iterator.next()?; + let value = match value { + Ok(value) => value, + Err(error) => return Some(Err(error.into())), + }; + let cond = match (self.filter)(&value) { + Ok(value) => value, + Err(error) => return Some(Err(error)), + }; + + match cond { + true => return Some(Ok(value)), + false => continue, + } + } + } +} diff --git a/src/vm/instruction.rs b/src/vm/instruction.rs index aeda002..430f00c 100644 --- a/src/vm/instruction.rs +++ b/src/vm/instruction.rs @@ -14,24 +14,59 @@ 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, +macro_rules! primitive_enum { + ( + $(#[$meta:meta])* + $vis:vis enum $ident:ident: $repr:ty { + $($variant:ident = $value:literal),* $(,)? + } + ) => { + $(#[$meta])* + #[repr($repr)] + $vis enum $ident { + $($variant = $value),* + } + + impl TryFrom<$repr> for $ident { + type Error = InstructionError; + + fn try_from(value: $repr) -> Result { + match value { + $($value => Ok(Self::$variant),)* + _ => Err(InstructionError::Invalid) + } + } + } + + impl From<$ident> for $repr { + fn from(value: $ident) -> $repr { + match value { + $($ident::$variant => $value,)* + } + } + } + }; } -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), - } +primitive_enum! { + #[derive(Debug, Clone, Copy, PartialEq)] + pub enum MathInstruction: u32 { + Add = 0, + Sub = 1, + Mul = 2, + Div = 3, + Mod = 4, + And = 5, + Or = 6, + BitwiseAnd = 7, + BitwiseOr = 8, + BitwiseXor = 9, + Gt = 10, + Lt = 11, + Eq = 12, + Ne = 13, + Ge = 14, + Le = 15, } } @@ -47,11 +82,9 @@ pub enum Instruction { GetGlobal, Call(U<6>), Return, - Add(U<6>), - Sub(U<6>), + Math(MathInstruction, U<6>), Branch(U<12>), Jump(U<12>), - Compare(bool, Comparison, U<6>), } pub type ConstantId = U<24>; @@ -91,15 +124,15 @@ impl fmt::Debug for U { } } -impl Into for U { - fn into(self) -> usize { - self.0 as usize +impl From> for usize { + fn from(value: U) -> Self { + value.0 as usize } } -impl Into for U { - fn into(self) -> u32 { - self.0 +impl From> for u32 { + fn from(value: U) -> Self { + value.0 } } @@ -112,16 +145,14 @@ impl From for u32 { Instruction::Return => 0b0000_0000_0000_0100, Instruction::SetGlobal => 0b0000_0000_0000_0101, Instruction::GetGlobal => 0b0000_0000_0000_0110, - Instruction::Add(count) => 0b0000_0001_0000_0000 | count.0, - Instruction::Sub(count) => 0b0000_0001_0100_0000 | count.0, Instruction::Call(count) => 0b0000_0000_0100_0000 | count.0, 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 + Instruction::Math(math, count) => { + 0b0000_0100_0000_0000 | (u32::from(math) << 6) | count.0 } } } @@ -140,16 +171,26 @@ impl TryFrom for Instruction { "0000_0000_0000_0100" => Ok(Instruction::Return), "0000_0000_0000_0101" => Ok(Instruction::SetGlobal), "0000_0000_0000_0110" => Ok(Instruction::GetGlobal), + "0000_0000_0000_0111" => todo!(), + "0000_0000_0000_1???" => todo!(), + + "0000_0000_0001_????" => todo!(), + "0000_0000_001?_????" => todo!(), "0000_0000_01xx_xxxx" => Ok(Instruction::Call(U(x))), "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_0000_11??_????" => todo!(), + + "0000_0001_????_????" => todo!(), + "0000_001?_????_????" => todo!(), + "0000_01xx_xxyy_yyyy" => Ok(Instruction::Math(x.try_into()?, U(y))), "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!(), + "0011_????_????_????" => todo!(), + "01??_????_????_????" => todo!(), + "1???_????_????_????" => todo!(), } } } diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 8ee8d72..a220539 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -1,11 +1,12 @@ -use std::{cmp::Ordering, collections::HashMap, fmt, rc::Rc}; +use std::{collections::HashMap, fmt, rc::Rc}; use crate::{ error::EvalError, vm::{ - instruction::{Comparison, ConstantId, Instruction, InstructionError}, + instruction::{ConstantId, Instruction, InstructionError, MathInstruction}, module::{Module, ModuleConstant, ModuleRef}, native::NativeFunction, + prelude, stack::Stack, value::{BytecodeFunction, Value}, }, @@ -29,6 +30,8 @@ pub enum MachineError { CallStackOverflow, #[error("Unbound identifier: {0:?}")] UnboundIdentifier(Rc), + #[error("Expected at least {0} arguments, got {1}")] + ArgumentCountMismatch(usize, usize), #[error("Invalid argument provided in a call")] InvalidArgument, } @@ -79,6 +82,10 @@ impl Default for Machine { } impl Machine { + pub fn ip(&self) -> Option { + self.ip.clone() + } + pub fn set_global>>(&mut self, identifier: S, value: Value) { self.globals.insert(identifier.into(), value); } @@ -111,15 +118,25 @@ impl Machine { arguments: Vec, ) -> Result<(), MachineError> { let source_ip = self.ip.clone().unwrap(); + let BytecodeFunction { + module, + required_count, + address, + } = bytecode.clone(); + if required_count > arguments.len() { + return Err(MachineError::ArgumentCountMismatch( + required_count, + arguments.len(), + )); + } let frame = CallFrame { arguments, - event: ExecutionEvent::BytecodeFunctionExit(bytecode.clone()), + event: ExecutionEvent::BytecodeFunctionExit(bytecode), return_address: InstructionPointer { module: source_ip.module, address: source_ip.address + 1, }, }; - let BytecodeFunction { module, address } = bytecode; if self.call_stack.push(frame).is_err() { return Err(MachineError::CallStackOverflow); } @@ -160,6 +177,19 @@ impl Machine { Ok(()) } + fn execute_builtin_native(&mut self, function: F, count: usize) -> Result<(), MachineError> + where + F: Fn(&mut Self, &[Value]) -> Result, + { + let mut args = vec![]; + for _ in 0..count { + args.push(self.pop()?); + } + let value = function(self, &args)?; + self.push(value)?; + Ok(()) + } + fn execute_return(&mut self) -> Result { let ip = self.ip.clone().unwrap(); if let Some(frame) = self.call_stack.pop() { @@ -171,59 +201,49 @@ impl Machine { } } - fn execute_add(&mut self, count: usize) -> Result<(), MachineError> { - let mut accumulator = 0i64; - for _ in 0..count { - let arg = self.pop()?; - match arg { - Value::Integer(value) => { - accumulator = accumulator.wrapping_add(value); - } - _ => todo!("{arg:?}"), - } - } - self.push(Value::Integer(accumulator))?; - Ok(()) - } + //fn execute_add(&mut self, count: usize) -> Result<(), MachineError> { + //} - fn execute_sub(&mut self, _count: usize) -> Result<(), MachineError> { - todo!() - } + // fn execute_sub(&mut self, _count: usize) -> Result<(), MachineError> { + // 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_compare( + // &mut self, + // not: bool, + // cmp: Comparison, + // count: usize, + // ) -> Result<(), MachineError> { + // let f = match (not, cmp) { + // (true, Comparison::Gt) => prelude::builtin_cmp_le, + // (false, Comparison::Gt) => prelude::builtin_cmp_gt, + // (true, Comparison::Eq) => prelude::builtin_cmp_eq, + // (false, Comparison::Eq) => prelude::builtin_cmp_ne, + // (true, Comparison::Lt) => prelude::builtin_cmp_ge, + // (false, Comparison::Lt) => prelude::builtin_cmp_lt, + // }; + // self.execute_builtin_native(f, count) + // } + fn execute_math(&mut self, math: MathInstruction, count: usize) -> Result<(), MachineError> { + let function = match math { + MathInstruction::Add => prelude::builtin_add, + MathInstruction::Sub => prelude::builtin_sub, + MathInstruction::Mul => prelude::builtin_mul, + MathInstruction::Div => prelude::builtin_div, + MathInstruction::Mod => prelude::builtin_mod, + MathInstruction::And => prelude::builtin_and, + MathInstruction::Or => prelude::builtin_or, + MathInstruction::BitwiseAnd => prelude::builtin_bitwise_and, + MathInstruction::BitwiseOr => prelude::builtin_bitwise_or, + MathInstruction::BitwiseXor => prelude::builtin_bitwise_xor, + MathInstruction::Gt => prelude::builtin_cmp_gt, + MathInstruction::Lt => prelude::builtin_cmp_lt, + MathInstruction::Eq => prelude::builtin_cmp_eq, + MathInstruction::Ne => prelude::builtin_cmp_ne, + MathInstruction::Ge => prelude::builtin_cmp_ge, + MathInstruction::Le => prelude::builtin_cmp_le, + }; + self.execute_builtin_native(function, count) } fn execute_branch(&mut self, offset: usize) -> Result { @@ -253,10 +273,13 @@ impl Machine { let ip = self.ip.as_ref().unwrap(); let constant = ip.module.constant(index).expect("TODO"); let value = match constant { - ModuleConstant::LocalFunction(address) => Value::BytecodeFunction(BytecodeFunction { - module: ip.module.clone(), - address, - }), + ModuleConstant::LocalFunction(address, required_count) => { + Value::BytecodeFunction(BytecodeFunction { + module: ip.module.clone(), + required_count, + address, + }) + } ModuleConstant::Integer(value) => Value::Integer(value), ModuleConstant::Identifier(identifier) => Value::Identifier(identifier), }; @@ -281,7 +304,7 @@ impl Machine { .globals .get(&ident) .cloned() - .ok_or_else(|| MachineError::UnboundIdentifier(ident))?; + .ok_or(MachineError::UnboundIdentifier(ident))?; self.push(value) } _ => todo!(), @@ -341,11 +364,8 @@ impl Machine { advance = false; self.execute_call(count.into())?; } - Instruction::Add(count) => { - self.execute_add(count.into())?; - } - Instruction::Sub(count) => { - self.execute_sub(count.into())?; + Instruction::Math(math, count) => { + self.execute_math(math, count.into())?; } Instruction::Branch(offset) => { advance = self.execute_branch(offset.into())?; @@ -354,9 +374,6 @@ impl Machine { 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 { diff --git a/src/vm/module.rs b/src/vm/module.rs index b1c03a8..cf96976 100644 --- a/src/vm/module.rs +++ b/src/vm/module.rs @@ -12,7 +12,7 @@ use crate::{ #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ModuleConstant { Integer(i64), - LocalFunction(usize), + LocalFunction(usize, usize), Identifier(Rc), } @@ -120,15 +120,22 @@ impl Module { module.compile_module() } - pub fn dump(&self, highlight: Option) { - for (i, instruction) in self.instructions.iter().enumerate() { - if highlight.is_some_and(|a| a == i) { + pub fn dump(&self, highlight: Option, context: usize) { + let window = highlight + .map(|end| (end + 1).min(self.instructions.len())) + .map(|end| end.saturating_sub(context)..end) + .unwrap_or(0..self.instructions.len()); + + let start = window.start; + for (i, instruction) in self.instructions[window].iter().enumerate() { + let offset = i + start; + if highlight.is_some_and(|a| a == offset) { print!(">"); } else { print!(" "); } - print!("{i:4}: {instruction:08x}"); + print!("{offset:4}: {instruction:08x}"); if let Ok(instruction) = Instruction::try_from(*instruction) { print!(" {instruction:?}"); @@ -183,7 +190,7 @@ impl fmt::Display for ModuleConstant { match self { Self::Integer(value) => fmt::Display::fmt(value, f), Self::Identifier(ident) => write!(f, "ident {ident:?}"), - Self::LocalFunction(address) => write!(f, "label {address}"), + Self::LocalFunction(address, _) => write!(f, "label {address}"), } } } diff --git a/src/vm/native.rs b/src/vm/native.rs index eeb5e29..b3771c5 100644 --- a/src/vm/native.rs +++ b/src/vm/native.rs @@ -5,10 +5,13 @@ use crate::vm::{ value::Value, }; +pub type NativeFunctionImpl = + Rc Result + 'static>; + #[derive(Clone)] pub struct NativeFunction { name: Rc, - inner: Rc Result + 'static>, + inner: NativeFunctionImpl, } impl NativeFunction { diff --git a/src/vm/prelude.rs b/src/vm/prelude.rs index 16f104d..ce46631 100644 --- a/src/vm/prelude.rs +++ b/src/vm/prelude.rs @@ -1,29 +1,289 @@ +use std::{ + cmp::Ordering, + ops::{BitAnd, BitOr, BitXor}, + slice, +}; + use crate::{ convert::{AnyFunction, TryFromValue}, + util::IteratorExt, vm::{ machine::{Machine, MachineError}, value::Value, }, }; +pub(crate) enum CompareOperation { + Eq, + Ne, + Lt, + Gt, + Le, + Ge, +} + +fn builtin_fold_integer( + fold: F, + mut accumulator: i64, + args: &[Value], +) -> Result +where + F: Fn(i64, i64) -> i64, +{ + for arg in args { + match arg { + &Value::Integer(value) => { + accumulator = fold(accumulator, value); + } + _ => todo!("Math for {arg}"), + } + } + Ok(Value::Integer(accumulator)) +} +fn builtin_fold_bool( + fold: F, + mut accumulator: bool, + args: &[Value], +) -> Result +where + F: Fn(bool, bool) -> bool, +{ + for arg in args { + let arg = bool::try_from_value(arg)?; + accumulator = fold(accumulator, arg); + } + Ok(Value::Boolean(accumulator)) +} + +pub(crate) fn builtin_add(_vm: &mut Machine, args: &[Value]) -> Result { + builtin_fold_integer(i64::wrapping_add, 0, args) +} +pub(crate) fn builtin_sub(_vm: &mut Machine, args: &[Value]) -> Result { + match args { + [] => Err(MachineError::InvalidArgument), + [x] if let &Value::Integer(x) = x => Ok(Value::Integer(-x)), + [x] => todo!("Math for {x}"), + [x, rest @ ..] => { + let &Value::Integer(x) = x else { + todo!("Math for {x}"); + }; + let mut accumulator = x; + for arg in rest { + match arg { + &Value::Integer(arg) => { + accumulator = accumulator.saturating_sub(arg); + } + _ => todo!("Math for {arg}"), + } + } + Ok(Value::Integer(accumulator)) + } + } +} +pub(crate) fn builtin_mul(_vm: &mut Machine, args: &[Value]) -> Result { + builtin_fold_integer(i64::wrapping_mul, 1, args) +} +pub(crate) fn builtin_mod(_vm: &mut Machine, args: &[Value]) -> Result { + let [x, y] = args else { + return Err(MachineError::InvalidArgument); + }; + let (&Value::Integer(x), &Value::Integer(y)) = (x, y) else { + return Err(MachineError::InvalidArgument); + }; + Ok(Value::Integer(x % y)) +} +pub(crate) fn builtin_div(_vm: &mut Machine, args: &[Value]) -> Result { + match args { + [] => Err(MachineError::InvalidArgument), + [x] if let &Value::Integer(_x) = x => todo!("Fractionals"), + [x] => todo!("Math for {x}"), + [x, rest @ ..] => { + let &Value::Integer(x) = x else { + todo!("Math for {x}"); + }; + let mut accumulator = x; + for arg in rest { + match arg { + &Value::Integer(arg) => { + accumulator = accumulator.saturating_div(arg); + } + _ => todo!("Math for {arg}"), + } + } + Ok(Value::Integer(accumulator)) + } + } +} + +pub(crate) fn builtin_and(_vm: &mut Machine, args: &[Value]) -> Result { + builtin_fold_bool(BitAnd::bitand, true, args) +} +pub(crate) fn builtin_or(_vm: &mut Machine, args: &[Value]) -> Result { + builtin_fold_bool(BitOr::bitor, false, args) +} +pub(crate) fn builtin_bitwise_and( + _vm: &mut Machine, + args: &[Value], +) -> Result { + builtin_fold_integer(BitAnd::bitand, 1, args) +} +pub(crate) fn builtin_bitwise_or(_vm: &mut Machine, args: &[Value]) -> Result { + builtin_fold_integer(BitOr::bitor, 0, args) +} +pub(crate) fn builtin_bitwise_xor( + _vm: &mut Machine, + args: &[Value], +) -> Result { + builtin_fold_integer(BitXor::bitxor, 0, args) +} + +pub(crate) fn builtin_cmp( + _vm: &mut Machine, + args: &[Value], + op: CompareOperation, +) -> Result { + fn ordering(a: &Value, b: &Value) -> Ordering { + match (a, b) { + (Value::Integer(a), Value::Integer(b)) => Ord::cmp(a, b), + (Value::Boolean(a), Value::Boolean(b)) => Ord::cmp(a, b), + _ => todo!(), + } + } + + let mut accumulator = true; + if !args.is_empty() { + for i in 0..args.len() - 1 { + let a = &args[i]; + let b = &args[i + 1]; + let ord = ordering(a, b); + + let check = match op { + CompareOperation::Eq => ord.is_eq(), + CompareOperation::Ne => ord.is_ne(), + CompareOperation::Lt => ord.is_lt(), + CompareOperation::Gt => ord.is_gt(), + CompareOperation::Le => ord.is_le(), + CompareOperation::Ge => ord.is_ge(), + }; + accumulator &= check; + } + } + Ok(Value::Boolean(accumulator)) +} +pub(crate) fn builtin_cmp_eq(vm: &mut Machine, args: &[Value]) -> Result { + builtin_cmp(vm, args, CompareOperation::Eq) +} +pub(crate) fn builtin_cmp_ne(vm: &mut Machine, args: &[Value]) -> Result { + builtin_cmp(vm, args, CompareOperation::Ne) +} +pub(crate) fn builtin_cmp_gt(vm: &mut Machine, args: &[Value]) -> Result { + builtin_cmp(vm, args, CompareOperation::Gt) +} +pub(crate) fn builtin_cmp_lt(vm: &mut Machine, args: &[Value]) -> Result { + builtin_cmp(vm, args, CompareOperation::Lt) +} +pub(crate) fn builtin_cmp_ge(vm: &mut Machine, args: &[Value]) -> Result { + builtin_cmp(vm, args, CompareOperation::Ge) +} +pub(crate) fn builtin_cmp_le(vm: &mut Machine, args: &[Value]) -> Result { + builtin_cmp(vm, args, CompareOperation::Le) +} + pub fn load(vm: &mut Machine) { + // math + vm.defun_native("+", builtin_add); + vm.defun_native("-", builtin_sub); + vm.defun_native("*", builtin_mul); + vm.defun_native("%", builtin_mod); + vm.defun_native("/", builtin_div); + + vm.defun_native("&&", builtin_and); + vm.defun_native("||", builtin_or); + vm.defun_native("&", builtin_bitwise_and); + vm.defun_native("|", builtin_bitwise_or); + vm.defun_native("^", builtin_bitwise_xor); + + vm.defun_native(">", builtin_cmp_gt); + vm.defun_native("<", builtin_cmp_lt); + vm.defun_native("=", builtin_cmp_eq); + vm.defun_native("/=", builtin_cmp_ne); + vm.defun_native(">=", builtin_cmp_ge); + vm.defun_native("<=", builtin_cmp_le); + + // lists vm.defun_native("map", |vm, args| { let [f, xs] = args else { return Err(MachineError::InvalidArgument); }; let f = AnyFunction::try_from_value(f)?; let xs = xs.proper_iter(); - let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, &[v?.clone()])))?; + let out = Value::try_list_or_nil(xs.map(|v| f.invoke(vm, slice::from_ref(v?))))?; Ok(out) }); + vm.defun_native("filter", |vm, args| { + let [f, xs] = args else { + return Err(MachineError::InvalidArgument); + }; + let f = AnyFunction::try_from_value(f)?; + let xs = xs.proper_iter().map(|x| x.cloned()); + let out = Value::try_list_or_nil(xs.try_filter(|v| { + let result = f.invoke(vm, slice::from_ref(v))?; + bool::try_from_value(&result) + }))?; + Ok(out) + }); + vm.defun_native("list", |_, args| { + let out = Value::list_or_nil(args.iter().cloned()); + Ok(out) + }); + + // functional vm.defun_native("identity", |_, args| { let [arg] = args else { return Err(MachineError::InvalidArgument); }; Ok(arg.clone()) }); - vm.defun_native("list", |_, args| { - let out = Value::list_or_nil(args.iter().cloned()); - Ok(out) + + // eval + vm.defun_native("apply", |vm, args| { + let [f, xs] = args else { + return Err(MachineError::InvalidArgument); + }; + let f = AnyFunction::try_from_value(f)?; + let args = xs + .proper_iter() + .map(|x| x.cloned()) + .collect::, _>>()?; + f.invoke(vm, &args[..]) + }); + vm.defun_native("assert", |vm, args| match args { + [] => Err(MachineError::InvalidArgument), + [cond] => { + let cond = bool::try_from_value(cond)?; + if !cond { + let ip = vm.ip(); + if let Some(ip) = ip { + eprintln!("Assertion failed at {ip}:"); + eprintln!(); + ip.module.dump(Some(ip.address), 8); + } + panic!("Assertion failed"); + } + Ok(Value::Nil) + } + _ => todo!(), + }); + + // io + vm.defun_native("print", |_, args| { + for (i, arg) in args.iter().enumerate() { + if i != 0 { + print!(" "); + } + print!("{arg}"); + } + println!(); + Ok(Value::Nil) }); } diff --git a/src/vm/value.rs b/src/vm/value.rs index 9ff6154..3dbd8a8 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -5,6 +5,7 @@ use crate::vm::{machine::MachineError, module::ModuleRef, native::NativeFunction #[derive(Debug, Clone, PartialEq)] pub struct BytecodeFunction { pub module: ModuleRef, + pub required_count: usize, pub address: usize, }