feat: error node

Errors as values, baby!!
This commit is contained in:
Roman Godmaire 2024-05-10 18:54:18 -04:00
parent ff6b12e408
commit 2e19a44c0a
4 changed files with 57 additions and 32 deletions

View file

@ -2,6 +2,7 @@ use std::borrow::Borrow;
use std::collections::HashMap;
use super::{NativeFunc, Value};
use crate::macros::arg_count;
use crate::Node;
pub(super) fn core() -> HashMap<String, Value> {
@ -63,13 +64,37 @@ pub(super) fn core() -> HashMap<String, Value> {
.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
("list", NativeFunc(Node::List)),
(
"list?",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(1, args.len());
arg_count!(1, args.len());
if let Node::List(_list) = args[0].borrow() {
return Node::Boolean(true);
@ -82,8 +107,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"vector?",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(1, args.len());
arg_count!(1, args.len());
if let Node::Vector(_vec) = args[0].borrow() {
return Node::Boolean(true);
@ -95,8 +119,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"hashmap",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(modulo: 2, args.len());
arg_count!(modulo: 2, args.len());
let mut index = -1;
let (keys, values): (Vec<_>, Vec<_>) = args.into_iter().partition(|_| {
@ -118,8 +141,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"hashmap?",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(1, args.len());
arg_count!(1, args.len());
if let Node::Map(_map) = args[0].borrow() {
return Node::Boolean(true);
@ -131,8 +153,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"empty?",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(1, args.len());
arg_count!(1, args.len());
match args[0].borrow() {
Node::List(list) => Node::Boolean(list.is_empty()),
@ -145,8 +166,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"count",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(1, args.len());
arg_count!(1, args.len());
match args[0].borrow() {
Node::List(list) => Node::Int(list.len() as i64),
@ -160,8 +180,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"eq?",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(2, args.len());
arg_count!(2, args.len());
let lhs = args[0].borrow();
let rhs = args[1].borrow();
@ -178,8 +197,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"not",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(1, args.len());
arg_count!(1, args.len());
let expr = args[0].borrow();
if let Node::Boolean(false) = expr {
@ -192,8 +210,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"<",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(2, args.len());
arg_count!(2, args.len());
let mut args = args.into_iter();
let lhs = args.next().unwrap();
@ -213,8 +230,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
">",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(2, args.len());
arg_count!(2, args.len());
let mut args = args.into_iter();
let lhs = args.next().unwrap();
@ -234,8 +250,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"<=",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(2, args.len());
arg_count!(2, args.len());
let mut args = args.into_iter();
let lhs = args.next().unwrap();
@ -255,8 +270,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
">=",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(2, args.len());
arg_count!(2, args.len());
let mut args = args.into_iter();
let lhs = args.next().unwrap();
@ -277,8 +291,7 @@ pub(super) fn core() -> HashMap<String, Value> {
(
"str",
NativeFunc(|args| {
// TODO: Reimplement this
// arg_count!(1, args.len());
arg_count!(1, args.len());
let val = args[0].borrow();
Node::String(val.to_string())

View file

@ -51,7 +51,8 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
.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) => {
let operator = env.get(sym).ok_or_else(|| Error::NotInEnv(sym.clone()))?;
match operator {
@ -66,7 +67,6 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
}
}
// TODO: Native functions???
_ => Err(Error::InvalidOperator)?,
}
}
@ -153,6 +153,7 @@ pub fn eval_node(env: &Environment, ast_node: Node) -> Result<Node> {
| Node::Boolean(_)
| Node::Nil
| Node::Void
| Node::Error(_)
| Node::Vector(_)
| Node::Map(_)
| Node::Function { .. } => ast_node,
@ -176,6 +177,11 @@ mod test {
#[case(":owo", ":owo")]
#[case("()", "()")]
#[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
#[case("(eval (list + 1 1))", "2")]
#[case("(apply + (list 1 2 3))", "6")]

View file

@ -2,18 +2,19 @@
macro_rules! arg_count {
($expected:expr, $given:expr) => {
if $expected != $given {
Err(Error::MismatchedArgCount($expected, $given))?
return Node::Error(format!("expected {} args, got {}", $expected, $given));
}
};
(modulo: $modulo:expr, $given:expr) => {
if $given % $modulo != 0 {
Err(Error::MismatchedArgCount(
return Node::Error(format!(
"expected {} args, got {}",
($given / $modulo) * $modulo + $modulo,
$given,
))?
));
}
};
}
pub(crate) use arg_count;

View file

@ -13,6 +13,8 @@ pub enum Node {
Nil,
Void, // XXX: Not sure if this should be a node
Error(String),
Vector(Vec<Node>),
Map(HashMap<String, Node>),
@ -62,6 +64,7 @@ impl Node {
Node::Boolean(_) => "boolean".to_string(),
Node::Nil => "nil".to_string(),
Node::Void => "void".to_string(),
Node::Error(_) => "error".to_string(),
Node::Vector(_) => "vector".to_string(),
Node::Map(_) => "map".to_string(),
Node::Define { .. } => "define".to_string(),
@ -101,6 +104,8 @@ impl std::fmt::Display for Node {
Node::Nil => write!(f, "()"),
Node::Void => write!(f, ""),
Node::Error(val) => write!(f, "error: {}", val),
Node::Vector(vec) => {
let s = vec
.iter()