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}