use super::term::OTerm;
use crate::engine::grammar::SPG;
use crate::engine::grammar::load::{Def, Sym};
use crate::engine::structure::FusionNode;
use crate::typing::rule::{Conclusion, Judgment, Premise};
use crate::typing::{Atom, Context, TypeExpr, TypingRule, TypingSynth};
#[derive(ocaml::FromValue)]
pub enum OSym {
Nt(String, Option<String>),
Lit(String, Option<String>),
Re(String, Option<String>),
}
type ODef = (String, Option<String>, Vec<Vec<OSym>>);
#[derive(ocaml::FromValue)]
pub enum OAtom {
Lit(String),
Hole(String),
Ref(String),
Ctx(String),
Inst(String),
Top,
Bot,
}
#[derive(ocaml::FromValue)]
pub enum OPremise {
Ascribe(Vec<(String, Vec<OAtom>)>, String, Vec<OAtom>),
Member(String),
Equate(Vec<OAtom>, Vec<OAtom>),
}
#[derive(ocaml::FromValue)]
pub enum ORule {
Parsed(String, String, String),
Made(String, Vec<OPremise>, Vec<(String, Vec<OAtom>)>, Vec<OAtom>),
}
pub struct Gram(pub SPG);
ocaml::custom!(Gram);
#[derive(ocaml::ToValue)]
pub enum OBuild {
Built(ocaml::Pointer<Gram>),
Invalid(String),
}
#[derive(ocaml::ToValue)]
pub enum OVerdict {
Typed(OTerm),
Live,
Dead(String),
}
fn texpr(atoms: Vec<OAtom>) -> TypeExpr {
TypeExpr(
atoms
.into_iter()
.map(|a| match a {
OAtom::Lit(s) => Atom::Lit(s),
OAtom::Hole(s) => Atom::Hole(s),
OAtom::Ref(s) => Atom::Ref(s),
OAtom::Ctx(s) => Atom::Ctx(s),
OAtom::Inst(s) => Atom::Inst(s),
OAtom::Top => Atom::Top,
OAtom::Bot => Atom::Bot,
})
.collect(),
)
}
fn extensions(exts: Vec<(String, Vec<OAtom>)>) -> Vec<(String, TypeExpr)> {
exts.into_iter().map(|(x, t)| (x, texpr(t))).collect()
}
fn premise(p: OPremise) -> Premise {
match p {
OPremise::Ascribe(under, binding, t) => Premise {
extensions: extensions(under),
judgment: Judgment::Ascription {
binding,
ty: texpr(t),
},
},
OPremise::Member(binding) => Premise {
extensions: Vec::new(),
judgment: Judgment::Membership { binding },
},
OPremise::Equate(l, r) => Premise {
extensions: Vec::new(),
judgment: Judgment::Equation {
left: texpr(l),
right: texpr(r),
},
},
}
}
fn rule(r: ORule) -> Result<TypingRule, String> {
match r {
ORule::Parsed(name, premises, conclusion) => TypingRule::new(premises, conclusion, name),
ORule::Made(name, premises, effects, conclusion) => Ok(TypingRule {
name,
premises: premises.into_iter().map(premise).collect(),
conclusion: Conclusion {
ty: texpr(conclusion),
effects: extensions(effects),
},
}),
}
}
fn sym(s: OSym) -> Sym {
match s {
OSym::Nt(n, b) => Sym::Nt(n, b),
OSym::Lit(t, b) => Sym::Lit(t, b),
OSym::Re(p, b) => Sym::Re(p, b),
}
}
fn build(
defs: Vec<ODef>,
rules: Vec<ORule>,
rewrites: Vec<(String, String)>,
start: Option<String>,
ty: Option<String>,
) -> Result<SPG, String> {
let defs: Vec<Def> = defs
.into_iter()
.map(|(n, r, alts)| {
let alts = alts
.into_iter()
.map(|a| a.into_iter().map(sym).collect())
.collect();
(n, r, alts)
})
.collect();
let rules = rules.into_iter().map(rule).collect::<Result<_, _>>()?;
SPG::assemble(defs, rules, rewrites, start, ty)
}
fn built(g: Result<SPG, String>) -> OBuild {
match g {
Ok(g) => OBuild::Built(ocaml::Pointer::alloc_custom(Gram(g))),
Err(e) => OBuild::Invalid(e),
}
}
#[ocaml::func]
#[must_use]
pub fn aufbau_grammar(
defs: Vec<ODef>,
rules: Vec<ORule>,
rewrites: Vec<(String, String)>,
start: Option<String>,
ty: Option<String>,
) -> OBuild {
built(build(defs, rules, rewrites, start, ty))
}
#[ocaml::func]
#[must_use]
pub fn aufbau_load(source: String) -> OBuild {
built(SPG::load(&source))
}
#[ocaml::func]
#[must_use]
pub fn aufbau_show(g: &Gram) -> String {
g.0.to_spec_string()
}
#[ocaml::func]
#[must_use]
pub fn aufbau_completeness(g: &Gram) -> (String, Vec<String>) {
match crate::typing::completeness(&g.0) {
crate::typing::Completeness::Syntactic => ("syntactic".into(), vec![]),
crate::typing::Completeness::Inhabited => ("inhabited".into(), vec![]),
crate::typing::Completeness::Sound { uninhabited } => ("sound".into(), uninhabited),
}
}
#[ocaml::func]
#[must_use]
pub fn aufbau_check(g: &Gram, program: String) -> OVerdict {
let mut synth = TypingSynth::new(g.0.clone(), &program);
match synth.parse_with(&Context::new()) {
Err(e) => OVerdict::Dead(e),
Ok(ast) => {
let rt = synth.runtime().clone();
ast.roots()
.filter(FusionNode::is_complete)
.find_map(|r| rt.evidence_of(r.evidence()))
.map_or(OVerdict::Live, |t| OVerdict::Typed(OTerm::lift(&t)))
}
}
}