pub mod annotation;
pub mod ast;
pub mod audit;
mod datetime;
pub mod debug;
mod document;
pub mod error;
mod eval;
pub mod functions;
mod host;
mod json;
mod lexer;
mod parser;
mod process;
mod regex;
pub mod signature;
pub mod stack;
mod value;
pub use annotation::Annotation;
pub use ast::{Ast, Node, NodeKind, Span};
pub use document::Document;
pub use error::{Diagnostic, Error, Result, Severity};
pub use functions::{Category, FunctionInfo, catalog as function_catalog};
pub use host::{HostCall, HostFunction};
pub use json::format_json;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use indexmap::IndexMap;
use serde_json::Value as JsonValue;
pub fn parse(source: &str) -> Result<Ast> {
let (raw, errors, warnings) = parser::parse(source, false);
debug_assert!(errors.is_empty());
let mut processor = process::Processor::new(source, false);
let root = processor.process_root(raw?)?;
Ok(Ast {
root,
slots: processor.slots,
diagnostics: warnings,
source: source.to_owned(),
})
}
pub fn parse_recover(source: &str) -> Ast {
let (raw, parse_errors, warnings) = parser::parse(source, true);
let mut processor = process::Processor::new(source, true);
let root = raw
.and_then(|raw| processor.process_root(raw))
.unwrap_or_else(|err| {
let span = err.span().unwrap_or_default();
processor.errors.push(err.clone());
Node::new(
NodeKind::Error {
error: Box::new(err),
},
span,
span.end,
)
});
let mut diagnostics: Vec<Diagnostic> = parse_errors
.iter()
.chain(&processor.errors)
.map(Diagnostic::from)
.collect();
diagnostics.extend(warnings);
diagnostics.sort_by_key(|d| d.span.start);
Ast {
root,
slots: processor.slots,
diagnostics,
source: source.to_owned(),
}
}
#[derive(Debug, Clone)]
pub struct Expression {
ast: Arc<Ast>,
}
impl Expression {
pub fn compile(source: &str) -> Result<Self> {
Ok(Expression {
ast: Arc::new(parse(source)?),
})
}
pub fn source(&self) -> &str {
&self.ast.source
}
pub fn ast(&self) -> &Ast {
&self.ast
}
pub fn evaluate(
&self,
input: Option<&JsonValue>,
bindings: &Bindings,
) -> Result<Option<JsonValue>> {
self.evaluate_with(input, bindings, &Options::default())
}
pub fn evaluate_with(
&self,
input: Option<&JsonValue>,
bindings: &Bindings,
options: &Options,
) -> Result<Option<JsonValue>> {
eval::evaluate(
&self.ast,
json_input(input),
bindings,
limits(options),
None,
)
}
pub fn evaluate_document(
&self,
document: &Document,
bindings: &Bindings,
options: &Options,
) -> Result<Option<JsonValue>> {
eval::evaluate(
&self.ast,
document.value().clone(),
bindings,
limits(options),
None,
)
}
pub fn evaluate_traced(
&self,
input: Option<&JsonValue>,
bindings: &Bindings,
options: &Options,
tracer: &mut dyn audit::Tracer,
) -> Result<Option<JsonValue>> {
eval::evaluate(
&self.ast,
json_input(input),
bindings,
limits(options),
Some(tracer),
)
}
pub fn evaluate_audited(
&self,
input: Option<&JsonValue>,
bindings: &Bindings,
options: &Options,
audit: &audit::AuditOptions,
) -> (Result<Option<JsonValue>>, audit::AuditTree) {
let mut collector = audit::AuditCollector::new(*audit, input);
let result = self.evaluate_traced(input, bindings, options, &mut collector);
(result, collector.finish())
}
}
fn json_input(input: Option<&JsonValue>) -> value::Value {
input.map_or(value::Value::Undefined, value::Value::from_json)
}
fn limits(options: &Options) -> eval::Limits {
eval::Limits {
max_depth: options.max_depth.unwrap_or(eval::DEFAULT_MAX_DEPTH),
timeout: options.timeout,
max_string_length: options
.max_string_length
.unwrap_or(eval::DEFAULT_MAX_STRING_LENGTH),
max_array_length: options
.max_array_length
.unwrap_or(eval::DEFAULT_MAX_ARRAY_LENGTH),
cancel_token: options.cancel_token.clone(),
now: options.now,
seed: options.seed,
}
}
#[derive(Debug, Clone, Default)]
pub struct Bindings {
vars: IndexMap<String, JsonValue>,
functions: IndexMap<String, HostFunction>,
}
impl Bindings {
pub fn new() -> Self {
Self::default()
}
pub fn var(mut self, name: impl Into<String>, value: impl Into<JsonValue>) -> Self {
let name = name.into();
self.functions.shift_remove(&name);
self.vars.insert(name, value.into());
self
}
pub fn function(mut self, name: impl Into<String>, function: HostFunction) -> Self {
let name = name.into();
self.vars.shift_remove(&name);
self.functions.insert(name, function);
self
}
pub fn get(&self, name: &str) -> Option<&JsonValue> {
self.vars.get(name)
}
pub fn get_function(&self, name: &str) -> Option<&HostFunction> {
self.functions.get(name)
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &JsonValue)> {
self.vars.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn functions(&self) -> impl Iterator<Item = (&str, &HostFunction)> {
self.functions.iter().map(|(k, f)| (k.as_str(), f))
}
}
impl FromIterator<(String, JsonValue)> for Bindings {
fn from_iter<T: IntoIterator<Item = (String, JsonValue)>>(iter: T) -> Self {
Bindings {
vars: iter.into_iter().collect(),
functions: IndexMap::new(),
}
}
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct Options {
pub timeout: Option<Duration>,
pub max_depth: Option<usize>,
pub now: Option<i64>,
pub seed: Option<u64>,
pub cancel_token: Option<CancelToken>,
pub max_string_length: Option<usize>,
pub max_array_length: Option<usize>,
}
impl Options {
pub fn new() -> Self {
Self::default()
}
pub fn now(mut self, millis: i64) -> Self {
self.now = Some(millis);
self
}
pub fn seed(mut self, seed: u64) -> Self {
self.seed = Some(seed);
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn max_depth(mut self, max_depth: usize) -> Self {
self.max_depth = Some(max_depth);
self
}
pub fn max_string_length(mut self, bytes: usize) -> Self {
self.max_string_length = Some(bytes);
self
}
pub fn max_array_length(mut self, items: usize) -> Self {
self.max_array_length = Some(items);
self
}
pub fn cancel_token(mut self, token: CancelToken) -> Self {
self.cancel_token = Some(token);
self
}
}
#[derive(Debug, Clone, Default)]
pub struct CancelToken(Arc<AtomicBool>);
impl CancelToken {
pub fn new() -> Self {
Self::default()
}
pub fn cancel(&self) {
self.0.store(true, Ordering::Relaxed);
}
pub fn is_cancelled(&self) -> bool {
self.0.load(Ordering::Relaxed)
}
}