feat: error node
Errors as values, baby!!
This commit is contained in:
parent
ff6b12e408
commit
2e19a44c0a
4 changed files with 57 additions and 32 deletions
65
mute-interpreter/src/env/core.rs
vendored
65
mute-interpreter/src/env/core.rs
vendored
|
@ -2,6 +2,7 @@ use std::borrow::Borrow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::{NativeFunc, Value};
|
use super::{NativeFunc, Value};
|
||||||
|
use crate::macros::arg_count;
|
||||||
use crate::Node;
|
use crate::Node;
|
||||||
|
|
||||||
pub(super) fn core() -> HashMap<String, Value> {
|
pub(super) fn core() -> HashMap<String, Value> {
|
||||||
|
@ -63,13 +64,37 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
.unwrap_or(Node::Int(0))
|
.unwrap_or(Node::Int(0))
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"error",
|
||||||
|
NativeFunc(|args| {
|
||||||
|
arg_count!(1, args.len());
|
||||||
|
|
||||||
|
let msg = match args.into_iter().next() {
|
||||||
|
Some(Node::String(msg)) => msg,
|
||||||
|
_ => return Node::Error("Expected string".to_string()),
|
||||||
|
};
|
||||||
|
|
||||||
|
Node::Error(msg)
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"error?",
|
||||||
|
NativeFunc(|args| {
|
||||||
|
arg_count!(1, args.len());
|
||||||
|
|
||||||
|
if let Node::Error(_) = args[0].borrow() {
|
||||||
|
return Node::Boolean(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Node::Boolean(false)
|
||||||
|
}),
|
||||||
|
),
|
||||||
// Collections
|
// Collections
|
||||||
("list", NativeFunc(Node::List)),
|
("list", NativeFunc(Node::List)),
|
||||||
(
|
(
|
||||||
"list?",
|
"list?",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(1, args.len());
|
||||||
// arg_count!(1, args.len());
|
|
||||||
|
|
||||||
if let Node::List(_list) = args[0].borrow() {
|
if let Node::List(_list) = args[0].borrow() {
|
||||||
return Node::Boolean(true);
|
return Node::Boolean(true);
|
||||||
|
@ -82,8 +107,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"vector?",
|
"vector?",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(1, args.len());
|
||||||
// arg_count!(1, args.len());
|
|
||||||
|
|
||||||
if let Node::Vector(_vec) = args[0].borrow() {
|
if let Node::Vector(_vec) = args[0].borrow() {
|
||||||
return Node::Boolean(true);
|
return Node::Boolean(true);
|
||||||
|
@ -95,8 +119,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"hashmap",
|
"hashmap",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(modulo: 2, args.len());
|
||||||
// arg_count!(modulo: 2, args.len());
|
|
||||||
|
|
||||||
let mut index = -1;
|
let mut index = -1;
|
||||||
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
|
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
|
||||||
|
@ -118,8 +141,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"hashmap?",
|
"hashmap?",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(1, args.len());
|
||||||
// arg_count!(1, args.len());
|
|
||||||
|
|
||||||
if let Node::Map(_map) = args[0].borrow() {
|
if let Node::Map(_map) = args[0].borrow() {
|
||||||
return Node::Boolean(true);
|
return Node::Boolean(true);
|
||||||
|
@ -131,8 +153,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"empty?",
|
"empty?",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(1, args.len());
|
||||||
// arg_count!(1, args.len());
|
|
||||||
|
|
||||||
match args[0].borrow() {
|
match args[0].borrow() {
|
||||||
Node::List(list) => Node::Boolean(list.is_empty()),
|
Node::List(list) => Node::Boolean(list.is_empty()),
|
||||||
|
@ -145,8 +166,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"count",
|
"count",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(1, args.len());
|
||||||
// arg_count!(1, args.len());
|
|
||||||
|
|
||||||
match args[0].borrow() {
|
match args[0].borrow() {
|
||||||
Node::List(list) => Node::Int(list.len() as i64),
|
Node::List(list) => Node::Int(list.len() as i64),
|
||||||
|
@ -160,8 +180,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"eq?",
|
"eq?",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(2, args.len());
|
||||||
// arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let lhs = args[0].borrow();
|
let lhs = args[0].borrow();
|
||||||
let rhs = args[1].borrow();
|
let rhs = args[1].borrow();
|
||||||
|
@ -178,8 +197,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"not",
|
"not",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(1, args.len());
|
||||||
// arg_count!(1, args.len());
|
|
||||||
|
|
||||||
let expr = args[0].borrow();
|
let expr = args[0].borrow();
|
||||||
if let Node::Boolean(false) = expr {
|
if let Node::Boolean(false) = expr {
|
||||||
|
@ -192,8 +210,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"<",
|
"<",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(2, args.len());
|
||||||
// arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -213,8 +230,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
">",
|
">",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(2, args.len());
|
||||||
// arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -234,8 +250,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"<=",
|
"<=",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(2, args.len());
|
||||||
// arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -255,8 +270,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
">=",
|
">=",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(2, args.len());
|
||||||
// arg_count!(2, args.len());
|
|
||||||
|
|
||||||
let mut args = args.into_iter();
|
let mut args = args.into_iter();
|
||||||
let lhs = args.next().unwrap();
|
let lhs = args.next().unwrap();
|
||||||
|
@ -277,8 +291,7 @@ pub(super) fn core() -> HashMap<String, Value> {
|
||||||
(
|
(
|
||||||
"str",
|
"str",
|
||||||
NativeFunc(|args| {
|
NativeFunc(|args| {
|
||||||
// TODO: Reimplement this
|
arg_count!(1, args.len());
|
||||||
// arg_count!(1, args.len());
|
|
||||||
|
|
||||||
let val = args[0].borrow();
|
let val = args[0].borrow();
|
||||||
Node::String(val.to_string())
|
Node::String(val.to_string())
|
||||||
|
|
|
@ -51,7 +51,8 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: This feels sooooooo wrong
|
// HACK: This feels sooooooo wrong but it works
|
||||||
|
// Native Functions /should/ be their own type, but they're not.
|
||||||
Node::Symbol(sym) => {
|
Node::Symbol(sym) => {
|
||||||
let operator = env.get(sym).ok_or_else(|| Error::NotInEnv(sym.clone()))?;
|
let operator = env.get(sym).ok_or_else(|| Error::NotInEnv(sym.clone()))?;
|
||||||
match operator {
|
match operator {
|
||||||
|
@ -66,7 +67,6 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Native functions???
|
|
||||||
_ => Err(Error::InvalidOperator)?,
|
_ => Err(Error::InvalidOperator)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,7 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
|
||||||
| Node::Boolean(_)
|
| Node::Boolean(_)
|
||||||
| Node::Nil
|
| Node::Nil
|
||||||
| Node::Void
|
| Node::Void
|
||||||
|
| Node::Error(_)
|
||||||
| Node::Vector(_)
|
| Node::Vector(_)
|
||||||
| Node::Map(_)
|
| Node::Map(_)
|
||||||
| Node::Function { .. } => ast_node,
|
| Node::Function { .. } => ast_node,
|
||||||
|
@ -176,6 +177,11 @@ mod test {
|
||||||
#[case(":owo", ":owo")]
|
#[case(":owo", ":owo")]
|
||||||
#[case("()", "()")]
|
#[case("()", "()")]
|
||||||
#[case("nil", "()")]
|
#[case("nil", "()")]
|
||||||
|
// Errors
|
||||||
|
#[case("(error \"this failed\")", "error: this failed")]
|
||||||
|
#[case("(error? (error \"this failed\"))", "true")]
|
||||||
|
#[case("(error? 69)", "false")]
|
||||||
|
#[case("(str 1 2 3)", "error: expected 1 args, got 3")]
|
||||||
// Meta
|
// Meta
|
||||||
#[case("(eval (list + 1 1))", "2")]
|
#[case("(eval (list + 1 1))", "2")]
|
||||||
#[case("(apply + (list 1 2 3))", "6")]
|
#[case("(apply + (list 1 2 3))", "6")]
|
||||||
|
|
|
@ -2,18 +2,19 @@
|
||||||
macro_rules! arg_count {
|
macro_rules! arg_count {
|
||||||
($expected:expr, $given:expr) => {
|
($expected:expr, $given:expr) => {
|
||||||
if $expected != $given {
|
if $expected != $given {
|
||||||
Err(Error::MismatchedArgCount($expected, $given))?
|
return Node::Error(format!("expected {} args, got {}", $expected, $given));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(modulo: $modulo:expr, $given:expr) => {
|
(modulo: $modulo:expr, $given:expr) => {
|
||||||
if $given % $modulo != 0 {
|
if $given % $modulo != 0 {
|
||||||
Err(Error::MismatchedArgCount(
|
return Node::Error(format!(
|
||||||
|
"expected {} args, got {}",
|
||||||
($given / $modulo) * $modulo + $modulo,
|
($given / $modulo) * $modulo + $modulo,
|
||||||
$given,
|
$given,
|
||||||
))?
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) use arg_count;
|
||||||
|
|
|
@ -13,6 +13,8 @@ pub enum Node {
|
||||||
Nil,
|
Nil,
|
||||||
Void, // XXX: Not sure if this should be a node
|
Void, // XXX: Not sure if this should be a node
|
||||||
|
|
||||||
|
Error(String),
|
||||||
|
|
||||||
Vector(Vec<Node>),
|
Vector(Vec<Node>),
|
||||||
Map(HashMap<String, Node>),
|
Map(HashMap<String, Node>),
|
||||||
|
|
||||||
|
@ -62,6 +64,7 @@ impl Node {
|
||||||
Node::Boolean(_) => "boolean".to_string(),
|
Node::Boolean(_) => "boolean".to_string(),
|
||||||
Node::Nil => "nil".to_string(),
|
Node::Nil => "nil".to_string(),
|
||||||
Node::Void => "void".to_string(),
|
Node::Void => "void".to_string(),
|
||||||
|
Node::Error(_) => "error".to_string(),
|
||||||
Node::Vector(_) => "vector".to_string(),
|
Node::Vector(_) => "vector".to_string(),
|
||||||
Node::Map(_) => "map".to_string(),
|
Node::Map(_) => "map".to_string(),
|
||||||
Node::Define { .. } => "define".to_string(),
|
Node::Define { .. } => "define".to_string(),
|
||||||
|
@ -101,6 +104,8 @@ impl std::fmt::Display for Node {
|
||||||
Node::Nil => write!(f, "()"),
|
Node::Nil => write!(f, "()"),
|
||||||
Node::Void => write!(f, ""),
|
Node::Void => write!(f, ""),
|
||||||
|
|
||||||
|
Node::Error(val) => write!(f, "error: {}", val),
|
||||||
|
|
||||||
Node::Vector(vec) => {
|
Node::Vector(vec) => {
|
||||||
let s = vec
|
let s = vec
|
||||||
.iter()
|
.iter()
|
||||||
|
|
Loading…
Reference in a new issue