diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs new file mode 100644 index 00000000..c09ac5f5 --- /dev/null +++ b/examples/arrays_and_structs.rs @@ -0,0 +1,28 @@ +extern crate rhai; +use rhai::{Engine, FnRegister}; + +#[derive(Clone, Debug)] +struct TestStruct { + x: i64 +} + +impl TestStruct { + fn update(&mut self) { + self.x += 1000; + } + + fn new() -> TestStruct { + TestStruct { x: 1 } + } +} + +fn main() { + let mut engine = Engine::new(); + + engine.register_type::(); + + engine.register_fn("update", TestStruct::update); + engine.register_fn("new_ts", TestStruct::new); + + println!("{:?}", engine.eval::("var x = [new_ts()]; x[0].update(); x[0]")); +} diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index 5fc2e894..62635b51 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -3,7 +3,7 @@ use rhai::{Engine, FnRegister}; #[derive(Clone)] struct TestStruct { - x: i32 + x: i64 } impl TestStruct { diff --git a/examples/hello.rs b/examples/hello.rs index d6f4e545..7f493b24 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -4,7 +4,7 @@ use rhai::Engine; fn main() { let mut engine = Engine::new(); - if let Ok(result) = engine.eval::("40 + 2") { + if let Ok(result) = engine.eval::("40 + 2") { println!("Answer: {}", result); // prints 42 } } \ No newline at end of file diff --git a/examples/reuse_scope.rs b/examples/reuse_scope.rs index 70661ec4..5a469271 100644 --- a/examples/reuse_scope.rs +++ b/examples/reuse_scope.rs @@ -5,9 +5,9 @@ fn main() { let mut engine = Engine::new(); let mut scope: Scope = Vec::new(); - if let Ok(_) = engine.eval_with_scope::<()>(&mut scope, "var x = 4 + 5") { } else { assert!(false); } + if let Ok(_) = engine.eval_with_scope::<()>(&mut scope, "var x = 4 + 5") { } else { assert!(false); } - if let Ok(result) = engine.eval_with_scope::(&mut scope, "x") { + if let Ok(result) = engine.eval_with_scope::(&mut scope, "x") { println!("result: {}", result); } } diff --git a/examples/simple_fn.rs b/examples/simple_fn.rs index 6a6c989c..541edf1e 100644 --- a/examples/simple_fn.rs +++ b/examples/simple_fn.rs @@ -1,7 +1,7 @@ extern crate rhai; use rhai::{Engine, FnRegister}; -fn add(x: i32, y: i32) -> i32 { +fn add(x: i64, y: i64) -> i64 { x + y } @@ -10,7 +10,7 @@ fn main() { engine.register_fn("add", add); - if let Ok(result) = engine.eval::("add(40, 2)") { + if let Ok(result) = engine.eval::("add(40, 2)") { println!("Answer: {}", result); // prints 42 } } diff --git a/src/engine.rs b/src/engine.rs index 9f2c87e6..e0e95ad4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -636,6 +636,7 @@ impl Engine { match *expr { Expr::IntConst(i) => Ok(Box::new(i)), Expr::StringConst(ref s) => Ok(Box::new(s.clone())), + Expr::CharConst(ref c) => Ok(Box::new(c.clone())), Expr::Identifier(ref id) => { for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() { if *id == *name { @@ -1029,6 +1030,30 @@ fn test_number_literal() { } } +#[test] +fn test_chars() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval::("'y'") { + assert_eq!(result, 'y'); + } + else { + assert!(false); + } + + if let Ok(result) = engine.eval::("'\\u2764'") { + assert_eq!(result, '❤'); + } + else { + assert!(false); + } + + match engine.eval::("''") { + Err(_) => (), + _ => assert!(false) + } +} + #[test] fn test_ops() { let mut engine = Engine::new(); diff --git a/src/parser.rs b/src/parser.rs index 6e750a2b..cf19f9d8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -9,7 +9,8 @@ use std::char; pub enum LexError { UnexpectedChar, MalformedEscapeSequence, - MalformedNumber + MalformedNumber, + MalformedChar } impl Error for LexError { @@ -17,7 +18,8 @@ impl Error for LexError { match *self { LexError::UnexpectedChar => "Unexpected character in input", LexError::MalformedEscapeSequence => "Unexpected values in escape sequence", - LexError::MalformedNumber => "Unexpected characters in number" + LexError::MalformedNumber => "Unexpected characters in number", + LexError::MalformedChar => "Char constant not a single character" } } @@ -32,7 +34,6 @@ impl fmt::Display for LexError { } } - #[derive(Debug)] pub enum ParseError { BadInput, @@ -86,17 +87,20 @@ pub struct FnDef { } #[derive(Debug, Clone)] -pub enum Stmt { If(Box, Box), IfElse(Box, Box, Box), While(Box, Box), - Var(String, Option>), Block(Box>), Expr(Box), Break, Return, ReturnWithVal(Box) } +pub enum Stmt { If(Box, Box), IfElse(Box, Box, Box), While(Box, + Box), Var(String, Option>), Block(Box>), Expr(Box), Break, + Return, ReturnWithVal(Box) } #[derive(Debug, Clone)] -pub enum Expr { IntConst(i64), Identifier(String), StringConst(String), FnCall(String, Box>), - Assignment(Box, Box), Dot(Box, Box), Index(String, Box), Array(Box>), True, False } +pub enum Expr { IntConst(i64), Identifier(String), CharConst(char), StringConst(String), + FnCall(String, Box>),Assignment(Box, Box), Dot(Box, Box), + Index(String, Box), Array(Box>), True, False } #[derive(Debug)] -pub enum Token { IntConst(i64), Identifier(String), StringConst(String), LCurly, RCurly, LParen, RParen, LSquare, RSquare, - Plus, Minus, Multiply, Divide, Semicolon, Colon, Comma, Period, Equals, True, False, Var, If, Else, While, - LessThan, GreaterThan, Bang, LessThanEqual, GreaterThanEqual, EqualTo, NotEqualTo, Pipe, Or, Ampersand, And, Fn, +pub enum Token { IntConst(i64), Identifier(String), CharConst(char), StringConst(String), + LCurly, RCurly, LParen, RParen, LSquare, RSquare, Plus, Minus, Multiply, Divide, Semicolon, + Colon, Comma, Period, Equals, True, False, Var, If, Else, While, LessThan, GreaterThan, + Bang, LessThanEqual, GreaterThanEqual, EqualTo, NotEqualTo, Pipe, Or, Ampersand, And, Fn, Break, Return, LexErr(LexError) } pub struct TokenIterator<'a> { @@ -267,6 +271,109 @@ impl<'a> Iterator for TokenIterator<'a> { let out : String = result.iter().cloned().collect(); return Some(Token::StringConst(out)) } + '\'' => { + let mut result = Vec::new(); + let mut escape = false; + + while let Some(nxt) = self.char_stream.next() { + match nxt { + '\'' if !escape => break, + '\\' if !escape => escape = true, + '\\' if escape => {escape = false; result.push('\\'); }, + 't' if escape => {escape = false; result.push('\t'); }, + 'n' if escape => {escape = false; result.push('\n'); }, + 'r' if escape => {escape = false; result.push('\r'); }, + 'x' if escape => { + escape = false; + let mut out_val: u32 = 0; + for _ in 0..2 { + if let Some(c) = self.char_stream.next() { + if let Some(d1) = c.to_digit(16) { + out_val *= 16; + out_val += d1; + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + + if let Some(r) = char::from_u32(out_val) { + result.push(r); + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + 'u' if escape => { + escape = false; + let mut out_val: u32 = 0; + for _ in 0..4 { + if let Some(c) = self.char_stream.next() { + if let Some(d1) = c.to_digit(16) { + out_val *= 16; + out_val += d1; + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + + if let Some(r) = char::from_u32(out_val) { + result.push(r); + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + 'U' if escape => { + escape = false; + let mut out_val: u32 = 0; + for _ in 0..8 { + if let Some(c) = self.char_stream.next() { + if let Some(d1) = c.to_digit(16) { + out_val *= 16; + out_val += d1; + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + + if let Some(r) = char::from_u32(out_val) { + result.push(r); + } + else { + return Some(Token::LexErr(LexError::MalformedEscapeSequence)); + } + } + _ if escape => return Some(Token::LexErr(LexError::MalformedEscapeSequence)), + _ => { escape = false; result.push(nxt); }, + } + } + + if result.len() != 1 { + return Some(Token::LexErr(LexError::MalformedChar)); + } + + if let Some(&out) = result.first() { + return Some(Token::CharConst(out)); + } + else { + return Some(Token::CharConst('\0')); + } + } '{' => { return Some(Token::LCurly); }, '}' => { return Some(Token::RCurly); }, '(' => { return Some(Token::LParen); }, @@ -449,6 +556,7 @@ fn parse_primary<'a>(input: &mut Peekable>) -> Result {Ok(Expr::IntConst(x.clone()))}, Token::StringConst(ref s) => {Ok(Expr::StringConst(s.clone()))}, + Token::CharConst(ref c) => {Ok(Expr::CharConst(c.clone()))}, Token::Identifier(ref s) => {parse_ident_expr(s.clone(), input)}, Token::LParen => {parse_paren_expr(input)}, Token::LSquare => {parse_array_expr(input)}, @@ -463,7 +571,9 @@ fn parse_primary<'a>(input: &mut Peekable>) -> Result(input: &mut Peekable>, prec: i32, lhs: Expr) -> Result { +fn parse_binop<'a>(input: &mut Peekable>, prec: i32, lhs: Expr) + -> Result +{ let mut lhs_curr = lhs; loop {