Add support for character constants
This commit is contained in:
parent
3364191781
commit
b9ea072d6a
28
examples/arrays_and_structs.rs
Normal file
28
examples/arrays_and_structs.rs
Normal 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]"));
|
||||||
|
}
|
@ -3,7 +3,7 @@ use rhai::{Engine, FnRegister};
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct TestStruct {
|
struct TestStruct {
|
||||||
x: i32
|
x: i64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestStruct {
|
impl TestStruct {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
132
src/parser.rs
132
src/parser.rs
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user