diff --git a/src/executor.rs b/src/executor.rs index 415ea5c..a904c05 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -18,6 +18,7 @@ pub enum RuntimeError { NotAVariable(String), ParseFail(String, Type), TypeError(Type, Type), + EmptyArray, IO(io::Error), } @@ -26,7 +27,7 @@ impl Display for RuntimeError { match self { Self::ParseError(e) => write!(f, "Parser Error: {e}"), Self::NoOverloadForTypes(op, values) - => write!(f, "No overload of `{op}` exists for the operands `[{}]`", + => write!(f, "No overload of `{op}` exists for the operands `{}`", values.iter().map(|x| format!("{}({x})", x.get_type())).collect::>().join(", ")), Self::ImmutableError(ident) => write!(f, "`{ident}` already exists and cannot be redefined"), Self::VariableUndefined(ident) => write!(f, "variable `{ident}` was not defined"), @@ -36,6 +37,7 @@ impl Display for RuntimeError { Self::ParseFail(s, t) => write!(f, "`\"{s}\"` couldn't be parsed into {}", t), Self::IO(e) => write!(f, "{e}"), Self::TypeError(left, right) => write!(f, "expected type `{left}` but got type `{right}`"), + Self::EmptyArray => write!(f, "attempt to access element from an empty array"), } } } @@ -112,8 +114,28 @@ where (Value::Int(x), Value::Float(y)) => Ok(Value::Float(x as f64 + y)), (Value::Float(x), Value::Float(y)) => Ok(Value::Float(x + y)), (Value::String(x), Value::String(y)) => Ok(Value::String(format!("{x}{y}"))), - (Value::Array(_, x), y) => Ok(Value::Array(Type::Any, [x, vec![y]].concat())), - (x, Value::Array(_, y)) => Ok(Value::Array(Type::Any, [vec![x], y].concat())), + (Value::Array(t, x), y) => { + let ytype = y.get_type(); + + if t != ytype { + return Err(RuntimeError::TypeError(t, ytype)); + } + + // NOTE: use y's type instead of the arrays type. + // an `empty` array has Any type, but any value will have a fixed type. + // this converts the empty array into a typed array. + Ok(Value::Array(ytype, [x, vec![y]].concat())) + }, + (x, Value::Array(t, y)) => { + let xtype = x.get_type(); + + if t != xtype { + return Err(RuntimeError::TypeError(t, xtype)); + } + + // NOTE: read above + Ok(Value::Array(xtype, [vec![x], y].concat())) + }, (x, y) => Err(RuntimeError::NoOverloadForTypes("+".into(), vec![x, y])) }, ParseTree::Sub(x, y) => match (self.exec(x, locals)?, self.exec(y, locals)?) { @@ -371,6 +393,22 @@ where } } } + ParseTree::Head(x) => match self.exec(x, locals)? { + Value::Array(_, x) => Ok(x.first().ok_or(RuntimeError::EmptyArray)?.clone()), + t => Err(RuntimeError::NoOverloadForTypes("head".into(), vec![t])) + }, + ParseTree::Tail(x) => match self.exec(x, locals)? { + Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[1..].to_vec() } else { vec![] })), + t => Err(RuntimeError::NoOverloadForTypes("tail".into(), vec![t])) + }, + ParseTree::Init(x) => match self.exec(x, locals)? { + Value::Array(t, x) => Ok(Value::Array(t, if x.len() > 0 { x[..x.len() - 1].to_vec() } else { vec![] })), + t => Err(RuntimeError::NoOverloadForTypes("init".into(), vec![t])) + }, + ParseTree::Fini(x) => match self.exec(x, locals)? { + Value::Array(_, x) => Ok(x.last().ok_or(RuntimeError::EmptyArray)?.clone()), + t => Err(RuntimeError::NoOverloadForTypes("fini".into(), vec![t])) + }, } } } diff --git a/src/lib.rs b/src/lib.rs index 628ec7f..15de2c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,7 +113,7 @@ pub struct Function { } impl Function { - pub fn lambda(t: FunctionType, arg_names: Vec, body: Option>) -> Self { + fn lambda(t: FunctionType, arg_names: Vec, body: Option>) -> Self { Self { name: None, t, @@ -122,7 +122,7 @@ impl Function { } } - pub fn named(name: &str, t: FunctionType, arg_names: Option>, body: Option>) -> Self { + fn named(name: &str, t: FunctionType, arg_names: Option>, body: Option>) -> Self { Self { name: Some(name.to_string()), t, diff --git a/src/parser.rs b/src/parser.rs index 6189376..338928b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -70,6 +70,10 @@ pub(crate) enum ParseTree { // Functional Operations Compose(Box, Box), Id(Box), + Head(Box), + Tail(Box), + Init(Box), + Fini(Box), // Branching If(Box, Box), @@ -262,6 +266,10 @@ impl ParseTree { let name = Self::get_identifier(tokens.next())?; Ok(ParseTree::NonCall(name)) }, + Op::Head => one_arg!(Head, tokens, globals, locals), + Op::Tail => one_arg!(Tail, tokens, globals, locals), + Op::Init => one_arg!(Init, tokens, globals, locals), + Op::Fini => one_arg!(Fini, tokens, globals, locals), op => Err(ParseError::UnwantedToken(Token::Operator(op))), } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index db5f24c..00a0353 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -78,6 +78,10 @@ pub(crate) enum Op { And, Or, NonCall, + Head, + Tail, + Init, + Fini, } #[derive(Debug, Clone)] @@ -118,6 +122,10 @@ impl Token { "string" => Ok(Token::Operator(Op::StringCast)), "print" => Ok(Token::Operator(Op::Print)), "empty" => Ok(Token::Operator(Op::Empty)), + "head" => Ok(Token::Operator(Op::Head)), + "tail" => Ok(Token::Operator(Op::Tail)), + "init" => Ok(Token::Operator(Op::Init)), + "fini" => Ok(Token::Operator(Op::Fini)), // Types "Any" => Ok(Token::Type(Type::Any)), @@ -412,7 +420,7 @@ mod tests { #[test] fn uwu() { - let program = ":. apply : f x f x apply ; x ** x 2 10"; + let program = ":. map ?: f Any -> Any ?. x [Any] -> [Any] ?? bool x + f head x map 'f tail x empty map ;x ** x 2 [1 2 3 4 5]"; let tokens: Vec = Tokenizer::from_str(program).unwrap().collect::>().unwrap();