Skip to main content

xlog_logic/
eir.rs

1//! Frontend construction for the Epistemic Intermediate Representation.
2
3use xlog_core::Result;
4use xlog_ir::{
5    EirAtom, EirBodyLiteral, EirConstraint, EirEpistemicLiteral, EirEpistemicMode, EirEpistemicOp,
6    EirProgram, EirRule, EirTerm,
7};
8
9use crate::ast::{
10    AggOp, Atom, BodyLiteral, Constraint as AstConstraint, EpistemicMode, EpistemicOp, Program,
11    Rule as AstRule, Term,
12};
13
14/// Build EIR from parsed frontend AST without lowering to RIR.
15pub fn build_eir(program: &Program) -> Result<EirProgram> {
16    Ok(EirProgram {
17        mode: convert_mode(program.directives.epistemic_mode_or_default()),
18        rules: program.rules.iter().map(convert_rule).collect(),
19        constraints: program.constraints.iter().map(convert_constraint).collect(),
20    })
21}
22
23fn convert_constraint(constraint: &AstConstraint) -> EirConstraint {
24    EirConstraint {
25        authored_index: constraint.authored_index,
26        body: constraint.body.iter().map(convert_body_literal).collect(),
27    }
28}
29
30fn convert_rule(rule: &AstRule) -> EirRule {
31    EirRule {
32        head: convert_atom(&rule.head),
33        body: rule.body.iter().map(convert_body_literal).collect(),
34    }
35}
36
37fn convert_body_literal(lit: &BodyLiteral) -> EirBodyLiteral {
38    match lit {
39        BodyLiteral::Positive(atom) => EirBodyLiteral::Relational {
40            negated: false,
41            atom: convert_atom(atom),
42        },
43        BodyLiteral::Negated(atom) => EirBodyLiteral::Relational {
44            negated: true,
45            atom: convert_atom(atom),
46        },
47        BodyLiteral::Epistemic(lit) => EirBodyLiteral::Epistemic(EirEpistemicLiteral {
48            op: convert_op(lit.op),
49            negated: lit.negated,
50            atom: convert_atom(&lit.atom),
51        }),
52        BodyLiteral::Comparison(_) => EirBodyLiteral::Constraint,
53        BodyLiteral::IsExpr(_) => EirBodyLiteral::Binding,
54        BodyLiteral::Univ(_) => EirBodyLiteral::Binding,
55    }
56}
57
58fn convert_atom(atom: &Atom) -> EirAtom {
59    EirAtom {
60        predicate: atom.predicate.clone(),
61        arity: atom.arity(),
62        terms: atom.terms.iter().map(convert_term).collect(),
63    }
64}
65
66pub(crate) fn convert_term(term: &Term) -> EirTerm {
67    match term {
68        Term::Variable(name) => EirTerm::Variable(name.clone()),
69        Term::Anonymous => EirTerm::Anonymous,
70        Term::Integer(value) => EirTerm::Integer(*value),
71        Term::Float(value) => EirTerm::FloatBits(value.to_bits()),
72        Term::String(value) => EirTerm::String(value.clone()),
73        Term::Symbol(id) => EirTerm::Symbol(*id),
74        Term::List(items) => EirTerm::List(items.iter().map(convert_term).collect()),
75        Term::Cons { head, tail } => EirTerm::Cons {
76            head: Box::new(convert_term(head)),
77            tail: Box::new(convert_term(tail)),
78        },
79        Term::Compound { functor, args } => EirTerm::Compound {
80            functor: functor.clone(),
81            args: args.iter().map(convert_term).collect(),
82        },
83        Term::PredRef(name) => EirTerm::PredRef(name.clone()),
84        Term::Aggregate(agg) => EirTerm::Aggregate {
85            op: convert_agg_op(agg.op).to_string(),
86            variable: agg.variable.clone(),
87        },
88    }
89}
90
91fn convert_agg_op(op: AggOp) -> &'static str {
92    match op {
93        AggOp::Count => "count",
94        AggOp::Sum => "sum",
95        AggOp::Min => "min",
96        AggOp::Max => "max",
97        AggOp::LogSumExp => "logsumexp",
98    }
99}
100
101fn convert_mode(mode: EpistemicMode) -> EirEpistemicMode {
102    match mode {
103        EpistemicMode::G91 => EirEpistemicMode::G91,
104        EpistemicMode::Faeel => EirEpistemicMode::Faeel,
105    }
106}
107
108fn convert_op(op: EpistemicOp) -> EirEpistemicOp {
109    match op {
110        EpistemicOp::Know => EirEpistemicOp::Know,
111        EpistemicOp::Possible => EirEpistemicOp::Possible,
112    }
113}