Allow comparisons between different types (returning false).
This commit is contained in:
parent
22a505b57b
commit
a5e09295f8
12
README.md
12
README.md
@ -393,6 +393,18 @@ let x = 3;
|
|||||||
let x = (1 + 2) * (6 - 4) / 2;
|
let x = (1 + 2) * (6 - 4) / 2;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Comparison operators
|
||||||
|
|
||||||
|
You can compare most values of the same data type. If you compare two values of _different_ data types, the result is always `false`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
42 == 42; // true
|
||||||
|
42 > 42; // false
|
||||||
|
"hello" > "foo"; // true
|
||||||
|
"42" == 42; // false
|
||||||
|
42 == 42.0; // false - i64 is different from f64
|
||||||
|
```
|
||||||
|
|
||||||
## Boolean operators
|
## Boolean operators
|
||||||
|
|
||||||
Double boolean operators `&&` and `||` _short-circuit_, meaning that the second operand will not be evaluated if the first one already proves the condition wrong.
|
Double boolean operators `&&` and `||` _short-circuit_, meaning that the second operand will not be evaluated if the first one already proves the condition wrong.
|
||||||
|
@ -73,7 +73,7 @@ impl Variant {
|
|||||||
|
|
||||||
impl Clone for Dynamic {
|
impl Clone for Dynamic {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Any::into_dynamic(self.as_ref() as &Variant)
|
Any::into_dynamic(self.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
173
src/api.rs
Normal file
173
src/api.rs
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
use crate::any::{Any, AnyExt};
|
||||||
|
use crate::engine::{FnIntExt, FnSpec};
|
||||||
|
use crate::parser::{lex, parse};
|
||||||
|
use crate::{Dynamic, Engine, EvalAltResult, ParseError, Scope, AST};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
impl Engine {
|
||||||
|
/// Compile a string into an AST
|
||||||
|
pub fn compile(input: &str) -> Result<AST, ParseError> {
|
||||||
|
let tokens = lex(input);
|
||||||
|
|
||||||
|
let mut peekables = tokens.peekable();
|
||||||
|
let tree = parse(&mut peekables);
|
||||||
|
|
||||||
|
tree
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile a file into an AST
|
||||||
|
pub fn compile_file(filename: &str) -> Result<AST, EvalAltResult> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut f = File::open(filename)
|
||||||
|
.map_err(|_| EvalAltResult::ErrorCantOpenScriptFile(filename.into()))?;
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
|
||||||
|
f.read_to_string(&mut contents)
|
||||||
|
.map_err(|_| EvalAltResult::ErrorCantOpenScriptFile(filename.into()))
|
||||||
|
.and_then(|_| Self::compile(&contents).map_err(EvalAltResult::ErrorParsing))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a file
|
||||||
|
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut f = File::open(filename)
|
||||||
|
.map_err(|_| EvalAltResult::ErrorCantOpenScriptFile(filename.into()))?;
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
|
||||||
|
f.read_to_string(&mut contents)
|
||||||
|
.map_err(|_| EvalAltResult::ErrorCantOpenScriptFile(filename.into()))
|
||||||
|
.and_then(|_| self.eval::<T>(&contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string
|
||||||
|
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
self.eval_with_scope(&mut scope, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string with own scope
|
||||||
|
pub fn eval_with_scope<T: Any + Clone>(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
input: &str,
|
||||||
|
) -> Result<T, EvalAltResult> {
|
||||||
|
let ast = Self::compile(input).map_err(EvalAltResult::ErrorParsing)?;
|
||||||
|
self.eval_ast_with_scope(scope, &ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate an AST
|
||||||
|
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> {
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
self.eval_ast_with_scope(&mut scope, ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate an AST with own scope
|
||||||
|
pub fn eval_ast_with_scope<T: Any + Clone>(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
ast: &AST,
|
||||||
|
) -> Result<T, EvalAltResult> {
|
||||||
|
let AST(os, fns) = ast;
|
||||||
|
|
||||||
|
for f in fns {
|
||||||
|
let spec = FnSpec {
|
||||||
|
ident: f.name.clone(),
|
||||||
|
args: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.script_fns
|
||||||
|
.insert(spec, Arc::new(FnIntExt::Int(f.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = os
|
||||||
|
.iter()
|
||||||
|
.try_fold(Box::new(()) as Dynamic, |_, o| self.eval_stmt(scope, o));
|
||||||
|
|
||||||
|
self.script_fns.clear(); // Clean up engine
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Err(EvalAltResult::Return(out)) | Ok(out) => Ok(*out
|
||||||
|
.downcast::<T>()
|
||||||
|
.map_err(|err| EvalAltResult::ErrorMismatchOutputType((*err).type_name()))?),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a file, but only return errors, if there are any.
|
||||||
|
/// Useful for when you don't need the result, but still need
|
||||||
|
/// to keep track of possible errors
|
||||||
|
pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
|
||||||
|
let mut f = File::open(filename)
|
||||||
|
.map_err(|_| EvalAltResult::ErrorCantOpenScriptFile(filename.into()))?;
|
||||||
|
|
||||||
|
let mut contents = String::new();
|
||||||
|
|
||||||
|
f.read_to_string(&mut contents)
|
||||||
|
.map_err(|_| EvalAltResult::ErrorCantOpenScriptFile(filename.into()))
|
||||||
|
.and_then(|_| self.consume(&contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string, but only return errors, if there are any.
|
||||||
|
/// Useful for when you don't need the result, but still need
|
||||||
|
/// to keep track of possible errors
|
||||||
|
pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> {
|
||||||
|
self.consume_with_scope(&mut Scope::new(), input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluate a string with own scope, but only return errors, if there are any.
|
||||||
|
/// Useful for when you don't need the result, but still need
|
||||||
|
/// to keep track of possible errors
|
||||||
|
pub fn consume_with_scope(
|
||||||
|
&mut self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
input: &str,
|
||||||
|
) -> Result<(), EvalAltResult> {
|
||||||
|
let tokens = lex(input);
|
||||||
|
|
||||||
|
parse(&mut tokens.peekable())
|
||||||
|
.map_err(|err| EvalAltResult::ErrorParsing(err))
|
||||||
|
.and_then(|AST(ref os, ref fns)| {
|
||||||
|
for f in fns {
|
||||||
|
if f.params.len() > 6 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let spec = FnSpec {
|
||||||
|
ident: f.name.clone(),
|
||||||
|
args: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.script_fns
|
||||||
|
.insert(spec, Arc::new(FnIntExt::Int(f.clone())));
|
||||||
|
}
|
||||||
|
|
||||||
|
let val = os
|
||||||
|
.iter()
|
||||||
|
.try_fold(Box::new(()) as Dynamic, |_, o| self.eval_stmt(scope, o))
|
||||||
|
.map(|_| ());
|
||||||
|
|
||||||
|
self.script_fns.clear(); // Clean up engine
|
||||||
|
|
||||||
|
val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides `on_print`
|
||||||
|
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
|
||||||
|
self.on_print = Box::new(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overrides `on_debug`
|
||||||
|
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
|
||||||
|
self.on_debug = Box::new(callback);
|
||||||
|
}
|
||||||
|
}
|
@ -195,11 +195,14 @@ impl Engine {
|
|||||||
fn print<T: Display>(x: T) -> String {
|
fn print<T: Display>(x: T) -> String {
|
||||||
format!("{}", x)
|
format!("{}", x)
|
||||||
}
|
}
|
||||||
|
fn println() -> String {
|
||||||
|
"\n".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
reg_func1!(self, "print", print, String, i32, i64, u32, u64);
|
reg_func1!(self, "print", print, String, i32, i64, u32, u64);
|
||||||
reg_func1!(self, "print", print, String, f32, f64, bool, char, String);
|
reg_func1!(self, "print", print, String, f32, f64, bool, char, String);
|
||||||
reg_func1!(self, "print", print_debug, String, Array);
|
reg_func1!(self, "print", print_debug, String, Array);
|
||||||
self.register_fn("print", |_: ()| println!());
|
self.register_fn("print", println);
|
||||||
|
|
||||||
reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64);
|
reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64);
|
||||||
reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char);
|
reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char);
|
||||||
|
294
src/engine.rs
294
src/engine.rs
@ -7,7 +7,7 @@ use std::sync::Arc;
|
|||||||
use crate::any::{Any, AnyExt, Dynamic, Variant};
|
use crate::any::{Any, AnyExt, Dynamic, Variant};
|
||||||
use crate::call::FunArgs;
|
use crate::call::FunArgs;
|
||||||
use crate::fn_register::RegisterFn;
|
use crate::fn_register::RegisterFn;
|
||||||
use crate::parser::{lex, parse, Expr, FnDef, ParseError, Stmt, AST};
|
use crate::parser::{Expr, FnDef, ParseError, Stmt};
|
||||||
|
|
||||||
pub type Array = Vec<Dynamic>;
|
pub type Array = Vec<Dynamic>;
|
||||||
pub type FnCallArgs<'a> = Vec<&'a mut Variant>;
|
pub type FnCallArgs<'a> = Vec<&'a mut Variant>;
|
||||||
@ -41,7 +41,6 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorVariableNotFound(s)
|
| Self::ErrorVariableNotFound(s)
|
||||||
| Self::ErrorFunctionNotFound(s)
|
| Self::ErrorFunctionNotFound(s)
|
||||||
| Self::ErrorMismatchOutputType(s)
|
| Self::ErrorMismatchOutputType(s)
|
||||||
| Self::ErrorCantOpenScriptFile(s)
|
|
||||||
| Self::ErrorArithmetic(s) => s,
|
| Self::ErrorArithmetic(s) => s,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
@ -159,8 +158,8 @@ impl std::fmt::Display for EvalAltResult {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct FnSpec {
|
pub struct FnSpec {
|
||||||
ident: String,
|
pub ident: String,
|
||||||
args: Option<Vec<TypeId>>,
|
pub args: Option<Vec<TypeId>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
||||||
@ -180,11 +179,15 @@ type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
/// A hashmap containing all functions known to the engine
|
/// A hashmap containing all compiled functions known to the engine
|
||||||
pub fns: HashMap<FnSpec, Arc<FnIntExt>>,
|
fns: HashMap<FnSpec, Arc<FnIntExt>>,
|
||||||
pub type_iterators: HashMap<TypeId, Arc<IteratorFn>>,
|
/// A hashmap containing all script-defined functions
|
||||||
on_print: Box<dyn Fn(&str)>,
|
pub(crate) script_fns: HashMap<FnSpec, Arc<FnIntExt>>,
|
||||||
on_debug: Box<dyn Fn(&str)>,
|
/// A hashmap containing all iterators known to the engine
|
||||||
|
type_iterators: HashMap<TypeId, Arc<IteratorFn>>,
|
||||||
|
|
||||||
|
pub(crate) on_print: Box<dyn Fn(&str)>,
|
||||||
|
pub(crate) on_debug: Box<dyn Fn(&str)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FnIntExt {
|
pub enum FnIntExt {
|
||||||
@ -217,7 +220,7 @@ impl Engine {
|
|||||||
A: FunArgs<'a>,
|
A: FunArgs<'a>,
|
||||||
T: Any + Clone,
|
T: Any + Clone,
|
||||||
{
|
{
|
||||||
self.call_fn_raw(ident.into(), args.into_vec())
|
self.call_fn_raw(ident.into(), args.into_vec(), None)
|
||||||
.and_then(|b| {
|
.and_then(|b| {
|
||||||
b.downcast()
|
b.downcast()
|
||||||
.map(|b| *b)
|
.map(|b| *b)
|
||||||
@ -227,7 +230,12 @@ impl Engine {
|
|||||||
|
|
||||||
/// Universal method for calling functions, that are either
|
/// Universal method for calling functions, that are either
|
||||||
/// registered with the `Engine` or written in Rhai
|
/// registered with the `Engine` or written in Rhai
|
||||||
fn call_fn_raw(&self, ident: String, args: FnCallArgs) -> Result<Dynamic, EvalAltResult> {
|
fn call_fn_raw(
|
||||||
|
&self,
|
||||||
|
ident: String,
|
||||||
|
args: FnCallArgs,
|
||||||
|
def_value: Option<&Dynamic>,
|
||||||
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
debug_println!(
|
debug_println!(
|
||||||
"Trying to call function {:?} with args {:?}",
|
"Trying to call function {:?} with args {:?}",
|
||||||
ident,
|
ident,
|
||||||
@ -241,28 +249,27 @@ impl Engine {
|
|||||||
args: Some(args.iter().map(|a| Any::type_id(&**a)).collect()),
|
args: Some(args.iter().map(|a| Any::type_id(&**a)).collect()),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.fns
|
// First search in script-defined functions (can override built-in),
|
||||||
|
// then in built-in's, then retry again with no arguments
|
||||||
|
let fn_def = self
|
||||||
|
.script_fns
|
||||||
.get(&spec)
|
.get(&spec)
|
||||||
|
.or_else(|| self.fns.get(&spec))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
let spec1 = FnSpec {
|
self.script_fns.get(&FnSpec {
|
||||||
ident: ident.clone(),
|
ident: ident.clone(),
|
||||||
args: None,
|
args: None,
|
||||||
};
|
})
|
||||||
self.fns.get(&spec1)
|
|
||||||
})
|
})
|
||||||
.ok_or_else(|| {
|
.or_else(|| {
|
||||||
let type_names = args
|
self.fns.get(&FnSpec {
|
||||||
.iter()
|
ident: ident.clone(),
|
||||||
.map(|x| (*(&**x).into_dynamic()).type_name())
|
args: None,
|
||||||
.collect::<Vec<_>>();
|
})
|
||||||
|
});
|
||||||
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(format!(
|
if let Some(f) = fn_def {
|
||||||
"{} ({})",
|
match **f {
|
||||||
ident,
|
|
||||||
type_names.join(", ")
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.and_then(move |f| match **f {
|
|
||||||
FnIntExt::Ext(ref f) => {
|
FnIntExt::Ext(ref f) => {
|
||||||
let r = f(args);
|
let r = f(args);
|
||||||
|
|
||||||
@ -299,7 +306,22 @@ impl Engine {
|
|||||||
other => other,
|
other => other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
} else if let Some(val) = def_value {
|
||||||
|
// Return default value
|
||||||
|
Ok(val.clone())
|
||||||
|
} else {
|
||||||
|
let type_names = args
|
||||||
|
.iter()
|
||||||
|
.map(|x| (*(&**x).into_dynamic()).type_name())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Err(EvalAltResult::ErrorFunctionNotFound(format!(
|
||||||
|
"{} ({})",
|
||||||
|
ident,
|
||||||
|
type_names.join(", ")
|
||||||
|
)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn register_fn_raw(
|
pub(crate) fn register_fn_raw(
|
||||||
@ -369,7 +391,7 @@ impl Engine {
|
|||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
|
|
||||||
match dot_rhs {
|
match dot_rhs {
|
||||||
Expr::FunctionCall(fn_name, args) => {
|
Expr::FunctionCall(fn_name, args, def_value) => {
|
||||||
let mut args: Array = args
|
let mut args: Array = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| self.eval_expr(scope, arg))
|
.map(|arg| self.eval_expr(scope, arg))
|
||||||
@ -379,13 +401,13 @@ impl Engine {
|
|||||||
.chain(args.iter_mut().map(|b| b.as_mut()))
|
.chain(args.iter_mut().map(|b| b.as_mut()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
self.call_fn_raw(fn_name.to_owned(), args)
|
self.call_fn_raw(fn_name.into(), args, def_value.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Identifier(id) => {
|
Expr::Identifier(id) => {
|
||||||
let get_fn_name = "get$".to_string() + id;
|
let get_fn_name = "get$".to_string() + id;
|
||||||
|
|
||||||
self.call_fn_raw(get_fn_name, vec![this_ptr])
|
self.call_fn_raw(get_fn_name, vec![this_ptr], None)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Index(id, idx_raw) => {
|
Expr::Index(id, idx_raw) => {
|
||||||
@ -397,7 +419,7 @@ impl Engine {
|
|||||||
|
|
||||||
let get_fn_name = "get$".to_string() + id;
|
let get_fn_name = "get$".to_string() + id;
|
||||||
|
|
||||||
let mut val = self.call_fn_raw(get_fn_name, vec![this_ptr])?;
|
let mut val = self.call_fn_raw(get_fn_name, vec![this_ptr], None)?;
|
||||||
|
|
||||||
if let Some(arr) = (*val).downcast_mut() as Option<&mut Array> {
|
if let Some(arr) = (*val).downcast_mut() as Option<&mut Array> {
|
||||||
if idx >= 0 {
|
if idx >= 0 {
|
||||||
@ -425,7 +447,7 @@ impl Engine {
|
|||||||
Expr::Identifier(ref id) => {
|
Expr::Identifier(ref id) => {
|
||||||
let get_fn_name = "get$".to_string() + id;
|
let get_fn_name = "get$".to_string() + id;
|
||||||
let value = self
|
let value = self
|
||||||
.call_fn_raw(get_fn_name, vec![this_ptr])
|
.call_fn_raw(get_fn_name, vec![this_ptr], None)
|
||||||
.and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), inner_rhs))?;
|
.and_then(|mut v| self.get_dot_val_helper(scope, v.as_mut(), inner_rhs))?;
|
||||||
|
|
||||||
// TODO - Should propagate changes back in this scenario:
|
// TODO - Should propagate changes back in this scenario:
|
||||||
@ -462,7 +484,7 @@ impl Engine {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
.find(|&(_, &mut (ref name, _))| id == name)
|
.find(|&(_, &mut (ref name, _))| id == name)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.to_owned()))
|
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(id.into()))
|
||||||
.and_then(move |(idx, &mut (_, ref mut val))| map(val.as_mut()).map(|val| (idx, val)))
|
.and_then(move |(idx, &mut (_, ref mut val))| map(val.as_mut()).map(|val| (idx, val)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,14 +597,14 @@ impl Engine {
|
|||||||
Expr::Identifier(id) => {
|
Expr::Identifier(id) => {
|
||||||
let set_fn_name = "set$".to_string() + id;
|
let set_fn_name = "set$".to_string() + id;
|
||||||
|
|
||||||
self.call_fn_raw(set_fn_name, vec![this_ptr, source_val.as_mut()])
|
self.call_fn_raw(set_fn_name, vec![this_ptr, source_val.as_mut()], None)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Dot(inner_lhs, inner_rhs) => match **inner_lhs {
|
Expr::Dot(inner_lhs, inner_rhs) => match **inner_lhs {
|
||||||
Expr::Identifier(ref id) => {
|
Expr::Identifier(ref id) => {
|
||||||
let get_fn_name = "get$".to_string() + id;
|
let get_fn_name = "get$".to_string() + id;
|
||||||
|
|
||||||
self.call_fn_raw(get_fn_name, vec![this_ptr])
|
self.call_fn_raw(get_fn_name, vec![this_ptr], None)
|
||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
self.set_dot_val_helper(v.as_mut(), inner_rhs, source_val)
|
self.set_dot_val_helper(v.as_mut(), inner_rhs, source_val)
|
||||||
.map(|_| v) // Discard Ok return value
|
.map(|_| v) // Discard Ok return value
|
||||||
@ -590,7 +612,7 @@ impl Engine {
|
|||||||
.and_then(|mut v| {
|
.and_then(|mut v| {
|
||||||
let set_fn_name = "set$".to_string() + id;
|
let set_fn_name = "set$".to_string() + id;
|
||||||
|
|
||||||
self.call_fn_raw(set_fn_name, vec![this_ptr, v.as_mut()])
|
self.call_fn_raw(set_fn_name, vec![this_ptr, v.as_mut()], None)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(EvalAltResult::ErrorDotExpr),
|
_ => Err(EvalAltResult::ErrorDotExpr),
|
||||||
@ -743,14 +765,15 @@ impl Engine {
|
|||||||
Ok(Box::new(arr))
|
Ok(Box::new(arr))
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::FunctionCall(fn_name, args) => self.call_fn_raw(
|
Expr::FunctionCall(fn_name, args, def_value) => self.call_fn_raw(
|
||||||
fn_name.to_owned(),
|
fn_name.into(),
|
||||||
args.iter()
|
args.iter()
|
||||||
.map(|expr| self.eval_expr(scope, expr))
|
.map(|expr| self.eval_expr(scope, expr))
|
||||||
.collect::<Result<Array, _>>()?
|
.collect::<Result<Array, _>>()?
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.map(|b| b.as_mut())
|
.map(|b| b.as_mut())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
def_value.as_ref(),
|
||||||
),
|
),
|
||||||
|
|
||||||
Expr::And(lhs, rhs) => Ok(Box::new(
|
Expr::And(lhs, rhs) => Ok(Box::new(
|
||||||
@ -781,7 +804,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_stmt(&self, scope: &mut Scope, stmt: &Stmt) -> Result<Dynamic, EvalAltResult> {
|
pub(crate) fn eval_stmt(
|
||||||
|
&self,
|
||||||
|
scope: &mut Scope,
|
||||||
|
stmt: &Stmt,
|
||||||
|
) -> Result<Dynamic, EvalAltResult> {
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::Expr(expr) => self.eval_expr(scope, expr),
|
Stmt::Expr(expr) => self.eval_expr(scope, expr),
|
||||||
|
|
||||||
@ -899,186 +926,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compile a string into an AST
|
|
||||||
pub fn compile(input: &str) -> Result<AST, ParseError> {
|
|
||||||
let tokens = lex(input);
|
|
||||||
|
|
||||||
let mut peekables = tokens.peekable();
|
|
||||||
let tree = parse(&mut peekables);
|
|
||||||
|
|
||||||
tree
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compile a file into an AST
|
|
||||||
pub fn compile_file(filename: &str) -> Result<AST, EvalAltResult> {
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
if let Ok(mut f) = File::open(filename) {
|
|
||||||
let mut contents = String::new();
|
|
||||||
|
|
||||||
if f.read_to_string(&mut contents).is_ok() {
|
|
||||||
Self::compile(&contents).map_err(|err| EvalAltResult::ErrorParsing(err))
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorCantOpenScriptFile(filename.to_owned()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorCantOpenScriptFile(filename.to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a file
|
|
||||||
pub fn eval_file<T: Any + Clone>(&mut self, filename: &str) -> Result<T, EvalAltResult> {
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
if let Ok(mut f) = File::open(filename) {
|
|
||||||
let mut contents = String::new();
|
|
||||||
|
|
||||||
if f.read_to_string(&mut contents).is_ok() {
|
|
||||||
self.eval::<T>(&contents)
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorCantOpenScriptFile(filename.to_owned()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorCantOpenScriptFile(filename.to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a string
|
|
||||||
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
self.eval_with_scope(&mut scope, input)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a string with own scope
|
|
||||||
pub fn eval_with_scope<T: Any + Clone>(
|
|
||||||
&mut self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
input: &str,
|
|
||||||
) -> Result<T, EvalAltResult> {
|
|
||||||
let ast = Self::compile(input).map_err(|err| EvalAltResult::ErrorParsing(err))?;
|
|
||||||
self.eval_ast_with_scope(scope, &ast)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate an AST
|
|
||||||
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> {
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
self.eval_ast_with_scope(&mut scope, ast)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate an AST with own scope
|
|
||||||
pub fn eval_ast_with_scope<T: Any + Clone>(
|
|
||||||
&mut self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
ast: &AST,
|
|
||||||
) -> Result<T, EvalAltResult> {
|
|
||||||
let AST(os, fns) = ast;
|
|
||||||
let mut x: Result<Dynamic, EvalAltResult> = Ok(Box::new(()));
|
|
||||||
|
|
||||||
for f in fns {
|
|
||||||
let name = f.name.clone();
|
|
||||||
let local_f = f.clone();
|
|
||||||
|
|
||||||
let spec = FnSpec {
|
|
||||||
ident: name,
|
|
||||||
args: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.fns.insert(spec, Arc::new(FnIntExt::Int(local_f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for o in os {
|
|
||||||
x = match self.eval_stmt(scope, o) {
|
|
||||||
Ok(v) => Ok(v),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let x = x?;
|
|
||||||
|
|
||||||
match x.downcast::<T>() {
|
|
||||||
Ok(out) => Ok(*out),
|
|
||||||
Err(a) => Err(EvalAltResult::ErrorMismatchOutputType((*a).type_name())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a file, but only return errors, if there are any.
|
|
||||||
/// Useful for when you don't need the result, but still need
|
|
||||||
/// to keep track of possible errors
|
|
||||||
pub fn consume_file(&mut self, filename: &str) -> Result<(), EvalAltResult> {
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
|
|
||||||
if let Ok(mut f) = File::open(filename) {
|
|
||||||
let mut contents = String::new();
|
|
||||||
|
|
||||||
if f.read_to_string(&mut contents).is_ok() {
|
|
||||||
if let e @ Err(_) = self.consume(&contents) {
|
|
||||||
e
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorCantOpenScriptFile(filename.to_owned()))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(EvalAltResult::ErrorCantOpenScriptFile(filename.to_owned()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a string, but only return errors, if there are any.
|
|
||||||
/// Useful for when you don't need the result, but still need
|
|
||||||
/// to keep track of possible errors
|
|
||||||
pub fn consume(&mut self, input: &str) -> Result<(), EvalAltResult> {
|
|
||||||
self.consume_with_scope(&mut Scope::new(), input)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a string with own scope, but only return errors, if there are any.
|
|
||||||
/// Useful for when you don't need the result, but still need
|
|
||||||
/// to keep track of possible errors
|
|
||||||
pub fn consume_with_scope(
|
|
||||||
&mut self,
|
|
||||||
scope: &mut Scope,
|
|
||||||
input: &str,
|
|
||||||
) -> Result<(), EvalAltResult> {
|
|
||||||
let tokens = lex(input);
|
|
||||||
|
|
||||||
let mut peekables = tokens.peekable();
|
|
||||||
|
|
||||||
match parse(&mut peekables) {
|
|
||||||
Ok(AST(ref os, ref fns)) => {
|
|
||||||
for f in fns {
|
|
||||||
if f.params.len() > 6 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let name = f.name.clone();
|
|
||||||
let local_f = f.clone();
|
|
||||||
|
|
||||||
let spec = FnSpec {
|
|
||||||
ident: name,
|
|
||||||
args: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.fns.insert(spec, Arc::new(FnIntExt::Int(local_f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for o in os {
|
|
||||||
if let Err(e) = self.eval_stmt(scope, o) {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Err(err) => Err(EvalAltResult::ErrorParsing(err)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a new engine
|
/// Make a new engine
|
||||||
pub fn new() -> Engine {
|
pub fn new() -> Engine {
|
||||||
let mut engine = Engine {
|
let mut engine = Engine {
|
||||||
fns: HashMap::new(),
|
fns: HashMap::new(),
|
||||||
|
script_fns: HashMap::new(),
|
||||||
type_iterators: HashMap::new(),
|
type_iterators: HashMap::new(),
|
||||||
on_print: Box::new(|x: &str| println!("{}", x)),
|
on_print: Box::new(|x: &str| println!("{}", x)),
|
||||||
on_debug: Box::new(|x: &str| println!("{}", x)),
|
on_debug: Box::new(|x: &str| println!("{}", x)),
|
||||||
@ -1088,14 +940,4 @@ impl Engine {
|
|||||||
|
|
||||||
engine
|
engine
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overrides `on_print`
|
|
||||||
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
|
|
||||||
self.on_print = Box::new(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Overrides `on_debug`
|
|
||||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
|
|
||||||
self.on_debug = Box::new(callback);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ macro_rules! def_register {
|
|||||||
let r = f($(($clone)($par)),*);
|
let r = f($(($clone)($par)),*);
|
||||||
Ok(Box::new(r) as Dynamic)
|
Ok(Box::new(r) as Dynamic)
|
||||||
};
|
};
|
||||||
self.register_fn_raw(name.to_owned(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
self.register_fn_raw(name.into(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ macro_rules! def_register {
|
|||||||
// potentially clone the value, otherwise pass the reference.
|
// potentially clone the value, otherwise pass the reference.
|
||||||
Ok(f($(($clone)($par)),*))
|
Ok(f($(($clone)($par)),*))
|
||||||
};
|
};
|
||||||
self.register_fn_raw(name.to_owned(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
self.register_fn_raw(name.into(), Some(vec![$(TypeId::of::<$par>()),*]), Box::new(fun));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ macro_rules! debug_println {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod any;
|
mod any;
|
||||||
|
mod api;
|
||||||
mod builtin;
|
mod builtin;
|
||||||
mod call;
|
mod call;
|
||||||
mod engine;
|
mod engine;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::Dynamic;
|
||||||
use std::char;
|
use std::char;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@ -145,14 +146,14 @@ impl fmt::Display for ParseError {
|
|||||||
|
|
||||||
pub struct AST(pub(crate) Vec<Stmt>, pub(crate) Vec<FnDef>);
|
pub struct AST(pub(crate) Vec<Stmt>, pub(crate) Vec<FnDef>);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct FnDef {
|
pub struct FnDef {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub params: Vec<String>,
|
pub params: Vec<String>,
|
||||||
pub body: Box<Stmt>,
|
pub body: Box<Stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
If(Box<Expr>, Box<Stmt>),
|
If(Box<Expr>, Box<Stmt>),
|
||||||
IfElse(Box<Expr>, Box<Stmt>, Box<Stmt>),
|
IfElse(Box<Expr>, Box<Stmt>, Box<Stmt>),
|
||||||
@ -167,14 +168,14 @@ pub enum Stmt {
|
|||||||
ReturnWithVal(Box<Expr>),
|
ReturnWithVal(Box<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
IntegerConstant(i64),
|
IntegerConstant(i64),
|
||||||
FloatConstant(f64),
|
FloatConstant(f64),
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
CharConstant(char),
|
CharConstant(char),
|
||||||
StringConstant(String),
|
StringConstant(String),
|
||||||
FunctionCall(String, Vec<Expr>),
|
FunctionCall(String, Vec<Expr>, Option<Dynamic>),
|
||||||
Assignment(Box<Expr>, Box<Expr>),
|
Assignment(Box<Expr>, Box<Expr>),
|
||||||
Dot(Box<Expr>, Box<Expr>),
|
Dot(Box<Expr>, Box<Expr>),
|
||||||
Index(String, Box<Expr>),
|
Index(String, Box<Expr>),
|
||||||
@ -997,7 +998,7 @@ fn parse_call_expr<'a>(
|
|||||||
|
|
||||||
if let Some(&(Token::RightParen, _)) = input.peek() {
|
if let Some(&(Token::RightParen, _)) = input.peek() {
|
||||||
input.next();
|
input.next();
|
||||||
return Ok(Expr::FunctionCall(id, args));
|
return Ok(Expr::FunctionCall(id, args, None));
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -1006,7 +1007,7 @@ fn parse_call_expr<'a>(
|
|||||||
match input.peek() {
|
match input.peek() {
|
||||||
Some(&(Token::RightParen, _)) => {
|
Some(&(Token::RightParen, _)) => {
|
||||||
input.next();
|
input.next();
|
||||||
return Ok(Expr::FunctionCall(id, args));
|
return Ok(Expr::FunctionCall(id, args, None));
|
||||||
}
|
}
|
||||||
Some(&(Token::Comma, _)) => (),
|
Some(&(Token::Comma, _)) => (),
|
||||||
Some(&(_, pos)) => return Err(ParseError(PERR::MalformedCallExpr, pos)),
|
Some(&(_, pos)) => return Err(ParseError(PERR::MalformedCallExpr, pos)),
|
||||||
@ -1116,7 +1117,11 @@ fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pars
|
|||||||
match token {
|
match token {
|
||||||
Token::UnaryMinus => {
|
Token::UnaryMinus => {
|
||||||
input.next();
|
input.next();
|
||||||
Ok(Expr::FunctionCall("-".into(), vec![parse_primary(input)?]))
|
Ok(Expr::FunctionCall(
|
||||||
|
"-".into(),
|
||||||
|
vec![parse_primary(input)?],
|
||||||
|
None,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
Token::UnaryPlus => {
|
Token::UnaryPlus => {
|
||||||
input.next();
|
input.next();
|
||||||
@ -1124,7 +1129,11 @@ fn parse_unary<'a>(input: &mut Peekable<TokenIterator<'a>>) -> Result<Expr, Pars
|
|||||||
}
|
}
|
||||||
Token::Bang => {
|
Token::Bang => {
|
||||||
input.next();
|
input.next();
|
||||||
Ok(Expr::FunctionCall("!".into(), vec![parse_primary(input)?]))
|
Ok(Expr::FunctionCall(
|
||||||
|
"!".into(),
|
||||||
|
vec![parse_primary(input)?],
|
||||||
|
Some(Box::new(false)), // NOT operator, when operating on invalid operand, defaults to false
|
||||||
|
))
|
||||||
}
|
}
|
||||||
_ => parse_primary(input),
|
_ => parse_primary(input),
|
||||||
}
|
}
|
||||||
@ -1165,102 +1174,118 @@ fn parse_binop<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
lhs_curr = match op_token {
|
lhs_curr = match op_token {
|
||||||
Token::Plus => Expr::FunctionCall("+".into(), vec![lhs_curr, rhs]),
|
Token::Plus => Expr::FunctionCall("+".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::Minus => Expr::FunctionCall("-".into(), vec![lhs_curr, rhs]),
|
Token::Minus => Expr::FunctionCall("-".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::Multiply => Expr::FunctionCall("*".into(), vec![lhs_curr, rhs]),
|
Token::Multiply => Expr::FunctionCall("*".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::Divide => Expr::FunctionCall("/".into(), vec![lhs_curr, rhs]),
|
Token::Divide => Expr::FunctionCall("/".into(), vec![lhs_curr, rhs], None),
|
||||||
|
|
||||||
Token::Equals => Expr::Assignment(Box::new(lhs_curr), Box::new(rhs)),
|
Token::Equals => Expr::Assignment(Box::new(lhs_curr), Box::new(rhs)),
|
||||||
Token::PlusAssign => {
|
Token::PlusAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("+".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("+".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::MinusAssign => {
|
Token::MinusAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("-".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("-".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::Period => Expr::Dot(Box::new(lhs_curr), Box::new(rhs)),
|
Token::Period => Expr::Dot(Box::new(lhs_curr), Box::new(rhs)),
|
||||||
Token::EqualsTo => Expr::FunctionCall("==".into(), vec![lhs_curr, rhs]),
|
|
||||||
Token::NotEqualsTo => Expr::FunctionCall("!=".into(), vec![lhs_curr, rhs]),
|
// Comparison operators default to false when passed invalid operands
|
||||||
Token::LessThan => Expr::FunctionCall("<".into(), vec![lhs_curr, rhs]),
|
Token::EqualsTo => {
|
||||||
Token::LessThanEqualsTo => Expr::FunctionCall("<=".into(), vec![lhs_curr, rhs]),
|
Expr::FunctionCall("==".into(), vec![lhs_curr, rhs], Some(Box::new(false)))
|
||||||
Token::GreaterThan => Expr::FunctionCall(">".into(), vec![lhs_curr, rhs]),
|
}
|
||||||
Token::GreaterThanEqualsTo => Expr::FunctionCall(">=".into(), vec![lhs_curr, rhs]),
|
Token::NotEqualsTo => {
|
||||||
|
Expr::FunctionCall("!=".into(), vec![lhs_curr, rhs], Some(Box::new(false)))
|
||||||
|
}
|
||||||
|
Token::LessThan => {
|
||||||
|
Expr::FunctionCall("<".into(), vec![lhs_curr, rhs], Some(Box::new(false)))
|
||||||
|
}
|
||||||
|
Token::LessThanEqualsTo => {
|
||||||
|
Expr::FunctionCall("<=".into(), vec![lhs_curr, rhs], Some(Box::new(false)))
|
||||||
|
}
|
||||||
|
Token::GreaterThan => {
|
||||||
|
Expr::FunctionCall(">".into(), vec![lhs_curr, rhs], Some(Box::new(false)))
|
||||||
|
}
|
||||||
|
Token::GreaterThanEqualsTo => {
|
||||||
|
Expr::FunctionCall(">=".into(), vec![lhs_curr, rhs], Some(Box::new(false)))
|
||||||
|
}
|
||||||
|
|
||||||
Token::Or => Expr::Or(Box::new(lhs_curr), Box::new(rhs)),
|
Token::Or => Expr::Or(Box::new(lhs_curr), Box::new(rhs)),
|
||||||
Token::And => Expr::And(Box::new(lhs_curr), Box::new(rhs)),
|
Token::And => Expr::And(Box::new(lhs_curr), Box::new(rhs)),
|
||||||
Token::XOr => Expr::FunctionCall("^".into(), vec![lhs_curr, rhs]),
|
Token::XOr => Expr::FunctionCall("^".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::OrAssign => {
|
Token::OrAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("|".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("|".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::AndAssign => {
|
Token::AndAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("&".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("&".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::XOrAssign => {
|
Token::XOrAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("^".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("^".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::MultiplyAssign => {
|
Token::MultiplyAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("*".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("*".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::DivideAssign => {
|
Token::DivideAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("/".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("/".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::Pipe => Expr::FunctionCall("|".into(), vec![lhs_curr, rhs]),
|
Token::Pipe => Expr::FunctionCall("|".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::LeftShift => Expr::FunctionCall("<<".into(), vec![lhs_curr, rhs]),
|
Token::LeftShift => Expr::FunctionCall("<<".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::RightShift => Expr::FunctionCall(">>".into(), vec![lhs_curr, rhs]),
|
Token::RightShift => Expr::FunctionCall(">>".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::LeftShiftAssign => {
|
Token::LeftShiftAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("<<".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("<<".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::RightShiftAssign => {
|
Token::RightShiftAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall(">>".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall(">>".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::Ampersand => Expr::FunctionCall("&".into(), vec![lhs_curr, rhs]),
|
Token::Ampersand => Expr::FunctionCall("&".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::Modulo => Expr::FunctionCall("%".into(), vec![lhs_curr, rhs]),
|
Token::Modulo => Expr::FunctionCall("%".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::ModuloAssign => {
|
Token::ModuloAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("%".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("%".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::PowerOf => Expr::FunctionCall("~".into(), vec![lhs_curr, rhs]),
|
Token::PowerOf => Expr::FunctionCall("~".into(), vec![lhs_curr, rhs], None),
|
||||||
Token::PowerOfAssign => {
|
Token::PowerOfAssign => {
|
||||||
let lhs_copy = lhs_curr.clone();
|
let lhs_copy = lhs_curr.clone();
|
||||||
Expr::Assignment(
|
Expr::Assignment(
|
||||||
Box::new(lhs_curr),
|
Box::new(lhs_curr),
|
||||||
Box::new(Expr::FunctionCall("~".into(), vec![lhs_copy, rhs])),
|
Box::new(Expr::FunctionCall("~".into(), vec![lhs_copy, rhs], None)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => return Err(ParseError(PERR::UnknownOperator, pos)),
|
_ => return Err(ParseError(PERR::UnknownOperator, pos)),
|
||||||
|
@ -10,4 +10,11 @@ fn test_binary_ops() {
|
|||||||
assert_eq!(engine.eval::<i64>("10 & 4"), Ok(0));
|
assert_eq!(engine.eval::<i64>("10 & 4"), Ok(0));
|
||||||
assert_eq!(engine.eval::<i64>("10 | 4"), Ok(14));
|
assert_eq!(engine.eval::<i64>("10 | 4"), Ok(14));
|
||||||
assert_eq!(engine.eval::<i64>("10 ^ 4"), Ok(14));
|
assert_eq!(engine.eval::<i64>("10 ^ 4"), Ok(14));
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<bool>("42 == 42"), Ok(true));
|
||||||
|
assert_eq!(engine.eval::<bool>("42 > 42"), Ok(false));
|
||||||
|
|
||||||
|
// Incompatible types compare to false
|
||||||
|
assert_eq!(engine.eval::<bool>("true == 42"), Ok(false));
|
||||||
|
assert_eq!(engine.eval::<bool>(r#""42" == 42"#), Ok(false));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user