diff --git a/src/executor.rs b/src/executor.rs index 4ab92fb..f34795c 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -116,6 +116,8 @@ 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([x, vec![y]].concat())), + (x, Value::Array(y)) => Ok(Value::Array([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)?) { @@ -216,7 +218,7 @@ where self.exec(scope, &mut Cow::Borrowed(&locals)) } }, - ParseTree::FunctionDefinition(ident, args, r, body, scope) => { + ParseTree::FunctionDefinition(ident, args, body, scope) => { let existing = locals.get(&ident).or(self.globals.get(&ident)).cloned(); match existing { @@ -225,7 +227,7 @@ where let locals = locals.to_mut(); locals.insert(ident.clone(), Object::Function(Function { - decl: FunctionDeclaration { _name: ident.clone(), _r: r, args }, + decl: FunctionDeclaration { _name: ident.clone(), args }, body: Some(body) })); @@ -243,6 +245,7 @@ where Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), + Value::Array(vec) => !vec.is_empty(), Value::Nil => false, } { self.exec(body, locals) @@ -254,6 +257,7 @@ where Value::Int(i) => i != 0, Value::Bool(b) => b, Value::String(s) => !s.is_empty(), + Value::Array(vec) => !vec.is_empty(), Value::Nil => false, } { self.exec(istrue, locals) @@ -267,7 +271,7 @@ where let locals = locals.to_mut(); let body = f.body.ok_or(RuntimeError::FunctionUndefined(ident.clone()))?; - for ((name, _), tree) in std::iter::zip(f.decl.args, args) { + for (name, tree) in std::iter::zip(f.decl.args, args) { locals.insert(name.clone(), Object::Variable(Evaluation::Computed(self.exec(Box::new(tree), &mut Cow::Borrowed(locals))?))); } @@ -321,6 +325,7 @@ where Value::Float(x) => Ok(Value::Bool(x != 0.0)), Value::Bool(x) => Ok(Value::Bool(x)), Value::String(x) => Ok(Value::Bool(!x.is_empty())), + Value::Array(vec) => Ok(Value::Bool(!vec.is_empty())), x => Err(RuntimeError::NoOverloadForTypes("bool".into(), vec![x])), }, ParseTree::StringCast(x) => Ok(Value::String(format!("{}", self.exec(x, locals)?))), diff --git a/src/lib.rs b/src/lib.rs index d5e1b4c..1527b32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ - mod tokenizer; mod parser; mod executor; @@ -10,15 +9,16 @@ use tokenizer::Tokenizer; use std::fmt::Display; use std::io::{Write, Read, BufRead}; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum Type { Float, Int, Bool, String, + Array, + _Function(Box, Vec), Nil, Any, - _Function(Box, Vec), } impl Display for Type { @@ -28,9 +28,10 @@ impl Display for Type { Self::Int => "Int".into(), Self::Bool => "Bool".into(), Self::String => "String".into(), + Self::Array => format!("Array"), + Self::_Function(r, _) => format!("Function -> {}", *r), Self::Nil => "Nil".into(), Self::Any => "Any".into(), - Self::_Function(r, _) => format!("Function -> {}", *r) }) } } @@ -42,6 +43,7 @@ pub enum Value { Int(i64), Bool(bool), String(String), + Array(Vec), Nil, } @@ -52,6 +54,7 @@ impl Value { Self::Int(_) => Type::Int, Self::Bool(_) => Type::Bool, Self::String(_) => Type::String, + Self::Array(_) => Type::Array, Self::Nil => Type::Nil, } } @@ -64,6 +67,7 @@ impl Display for Value { Self::Int(x) => write!(f, "{x}"), Self::Bool(x) => write!(f, "{}", if *x { "true" } else { "false" }), Self::String(x) => write!(f, "{x}"), + Self::Array(v) => write!(f, "[{}]", v.iter().map(|x| format!("{x}")).collect::>().join(" ")), Self::Nil => write!(f, "nil"), } } @@ -72,8 +76,7 @@ impl Display for Value { #[derive(Clone, Debug)] pub(crate) struct FunctionDeclaration { _name: String, - _r: Type, - args: Vec<(String, Type)>, + args: Vec, } pub struct Runtime<'a, R: BufRead> { diff --git a/src/parser.rs b/src/parser.rs index 45e6468..840f8ee 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,6 +14,7 @@ pub enum ParseError { InvalidIdentifier, FunctionUndefined(String), VariableUndefined(String), + UnmatchedArrayClose, TokenizeError(TokenizeError), } @@ -26,6 +27,7 @@ impl Display for ParseError { ParseError::FunctionUndefined(name) => write!(f, "Undefined function `{name}`"), ParseError::VariableUndefined(name) => write!(f, "Undefined variable `{name}`"), ParseError::NoInput => write!(f, "No input given"), + ParseError::UnmatchedArrayClose => write!(f, "there was an unmatched array closing operator `]`"), ParseError::TokenizeError(e) => write!(f, "{e}"), } } @@ -54,7 +56,7 @@ pub(crate) enum ParseTree { // Defining Objects Equ(String, Box, Box), LazyEqu(String, Box, Box), - FunctionDefinition(String, Vec<(String, Type)>, Type, Box, Box), + FunctionDefinition(String, Vec, Box, Box), // Functional Operations Compose(Box, Box), @@ -157,9 +159,9 @@ impl ParseTree { .map_err(|e| ParseError::TokenizeError(e))?; if let Token::Identifier(ident) = token { - let args: Vec<(String, Type)> = tokens.take(nargs) + let args: Vec = tokens.take(nargs) .map(|token| match token { - Ok(Token::Identifier(ident)) => Ok((ident, Type::Any)), + Ok(Token::Identifier(ident)) => Ok(ident), Ok(_) => Err(ParseError::InvalidIdentifier), Err(e) => Err(ParseError::TokenizeError(e)), }) @@ -169,14 +171,12 @@ impl ParseTree { locals.insert(ident.clone(), FunctionDeclaration { _name: ident.clone(), - _r: Type::Any, args: args.clone(), }); Ok(ParseTree::FunctionDefinition( ident, args, - Type::Any, Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?), Box::new(ParseTree::parse(tokens, globals, &mut Cow::Borrowed(&*locals))?))) } else { @@ -224,7 +224,35 @@ impl ParseTree { Op::FloatCast => Ok(ParseTree::FloatCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::BoolCast => Ok(ParseTree::BoolCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), Op::StringCast => Ok(ParseTree::StringCast(Box::new(ParseTree::parse(tokens, globals, locals)?))), - Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))) + Op::Print => Ok(ParseTree::Print(Box::new(ParseTree::parse(tokens, globals, locals)?))), + Op::OpenArray => { + let mut depth = 1; + + // take tokens until we reach the end of this array + // if we don't collect them here it causes rust to overflow computing the types + let mut array_tokens = tokens.by_ref().take_while(|t| match t { + Ok(Token::Operator(Op::OpenArray)) => { + depth += 1; + true + }, + Ok(Token::Operator(Op::CloseArray)) => { + depth -= 1; + depth > 0 + } + _ => true, + }).collect::>>().into_iter(); + + let parser: Vec = Parser::new(&mut array_tokens).collect::>()?; + + let tree = parser.iter().fold( + ParseTree::Constant(Value::Array(vec![])), + |acc, x| ParseTree::Add(Box::new(acc), Box::new(x.clone())), + ); + + Ok(tree) + } + Op::Empty => Ok(ParseTree::Constant(Value::Array(vec![]))), + Op::CloseArray => Err(ParseError::UnmatchedArrayClose), } } } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6fc705a..7c544ea 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -65,6 +65,9 @@ pub(crate) enum Op { BoolCast, StringCast, Print, + OpenArray, + CloseArray, + Empty, } #[derive(Debug, Clone)] @@ -110,6 +113,8 @@ impl Token { ">=" => Ok(Token::Operator(Op::GreaterThanOrEqualTo)), "<=" => Ok(Token::Operator(Op::LessThanOrEqualTo)), "==" => Ok(Token::Operator(Op::EqualTo)), + "[" => Ok(Token::Operator(Op::OpenArray)), + "]" => Ok(Token::Operator(Op::CloseArray)), // then some keywords "true" => Ok(Token::Constant(Value::Bool(true))), @@ -124,6 +129,7 @@ impl Token { // misc "print" => Ok(Token::Operator(Op::Print)), + "empty" => Ok(Token::Operator(Op::Empty)), // then variable length keywords _ => {