slac/
lib.rs

1//! The **Simple Logic & Arithmetic Compiler** is a library to convert an single
2//! expression statement into a structured [`Expression`] [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
3//!
4//! The AST can be validated, (de)serialized, and executed using the built-in interpreter.
5//!
6//! # Example
7//! ```
8//! use slac::{check_variables_and_functions, compile, execute, StaticEnvironment, Value};
9//! use slac::stdlib::extend_environment;
10//!
11//! let ast = compile("max(10, 20) + 1").expect("compiles the ast");
12//! let mut env = StaticEnvironment::default();
13//!
14//! extend_environment(&mut env);
15//! check_variables_and_functions(&env, &ast).expect("find the usage of max");
16//!
17//! let result = execute(&env, &ast).expect("execute the expression");
18//! assert_eq!(Value::Number(21.0), result);
19//! ```
20//!
21//! # Serialization / Deserialization
22//!
23//! The [`Expression`] can be fully serialized into an (e.g.) JSON string for precompilation
24//! and cached execution using [serde](https://crates.io/crates/serde). See `test/serde_test.rs`
25//! for the resulting JSON.
26
27mod ast;
28mod compiler;
29pub mod environment;
30mod error;
31pub mod function;
32mod interpreter;
33mod operator;
34pub mod optimizer;
35mod scanner;
36pub mod stdlib;
37mod token;
38mod validate;
39mod value;
40
41use crate::environment::Environment;
42
43#[doc(inline)]
44pub use crate::ast::Expression;
45#[doc(inline)]
46pub use crate::compiler::Compiler;
47#[doc(inline)]
48pub use crate::environment::StaticEnvironment;
49#[doc(inline)]
50pub use crate::error::{Error, Result};
51#[doc(inline)]
52pub use crate::operator::Operator;
53#[doc(inline)]
54pub use crate::optimizer::optimize;
55#[doc(inline)]
56pub use crate::scanner::Scanner;
57#[doc(inline)]
58pub use crate::token::Token;
59#[doc(inline)]
60pub use crate::validate::{check_boolean_result, check_variables_and_functions};
61#[doc(inline)]
62pub use crate::value::Value;
63
64/// Compiles a string into an [`Expression`] tree.
65///
66/// # Errors
67/// Returns an [`Error`] when encountering invalid Input.
68///
69/// # Examples
70/// ```
71/// use slac::{compile, Expression, Operator, Token, Value};
72/// let ast = compile("10 + 20 >= 30");
73/// let expected = Expression::Binary {
74///     left: Box::new(Expression::Binary {
75///         left: Box::new(Expression::Literal {
76///             value : Value::Number(10.0)
77///         }),
78///         right: Box::new(Expression::Literal {
79///             value : Value::Number(20.0)
80///         }),
81///         operator: Operator::Plus,
82///     }),
83///     right: Box::new(Expression::Literal {
84///         value : Value::Number(30.0)
85///     }),
86///     operator: Operator::GreaterEqual,
87/// };
88///
89/// assert_eq!(ast, Ok(expected));
90/// ```
91pub fn compile(source: &str) -> Result<Expression> {
92    let tokens = Scanner::tokenize(source)?;
93    let ast = Compiler::compile_ast(tokens)?;
94
95    Ok(ast)
96}
97
98/// Executes an [`Expression`] using an [`Environment`].
99///
100/// # Example
101/// ```
102/// use slac::{Expression, Operator, Value};
103/// use slac::{execute, StaticEnvironment};
104///
105/// let env = StaticEnvironment::default();
106/// let ast = Expression::Binary {
107///     left: Box::new(Expression::Literal {
108///         value: Value::Number(40.0),
109///     }),
110///     right: Box::new(Expression::Literal {
111///         value: Value::Number(2.0),
112///     }),
113///     operator: Operator::Plus,
114/// };
115///
116/// assert_eq!(Ok(Value::Number(42.0)), execute(&env, &ast));
117/// ```
118///
119/// # Remarks
120/// * Currently uses an `TreeWalkingInterpreter` to evaluate the AST.
121/// * Will [short-circuit](https://en.wikipedia.org/wiki/Short-circuit_evaluation) boolean expression.
122/// * Invalid operations will be evaluated to an [`Error`].
123/// * Comparison of empty Values ([`Value::empty()`]) against [`Error::UndefinedVariable`] is a valid operation
124///   * e.g: `undefined_var = ''` is valid
125///
126/// # Errors
127///
128/// Returns [`Error`] when encountering an error at runtime.
129pub fn execute(env: &impl Environment, ast: &Expression) -> Result<Value> {
130    interpreter::TreeWalkingInterpreter::interprete(env, ast)
131}