pub mod arena;
mod ast;
mod error;
mod lexer;
mod parser;
#[cfg(test)]
mod tests;
mod token;
mod typing;
use crate::arena::Arena;
use crate::lexer::tokenize;
use crate::prelude::{
Analysis, AnalysisOptions, FunArgs, Scope, Typed, display_type, parse, resolve_type_from_str,
};
use crate::token::Token;
use crate::typing::TypeRef;
pub use ast::*;
use rustc_hash::FxHashMap;
pub use typing::Type;
pub mod prelude {
pub use super::arena::*;
pub use super::ast::*;
pub use super::error::*;
pub use super::parser::*;
pub use super::token::*;
pub use super::typing::analysis::*;
pub use super::typing::*;
}
pub struct FunArgsBuilder<'a> {
args: &'a [Type],
required: usize,
}
impl<'a> FunArgsBuilder<'a> {
pub fn new(args: &'a [Type], required: usize) -> Self {
Self { args, required }
}
}
impl<'a> From<&'a [Type]> for FunArgsBuilder<'a> {
fn from(args: &'a [Type]) -> Self {
Self {
args,
required: args.len(),
}
}
}
impl<'a, const N: usize> From<&'a [Type; N]> for FunArgsBuilder<'a> {
fn from(value: &'a [Type; N]) -> Self {
Self {
args: value.as_slice(),
required: value.len(),
}
}
}
pub struct EventTypeBuilder<'a> {
parent: &'a mut SessionBuilder,
}
impl<'a> EventTypeBuilder<'a> {
pub fn define_record(self) -> EventTypeRecordBuilder<'a> {
EventTypeRecordBuilder {
inner: self,
props: Default::default(),
}
}
pub fn data_source(self, data_source: &str, tpe: Type) -> Self {
let data_source = self.parent.arena.strings.alloc_no_case(data_source);
self.parent.options.data_sources.insert(data_source, tpe);
self
}
pub fn done(self) {}
}
pub struct EventTypeRecordBuilder<'a> {
inner: EventTypeBuilder<'a>,
props: FxHashMap<StrRef, Type>,
}
impl<'a> EventTypeRecordBuilder<'a> {
pub fn prop_when(mut self, test: bool, name: &str, tpe: Type) -> Self {
if test {
self.props
.insert(self.inner.parent.arena.strings.alloc(name), tpe);
}
self
}
pub fn prop(mut self, name: &str, tpe: Type) -> Self {
self.props
.insert(self.inner.parent.arena.strings.alloc(name), tpe);
self
}
pub fn as_default_event_type(self) -> EventTypeBuilder<'a> {
let ptr = self.inner.parent.arena.types.alloc_record(self.props);
self.inner.parent.set_default_event_type(Type::Record(ptr));
self.inner
}
pub fn for_data_source(self, data_source: &str) -> EventTypeBuilder<'a> {
let data_source = self.inner.parent.arena.strings.alloc_no_case(data_source);
let ptr = self.inner.parent.arena.types.alloc_record(self.props);
self.inner
.parent
.options
.data_sources
.insert(data_source, Type::Record(ptr));
self.inner
}
pub fn build(self) -> (Type, TypeRef) {
let ptr = self.inner.parent.arena.types.alloc_record(self.props);
let tpe = Type::Record(ptr);
let type_ref = self.inner.parent.arena.types.register_type(tpe);
(tpe, type_ref)
}
}
pub type Result<A> = std::result::Result<A, error::Error>;
#[derive(Default)]
pub struct SessionBuilder {
arena: Arena,
options: AnalysisOptions,
}
impl SessionBuilder {
pub fn declare_func<'a>(
&mut self,
name: &'a str,
args: impl Into<FunArgsBuilder<'a>>,
result: Type,
) {
let builder = args.into();
let name = self.arena.strings.alloc_no_case(name);
let args = self.arena.types.alloc_args(builder.args);
self.options.default_scope.declare(
name,
Type::App {
args: FunArgs {
values: args,
needed: builder.required,
},
result: self.arena.types.register_type(result),
aggregate: false,
},
);
}
pub fn declare_agg_func<'a>(
&mut self,
name: &'a str,
args: impl Into<FunArgsBuilder<'a>>,
result: Type,
) {
let builder = args.into();
let name = self.arena.strings.alloc_no_case(name);
let args = self.arena.types.alloc_args(builder.args);
self.options.default_scope.declare(
name,
Type::App {
args: FunArgs {
values: args,
needed: builder.required,
},
result: self.arena.types.register_type(result),
aggregate: true,
},
);
}
pub fn set_default_event_type(&mut self, tpe: Type) {
self.options.default_event_type = tpe;
}
pub fn declare_type(&mut self) -> EventTypeBuilder<'_> {
EventTypeBuilder { parent: self }
}
pub fn use_stdlib(mut self) -> Self {
self.declare_func("abs", &[Type::Number], Type::Number);
self.declare_func("ceil", &[Type::Number], Type::Number);
self.declare_func("floor", &[Type::Number], Type::Number);
self.declare_func("round", &[Type::Number], Type::Number);
self.declare_func("cos", &[Type::Number], Type::Number);
self.declare_func("exp", &[Type::Number], Type::Number);
self.declare_func("pow", &[Type::Number, Type::Number], Type::Number);
self.declare_func("sqrt", &[Type::Number], Type::Number);
self.declare_func("rand", &[], Type::Number);
self.declare_func("pi", &[Type::Number], Type::Number);
self.declare_func("lower", &[Type::String], Type::String);
self.declare_func("upper", &[Type::String], Type::String);
self.declare_func("trim", &[Type::String], Type::String);
self.declare_func("ltrim", &[Type::String], Type::String);
self.declare_func("rtrim", &[Type::String], Type::String);
self.declare_func("len", &[Type::String], Type::Number);
self.declare_func("instr", &[Type::String], Type::Number);
self.declare_func(
"substring",
&[Type::String, Type::Number, Type::Number],
Type::String,
);
self.declare_func(
"replace",
&[Type::String, Type::String, Type::String],
Type::String,
);
self.declare_func("startswith", &[Type::String, Type::String], Type::Bool);
self.declare_func("endswith", &[Type::String, Type::String], Type::Bool);
self.declare_func("now", &[], Type::DateTime);
self.declare_func("year", &[Type::Date], Type::Number);
self.declare_func("month", &[Type::Date], Type::Number);
self.declare_func("day", &[Type::Date], Type::Number);
self.declare_func("hour", &[Type::Time], Type::Number);
self.declare_func("minute", &[Type::Time], Type::Number);
self.declare_func("second", &[Type::Time], Type::Number);
self.declare_func("weekday", &[Type::Date], Type::Number);
self.declare_func(
"IF",
&[Type::Bool, Type::Unspecified, Type::Unspecified],
Type::Unspecified,
);
self.declare_agg_func(
"count",
FunArgsBuilder {
args: &[Type::Bool],
required: 0,
},
Type::Number,
);
self.declare_agg_func("sum", &[Type::Number], Type::Number);
self.declare_agg_func("avg", &[Type::Number], Type::Number);
self.declare_agg_func("min", &[Type::Number], Type::Number);
self.declare_agg_func("max", &[Type::Number], Type::Number);
self.declare_agg_func("median", &[Type::Number], Type::Number);
self.declare_agg_func("stddev", &[Type::Number], Type::Number);
self.declare_agg_func("variance", &[Type::Number], Type::Number);
self.declare_agg_func("unique", &[Type::Unspecified], Type::Unspecified);
self.declare_type()
.data_source("eventtypes", Type::String)
.data_source("subjects", Type::String)
.define_record()
.prop("specversion", Type::String)
.prop("id", Type::String)
.prop("time", Type::DateTime)
.prop("source", Type::String)
.prop("subject", Type::Subject)
.prop("type", Type::String)
.prop("datacontenttype", Type::String)
.prop("data", Type::Unspecified)
.prop("predecessorhash", Type::String)
.prop("hash", Type::String)
.prop("traceparent", Type::String)
.prop("tracestate", Type::String)
.prop("signature", Type::String)
.as_default_event_type();
self
}
pub fn build(mut self) -> Session {
self.arena.types.freeze();
Session {
arena: self.arena,
options: self.options,
}
}
}
pub struct Session {
arena: Arena,
options: AnalysisOptions,
}
impl Session {
pub fn builder() -> SessionBuilder {
SessionBuilder::default()
}
pub fn tokenize<'a>(&self, input: &'a str) -> Result<Vec<Token<'a>>> {
let tokens = tokenize(input)?;
Ok(tokens)
}
pub fn parse(&mut self, input: &str) -> Result<Query<Raw>> {
let tokens = self.tokenize(input)?;
Ok(parse(&mut self.arena, tokens.as_slice())?)
}
pub fn run_static_analysis(&mut self, query: Query<Raw>) -> Result<Query<Typed>> {
let mut analysis = self.analysis();
Ok(analysis.analyze_query(query)?)
}
pub fn resolve_type(&self, name: &str) -> Option<Type> {
resolve_type_from_str(name)
}
pub fn display_type(&self, tpe: Type) -> String {
display_type(&self.arena, tpe)
}
pub fn analysis(&mut self) -> Analysis<'_> {
Analysis::new(&mut self.arena, &self.options)
}
pub fn arena(&self) -> &Arena {
&self.arena
}
pub fn arena_mut(&mut self) -> &mut Arena {
&mut self.arena
}
pub fn global_scope(&self) -> &Scope {
&self.options.default_scope
}
}