Initial commit
This commit is contained in:
commit
6739706b64
5
Cargo.toml
Normal file
5
Cargo.toml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[package]
|
||||||
|
name = "rhai"
|
||||||
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[dependencies]
|
7
README.md
Normal file
7
README.md
Normal 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
273
src/engine.rs
Normal 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
57
src/fn_register.rs
Normal 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
200
src/main.rs
Normal 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
308
src/ops.rs
Normal 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
356
src/parser.rs
Normal 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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user