From 6739706b64bf2565bfae3e006f5f54f48d69d23e Mon Sep 17 00:00:00 2001 From: jonathandturner Date: Mon, 29 Feb 2016 16:43:45 -0500 Subject: [PATCH] Initial commit --- Cargo.toml | 5 + README.md | 7 + src/engine.rs | 273 ++++++++++++++++++++++++++++++++++ src/fn_register.rs | 57 ++++++++ src/main.rs | 200 +++++++++++++++++++++++++ src/ops.rs | 308 +++++++++++++++++++++++++++++++++++++++ src/parser.rs | 356 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1206 insertions(+) create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/engine.rs create mode 100644 src/fn_register.rs create mode 100644 src/main.rs create mode 100644 src/ops.rs create mode 100644 src/parser.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..35eae335 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "rhai" +version = "0.1.0" + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 00000000..bd1c10fc --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Rhai - embedded scripting for Rust + +Rhai is a simple embedded scripting language for Rust that doesn't use any additional dependencies, unsafe code, or a set of APIs outside of what you provide in your program. This allows you to have rich control over the functionality exposed to the scripting context. + +Currently, it's pre-0.1, and is likely to change a bit before it stabilizes enough for a crates.io release. + + diff --git a/src/engine.rs b/src/engine.rs new file mode 100644 index 00000000..5e5cfb0e --- /dev/null +++ b/src/engine.rs @@ -0,0 +1,273 @@ +use std::collections::HashMap; +use std::error::Error; +use std::any::Any; +use std::boxed::Box; +use std::fmt; + +use parser::{lex, parse, Expr, Stmt }; +use fn_register::FnRegister; +use ops::{add_boxes, sub_boxes, mult_boxes, div_boxes}; + +#[derive(Debug)] +pub enum EvalError { + FunctionNotFound, + FunctionArgMismatch, + FunctionCallNotSupported, + TypeMismatchForOperands, + IfGuardMismatch, + VariableNotFound +} + +impl Error for EvalError { + fn description(&self) -> &str { + match *self { + EvalError::FunctionNotFound => "Function not found", + EvalError::FunctionArgMismatch => "Function argument types do not match", + EvalError::FunctionCallNotSupported => "Function call with > 1 argument not supported", + EvalError::TypeMismatchForOperands => "Operand types incompatible with operator", + EvalError::IfGuardMismatch => "If guards expect boolean expression", + EvalError::VariableNotFound => "Variable not found", + } + } + + fn cause(&self) -> Option<&Error> { + None + } +} + +impl fmt::Display for EvalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +pub struct Engine { + pub fns_arity_1: HashMap)->Result, EvalError>>>>, + pub fns_arity_0: HashMapResult, EvalError>>>>, + pub scope: Vec<(String, Box)> +} + +impl Engine { + pub fn call_fn_0_arg(&self, name: &str) -> Result, EvalError> { + match self.fns_arity_0.get(name) { + Some(vf) => { + for f in vf { + let invoke = f(); + match invoke { + Ok(v) => return Ok(v), + _ => () + } + }; + Err(EvalError::FunctionArgMismatch) + }, + None => Err(EvalError::FunctionNotFound) + } + } + + pub fn call_fn_1_arg(&self, name: &str, arg1: &mut Box) -> Result, EvalError> { + match self.fns_arity_1.get(name) { + Some(vf) => { + for f in vf { + let invoke = f(arg1); + 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() }; + + &(clone_helper as fn(T)->T).register(self, "clone"); + } + + fn eval_expr(&mut self, expr: &Expr) -> Result, EvalError> { + match *expr { + Expr::IntConst(i) => Ok(Box::new(i)), + Expr::Identifier(ref id) => { + for &mut (ref name, ref mut val) in &mut self.scope.iter_mut().rev() { + if *id == *name { + //Ideally, we wouldn't have to inline this call above + //let result = self.call_fn_1_arg("clone", val); + let result = match self.fns_arity_1.get("clone") { + Some(vf) => { + for f in vf { + let invoke = f(val); + match invoke { + Ok(v) => return Ok(v), + _ => () + } + }; + Err(EvalError::FunctionArgMismatch) + } + None => Err(EvalError::FunctionNotFound) + }; + + return result; + } + } + Err(EvalError::VariableNotFound) + } + Expr::Assignment(ref id, ref rhs) => { + match **id { + Expr::Identifier(ref n) => { + let rhs_val = try!(self.eval_expr(rhs)); + for &mut (ref name, ref mut val) in &mut self.scope.iter_mut().rev() { + if *n == *name { + + *val = rhs_val; + + return Ok(Box::new(())); + } + } + Err(EvalError::VariableNotFound) + } + _ => Err(EvalError::VariableNotFound) + } + } + Expr::Call(ref fn_name, ref args) => { + if args.len() == 0 { + self.call_fn_0_arg(&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) + } + else { + Err(EvalError::FunctionCallNotSupported) + } + } + Expr::Plus(ref a, ref b) => { + let arg1 = try!(self.eval_expr(a)); + let arg2 = try!(self.eval_expr(b)); + add_boxes(arg1, arg2) + } + Expr::Minus(ref a, ref b) => { + let arg1 = try!(self.eval_expr(a)); + let arg2 = try!(self.eval_expr(b)); + sub_boxes(arg1, arg2) + } + Expr::Multiply(ref a, ref b) => { + let arg1 = try!(self.eval_expr(a)); + let arg2 = try!(self.eval_expr(b)); + mult_boxes(arg1, arg2) + } + Expr::Divide(ref a, ref b) => { + let arg1 = try!(self.eval_expr(a)); + let arg2 = try!(self.eval_expr(b)); + div_boxes(arg1, arg2) + } + Expr::True => { + Ok(Box::new(true)) + } + Expr::False => { + Ok(Box::new(false)) + } + } + } + + fn eval_stmt(&mut self, stmt: &Stmt) -> Result, EvalError> { + match *stmt { + Stmt::Expr(ref e) => { + self.eval_expr(e) + } + Stmt::Block(ref b) => { + let prev_len = self.scope.len(); + let mut last_result : Result, EvalError> = Ok(Box::new(0)); + + for s in b.iter() { + last_result = self.eval_stmt(s) + } + + while self.scope.len() > prev_len { + self.scope.pop(); + } + + return last_result; + } + Stmt::If(ref guard, ref body) => { + let guard_result = try!(self.eval_expr(guard)); + match guard_result.downcast::() { + Ok(g) => { + if *g { + self.eval_stmt(body) + } + else { + Ok(Box::new(())) + } + } + Err(_) => Err(EvalError::IfGuardMismatch) + } + } + Stmt::While(ref guard, ref body) => { + loop { + let guard_result = try!(self.eval_expr(guard)); + match guard_result.downcast::() { + Ok(g) => { + if *g { + try!(self.eval_stmt(body)); + } + else { + return Ok(Box::new(())); + } + } + Err(_) => return Err(EvalError::IfGuardMismatch) + } + } + } + Stmt::Var(ref name, ref init) => { + match init { + & Some(ref v) => { + let i = try!(self.eval_expr(v)); + self.scope.push((name.clone(), i)); + }, + & None => { + self.scope.push((name.clone(), Box::new(()))); + } + }; + Ok(Box::new(())) + } + } + } + + pub fn eval(&mut self, input: String) -> Result, EvalError> { + let tokens = lex(&input); + + let mut peekables = tokens.peekable(); + let tree = parse(&mut peekables); + + match tree { + Ok(os) => { + let mut x: Result, EvalError> = Ok(Box::new(())); + for o in os { + x = self.eval_stmt(&o) + } + x + } + Err(_) => Err(EvalError::FunctionArgMismatch), + } + } + + pub fn new() -> Engine { + let mut output = Engine { fns_arity_0: HashMap::new(), fns_arity_1: HashMap::new(), scope: Vec::new() }; + + output.register_type::(); + output.register_type::(); + output.register_type::(); + output.register_type::(); + output.register_type::(); + output.register_type::(); + output.register_type::(); + output.register_type::(); + output.register_type::(); + + output + } +} + diff --git a/src/fn_register.rs b/src/fn_register.rs new file mode 100644 index 00000000..4cfcad9c --- /dev/null +++ b/src/fn_register.rs @@ -0,0 +1,57 @@ +use std::any::Any; +use std::boxed::Box; + +use engine::{EvalError, Engine}; + +pub trait FnRegister { + fn register(self, engine: &mut Engine, name: &str); +} + +impl FnRegister for fn(&mut T)->U { + fn register(self, engine: &mut Engine, name: &str) { + let wrapped : Box)->Result, EvalError>> = + Box::new( + move |x: &mut Box| { + let inside = (*x).downcast_mut() as Option<&mut T>; + + match inside { + Some(b) => Ok(Box::new(self(b)) as Box), + None => Err(EvalError::FunctionArgMismatch) + } + } + ); + + let ent = engine.fns_arity_1.entry(name.to_string()).or_insert(Vec::new()); + (*ent).push(wrapped); + } +} + +impl FnRegister for fn(T)->U { + fn register(self, engine: &mut Engine, name: &str) { + let wrapped : Box)->Result, EvalError>> = + Box::new( + move |x: &mut Box| { + let inside = (*x).downcast_mut() as Option<&mut T>; + match inside { + Some(b) => Ok(Box::new(self(b.clone())) as Box), + None => Err(EvalError::FunctionArgMismatch) + } + } + ); + + let ent = engine.fns_arity_1.entry(name.to_string()).or_insert(Vec::new()); + (*ent).push(wrapped); + } +} + +impl FnRegister for fn()->T { + fn register(self, engine: &mut Engine, name: &str) { + let wrapped : BoxResult, EvalError>> = + Box::new( + move || { Ok(Box::new(self()) as Box) } + ); + + let ent = engine.fns_arity_0.entry(name.to_string()).or_insert(Vec::new()); + (*ent).push(wrapped); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 00000000..083e239c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,200 @@ +use std::env; +use std::fs::File; +use std::io::prelude::*; +use std::fmt::Debug; + +mod engine; +use engine::Engine; + +mod fn_register; +use fn_register::FnRegister; + +mod ops; + +mod parser; + +// Todo (in no particular order): +// * Function in script +// * Doc some examples +// * Refactor identifer to not require inlining of clone lookup in engine +// * Remove empty box values? +// * Method/dot access +// * Comparison ops + +fn showit(x: &mut T) -> () { + println!("{:?}", x) +} + +/* +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); +} +*/ + +#[test] +fn test_number_literal() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval("65".to_string()).unwrap().downcast::() { + assert_eq!(*result, 65); + } + else { + assert!(false); + } +} + +#[test] +fn test_addition() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval("60 + 5".to_string()).unwrap().downcast::() { + assert_eq!(*result, 65); + } + else { + assert!(false); + } +} + +#[test] +fn test_boolean() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval("true".to_string()).unwrap().downcast::() { + assert_eq!(*result, true); + } + else { + assert!(false); + } +} + +#[test] +fn test_if() { + let mut engine = Engine::new(); + + if let Ok(result) = engine.eval("if true { 55 }".to_string()).unwrap().downcast::() { + assert_eq!(*result, 55); + } + else { + assert!(false); + } +} + +#[test] +fn test_var_scope() { + let mut engine = Engine::new(); + + if let Ok(_) = engine.eval("var x = 4 + 5".to_string()) { } else { assert!(false); } + + if let Ok(result) = engine.eval("x".to_string()).unwrap().downcast::() { + assert_eq!(*result, 9); + } + else { + assert!(false); + } + + if let Ok(_) = engine.eval("x = x + 1; x = x + 2;".to_string()) { } else { assert!(false); } + + if let Ok(result) = engine.eval("x".to_string()).unwrap().downcast::() { + assert_eq!(*result, 12); + } + else { + assert!(false); + } + + if let Ok(_) = engine.eval("{var x = 3}".to_string()) { } else { assert!(false); } + + if let Ok(result) = engine.eval("x".to_string()).unwrap().downcast::() { + assert_eq!(*result, 12); + } + else { + assert!(false); + } +} + +fn main() { + let mut engine = Engine::new(); + &(showit as fn(x: &mut i32)->()).register(&mut engine, "print"); + &(showit as fn(x: &mut i64)->()).register(&mut engine, "print"); + &(showit as fn(x: &mut u32)->()).register(&mut engine, "print"); + &(showit as fn(x: &mut u64)->()).register(&mut engine, "print"); + &(showit as fn(x: &mut f32)->()).register(&mut engine, "print"); + &(showit as fn(x: &mut f64)->()).register(&mut engine, "print"); + &(showit as fn(x: &mut bool)->()).register(&mut engine, "print"); + &(showit as fn(x: &mut String)->()).register(&mut engine, "print"); + + for fname in env::args().skip(1) { + if let Ok(mut f) = File::open(fname.clone()) { + let mut contents = String::new(); + if let Ok(_) = f.read_to_string(&mut contents) { + + match engine.eval(contents) { + Ok(_) => (), + Err(e) => {println!("Error: {:?}", e)} + } + } + } + } +} diff --git a/src/ops.rs b/src/ops.rs new file mode 100644 index 00000000..11f95abd --- /dev/null +++ b/src/ops.rs @@ -0,0 +1,308 @@ +use std::any::Any; +use std::boxed::Box; + +use engine::{EvalError}; + +pub fn add_boxes(arg1: Box, arg2: Box) -> Result, EvalError> { + match arg1.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 + *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 + *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 + *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 + *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 + *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 + *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(_) => { + return Err(EvalError::TypeMismatchForOperands) + } + } + } + } + } + } + } + } + } + } + } + } +} + +pub fn sub_boxes(arg1: Box, arg2: Box) -> Result, EvalError> { + match arg1.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 - *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 - *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 - *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 - *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 - *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 - *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(_) => { + return Err(EvalError::TypeMismatchForOperands) + } + } + } + } + } + } + } + } + } + } + } + } +} + +pub fn mult_boxes(arg1: Box, arg2: Box) -> Result, EvalError> { + match arg1.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 * *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 * *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 * *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 * *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 * *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 * *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(_) => { + return Err(EvalError::TypeMismatchForOperands) + } + } + } + } + } + } + } + } + } + } + } + } +} + +pub fn div_boxes(arg1: Box, arg2: Box) -> Result, EvalError> { + match arg1.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 / *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 / *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 / *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 / *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 / *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(arg1_again) => { + match arg1_again.downcast::() { + Ok(v1) => { + if let Ok(v2) = arg2.downcast::() { + return Ok(Box::new(*v1 / *v2)) + } + else { + return Err(EvalError::TypeMismatchForOperands) + } + }, + Err(_) => { + return Err(EvalError::TypeMismatchForOperands) + } + } + } + } + } + } + } + } + } + } + } + } +} diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 00000000..77f6190e --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,356 @@ +use std::io::prelude::*; +use std::error::Error; +use std::fmt; +use std::iter::Peekable; +use std::str::Chars; + +#[derive(Debug)] +pub enum ParseError { + BadInput, + InputPastEndOfFile, + UnknownOperator, + MissingRParen, + MissingLCurly, + MissingRCurly, + MalformedCallExpr, + VarExpectsIdentifier +} + +impl Error for ParseError { + fn description(&self) -> &str { + match *self { + ParseError::BadInput => "Unparseable characters in the input stream", + ParseError::InputPastEndOfFile => "Input past end of file", + ParseError::UnknownOperator => "Unknown operator", + ParseError::MissingRParen => "Expected ')'", + ParseError::MissingLCurly => "Expected '{'", + ParseError::MissingRCurly => "Expected '}'", + ParseError::MalformedCallExpr => "Call contains bad expression", + ParseError::VarExpectsIdentifier => "'var' expects the name of a variable" + } + } + + fn cause(&self) -> Option<&Error> { + None + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +#[derive(Debug)] +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), Plus(Box, Box), Minus(Box, Box), + Multiply(Box, Box), Divide(Box, Box), Call(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 } + +pub struct TokenIterator<'a> { + char_stream: Peekable> +} + +impl<'a> Iterator for TokenIterator<'a> { + type Item = Token; + + fn next(&mut self) -> Option { + while let Some(c) = self.char_stream.next() { + match c { + '0'...'9' => { + let mut result = Vec::new(); + result.push(c); + + while let Some(&nxt) = self.char_stream.peek() { + match nxt { + '0'...'9' => { result.push(nxt); self.char_stream.next(); }, + _ => break + } + } + + let out : String = result.iter().cloned().collect(); + + if let Ok(val) = out.parse::() { + return Some(Token::Int(val)); + } + return None; + }, + 'A'...'Z' | 'a'...'z' | '_' => { + let mut result = Vec::new(); + result.push(c); + + while let Some(&nxt) = self.char_stream.peek() { + match nxt { + '0'...'9' | 'A'...'Z' | 'a'...'z' | '_' => { + result.push(nxt); self.char_stream.next(); }, + _ => break + } + } + + let out : String = result.iter().cloned().collect(); + + if out == "true" { + return Some(Token::True); + } + else if out == "false" { + return Some(Token::False); + } + else if out == "var" { + return Some(Token::Var); + } + else if out == "if" { + return Some(Token::If); + } + else if out == "while" { + return Some(Token::While); + } + else { + return Some(Token::Id(out)); + } + }, + '{' => { return Some(Token::LCurly); }, + '}' => { return Some(Token::RCurly); }, + '(' => { return Some(Token::LParen); }, + ')' => { return Some(Token::RParen); }, + '[' => { return Some(Token::LSquare); }, + ']' => { return Some(Token::RSquare); }, + '+' => { return Some(Token::Plus); }, + '-' => { return Some(Token::Minus); }, + '*' => { return Some(Token::Multiply); }, + '/' => { return Some(Token::Divide); }, + ';' => { return Some(Token::Semicolon); }, + ':' => { return Some(Token::Colon); }, + ',' => { return Some(Token::Comma); }, + '=' => { return Some(Token::Equals); }, + ' ' | '\n' | '\r' => (), + _ => return None + } + } + + None + } +} + +pub fn lex<'a>(input: &'a String) -> TokenIterator<'a> { + TokenIterator { char_stream: input.chars().peekable() } +} + +fn get_precedence(token: &Token) -> i32 { + match *token { + Token::Equals => 10, + Token::Plus => 20, + Token::Minus => 20, + Token::Multiply => 40, + _ => -1 + } +} + +fn parse_paren_expr<'a>(input: &mut Peekable>) -> Result { + let expr = try!(parse_expr(input)); + + match input.next() { + Some(Token::RParen) => Ok(expr), + _ => Err(ParseError::MissingRParen) + } +} + +fn parse_ident_expr<'a>(id: String, input: &mut Peekable>) -> Result { + match input.peek() { + Some(&Token::LParen) => (), + _ => 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)))}, + _ => () + } + + loop { + if let Ok(arg) = parse_expr(input) { + args.push(arg); + } + else { + return Err(ParseError::MalformedCallExpr); + } + + match input.peek() { + Some(&Token::RParen) => {input.next(); return Ok(Expr::Call(id, Box::new(args)))}, + Some(&Token::Comma) => (), + _ => return Err(ParseError::MalformedCallExpr) + } + + input.next(); + } +} + +fn parse_primary<'a>(input: &mut Peekable>) -> Result { + if let Some(token) = input.next() { + match token { + Token::Int(ref x) => {Ok(Expr::IntConst(x.clone()))}, + Token::Id(ref s) => {parse_ident_expr(s.clone(), input)}, + Token::LParen => {parse_paren_expr(input)}, + Token::True => {Ok(Expr::True)}, + Token::False => {Ok(Expr::False)}, + _ => {println!("Can't parse: {:?}", token); Err(ParseError::BadInput)} + } + } + else { + Err(ParseError::InputPastEndOfFile) + } +} + +fn parse_binop<'a>(input: &mut Peekable>, prec: i32, lhs: Expr) -> Result { + let mut lhs_curr = lhs; + + loop { + let mut curr_prec = -1; + + if let Some(curr_op) = input.peek() { + curr_prec = get_precedence(curr_op); + } + + if curr_prec < prec { + return Ok(lhs_curr); + } + + if let Some(op_token) = input.next() { + let mut rhs = try!(parse_primary(input)); + + let mut next_prec = -1; + + if let Some(next_op) = input.peek() { + next_prec = get_precedence(next_op); + } + + if curr_prec < next_prec { + rhs = try!(parse_binop(input, curr_prec+1, rhs)); + } + + lhs_curr = match op_token { + Token::Plus => Expr::Plus(Box::new(lhs_curr), Box::new(rhs)), + Token::Minus => Expr::Minus(Box::new(lhs_curr), Box::new(rhs)), + Token::Multiply => Expr::Multiply(Box::new(lhs_curr), Box::new(rhs)), + Token::Divide => Expr::Divide(Box::new(lhs_curr), Box::new(rhs)), + Token::Equals => Expr::Assignment(Box::new(lhs_curr), Box::new(rhs)), + _ => return Err(ParseError::UnknownOperator) + }; + } + } +} + +fn parse_expr<'a>(input: &mut Peekable>) -> Result { + let lhs = try!(parse_primary(input)); + + parse_binop(input, 0, lhs) +} + +fn parse_if<'a>(input: &mut Peekable>) -> Result { + input.next(); + + let guard = try!(parse_expr(input)); + let body = try!(parse_block(input)); + + Ok(Stmt::If(Box::new(guard), Box::new(body))) +} + +fn parse_while<'a>(input: &mut Peekable>) -> Result { + input.next(); + + let guard = try!(parse_expr(input)); + let body = try!(parse_block(input)); + + Ok(Stmt::While(Box::new(guard), Box::new(body))) +} + +fn parse_var<'a>(input: &mut Peekable>) -> Result { + input.next(); + + let name = match input.next() { + Some(Token::Id(ref s)) => s.clone(), + _ => return Err(ParseError::VarExpectsIdentifier) + }; + + match input.peek() { + Some(&Token::Equals) => { + input.next(); + let initializer = try!(parse_expr(input)); + Ok(Stmt::Var(name, Some(Box::new(initializer)))) + } + _ => Ok(Stmt::Var(name, None)) + } +} + +fn parse_block<'a>(input: &mut Peekable>) -> Result { + match input.peek() { + Some(& Token::LCurly) => (), + _ => return Err(ParseError::MissingLCurly) + } + + input.next(); + + let stmts = try!(parse_stmts(input, true)); + + match input.peek() { + Some(& Token::RCurly) => {input.next(); Ok(Stmt::Block(Box::new(stmts)))}, + _ => Err(ParseError::MissingRCurly) + } +} + +fn parse_expr_stmt<'a>(input: &mut Peekable>) -> Result { + let expr = try!(parse_expr(input)); + Ok(Stmt::Expr(Box::new(expr))) +} + +fn parse_stmt<'a>(input: &mut Peekable>) -> Result { + match input.peek() { + Some(& Token::If) => parse_if(input), + Some(& Token::While) => parse_while(input), + Some(& Token::LCurly) => parse_block(input), + Some(& Token::Var) => parse_var(input), + _ => parse_expr_stmt(input) + } +} + +fn parse_stmts<'a>(input: &mut Peekable>, check_for_rcurly: bool) -> Result, ParseError> { + let mut result = Vec::new(); + + if check_for_rcurly { + match input.peek() { + Some(& Token::RCurly) => return Ok(result), + _ => () + } + } + + while let Some(_) = input.peek() { + result.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) +} + +pub fn parse<'a>(input: &mut Peekable>) -> Result, ParseError> { + let result = parse_stmts(input, false); + result +}