From bcee1cf16078fdcb3fda1a17f8f66c5614082e9a Mon Sep 17 00:00:00 2001 From: mat ess Date: Mon, 24 Jul 2023 12:23:13 -0400 Subject: [PATCH] Add builtins --- docs/ROADMAP.md | 3 ++ src/builtins.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 9 ++-- src/syntax.rs | 1 + 4 files changed, 140 insertions(+), 5 deletions(-) create mode 100644 src/builtins.rs diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 3fac9ee..1af86f2 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -14,6 +14,9 @@ - [x] integers - [ ] floating point numbers - [ ] text +- [ ] items + - [x] function definitions + - [ ] type definitions - [ ] functions - [x] lambdas / closures - [ ] generic functions diff --git a/src/builtins.rs b/src/builtins.rs new file mode 100644 index 0000000..267349b --- /dev/null +++ b/src/builtins.rs @@ -0,0 +1,132 @@ +use std::fmt::Write; + +use thiserror::Error; + +use crate::evaluate::{Dictionary, Value}; + +#[derive(Debug, Copy, Clone)] +pub enum Builtin { + Print, + Add, + Sub, + Mul, + Div, + Equals, + GreaterThan, + LessThan, +} + +#[derive(Debug, Error)] +pub enum Error { + #[error("Tried to {0} a non-integer: {1}")] + NonInteger(&'static str, Value), +} + +type Result = std::result::Result; + +fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> Result { + move |value| match value { + Value::Integer(i) => Ok(i), + _ => Err(Error::NonInteger(op_name, value)), + } +} + +macro_rules! int_op { + ($arguments:expr, $op:tt, $op_name:expr) => { + Value::Integer( + $arguments + .into_iter() + .map(unwrap_int_value($op_name)) + .reduce(|acc, v| Ok(acc? $op v?)) + .unwrap()?, + ) + }; +} + +macro_rules! int_cmp { + ($arguments:expr, $op:tt) => {{ + let mut arguments = $arguments; + let unwrap = unwrap_int_value("compare"); + let y = unwrap(arguments.pop().unwrap())?; + let x = unwrap(arguments.pop().unwrap())?; + Value::Boolean(x $op y) + }}; +} + +impl Builtin { + pub fn dictionary() -> Dictionary { + [ + ("_print", Builtin::Print), + ("_add", Builtin::Add), + ("_sub", Builtin::Sub), + ("_mul", Builtin::Mul), + ("_div", Builtin::Div), + ("_equals", Builtin::Equals), + ("_greaterthan", Builtin::GreaterThan), + ("_lessthan", Builtin::LessThan), + ] + .map(|(name, builtin)| (name.to_string(), Value::Builtin(builtin))) + .into() + } + + pub fn min_parameters(&self) -> usize { + match self { + Builtin::Print => 0, + Builtin::Add + | Builtin::Sub + | Builtin::Mul + | Builtin::Div + | Builtin::Equals + | Builtin::GreaterThan + | Builtin::LessThan => 2, + } + } + pub fn max_parameters(&self) -> Option { + match self { + Builtin::Print + | Builtin::Add + | Builtin::Sub + | Builtin::Mul + | Builtin::Div + | Builtin::Equals => None, + Builtin::GreaterThan | Builtin::LessThan => Some(2), + } + } + + pub fn call(&self, arguments: Vec) -> Result { + let result = match self { + Builtin::Print => { + let mut output = String::new(); + let mut arguments = arguments.into_iter().peekable(); + while let Some(term) = arguments.next() { + if arguments.peek().is_some() { + write!(output, "{term} ").expect("Error during printing"); + } else { + write!(output, "{term}").expect("Error during printing"); + } + } + println!("{output}"); + Value::Unit + } + Builtin::Add => int_op!(arguments, +, "add"), + Builtin::Sub => int_op!(arguments, -, "subtract"), + Builtin::Mul => int_op!(arguments, *, "multiply"), + Builtin::Div => int_op!(arguments, /, "divide"), + Builtin::Equals => { + let mut arguments = arguments; + let y = arguments.pop().unwrap(); + let x = arguments.pop().unwrap(); + let b = match (x, y) { + (Value::Integer(x), Value::Integer(y)) => x == y, + (Value::Boolean(x), Value::Boolean(y)) => x == y, + (Value::Unit, Value::Unit) => true, + _ => false, + }; + Value::Boolean(b) + } + Builtin::GreaterThan => int_cmp!(arguments, >), + Builtin::LessThan => int_cmp!(arguments, <), + }; + Ok(result) + } +} diff --git a/src/main.rs b/src/main.rs index 5b628be..6be69f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,8 @@ use std::path::Path; use anyhow::{anyhow, Context, Result}; use mul::cli::{Command, USAGE}; -use mul::phase::evaluate::Interpreter; -use mul::phase::parse::{parse_interactive_entry, parse_program}; +use mul::evaluate::Interpreter; +use mul::parse::{parse_interactive_entry, parse_program}; use mul::syntax::Program; fn main() -> Result<()> { @@ -23,8 +23,7 @@ fn main() -> Result<()> { Command::Run { path } => { let program = parse(path.as_path())?; let mut interpreter = Interpreter::new(); - let value = interpreter.run(&program)?; - println!("{value:#?}"); + let _result = interpreter.run(&program)?; Ok(()) } Command::Repl => repl(), @@ -64,7 +63,7 @@ fn repl() -> Result<()> { } }; let value = interpreter.evaluate_interactive_entry(&parsed)?; - println!("{value:?}"); + println!("{value}"); } Ok(()) } diff --git a/src/syntax.rs b/src/syntax.rs index 20cd2cf..312aeeb 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -46,6 +46,7 @@ pub enum Expression { }, Boolean(bool), Integer(i64), + Unit, } /// A sequence of bindings with a final result.