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 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())

View file

@ -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")]

View file

@ -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;

View file

@ -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()