Initial commit

This commit is contained in:
jonathandturner 2016-02-29 16:43:45 -05:00
commit 6739706b64
7 changed files with 1206 additions and 0 deletions

5
Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[package]
name = "rhai"
version = "0.1.0"
[dependencies]

7
README.md Normal file
View File

@ -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.

273
src/engine.rs Normal file
View File

@ -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<String, Vec<Box<Fn(&mut Box<Any>)->Result<Box<Any>, EvalError>>>>,
pub fns_arity_0: HashMap<String, Vec<Box<Fn()->Result<Box<Any>, EvalError>>>>,
pub scope: Vec<(String, Box<Any>)>
}
impl Engine {
pub fn call_fn_0_arg(&self, name: &str) -> Result<Box<Any>, 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<Any>) -> Result<Box<Any>, 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<T: Clone+Any>(&mut self) {
fn clone_helper<T: Clone>(t:T)->T { t.clone() };
&(clone_helper as fn(T)->T).register(self, "clone");
}
fn eval_expr(&mut self, expr: &Expr) -> Result<Box<Any>, 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<Box<Any>, 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<Box<Any>, 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::<bool>() {
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::<bool>() {
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<Box<Any>, EvalError> {
let tokens = lex(&input);
let mut peekables = tokens.peekable();
let tree = parse(&mut peekables);
match tree {
Ok(os) => {
let mut x: Result<Box<Any>, 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::<i32>();
output.register_type::<u32>();
output.register_type::<i64>();
output.register_type::<u64>();
output.register_type::<f32>();
output.register_type::<f64>();
output.register_type::<String>();
output.register_type::<char>();
output.register_type::<bool>();
output
}
}

57
src/fn_register.rs Normal file
View File

@ -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<T: Any, U: Any> FnRegister for fn(&mut T)->U {
fn register(self, engine: &mut Engine, name: &str) {
let wrapped : Box<Fn(&mut Box<Any>)->Result<Box<Any>, EvalError>> =
Box::new(
move |x: &mut Box<Any>| {
let inside = (*x).downcast_mut() as Option<&mut T>;
match inside {
Some(b) => Ok(Box::new(self(b)) as Box<Any>),
None => Err(EvalError::FunctionArgMismatch)
}
}
);
let ent = engine.fns_arity_1.entry(name.to_string()).or_insert(Vec::new());
(*ent).push(wrapped);
}
}
impl<T: Any+Clone, U: Any> FnRegister for fn(T)->U {
fn register(self, engine: &mut Engine, name: &str) {
let wrapped : Box<Fn(&mut Box<Any>)->Result<Box<Any>, EvalError>> =
Box::new(
move |x: &mut Box<Any>| {
let inside = (*x).downcast_mut() as Option<&mut T>;
match inside {
Some(b) => Ok(Box::new(self(b.clone())) as Box<Any>),
None => Err(EvalError::FunctionArgMismatch)
}
}
);
let ent = engine.fns_arity_1.entry(name.to_string()).or_insert(Vec::new());
(*ent).push(wrapped);
}
}
impl<T: Any> FnRegister for fn()->T {
fn register(self, engine: &mut Engine, name: &str) {
let wrapped : Box<Fn()->Result<Box<Any>, EvalError>> =
Box::new(
move || { Ok(Box::new(self()) as Box<Any>) }
);
let ent = engine.fns_arity_0.entry(name.to_string()).or_insert(Vec::new());
(*ent).push(wrapped);
}
}

200
src/main.rs Normal file
View File

@ -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<T: Debug>(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<Debug>)->()).register(&mut engine, "showit");
&(TestStruct2::update as fn(&mut TestStruct2)->()).register(&mut engine, "update");
let mut arg : Box<Any> = Box::new(2);
println!("Result: {:?}", engine.call_fn_1_arg("simple_fn" , &mut arg).unwrap().downcast::<bool>());
println!("Result: {:?}", engine.call_fn_1_arg("simple_fn2", &mut arg).unwrap().downcast::<bool>());
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<Any> = Box::new(TestStruct { x: 6 });
engine.call_fn_1_arg("update" , &mut ts);
let result : Result<Box<TestStruct>, Box<Any>> = ts.downcast();
println!("TS: {:?}", result);
let myts = engine.call_fn_0_arg("newteststruct").unwrap().downcast::<TestStruct>();
println!("MyTS: {:?}", myts);
let mut mybox = Box::new(Box::new(56) as Box<Debug>) as Box<Any>;
engine.call_fn_1_arg("showit", &mut mybox);
let mut ts2 : Box<Any> = Box::new(TestStruct2 { x: false });
engine.call_fn_1_arg("update" , &mut ts2);
let result2 : Result<Box<TestStruct2>, Box<Any>> = 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::<i32>() {
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::<i32>() {
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::<bool>() {
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::<i32>() {
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::<i32>() {
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::<i32>() {
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::<i32>() {
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)}
}
}
}
}
}

308
src/ops.rs Normal file
View File

@ -0,0 +1,308 @@
use std::any::Any;
use std::boxed::Box;
use engine::{EvalError};
pub fn add_boxes(arg1: Box<Any>, arg2: Box<Any>) -> Result<Box<Any>, EvalError> {
match arg1.downcast::<i32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i32>() {
return Ok(Box::new(*v1 + *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u32>() {
return Ok(Box::new(*v1 + *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<i64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i64>() {
return Ok(Box::new(*v1 + *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u64>() {
return Ok(Box::new(*v1 + *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f32>() {
return Ok(Box::new(*v1 + *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f64>() {
return Ok(Box::new(*v1 + *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(_) => {
return Err(EvalError::TypeMismatchForOperands)
}
}
}
}
}
}
}
}
}
}
}
}
}
pub fn sub_boxes(arg1: Box<Any>, arg2: Box<Any>) -> Result<Box<Any>, EvalError> {
match arg1.downcast::<i32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i32>() {
return Ok(Box::new(*v1 - *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u32>() {
return Ok(Box::new(*v1 - *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<i64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i64>() {
return Ok(Box::new(*v1 - *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u64>() {
return Ok(Box::new(*v1 - *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f32>() {
return Ok(Box::new(*v1 - *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f64>() {
return Ok(Box::new(*v1 - *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(_) => {
return Err(EvalError::TypeMismatchForOperands)
}
}
}
}
}
}
}
}
}
}
}
}
}
pub fn mult_boxes(arg1: Box<Any>, arg2: Box<Any>) -> Result<Box<Any>, EvalError> {
match arg1.downcast::<i32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i32>() {
return Ok(Box::new(*v1 * *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u32>() {
return Ok(Box::new(*v1 * *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<i64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i64>() {
return Ok(Box::new(*v1 * *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u64>() {
return Ok(Box::new(*v1 * *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f32>() {
return Ok(Box::new(*v1 * *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f64>() {
return Ok(Box::new(*v1 * *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(_) => {
return Err(EvalError::TypeMismatchForOperands)
}
}
}
}
}
}
}
}
}
}
}
}
}
pub fn div_boxes(arg1: Box<Any>, arg2: Box<Any>) -> Result<Box<Any>, EvalError> {
match arg1.downcast::<i32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i32>() {
return Ok(Box::new(*v1 / *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u32>() {
return Ok(Box::new(*v1 / *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<i64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<i64>() {
return Ok(Box::new(*v1 / *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<u64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<u64>() {
return Ok(Box::new(*v1 / *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f32>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f32>() {
return Ok(Box::new(*v1 / *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(arg1_again) => {
match arg1_again.downcast::<f64>() {
Ok(v1) => {
if let Ok(v2) = arg2.downcast::<f64>() {
return Ok(Box::new(*v1 / *v2))
}
else {
return Err(EvalError::TypeMismatchForOperands)
}
},
Err(_) => {
return Err(EvalError::TypeMismatchForOperands)
}
}
}
}
}
}
}
}
}
}
}
}
}

356
src/parser.rs Normal file
View File

@ -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<Expr>, Box<Stmt>), While(Box<Expr>, Box<Stmt>), Var(String, Option<Box<Expr>>),
Block(Box<Vec<Stmt>>), Expr(Box<Expr>) }
#[derive(Debug)]
pub enum Expr { IntConst(i32), Identifier(String), Plus(Box<Expr>, Box<Expr>), Minus(Box<Expr>, Box<Expr>),
Multiply(Box<Expr>, Box<Expr>), Divide(Box<Expr>, Box<Expr>), Call(String, Box<Vec<Expr>>),
Assignment(Box<Expr>, Box<Expr>), 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<Chars<'a>>
}
impl<'a> Iterator for TokenIterator<'a> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
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::<i32>() {
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<TokenIterator<'a>>) -> Result<Expr, ParseError> {
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<TokenIterator<'a>>) -> Result<Expr, ParseError> {
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<TokenIterator<'a>>) -> Result<Expr, ParseError> {
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<TokenIterator<'a>>, prec: i32, lhs: Expr) -> Result<Expr, ParseError> {
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<TokenIterator<'a>>) -> Result<Expr, ParseError> {
let lhs = try!(parse_primary(input));
parse_binop(input, 0, lhs)
}
fn parse_if<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
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<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
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<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
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<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
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<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
let expr = try!(parse_expr(input));
Ok(Stmt::Expr(Box::new(expr)))
}
fn parse_stmt<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Stmt, ParseError> {
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<TokenIterator<'a>>, check_for_rcurly: bool) -> Result<Vec<Stmt>, 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<TokenIterator<'a>>) -> Result<Vec<Stmt>, ParseError> {
let result = parse_stmts(input, false);
result
}