Add support for character constants

This commit is contained in:
jonathandturner 2016-04-13 18:40:06 -07:00
parent 3364191781
commit b9ea072d6a
7 changed files with 180 additions and 17 deletions

View File

@ -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::<TestStruct>();
engine.register_fn("update", TestStruct::update);
engine.register_fn("new_ts", TestStruct::new);
println!("{:?}", engine.eval::<TestStruct>("var x = [new_ts()]; x[0].update(); x[0]"));
}

View File

@ -3,7 +3,7 @@ use rhai::{Engine, FnRegister};
#[derive(Clone)] #[derive(Clone)]
struct TestStruct { struct TestStruct {
x: i32 x: i64
} }
impl TestStruct { impl TestStruct {

View File

@ -4,7 +4,7 @@ use rhai::Engine;
fn main() { fn main() {
let mut engine = Engine::new(); let mut engine = Engine::new();
if let Ok(result) = engine.eval::<i32>("40 + 2") { if let Ok(result) = engine.eval::<i64>("40 + 2") {
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
} }
} }

View File

@ -5,9 +5,9 @@ fn main() {
let mut engine = Engine::new(); let mut engine = Engine::new();
let mut scope: Scope = Vec::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::<i32>(&mut scope, "x") { if let Ok(result) = engine.eval_with_scope::<i64>(&mut scope, "x") {
println!("result: {}", result); println!("result: {}", result);
} }
} }

View File

@ -1,7 +1,7 @@
extern crate rhai; extern crate rhai;
use rhai::{Engine, FnRegister}; use rhai::{Engine, FnRegister};
fn add(x: i32, y: i32) -> i32 { fn add(x: i64, y: i64) -> i64 {
x + y x + y
} }
@ -10,7 +10,7 @@ fn main() {
engine.register_fn("add", add); engine.register_fn("add", add);
if let Ok(result) = engine.eval::<i32>("add(40, 2)") { if let Ok(result) = engine.eval::<i64>("add(40, 2)") {
println!("Answer: {}", result); // prints 42 println!("Answer: {}", result); // prints 42
} }
} }

View File

@ -636,6 +636,7 @@ impl Engine {
match *expr { match *expr {
Expr::IntConst(i) => Ok(Box::new(i)), Expr::IntConst(i) => Ok(Box::new(i)),
Expr::StringConst(ref s) => Ok(Box::new(s.clone())), Expr::StringConst(ref s) => Ok(Box::new(s.clone())),
Expr::CharConst(ref c) => Ok(Box::new(c.clone())),
Expr::Identifier(ref id) => { Expr::Identifier(ref id) => {
for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() { for &mut (ref name, ref mut val) in &mut scope.iter_mut().rev() {
if *id == *name { 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::<char>("'y'") {
assert_eq!(result, 'y');
}
else {
assert!(false);
}
if let Ok(result) = engine.eval::<char>("'\\u2764'") {
assert_eq!(result, '❤');
}
else {
assert!(false);
}
match engine.eval::<char>("''") {
Err(_) => (),
_ => assert!(false)
}
}
#[test] #[test]
fn test_ops() { fn test_ops() {
let mut engine = Engine::new(); let mut engine = Engine::new();

View File

@ -9,7 +9,8 @@ use std::char;
pub enum LexError { pub enum LexError {
UnexpectedChar, UnexpectedChar,
MalformedEscapeSequence, MalformedEscapeSequence,
MalformedNumber MalformedNumber,
MalformedChar
} }
impl Error for LexError { impl Error for LexError {
@ -17,7 +18,8 @@ impl Error for LexError {
match *self { match *self {
LexError::UnexpectedChar => "Unexpected character in input", LexError::UnexpectedChar => "Unexpected character in input",
LexError::MalformedEscapeSequence => "Unexpected values in escape sequence", 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)] #[derive(Debug)]
pub enum ParseError { pub enum ParseError {
BadInput, BadInput,
@ -86,17 +87,20 @@ pub struct FnDef {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Stmt { If(Box<Expr>, Box<Stmt>), IfElse(Box<Expr>, Box<Stmt>, Box<Stmt>), While(Box<Expr>, Box<Stmt>), pub enum Stmt { If(Box<Expr>, Box<Stmt>), IfElse(Box<Expr>, Box<Stmt>, Box<Stmt>), While(Box<Expr>,
Var(String, Option<Box<Expr>>), Block(Box<Vec<Stmt>>), Expr(Box<Expr>), Break, Return, ReturnWithVal(Box<Expr>) } Box<Stmt>), Var(String, Option<Box<Expr>>), Block(Box<Vec<Stmt>>), Expr(Box<Expr>), Break,
Return, ReturnWithVal(Box<Expr>) }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Expr { IntConst(i64), Identifier(String), StringConst(String), FnCall(String, Box<Vec<Expr>>), pub enum Expr { IntConst(i64), Identifier(String), CharConst(char), StringConst(String),
Assignment(Box<Expr>, Box<Expr>), Dot(Box<Expr>, Box<Expr>), Index(String, Box<Expr>), Array(Box<Vec<Expr>>), True, False } FnCall(String, Box<Vec<Expr>>),Assignment(Box<Expr>, Box<Expr>), Dot(Box<Expr>, Box<Expr>),
Index(String, Box<Expr>), Array(Box<Vec<Expr>>), True, False }
#[derive(Debug)] #[derive(Debug)]
pub enum Token { IntConst(i64), Identifier(String), StringConst(String), LCurly, RCurly, LParen, RParen, LSquare, RSquare, pub enum Token { IntConst(i64), Identifier(String), CharConst(char), StringConst(String),
Plus, Minus, Multiply, Divide, Semicolon, Colon, Comma, Period, Equals, True, False, Var, If, Else, While, LCurly, RCurly, LParen, RParen, LSquare, RSquare, Plus, Minus, Multiply, Divide, Semicolon,
LessThan, GreaterThan, Bang, LessThanEqual, GreaterThanEqual, EqualTo, NotEqualTo, Pipe, Or, Ampersand, And, Fn, 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) } Break, Return, LexErr(LexError) }
pub struct TokenIterator<'a> { pub struct TokenIterator<'a> {
@ -267,6 +271,109 @@ impl<'a> Iterator for TokenIterator<'a> {
let out : String = result.iter().cloned().collect(); let out : String = result.iter().cloned().collect();
return Some(Token::StringConst(out)) 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::LCurly); },
'}' => { return Some(Token::RCurly); }, '}' => { return Some(Token::RCurly); },
'(' => { return Some(Token::LParen); }, '(' => { return Some(Token::LParen); },
@ -449,6 +556,7 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
match token { match token {
Token::IntConst(ref x) => {Ok(Expr::IntConst(x.clone()))}, Token::IntConst(ref x) => {Ok(Expr::IntConst(x.clone()))},
Token::StringConst(ref s) => {Ok(Expr::StringConst(s.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::Identifier(ref s) => {parse_ident_expr(s.clone(), input)},
Token::LParen => {parse_paren_expr(input)}, Token::LParen => {parse_paren_expr(input)},
Token::LSquare => {parse_array_expr(input)}, Token::LSquare => {parse_array_expr(input)},
@ -463,7 +571,9 @@ fn parse_primary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pa
} }
} }
fn parse_binop<'a>(input: &mut Peekable<TokenIterator<'a>>, prec: i32, lhs: Expr) -> Result<Expr, ParseError> { fn parse_binop<'a>(input: &mut Peekable<TokenIterator<'a>>, prec: i32, lhs: Expr)
-> Result<Expr, ParseError>
{
let mut lhs_curr = lhs; let mut lhs_curr = lhs;
loop { loop {