#[derive(Clone, Debug, PartialEq)]
pub enum Term {
Var(String),
Int(i64),
Bool(bool),
Str(String),
Field(u64),
Addr(Addr),
List(Vec<Term>),
Call(Box<Call>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Addr {
Particle(String),
Neuron(String),
Name(String),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Call {
pub sibling: Option<String>, pub func: String,
pub args: Vec<Term>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Binds {
Named(Vec<(String, Term)>),
Pos(Vec<Term>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Atom {
Read { rel: String, binds: Binds },
Apply { rule: String, args: Vec<Term> },
Cond(Call),
Bind { var: String, term: Term },
Not(Box<Atom>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum HeadArg {
Var(String),
Aggr { op: String, var: String },
}
#[derive(Clone, Debug, PartialEq)]
pub struct Head {
pub name: Option<String>,
pub args: Vec<HeadArg>,
}
impl Head {
pub fn has_aggr(&self) -> bool {
self.args.iter().any(|a| matches!(a, HeadArg::Aggr { .. }))
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Fixed {
pub algo: String,
pub edges: String,
pub params: Vec<(String, Term)>,
pub pos: Vec<Term>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Rule {
pub head: Head,
pub body: Vec<Atom>,
pub bound: Option<u64>,
pub fixed: Option<Fixed>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Mutation {
Link(Binds),
Unlink(Binds),
Put { rel: String, binds: Binds },
Rm { rel: String, binds: Binds },
}
#[derive(Clone, Debug, PartialEq)]
pub enum Dir {
In,
Out,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Type {
Named(String),
Array(Box<Type>, u64),
Record(Vec<(String, Type)>),
}
#[derive(Clone, Debug, PartialEq)]
pub struct Decl {
pub dir: Dir,
pub name: String,
pub ty: Type,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Opt {
Limit(u64),
Offset(u64),
Sort { col: String, desc: bool },
AssertNone,
AssertSome,
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct Program {
pub decls: Vec<Decl>,
pub rules: Vec<Rule>,
pub mutation: Option<Mutation>,
pub opts: Vec<Opt>,
pub subscribe: Option<(String, Binds)>,
}
pub const ENTRY: &str = "?";
impl Program {
pub fn entry(&self) -> Option<&Rule> {
self.rules.iter().find(|r| r.head.name.is_none())
}
}
#[derive(Clone, Debug)]
pub struct Stratum {
pub rules: Vec<Rule>,
pub recursive: bool,
pub bound: Option<u64>,
}
#[derive(Clone, Debug)]
pub struct IrProgram {
pub strata: Vec<Stratum>,
pub entry: String,
pub mutation: Option<Mutation>,
pub opts: Vec<Opt>,
pub subscribe: Option<(String, Binds)>,
}
impl Atom {
pub fn rel_name(&self) -> Option<&str> {
match self {
Atom::Read { rel, .. } => Some(rel),
Atom::Apply { rule, .. } => Some(rule),
Atom::Not(inner) => inner.rel_name(),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn entry_lookup() {
let p = Program {
rules: vec![
Rule {
head: Head { name: Some("r".into()), args: vec![HeadArg::Var("x".into())] },
body: vec![],
bound: None,
fixed: None,
},
Rule {
head: Head { name: None, args: vec![HeadArg::Var("x".into())] },
body: vec![Atom::Apply { rule: "r".into(), args: vec![Term::Var("x".into())] }],
bound: None,
fixed: None,
},
],
decls: vec![],
mutation: None,
opts: vec![Opt::Limit(20)],
subscribe: None,
};
assert!(p.entry().is_some());
assert!(!p.entry().unwrap().head.has_aggr());
}
#[test]
fn aggr_head_detected() {
let h = Head {
name: None,
args: vec![HeadArg::Var("n".into()), HeadArg::Aggr { op: "count".into(), var: "p".into() }],
};
assert!(h.has_aggr());
}
}