diff --git a/scripts/array.rhai b/scripts/array.rhai new file mode 100644 index 00000000..77c186cf --- /dev/null +++ b/scripts/array.rhai @@ -0,0 +1,4 @@ +var x = [1, 2, 3]; +print(x[1]); +x[1] = 5; +print(x[1]); diff --git a/src/engine.rs b/src/engine.rs index c5a5012b..83fb3892 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -15,6 +15,7 @@ pub enum EvalAltResult { ErrorFunctionNotFound, ErrorFunctionArgMismatch, ErrorFunctionCallNotSupported, + ErrorIndexMismatch, ErrorIfGuardMismatch, ErrorVariableNotFound(String), ErrorFunctionArityNotSupported, @@ -26,12 +27,14 @@ pub enum EvalAltResult { Return(Box) } + impl Error for EvalAltResult { fn description(&self) -> &str { match *self { EvalAltResult::ErrorFunctionNotFound => "Function not found", EvalAltResult::ErrorFunctionArgMismatch => "Function argument types do not match", EvalAltResult::ErrorFunctionCallNotSupported => "Function call with > 2 argument not supported", + EvalAltResult::ErrorIndexMismatch => "Index does not match array", EvalAltResult::ErrorIfGuardMismatch => "If guards expect boolean expression", EvalAltResult::ErrorVariableNotFound(_) => "Variable not found", EvalAltResult::ErrorFunctionArityNotSupported => "Functions of more than 3 parameters are not yet supported", @@ -537,10 +540,32 @@ impl Engine { } Err(EvalAltResult::ErrorVariableNotFound(id.clone())) } + Expr::Index(ref id, ref idx_raw) => { + let idx = try!(self.eval_expr(scope, idx_raw)); + + for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() { + if *id == *name { + if let Ok(i) = idx.downcast::() { + if let Some(arr_typed) = (*val).downcast_mut() as Option<&mut Vec>> { + return self.call_fn("clone", Some(&mut arr_typed[*i as usize]), None, None, None, None, None); + } + else { + return Err(EvalAltResult::ErrorIndexMismatch); + } + } + else { + return Err(EvalAltResult::ErrorIndexMismatch); + } + } + } + + Err(EvalAltResult::ErrorVariableNotFound(id.clone())) + } Expr::Assignment(ref id, ref rhs) => { + let rhs_val = try!(self.eval_expr(scope, rhs)); + match **id { Expr::Identifier(ref n) => { - let rhs_val = try!(self.eval_expr(scope, rhs)); for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() { if *n == *name { @@ -551,9 +576,29 @@ impl Engine { } Err(EvalAltResult::ErrorVariableNotFound(n.clone())) } - Expr::Dot(ref dot_lhs, ref dot_rhs) => { - let rhs_val = try!(self.eval_expr(scope, rhs)); + Expr::Index(ref id, ref idx_raw) => { + let idx = try!(self.eval_expr(scope, idx_raw)); + for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() { + if *id == *name { + if let Ok(i) = idx.downcast::() { + if let Some(arr_typed) = (*val).downcast_mut() as Option<&mut Vec>> { + arr_typed[*i as usize] = rhs_val; + return Ok(Box::new(())); + } + else { + return Err(EvalAltResult::ErrorIndexMismatch); + } + } + else { + return Err(EvalAltResult::ErrorIndexMismatch); + } + } + } + + Err(EvalAltResult::ErrorVariableNotFound(id.clone())) + } + Expr::Dot(ref dot_lhs, ref dot_rhs) => { self.set_dot_val(scope, dot_lhs, dot_rhs, rhs_val) } _ => Err(EvalAltResult::ErrorAssignmentToUnknownLHS) @@ -562,6 +607,16 @@ impl Engine { Expr::Dot(ref lhs, ref rhs) => { self.get_dot_val(scope, lhs, rhs) } + Expr::Array(ref contents) => { + let mut arr = Vec::new(); + + for item in (*contents).iter() { + let arg = try!(self.eval_expr(scope, item)); + arr.push(arg); + } + + Ok(Box::new(arr)) + } Expr::FnCall(ref fn_name, ref args) => { if args.len() == 0 { self.call_fn(&fn_name, None, None, None, None, None, None) @@ -838,6 +893,12 @@ impl Engine { reg_op!(engine, "||", or, bool); reg_op!(engine, "&&", and, bool); + + //engine.register_fn("[]", idx); + //FIXME? Registering array lookups are a special case because we want to return boxes directly + //let ent = engine.fns.entry("[]".to_string()).or_insert(Vec::new()); + //(*ent).push(FnType::ExternalFn2(Box::new(idx))); + } pub fn new() -> Engine { @@ -1170,3 +1231,24 @@ fn test_string() { assert!(false); } } + +#[test] +fn test_vec() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval::("var x = [1, 2, 3]; x[1]") { + assert_eq!(result, 2); + } + else { + assert!(false); + } + + if let Ok(result) = engine.eval::("var y = [1, 2, 3]; y[1] = 5; y[1]") { + assert_eq!(result, 5); + } + else { + assert!(false); + } +} + + diff --git a/src/fn_register.rs b/src/fn_register.rs index eb1f2b47..5c4f7fbf 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -8,8 +8,8 @@ pub trait FnRegister { } impl<'a, A, T, U, V, W, X, Y, Z> FnRegister for Engine - where A: 'static+Fn(&mut T, U, V, W, X, Y) -> Z, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, - X: Clone+Any, Y: Clone+Any, Z: Clone+Any + where A: 'static+Fn(&mut T, U, V, W, X, Y) -> Z, T: Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, + X: Clone+Any, Y: Clone+Any, Z: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box, &mut Box, &mut Box, @@ -41,7 +41,7 @@ impl<'a, A, T, U, V, W, X, Y, Z> FnRegister fo impl<'a, A, T, U, V, W, X, Y, Z> FnRegister for Engine where A: 'static+Fn(T, U, V, W, X, Y) -> Z, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, - X: Clone+Any, Y: Clone+Any, Z: Clone+Any + X: Clone+Any, Y: Clone+Any, Z: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box, &mut Box, &mut Box, @@ -72,7 +72,7 @@ impl<'a, A, T, U, V, W, X, Y, Z> FnRegister for En } impl<'a, A, T, U, V, W, X, Y> FnRegister for Engine - where A: 'static+Fn(&mut T, U, V, W, X) -> Y, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Clone+Any, Y: Clone+Any + where A: 'static+Fn(&mut T, U, V, W, X) -> Y, T: Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Clone+Any, Y: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box, &mut Box, &mut Box)->Result, EvalAltResult>> = @@ -100,7 +100,7 @@ impl<'a, A, T, U, V, W, X, Y> FnRegister for Engi } impl<'a, A, T, U, V, W, X, Y> FnRegister for Engine - where A: 'static+Fn(T, U, V, W, X) -> Y, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Clone+Any, Y: Clone+Any + where A: 'static+Fn(T, U, V, W, X) -> Y, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Clone+Any, Y: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box, &mut Box, &mut Box)->Result, EvalAltResult>> = @@ -128,7 +128,7 @@ impl<'a, A, T, U, V, W, X, Y> FnRegister for Engine } impl<'a, A, T, U, V, W, X> FnRegister for Engine - where A: 'static+Fn(&mut T, U, V, W) -> X, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Clone+Any + where A: 'static+Fn(&mut T, U, V, W) -> X, T: Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box, &mut Box)->Result, EvalAltResult>> = @@ -152,7 +152,7 @@ impl<'a, A, T, U, V, W, X> FnRegister for Engine } impl<'a, A, T, U, V, W, X> FnRegister for Engine - where A: 'static+Fn(T, U, V, W) -> X, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Clone+Any + where A: 'static+Fn(T, U, V, W) -> X, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any, X: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box, &mut Box)->Result, EvalAltResult>> = @@ -176,7 +176,7 @@ impl<'a, A, T, U, V, W, X> FnRegister for Engine } impl<'a, A, T, U, V, W> FnRegister for Engine - where A: 'static+Fn(&mut T, U, V) -> W, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any + where A: 'static+Fn(&mut T, U, V) -> W, T: Any, U: Clone+Any, V: Clone+Any, W: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box)->Result, EvalAltResult>> = @@ -199,7 +199,7 @@ impl<'a, A, T, U, V, W> FnRegister for Engine } impl<'a, A, T, U, V, W> FnRegister for Engine - where A: 'static+Fn(T, U, V) -> W, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Clone+Any + where A: 'static+Fn(T, U, V) -> W, T: Clone+Any, U: Clone+Any, V: Clone+Any, W: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box, &mut Box)->Result, EvalAltResult>> = @@ -222,7 +222,7 @@ impl<'a, A, T, U, V, W> FnRegister for Engine } impl<'a, A, T, U, V> FnRegister for Engine - where A: 'static+Fn(&mut T, U) -> V, T: Clone+Any, U: Clone+Any, V: Clone+Any + where A: 'static+Fn(&mut T, U) -> V, T: Any, U: Clone+Any, V: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box)->Result, EvalAltResult>> = @@ -244,7 +244,7 @@ impl<'a, A, T, U, V> FnRegister for Engine } impl<'a, A, T, U, V> FnRegister for Engine - where A: 'static+Fn(T, U) -> V, T: Clone+Any, U: Clone+Any, V: Clone+Any + where A: 'static+Fn(T, U) -> V, T: Clone+Any, U: Clone+Any, V: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box, &mut Box)->Result, EvalAltResult>> = @@ -266,7 +266,7 @@ impl<'a, A, T, U, V> FnRegister for Engine } impl<'a, A, T, U> FnRegister for Engine - where A: 'static+Fn(&mut T) -> U, T: Clone+Any, U: Clone+Any + where A: 'static+Fn(&mut T) -> U, T: Any, U: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box)->Result, EvalAltResult>> = @@ -288,7 +288,7 @@ impl<'a, A, T, U> FnRegister for Engine impl<'a, A, T, U> FnRegister for Engine - where A: 'static+Fn(T) -> U, T: Clone+Any, U: Clone+Any + where A: 'static+Fn(T) -> U, T: Clone+Any, U: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : Box)->Result, EvalAltResult>> = @@ -308,7 +308,7 @@ impl<'a, A, T, U> FnRegister for Engine } impl FnRegister for Engine - where A: 'static+Fn() -> T, T: Clone+Any + where A: 'static+Fn() -> T, T: Any { fn register_fn(&mut self, name: &str, fun: A) { let wrapped : BoxResult, EvalAltResult>> = diff --git a/src/parser.rs b/src/parser.rs index 82eb2e42..3a735ce9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -41,7 +41,9 @@ pub enum ParseError { MissingRParen, MissingLCurly, MissingRCurly, + MissingRSquare, MalformedCallExpr, + MalformedIndexExpr, VarExpectsIdentifier, FnMissingName, FnMissingParams @@ -56,7 +58,9 @@ impl Error for ParseError { ParseError::MissingRParen => "Expected ')'", ParseError::MissingLCurly => "Expected '{'", ParseError::MissingRCurly => "Expected '}'", + ParseError::MissingRSquare => "Expected ']'", ParseError::MalformedCallExpr => "Call contains bad expression", + ParseError::MalformedIndexExpr => "Indexing expression missing correct index", ParseError::VarExpectsIdentifier => "'var' expects the name of a variable", ParseError::FnMissingName => "Function declaration is missing name", ParseError::FnMissingParams => "Function declaration is missing parameters" @@ -87,7 +91,7 @@ pub enum Stmt { If(Box, Box), IfElse(Box, Box, Box #[derive(Debug, Clone)] pub enum Expr { IntConst(i32), Identifier(String), StringConst(String), FnCall(String, Box>), - Assignment(Box, Box), Dot(Box, Box), True, False } + Assignment(Box, Box), Dot(Box, Box), Index(String, Box), Array(Box>), True, False } #[derive(Debug)] pub enum Token { IntConst(i32), Identifier(String), StringConst(String), LCurly, RCurly, LParen, RParen, LSquare, RSquare, @@ -355,12 +359,7 @@ fn parse_paren_expr<'a>(input: &mut Peekable>) -> Result(id: String, input: &mut Peekable>) -> Result { - match input.peek() { - Some(&Token::LParen) => {input.next();}, - _ => return Ok(Expr::Identifier(id)) - } - +fn parse_call_expr<'a>(id: String, input: &mut Peekable>) -> Result { let mut args = Vec::new(); match input.peek() { @@ -392,6 +391,59 @@ fn parse_ident_expr<'a>(id: String, input: &mut Peekable>) -> } } +fn parse_index_expr<'a>(id: String, input: &mut Peekable>) -> Result { + if let Ok(idx) = parse_expr(input) { + match input.peek() { + Some(&Token::RSquare) => { + input.next(); + return Ok(Expr::Index(id, Box::new(idx))) + }, + _ => return Err(ParseError::MalformedIndexExpr) + } + } + else { + return Err(ParseError::MalformedIndexExpr); + } +} + +fn parse_ident_expr<'a>(id: String, input: &mut Peekable>) -> Result { + match input.peek() { + Some(&Token::LParen) => {input.next(); parse_call_expr(id, input)}, + Some(&Token::LSquare) => {input.next(); parse_index_expr(id, input)}, + _ => return Ok(Expr::Identifier(id)) + } +} + +fn parse_array_expr<'a>(input: &mut Peekable>) -> Result { + let mut arr = Vec::new(); + + let skip_contents = match input.peek() { + Some(& Token::RSquare) => true, + _ => false + }; + + if !skip_contents { + while let Some(_) = input.peek() { + arr.push(try!(parse_expr(input))); + match input.peek() { + Some(& Token::Comma) => {input.next();}, + _ => () + } + + match input.peek() { + Some(& Token::RSquare) => break, + _ => () + } + } + } + + match input.peek() { + Some(& Token::RSquare) => {input.next(); Ok(Expr::Array(Box::new(arr)))}, + _ => Err(ParseError::MissingRSquare) + } + +} + fn parse_primary<'a>(input: &mut Peekable>) -> Result { if let Some(token) = input.next() { match token { @@ -399,6 +451,7 @@ fn parse_primary<'a>(input: &mut Peekable>) -> Result {Ok(Expr::StringConst(s.clone()))}, Token::Identifier(ref s) => {parse_ident_expr(s.clone(), input)}, Token::LParen => {parse_paren_expr(input)}, + Token::LSquare => {parse_array_expr(input)}, Token::True => {Ok(Expr::True)}, Token::False => {Ok(Expr::False)}, Token::LexErr(le) => {println!("Error: {}", le); Err(ParseError::BadInput)}