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