Add builtins
parent
e84b351daa
commit
bcee1cf160
|
@ -14,6 +14,9 @@
|
|||
- [x] integers
|
||||
- [ ] floating point numbers
|
||||
- [ ] text
|
||||
- [ ] items
|
||||
- [x] function definitions
|
||||
- [ ] type definitions
|
||||
- [ ] functions
|
||||
- [x] lambdas / closures
|
||||
- [ ] generic functions
|
||||
|
|
|
@ -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<T> = std::result::Result<T, Error>;
|
||||
|
||||
fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> Result<i64> {
|
||||
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<Value> {
|
||||
[
|
||||
("_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<usize> {
|
||||
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<Value>) -> Result<Value> {
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ pub enum Expression {
|
|||
},
|
||||
Boolean(bool),
|
||||
Integer(i64),
|
||||
Unit,
|
||||
}
|
||||
|
||||
/// A sequence of bindings with a final result.
|
||||
|
|
Loading…
Reference in New Issue