pub mod ast;
pub mod bytecode;
pub mod parser;
pub mod typecheck;
pub mod vm;
use crate::location::state::LocationView;
use crate::{
scoring::{BuildErr, Scorer, ScorerBuilder, ScorerSpec},
Schema,
};
pub struct PredicateBuilder;
pub struct PredicateScorer {
program: bytecode::Program,
}
impl Scorer for PredicateScorer {
fn score(&self, state: &LocationView<'_>) -> f32 {
vm::run(&self.program, state)
}
}
impl ScorerBuilder for PredicateBuilder {
type Scorer = PredicateScorer;
fn build(&self, spec: &ScorerSpec<'_>, schema: &Schema) -> Result<Self::Scorer, BuildErr> {
let src = match spec {
ScorerSpec::Predicate(s) => *s,
ScorerSpec::Vector { .. } => {
return Err(BuildErr::Type(
"PredicateBuilder requires ScorerSpec::Predicate".into(),
))
}
};
let ast = parser::Parser::new(src).parse().map_err(BuildErr::Parse)?;
let program = typecheck::compile(&ast, schema)?;
Ok(PredicateScorer { program })
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::event::{AttrSet, KindRef};
use crate::location::state::LocationState;
use crate::schema::attr::Value;
use crate::schema::{AttrType, SchemaBuilder};
use smallvec::smallvec;
#[test]
fn end_to_end_predicate_backend() {
let mut b = SchemaBuilder::new();
let _kid_builder = b.kind(
"audience",
&[("male_frac", AttrType::F32), ("dwell", AttrType::Int)],
);
let schema = b.build();
let scorer = PredicateBuilder
.build(
&ScorerSpec::Predicate("$audience.male_frac * $audience.dwell"),
&schema,
)
.unwrap();
let mut st: LocationState<()> = LocationState::new(schema.clone());
let kid = schema.kind("audience").unwrap();
let attrs = AttrSet {
entries: smallvec![
(schema.attr("male_frac").unwrap(), Value::F32(0.4)),
(schema.attr("dwell").unwrap(), Value::Int(30)),
],
};
st.apply_update(KindRef::Id(kid), &attrs);
assert!((scorer.score(&st.view()) - 12.0).abs() < 1e-4);
}
}