170 lines
4.9 KiB
Rust
170 lines
4.9 KiB
Rust
//! Language builtins, wired into the implementation.
|
|
|
|
use std::fmt::Write;
|
|
|
|
use crate::dictionary::Dictionary;
|
|
use crate::evaluate::Value;
|
|
use crate::syntax::Type;
|
|
|
|
/// Builtin functions.
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub enum Builtin {
|
|
Print,
|
|
Add,
|
|
Sub,
|
|
Mul,
|
|
Div,
|
|
Equals,
|
|
GreaterThan,
|
|
LessThan,
|
|
}
|
|
|
|
#[track_caller]
|
|
fn unwrap_int_value(op_name: &'static str) -> impl Fn(Value) -> i64 {
|
|
move |value| match value {
|
|
Value::Integer(i) => i,
|
|
_ => panic!("got non-integer during {op_name}"),
|
|
}
|
|
}
|
|
|
|
macro_rules! int_op {
|
|
($arguments:expr, $op:tt, $op_name:expr) => {{
|
|
let i = $arguments
|
|
.into_iter()
|
|
.map(unwrap_int_value($op_name))
|
|
.reduce(|acc, v| acc $op v)
|
|
.unwrap();
|
|
Value::Integer(i)
|
|
}};
|
|
}
|
|
|
|
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)
|
|
}};
|
|
}
|
|
|
|
fn int_type() -> Type {
|
|
Type::Constructor("Integer".to_string())
|
|
}
|
|
|
|
fn bool_type() -> Type {
|
|
Type::Constructor("Boolean".to_string())
|
|
}
|
|
|
|
fn int_op_type() -> Type {
|
|
Type::Function {
|
|
parameters: vec![int_type(), int_type()],
|
|
result: Box::new(int_type()),
|
|
}
|
|
}
|
|
|
|
fn int_cmp_type() -> Type {
|
|
Type::Function {
|
|
parameters: vec![int_type(), int_type()],
|
|
result: Box::new(bool_type()),
|
|
}
|
|
}
|
|
|
|
impl Builtin {
|
|
/// A mapping from runtime names to builtin sentinels.
|
|
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()
|
|
}
|
|
|
|
/// A mapping from runtime names to builtin types.
|
|
pub fn type_dictionary() -> Dictionary<Type> {
|
|
[
|
|
("_print", Type::Print),
|
|
("_add", int_op_type()),
|
|
("_sub", int_op_type()),
|
|
("_mul", int_op_type()),
|
|
("_div", int_op_type()),
|
|
("_equals", Type::Equals),
|
|
("_greaterthan", int_cmp_type()),
|
|
("_lessthan", int_cmp_type()),
|
|
]
|
|
.map(|(name, ty)| (name.to_string(), ty))
|
|
.into()
|
|
}
|
|
|
|
/// Minimum # of required arguments for a builtin.
|
|
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,
|
|
}
|
|
}
|
|
|
|
/// Maximum # of parameters allowed, if any.
|
|
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),
|
|
}
|
|
}
|
|
|
|
/// Execute the given builtin on the provided arguments.
|
|
pub fn call(&self, arguments: Vec<Value>) -> Value {
|
|
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, <),
|
|
}
|
|
}
|
|
}
|