Library Usage
use expression_parser::{Environment, ExpressionFile};
let input = r#"
a = 1 + 1;
a + 5
"#;
// ofcourse handle this better in you code
let parsed_expression = ExpressionFile::parse(input).unwrap();
// you can now decide what to do with the expression
// we will just evaluate it here with default variables.
let mut vars = Environment::default();
let output = ExpressionFile::eval(parsed_expression, &mut vars).unwrap();
assert_eq!(output, 7.into());
Add extra variables you can use in your code:
use expression_parser::{Env, Environment, ExpressionFile};
let input = r#"
5 * DATA
"#;
let parsed_expression = ExpressionFile::parse(input).unwrap();
let mut env = Environment::default();
env.variables_mut().insert("DATA", 1234.into());
let output = ExpressionFile::eval(parsed_expression, &mut env).unwrap();
assert_eq!(output, 6170.into());
Define your own functions in Rust like:
use expression_parser::{
Closure, Env, Environment, Error, ExpressionFile, ExpressionValue,
};
use std::sync::Arc;
let mut env = Environment::default();
// a `Closure` struct is just a container for holding the function.
// `new` takes a list of the arguments used (only for debugging purposes)
// and an `Arc` with a `Box`ed function with two arguments.
// the first is a list containing all the arguments given by the user. These need to be validated yourself.
// the second argument is a `Environment` struct that has methods to access variables outside the function and side effects.
// the return value is a `Result<ExpressionValue, Error>`
let closure = Closure::new(
vec!["x".to_string(), "y".to_string()],
Arc::new(Box::new(|vars, _| {
/// validate the arguments or return an error
fn validate_number(x: Option<&ExpressionValue>) -> Result<f64, Error> {
x.ok_or(Error::new_static("missing arguments"))?
.as_number()
.ok_or(Error::new_static("argument not a number"))
}
let x = validate_number(vars.get(0))?;
let y = validate_number(vars.get(1))?;
let result = x * y;
// the `into` casts the rust value into a `ExpressionValue` enum
Ok(result.into())
})),
);
env.variables_mut().insert("external_func", closure.into());
let script = r#"
external_func.(6, 2)
"#;
let parsed = ExpressionFile::parse(script).unwrap();
let result = ExpressionFile::eval(parsed, &mut env);
assert_eq!(result, Ok(12.into()))
use expression_parser::{Closure, Env, Environment, Error, Expression, ExpressionFile};
use std::sync::Arc;
let closure = Closure::new(
vec!["map".to_string(), "key".to_string()],
Arc::new(Box::new(|vars, context| {
let map = vars
.get(0)
.ok_or(Error::new_static("expect a map as the first argument"))?
// probably do something more smart than just clone
.clone()
.as_map()
.ok_or(Error::new_static("expect a map as the first argument"))?;
let key = vars
.get(1)
.ok_or(Error::new_static("expect a key as the second argument"))?
.as_string()
.ok_or(Error::new_static("expect a key as the second argument"))?;
// get access to the underlying HashMap
let result = map.0.get(&key).ok_or(Error::new_static("key not found"))?;
// the result is an expression, these can include variables ect.
// We can just match on the value or eval the Expression with the current context.
let result = Expression::eval(result.clone(), &mut Box::new(context))?;
Ok(result)
})),
);
let mut env = Environment::default();
env.variables_mut().insert("map_get", closure.into());
let script = r#"
map_get.(
{"test": "some_value"},
"test"
)
"#;
let result = ExpressionFile::run(script, &mut env);
assert_eq!(result, Ok("some_value".into()));
let script = r#"
text = "some_variable";
map_get.(
{"test": text},
"test"
)
"#;
let result = ExpressionFile::run(script, &mut env);
assert_eq!(result, Ok("some_variable".into()));
Use your own variables:
use expression_parser::{Environment, ExpressionFile, ExpressionValue};
use std::collections::HashMap;
// if you like python keywords better than the names I picked
let mut my_variables = HashMap::new();
my_variables.insert(String::from("True"), ExpressionValue::Bool(true));
my_variables.insert(String::from("False"), ExpressionValue::Bool(false));
my_variables.insert(String::from("None"), ExpressionValue::Null);
// `Environment::builder` lets you configure your environment yourself.
let mut env = Environment::builder()
.with_variables(Box::new(my_variables))
.build();
let input = r#"
a = True and True;
b = False or True;
c = None or True;
all(a, b, c)
"#;
let parsed_expression = ExpressionFile::parse(input).unwrap();
let output = ExpressionFile::eval(parsed_expression, &mut env).unwrap();
assert_eq!(output, true.into());
Or extend the default variables:
use expression_parser::{Environment, ExpressionFile, VariableMap, Variables};
// `Variables::default` returns a `VariableMap` with all the default variables
let mut my_variables = Variables::default();
// so this allready exists in the variables.
assert!(my_variables.insert("true", true.into()).is_some());
my_variables.insert("DATA", vec![1, 5, 1].into());
my_variables.insert("SOME", true.into());
// `Environment::builder` lets you configure your environment yourself.
let mut env = Environment::builder()
.with_variables(Box::new(my_variables))
.build();
let input = r#"
// returns DATA because SOME is true
if(SOME, DATA, error("data not found"))
"#;
let parsed_expression = ExpressionFile::parse(input).unwrap();
let output = ExpressionFile::eval(parsed_expression, &mut env).unwrap();
assert_eq!(output, vec![1, 5, 1].into());