diff --git a/src/executor.rs b/src/executor.rs index d43c031..09cbb8a 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -102,6 +102,12 @@ impl Executor { [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::Nil, x] => Ok(x.clone()), + [x, Value::Nil] => Ok(x.clone()), + [x, y] => Err(Error::new(format!("no overload of + exists for types {} and {}", x.get_type(), y.get_type()))), + _ => unreachable!(), + } + Op::Concat => match &args[..] { [Value::Array(xtype, x), Value::Array(ytype, y)] => { if xtype != ytype { return Err(Error::new(format!("expected type {} but found {}", xtype, ytype))); @@ -109,20 +115,9 @@ impl Executor { Ok(Value::Array(xtype.clone(), [x.clone(), y.clone()].concat())) }, - [Value::Nil, x] => Ok(x.clone()), - [x, Value::Nil] => Ok(x.clone()), - [Value::Array(t, x), y] => { - let ytype = y.get_type(); - - if *t != ytype { - return Err(Error::new(format!("expected type {} but found {}", 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.clone(), vec![y.clone()]].concat())) - }, + _ => Err(Error::new("++".into())), + } + Op::Prepend => match &args[..] { [x, Value::Array(t, y)] => { let xtype = x.get_type(); @@ -130,10 +125,41 @@ impl Executor { return Err(Error::new(format!("expected type {} but found {}", t, xtype))); } - // NOTE: read above Ok(Value::Array(xtype, [vec![x.clone()], y.clone()].concat())) }, - _ => Err(Error::new("todo: add".into())), + [x, y] => Err(Error::new(format!("no overload of [+ exists for types {} and {}", x.get_type(), y.get_type()))), + _ => unreachable!(), + } + Op::Append => match &args[..] { + [Value::Array(t, y), x] => { + let xtype = x.get_type(); + + if *t != xtype { + return Err(Error::new(format!("expected type {} but found {}", t, xtype))); + } + + Ok(Value::Array(xtype, [y.clone(), vec![x.clone()]].concat())) + }, + _ => Err(Error::new("+]".into())), + } + Op::Insert => match &args[..] { + [Value::Int(idx), x, Value::Array(t, y)] => { + let mut y = y.clone(); + let xtype = x.get_type(); + + if *t != xtype { + return Err(Error::new(format!("expected type {} but found {}", t, xtype))); + } + + if *idx as usize > y.len() { + return Err(Error::new("attempt to insert out of array len".into())); + } + + y.insert(*idx as usize, x.clone()); + + Ok(Value::Array(t.clone(), y)) + }, + _ => Err(Error::new("[+]".into())), } Op::Sub => match &args[..] { [Value::Int(x), Value::Int(y)] => Ok(Value::Int(x - y)), diff --git a/src/parser.rs b/src/parser.rs index 6b86b73..238cfcb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -125,6 +125,10 @@ impl Parser { (Op::And, FunctionType(Box::new(Type::Bool), vec![Type::Bool, Type::Bool])), (Op::Or, FunctionType(Box::new(Type::Bool), vec![Type::Bool, Type::Bool])), (Op::Head, FunctionType(Box::new(Type::Any), vec![Type::Array(Box::new(Type::Any))])), + (Op::Concat, FunctionType(Box::new(Type::Array(Box::new(Type::Any))), vec![Type::Array(Box::new(Type::Any)), Type::Array(Box::new(Type::Any))])), + (Op::Prepend, FunctionType(Box::new(Type::Array(Box::new(Type::Any))), vec![Type::Any, Type::Array(Box::new(Type::Any))])), + (Op::Append, FunctionType(Box::new(Type::Array(Box::new(Type::Any))), vec![Type::Array(Box::new(Type::Any)), Type::Any])), + (Op::Insert, FunctionType(Box::new(Type::Array(Box::new(Type::Any))), vec![Type::Int, Type::Any, Type::Array(Box::new(Type::Any))])), (Op::Tail, FunctionType(Box::new(Type::Array(Box::new(Type::Any))), vec![Type::Array(Box::new(Type::Any))])), (Op::Init, FunctionType(Box::new(Type::Array(Box::new(Type::Any))), vec![Type::Array(Box::new(Type::Any))])), (Op::Fini, FunctionType(Box::new(Type::Any), vec![Type::Array(Box::new(Type::Any))])), @@ -238,7 +242,7 @@ impl Parser { let tree = trees.into_iter().fold( ParseTree::Value(Value::Array(Type::Any, vec![])), - |acc, x| ParseTree::Operator(Op::Add, vec![acc, x.clone()]), + |acc, x| ParseTree::Operator(Op::Append, vec![acc, x.clone()]), ); Ok(Some(tree)) @@ -372,21 +376,21 @@ impl Parser { }, Op::IfElse => { let cond = self.parse(tokens)? - .ok_or(Error::new("?? statement requires a condition".into()) - .location(token.line, token.location.clone()))?; + .ok_or(Error::new("?? statement requires a condition".into()) + .location(token.line, token.location.clone()))?; let truebranch = self.parse(tokens)? - .ok_or(Error::new("?? statement requires a branch".into()) - .location(token.line, token.location.clone()))?; + .ok_or(Error::new("?? statement requires a branch".into()) + .location(token.line, token.location.clone()))?; let falsebranch = self.parse(tokens)? - .ok_or(Error::new("?? statement requires a false branch".into()) - .location(token.line, token.location))?; + .ok_or(Error::new("?? statement requires a false branch".into()) + .location(token.line, token.location))?; Ok(Some(ParseTree::IfElse( Box::new(cond), Box::new(truebranch), Box::new(falsebranch)))) }, Op::Export => { let token = tokens.next() - .ok_or(Error::new("export expects one argument of [String], but found nothing".into()) + .ok_or(Error::new("export expects an identifer or multiple inside of parens".into()) .location(token.line, token.location.clone()))??; let names = match token.token() { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 8d4d446..7ad60f2 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -42,6 +42,10 @@ pub enum Op { Print, OpenArray, CloseArray, + Concat, + Prepend, + Append, + Insert, OpenStatement, CloseStatement, Empty, @@ -207,6 +211,10 @@ impl Tokenizer { ("!=", Op::NotEqualTo), ("[", Op::OpenArray), ("]", Op::CloseArray), + ("++", Op::Concat), + ("[+", Op::Prepend), + ("+]", Op::Append), + ("[+]", Op::Insert), ("(", Op::OpenStatement), (")", Op::CloseStatement), ("!", Op::Not), @@ -365,4 +373,19 @@ impl Iterator for Tokenizer { fn next(&mut self) -> Option { self.tokenize().transpose() } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::io::Cursor; + + #[test] + fn meow() { + let program = "[+] 0 1 [2 3]"; + + let tokens: Vec<_> = Tokenizer::new(Arc::new(Mutex::new(CodeIter::new(Cursor::new(program))))).collect(); + + println!("{tokens:#?}"); + } } \ No newline at end of file