diff --git a/src/engine.rs b/src/engine.rs index 6308522e..5fd83f31 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -6,7 +6,6 @@ use std::fmt; use parser::{lex, parse, Expr, Stmt }; use fn_register::FnRegister; -//use ops::{add_boxes, sub_boxes, mult_boxes, div_boxes}; use std::ops::{Add, Sub, Mul, Div}; use std::cmp::{Ord, Eq}; @@ -91,6 +90,7 @@ fn or(x: bool, y: bool) -> bool { } pub struct Engine { + pub fns_arity_3: HashMap, &mut Box, &mut Box)->Result, EvalError>>>>, pub fns_arity_2: HashMap, &mut Box)->Result, EvalError>>>>, pub fns_arity_1: HashMap)->Result, EvalError>>>>, pub fns_arity_0: HashMapResult, EvalError>>>>, @@ -98,8 +98,8 @@ pub struct Engine { } impl Engine { - pub fn call_fn_0_arg(&self, name: &str) -> Result, EvalError> { - match self.fns_arity_0.get(name) { + pub fn call_fn_0_arg(fns_arity_0: &HashMapResult, EvalError>>>>, name: &str) -> Result, EvalError> { + match fns_arity_0.get(name) { Some(vf) => { for f in vf { let invoke = f(); @@ -114,8 +114,10 @@ impl Engine { } } - pub fn call_fn_1_arg(&self, name: &str, arg1: &mut Box) -> Result, EvalError> { - match self.fns_arity_1.get(name) { + pub fn call_fn_1_arg(fns_arity_1: &HashMap)->Result, EvalError>>>>, + name: &str, arg1: &mut Box) -> Result, EvalError> { + + match fns_arity_1.get(name) { Some(vf) => { for f in vf { let invoke = f(arg1); @@ -130,8 +132,10 @@ impl Engine { } } - pub fn call_fn_2_arg(&self, name: &str, arg1: &mut Box, arg2: &mut Box) -> Result, EvalError> { - match self.fns_arity_2.get(name) { + pub fn call_fn_2_arg(fns_arity_2: &HashMap, &mut Box)->Result, EvalError>>>>, + name: &str, arg1: &mut Box, arg2: &mut Box) -> Result, EvalError> { + + match fns_arity_2.get(name) { Some(vf) => { for f in vf { let invoke = f(arg1, arg2); @@ -146,6 +150,24 @@ impl Engine { } } + pub fn call_fn_3_arg(fns_arity_3: &HashMap, &mut Box, &mut Box)->Result, EvalError>>>>, + name: &str, arg1: &mut Box, arg2: &mut Box, arg3: &mut Box) -> Result, EvalError> { + + match fns_arity_3.get(name) { + Some(vf) => { + for f in vf { + let invoke = f(arg1, arg2, arg3); + match invoke { + Ok(v) => return Ok(v), + _ => () + } + }; + Err(EvalError::FunctionArgMismatch) + } + None => Err(EvalError::FunctionNotFound) + } + } + fn register_type(&mut self) { fn clone_helper(t:T)->T { t.clone() }; @@ -196,20 +218,61 @@ impl Engine { _ => Err(EvalError::VariableNotFound) } } - Expr::Call(ref fn_name, ref args) => { + Expr::FnCall(ref fn_name, ref args) => { if args.len() == 0 { - self.call_fn_0_arg(&fn_name) + Engine::call_fn_0_arg(&self.fns_arity_0, &fn_name) } else if args.len() == 1 { let mut arg = try!(self.eval_expr(&args[0])); - self.call_fn_1_arg(&fn_name, &mut arg) + Engine::call_fn_1_arg(&self.fns_arity_1, &fn_name, &mut arg) } else if args.len() == 2 { let mut arg1 = try!(self.eval_expr(&args[0])); let mut arg2 = try!(self.eval_expr(&args[1])); - self.call_fn_2_arg(&fn_name, &mut arg1, &mut arg2) + Engine::call_fn_2_arg(&self.fns_arity_2, &fn_name, &mut arg1, &mut arg2) + } + else if args.len() == 3 { + let mut arg1 = try!(self.eval_expr(&args[0])); + let mut arg2 = try!(self.eval_expr(&args[1])); + let mut arg3 = try!(self.eval_expr(&args[1])); + + Engine::call_fn_3_arg(&self.fns_arity_3, &fn_name, &mut arg1, &mut arg2, &mut arg3) + } + else { + Err(EvalError::FunctionCallNotSupported) + } + } + Expr::MethodCall(ref target, ref fn_name, ref args) => { + if args.len() == 0 { + for &mut (ref name, ref mut val) in &mut self.scope.iter_mut().rev() { + if *target == *name { + return Engine::call_fn_1_arg(&self.fns_arity_1, &fn_name, val); + } + } + Err(EvalError::VariableNotFound) + } + else if args.len() == 1 { + let mut arg = try!(self.eval_expr(&args[0])); + + for &mut (ref name, ref mut val) in &mut self.scope.iter_mut().rev() { + if *target == *name { + return Engine::call_fn_2_arg(&self.fns_arity_2, &fn_name, val, &mut arg); + } + } + Err(EvalError::VariableNotFound) + } + else if args.len() == 2 { + let mut arg1 = try!(self.eval_expr(&args[0])); + let mut arg2 = try!(self.eval_expr(&args[1])); + + for &mut (ref name, ref mut val) in &mut self.scope.iter_mut().rev() { + if *target == *name { + return Engine::call_fn_3_arg(&self.fns_arity_3, &fn_name, val, &mut arg1, &mut arg2); + } + } + Err(EvalError::VariableNotFound) } else { Err(EvalError::FunctionCallNotSupported) @@ -295,8 +358,19 @@ impl Engine { let tree = parse(&mut peekables); match tree { - Ok(os) => { + Ok((ref os, ref fns)) => { let mut x: Result, EvalError> = Ok(Box::new(())); + for f in fns { + if f.params.len() == 0 { + let local_f = f.clone(); + let ent = self.fns_arity_0.entry(local_f.name).or_insert(Vec::new()); + let wrapped : BoxResult, EvalError>> = + Box::new(move || { Ok(Box::new(0)) } ); + //move || { self.eval_stmt(&local_f.body) } + (*ent).push(wrapped); + } + } + for o in os { x = self.eval_stmt(&o) } @@ -342,8 +416,8 @@ impl Engine { reg_cmp!(engine, "<=", lte, i32, i64, u32, u64); reg_cmp!(engine, ">", gt, i32, i64, u32, u64); reg_cmp!(engine, ">=", gte, i32, i64, u32, u64); - reg_cmp!(engine, "==", eq, i32, i64, u32, u64); - reg_cmp!(engine, "!=", ne, i32, i64, u32, u64); + reg_cmp!(engine, "==", eq, i32, i64, u32, u64, bool); + reg_cmp!(engine, "!=", ne, i32, i64, u32, u64, bool); reg_op!(engine, "||", or, bool); reg_op!(engine, "&&", and, bool); @@ -354,6 +428,7 @@ impl Engine { fns_arity_0: HashMap::new(), fns_arity_1: HashMap::new(), fns_arity_2: HashMap::new(), + fns_arity_3: HashMap::new(), scope: Vec::new() }; @@ -463,4 +538,35 @@ fn test_var_scope() { } } +#[test] +fn test_method_call() { + #[derive(Debug, Clone)] + struct TestStruct { + x: i32 + } + impl TestStruct { + fn update(&mut self) { + self.x += 1000; + } + + fn new() -> TestStruct { + TestStruct { x: 1 } + } + } + + let mut engine = Engine::new(); + + engine.register_type::(); + + &(TestStruct::update as fn(&mut TestStruct)->()).register(&mut engine, "update"); + &(TestStruct::new as fn()->TestStruct).register(&mut engine, "new_ts"); + + if let Ok(result) = engine.eval("var x = new_ts(); x.update(); x".to_string()).unwrap().downcast::() { + assert_eq!(result.x, 1001); + } + else { + assert!(false); + } + +} diff --git a/src/fn_register.rs b/src/fn_register.rs index 9f6f8f0e..45a0824e 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -7,6 +7,48 @@ pub trait FnRegister { fn register(self, engine: &mut Engine, name: &str); } +impl FnRegister for fn(&mut T, U, V)->W { + fn register(self, engine: &mut Engine, name: &str) { + let wrapped : Box, &mut Box, &mut Box)->Result, EvalError>> = + Box::new( + move |x: &mut Box, y: &mut Box, z: &mut Box| { + let inside1 = (*x).downcast_mut() as Option<&mut T>; + let inside2 = (*y).downcast_mut() as Option<&mut U>; + let inside3 = (*z).downcast_mut() as Option<&mut V>; + + match (inside1, inside2, inside3) { + (Some(b), Some(c), Some(d)) => Ok(Box::new(self(b, c.clone(), d.clone())) as Box), + _ => Err(EvalError::FunctionArgMismatch) + } + } + ); + + let ent = engine.fns_arity_3.entry(name.to_string()).or_insert(Vec::new()); + (*ent).push(wrapped); + } +} + +impl FnRegister for fn(T, U, V)->W { + fn register(self, engine: &mut Engine, name: &str) { + let wrapped : Box, &mut Box, &mut Box)->Result, EvalError>> = + Box::new( + move |x: &mut Box, y: &mut Box, z: &mut Box| { + let inside1 = (*x).downcast_mut() as Option<&mut T>; + let inside2 = (*y).downcast_mut() as Option<&mut U>; + let inside3 = (*z).downcast_mut() as Option<&mut V>; + + match (inside1, inside2, inside3) { + (Some(b), Some(c), Some(d)) => Ok(Box::new(self(b.clone(), c.clone(), d.clone())) as Box), + _ => Err(EvalError::FunctionArgMismatch) + } + } + ); + + let ent = engine.fns_arity_3.entry(name.to_string()).or_insert(Vec::new()); + (*ent).push(wrapped); + } +} + impl FnRegister for fn(&mut T, U)->V { fn register(self, engine: &mut Engine, name: &str) { let wrapped : Box, &mut Box)->Result, EvalError>> = diff --git a/src/main.rs b/src/main.rs index 42339040..97061691 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,78 +14,9 @@ mod parser; // Todo (in no particular order): // * Function in script // * Doc some examples -// * Refactor identifer to not require inlining of clone lookup in engine +// * String constants // * Remove empty box values? -// * Methods - -/* -fn simple_fn(x: i32) -> bool { x == 1 } -fn simple_fn2(x: &mut i32) -> bool { x.clone() == 2 } - -#[derive(Debug)] -struct TestStruct { - x: i32 -} - -impl TestStruct { - fn update(&mut self) { - self.x += 1000; - } - - fn new() -> TestStruct { - TestStruct { x: 1 } - } -} - -#[derive(Debug)] -struct TestStruct2 { - x: bool -} - -impl TestStruct2 { - fn update(&mut self) { - self.x = true; - } -} - -fn engine_test() { - let mut engine = Engine::new(); - - &(simple_fn as fn(i32)->bool).register(&mut engine, "simple_fn"); - &(simple_fn2 as fn(&mut i32)->bool).register(&mut engine, "simple_fn2"); - &(TestStruct::update as fn(&mut TestStruct)->()).register(&mut engine, "update"); - &(TestStruct::new as fn()->TestStruct).register(&mut engine, "newteststruct"); - &(showit as fn(x: &mut Box)->()).register(&mut engine, "showit"); - &(TestStruct2::update as fn(&mut TestStruct2)->()).register(&mut engine, "update"); - - let mut arg : Box = Box::new(2); - - println!("Result: {:?}", engine.call_fn_1_arg("simple_fn" , &mut arg).unwrap().downcast::()); - println!("Result: {:?}", engine.call_fn_1_arg("simple_fn2", &mut arg).unwrap().downcast::()); - println!("Intentional errors: "); - println!(" Result: {:?}", engine.call_fn_1_arg("simple_fn3", &mut arg)); - arg = Box::new("foo"); - println!(" Result: {:?}", engine.call_fn_1_arg("simple_fn", &mut arg)); - - let mut ts : Box = Box::new(TestStruct { x: 6 }); - engine.call_fn_1_arg("update" , &mut ts); - - let result : Result, Box> = ts.downcast(); - println!("TS: {:?}", result); - - let myts = engine.call_fn_0_arg("newteststruct").unwrap().downcast::(); - println!("MyTS: {:?}", myts); - - let mut mybox = Box::new(Box::new(56) as Box) as Box; - engine.call_fn_1_arg("showit", &mut mybox); - - let mut ts2 : Box = Box::new(TestStruct2 { x: false }); - engine.call_fn_1_arg("update" , &mut ts2); - - let result2 : Result, Box> = ts2.downcast(); - println!("TS2: {:?}", result2); -} -*/ +// * Refactor identifer to not require inlining of clone lookup in engine? fn showit(x: &mut T) -> () { println!("{}", x) diff --git a/src/parser.rs b/src/parser.rs index 210ec5e1..17035dcb 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -13,7 +13,10 @@ pub enum ParseError { MissingLCurly, MissingRCurly, MalformedCallExpr, - VarExpectsIdentifier + VarExpectsIdentifier, + ExpectedMethodInvocation, + FnMissingName, + FnMissingParams } impl Error for ParseError { @@ -26,7 +29,10 @@ impl Error for ParseError { ParseError::MissingLCurly => "Expected '{'", ParseError::MissingRCurly => "Expected '}'", ParseError::MalformedCallExpr => "Call contains bad expression", - ParseError::VarExpectsIdentifier => "'var' expects the name of a variable" + ParseError::VarExpectsIdentifier => "'var' expects the name of a variable", + ParseError::ExpectedMethodInvocation => "Expected method call after '.'", + ParseError::FnMissingName => "Function declaration is missing name", + ParseError::FnMissingParams => "Function declaration is missing parameters" } } @@ -41,17 +47,25 @@ impl fmt::Display for ParseError { } } -#[derive(Debug)] +#[derive(Debug, Clone)] +pub struct FnDef { + pub name: String, + pub params: Vec, + pub body: Box +} + +#[derive(Debug, Clone)] pub enum Stmt { If(Box, Box), While(Box, Box), Var(String, Option>), Block(Box>), Expr(Box) } -#[derive(Debug)] -pub enum Expr { IntConst(i32), Identifier(String), Call(String, Box>), Assignment(Box, Box), True, False } +#[derive(Debug, Clone)] +pub enum Expr { IntConst(i32), Identifier(String), FnCall(String, Box>), MethodCall(String, String, Box>), + Assignment(Box, Box), True, False } #[derive(Debug)] pub enum Token { Int(i32), Id(String), LCurly, RCurly, LParen, RParen, LSquare, RSquare, - Plus, Minus, Multiply, Divide, Semicolon, Colon, Comma, Equals, True, False, Var, If, While, - LessThan, GreaterThan, Bang, LessThanEqual, GreaterThanEqual, EqualTo, NotEqualTo, Pipe, Or, Ampersand, And } + Plus, Minus, Multiply, Divide, Semicolon, Colon, Comma, Period, Equals, True, False, Var, If, While, + LessThan, GreaterThan, Bang, LessThanEqual, GreaterThanEqual, EqualTo, NotEqualTo, Pipe, Or, Ampersand, And, Fn } pub struct TokenIterator<'a> { char_stream: Peekable> @@ -110,6 +124,9 @@ impl<'a> Iterator for TokenIterator<'a> { else if out == "while" { return Some(Token::While); } + else if out == "fn" { + return Some(Token::Fn); + } else { return Some(Token::Id(out)); } @@ -127,6 +144,7 @@ impl<'a> Iterator for TokenIterator<'a> { ';' => { return Some(Token::Semicolon); }, ':' => { return Some(Token::Colon); }, ',' => { return Some(Token::Comma); }, + '.' => { return Some(Token::Period); }, '=' => { match self.char_stream.peek() { Some(&'=') => {self.char_stream.next(); return Some(Token::EqualTo); }, @@ -204,17 +222,35 @@ fn parse_paren_expr<'a>(input: &mut Peekable>) -> Result(id: String, input: &mut Peekable>) -> Result { + let id2 = match input.peek() { + Some(&Token::Period) => { + input.next(); + match input.next() { + Some(Token::Id(ref s)) => { + s.clone() + } + _ => return Err(ParseError::ExpectedMethodInvocation) + } + }, + _ => String::new() + }; match input.peek() { - Some(&Token::LParen) => (), + Some(&Token::LParen) => {input.next();}, _ => return Ok(Expr::Identifier(id)) } - input.next(); - let mut args = Vec::new(); match input.peek() { - Some(&Token::RParen) => {input.next(); return Ok(Expr::Call(id, Box::new(args)))}, + Some(&Token::RParen) => { + input.next(); + if id2 == "" { + return Ok(Expr::FnCall(id, Box::new(args))) + } + else { + return Ok(Expr::MethodCall(id, id2, Box::new(args))) + } + }, _ => () } @@ -227,7 +263,15 @@ fn parse_ident_expr<'a>(id: String, input: &mut Peekable>) -> } match input.peek() { - Some(&Token::RParen) => {input.next(); return Ok(Expr::Call(id, Box::new(args)))}, + Some(&Token::RParen) => { + input.next(); + if id2 == "" { + return Ok(Expr::FnCall(id, Box::new(args))) + } + else { + return Ok(Expr::MethodCall(id, id2, Box::new(args))) + } + }, Some(&Token::Comma) => (), _ => return Err(ParseError::MalformedCallExpr) } @@ -280,19 +324,19 @@ fn parse_binop<'a>(input: &mut Peekable>, prec: i32, lhs: Expr } lhs_curr = match op_token { - Token::Plus => Expr::Call("+".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::Minus => Expr::Call("-".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::Multiply => Expr::Call("*".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::Divide => Expr::Call("/".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::Plus => Expr::FnCall("+".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::Minus => Expr::FnCall("-".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::Multiply => Expr::FnCall("*".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::Divide => Expr::FnCall("/".to_string(), Box::new(vec![lhs_curr, rhs])), Token::Equals => Expr::Assignment(Box::new(lhs_curr), Box::new(rhs)), - Token::EqualTo => Expr::Call("==".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::NotEqualTo => Expr::Call("!=".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::LessThan => Expr::Call("<".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::LessThanEqual => Expr::Call("<=".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::GreaterThan => Expr::Call(">".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::GreaterThanEqual => Expr::Call(">=".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::Or => Expr::Call("||".to_string(), Box::new(vec![lhs_curr, rhs])), - Token::And => Expr::Call("&&".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::EqualTo => Expr::FnCall("==".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::NotEqualTo => Expr::FnCall("!=".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::LessThan => Expr::FnCall("<".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::LessThanEqual => Expr::FnCall("<=".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::GreaterThan => Expr::FnCall(">".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::GreaterThanEqual => Expr::FnCall(">=".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::Or => Expr::FnCall("||".to_string(), Box::new(vec![lhs_curr, rhs])), + Token::And => Expr::FnCall("&&".to_string(), Box::new(vec![lhs_curr, rhs])), _ => return Err(ParseError::UnknownOperator) }; } @@ -349,7 +393,27 @@ fn parse_block<'a>(input: &mut Peekable>) -> Result true, + _ => false + }; + + if !skip_body { + while let Some(_) = input.peek() { + stmts.push(try!(parse_stmt(input))); + match input.peek() { + Some(& Token::Semicolon) => {input.next();}, + _ => () + } + + match input.peek() { + Some(& Token::RCurly) => break, + _ => () + } + } + } match input.peek() { Some(& Token::RCurly) => {input.next(); Ok(Stmt::Block(Box::new(stmts)))}, @@ -372,35 +436,61 @@ fn parse_stmt<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>, check_for_rcurly: bool) -> Result, ParseError> { - let mut result = Vec::new(); +fn parse_fn<'a>(input: &mut Peekable>) -> Result { + input.next(); - if check_for_rcurly { - match input.peek() { - Some(& Token::RCurly) => return Ok(result), - _ => () + let name = match input.next() { + Some(Token::Id(ref s)) => s.clone(), + _ => return Err(ParseError::FnMissingName) + }; + + match input.peek() { + Some(&Token::LParen) => {input.next();}, + _ => return Err(ParseError::FnMissingParams) + } + + let mut params = Vec::new(); + + let skip_params = match input.peek() { + Some(&Token::RParen) => { input.next(); true } + _ => false + }; + + if !skip_params { + loop { + match input.next() { + Some(Token::RParen) => { break }, + Some(Token::Comma) => (), + Some(Token::Id(ref s)) => { params.push(s.clone()); }, + _ => return Err(ParseError::MalformedCallExpr) + } } } + let body = try!(parse_block(input)); + + Ok(FnDef{name: name, params: params, body: Box::new(body)}) +} + +fn parse_top_level<'a>(input: &mut Peekable>) -> Result<(Vec, Vec), ParseError> { + let mut stmts = Vec::new(); + let mut fndefs = Vec::new(); + while let Some(_) = input.peek() { - result.push(try!(parse_stmt(input))); + match input.peek() { + Some(& Token::Fn) => fndefs.push(try!(parse_fn(input))), + _ => stmts.push(try!(parse_stmt(input))) + } + match input.peek() { Some(& Token::Semicolon) => {input.next();}, _ => () } - - if check_for_rcurly { - match input.peek() { - Some(& Token::RCurly) => return Ok(result), - _ => () - } - } } - Ok(result) + Ok((stmts, fndefs)) } -pub fn parse<'a>(input: &mut Peekable>) -> Result, ParseError> { - let result = parse_stmts(input, false); - result +pub fn parse<'a>(input: &mut Peekable>) -> Result<(Vec, Vec), ParseError> { + parse_top_level(input) }