eqlog 0.9.0

Datalog with equality
Documentation
use crate::ast::*;
use crate::grammar_util::{Location, NeverType};

grammar(ast: &mut Ast);

extern {
    type Error = NeverType;
}

Id: String = {
    <s : r"[A-Za-z][A-Za-z0-9'_]*"> => s.to_string(),
}

PairFirst<First, Second>: First = {
    <first: First> <second: Second> => first,
}

NonEmptyIntersperse<Item, Separator>: Vec<Item> = {
    <mut init: PairFirst<Item, Separator>*> <last: Item> => {
        init.push(last);
        init
    },
}

Intersperse<Item, Separator>: Vec<Item> = {
    <non_empty: NonEmptyIntersperse<Item, Separator>> => non_empty,
    () => vec![],
}

Variable: TermId = {
    <l: @L> <name: Id> <r: @R> => {
        let loc = Location(l, r);
        let v = ast.push_var_term(loc, VarTerm { name });
        ast.push_term(loc, Term::Var(v))
    },
}

Wildcard: TermId = {
    <l: @L> "_" <r: @R> => ast.push_term(Location(l, r), Term::Wildcard),
}

ApplicationTerm: TermId = {
    <l: @L> <func: FuncExpr> <args: ArgList> <r: @R> => {
        let loc = Location(l, r);
        let a = ast.push_app_term(loc, AppTerm { func, args });
        ast.push_term(loc, Term::App(a))
    },
}

DomTerm: TermId = {
    <l: @L> "dom" "(" <arg: Term> ")" <r: @R> => {
        let loc = Location(l, r);
        let d = ast.push_dom_term(loc, DomTerm { arg });
        ast.push_term(loc, Term::Dom(d))
    },
}

CodTerm: TermId = {
    <l: @L> "cod" "(" <arg: Term> ")" <r: @R> => {
        let loc = Location(l, r);
        let c = ast.push_cod_term(loc, CodTerm { arg });
        ast.push_term(loc, Term::Cod(c))
    },
}

MorAppTerm: TermId = {
    <l: @L> <mor: Term> "@" "(" <arg: Term> ")" <r: @R> => {
        let loc = Location(l, r);
        let m = ast.push_mor_app_term(loc, MorAppTerm { mor, arg });
        ast.push_term(loc, Term::MorApp(m))
    },
}

Term: TermId = {
    <var: Variable> => var,
    <wildcard: Wildcard> => wildcard,
    <application: ApplicationTerm> => application,
    <dom_term: DomTerm> => dom_term,
    <cod_term: CodTerm> => cod_term,
    <app_term: MorAppTerm> => app_term,
}

TypeExpr: TypeExprId = {
    <l: @L> <name: Id> <r: @R> => {
        let loc = Location(l, r);
        let a = ast.push_ambient_type_expr(loc, AmbientTypeExpr { name });
        ast.push_type_expr(loc, TypeExpr::Ambient(a))
    },
    <l: @L> <tm: Term> "." <name: Id> <r: @R> => {
        let loc = Location(l, r);
        let m = ast.push_member_type_expr(loc, MemberTypeExpr { term: tm, name });
        ast.push_type_expr(loc, TypeExpr::Member(m))
    },
    <l: @L> "Mor" "(" <name: Id> ")" <r: @R> => {
        let loc = Location(l, r);
        let m = ast.push_mor_type_expr(loc, MorTypeExpr { name });
        ast.push_type_expr(loc, TypeExpr::Mor(m))
    },
}

#[inline]
PredExpr: PredExprId = {
    <l: @L> <name: Id> <r: @R> => {
        let loc = Location(l, r);
        let a = ast.push_ambient_pred_expr(loc, AmbientPredExpr { name });
        ast.push_pred_expr(loc, PredExpr::Ambient(a))
    },
    <l: @L> <tm: Term> "." <name: Id> <r: @R> => {
        let loc = Location(l, r);
        let m = ast.push_member_pred_expr(loc, MemberPredExpr { term: tm, name });
        ast.push_pred_expr(loc, PredExpr::Member(m))
    },
}

#[inline]
FuncExpr: FuncExprId = {
    <l: @L> <name: Id> <r: @R> => {
        let loc = Location(l, r);
        let a = ast.push_ambient_func_expr(loc, AmbientFuncExpr { name });
        ast.push_func_expr(loc, FuncExpr::Ambient(a))
    },
    <l: @L> <tm: Term> "." <name: Id> <r: @R> => {
        let loc = Location(l, r);
        let m = ast.push_member_func_expr(loc, MemberFuncExpr { term: tm, name });
        ast.push_func_expr(loc, FuncExpr::Member(m))
    },
}

ArgList: TermListId = {
    <l: @L> "(" <args: Intersperse<Term, ",">> ")" <r: @R> =>
        ast.push_term_list(Location(l, r), TermList { terms: args }),
}

IfAtom: IfAtomId = {
    <l: @L> <lhs: Term> "=" <rhs: Term> <r: @R> => {
        let loc = Location(l, r);
        let e = ast.push_equal_atom(loc, EqualAtom { lhs, rhs });
        ast.push_if_atom(loc, IfAtom::Equal(e))
    },
    <l: @L> <tm: Term> "!" <r: @R> => {
        let loc = Location(l, r);
        let d = ast.push_defined_if_atom(loc, DefinedIfAtom { term: tm });
        ast.push_if_atom(loc, IfAtom::Defined(d))
    },
    <l: @L> <pred: PredExpr> <args: ArgList> <r: @R> => {
        let loc = Location(l, r);
        let p = ast.push_pred_atom(loc, PredAtom { pred, args });
        ast.push_if_atom(loc, IfAtom::Pred(p))
    },
    <l: @L> <term: Term> ":" <typ: TypeExpr> <r: @R> => {
        let loc = Location(l, r);
        let v = ast.push_var_if_atom(loc, VarIfAtom { term, typ });
        ast.push_if_atom(loc, IfAtom::Var(v))
    },
}

ThenAtom: ThenAtomId = {
    <l: @L> <lhs: Term> "=" <rhs: Term> <r: @R> => {
        let loc = Location(l, r);
        let e = ast.push_equal_atom(loc, EqualAtom { lhs, rhs });
        ast.push_then_atom(loc, ThenAtom::Equal(e))
    },
    <l: @L> <var: PairFirst<Term, ":=">?> <term: Term> "!" <r: @R> => {
        let loc = Location(l, r);
        let d = ast.push_defined_then_atom(loc, DefinedThenAtom { var, term });
        ast.push_then_atom(loc, ThenAtom::Defined(d))
    },
    <l: @L> <pred: PredExpr> <args: ArgList> <r: @R> => {
        let loc = Location(l, r);
        let p = ast.push_pred_atom(loc, PredAtom { pred, args });
        ast.push_then_atom(loc, ThenAtom::Pred(p))
    },
}

StmtBlock: Vec<StmtId> = {
    "{" <stmts: Stmt*> "}" => stmts,
}

MatchCase: MatchCaseId = {
    <l: @L> <pattern: Term> "=>" <body: StmtBlock> <r: @R> =>
        ast.push_match_case(Location(l, r), MatchCase { pattern, body }),
}

Stmt: StmtId = {
    <l: @L> "branch" <blocks: NonEmptyIntersperse<StmtBlock, "along">> <r: @R> => {
        let loc = Location(l, r);
        let b = ast.push_branch_stmt(loc, BranchStmt { blocks });
        ast.push_stmt(loc, Stmt::Branch(b))
    },
    <l: @L> "if" <atom: IfAtom> ";" <r: @R> => {
        let loc = Location(l, r);
        let s = ast.push_if_stmt(loc, IfStmt { atom });
        ast.push_stmt(loc, Stmt::If(s))
    },
    <l: @L> "then" <atom: ThenAtom> ";" <r: @R> => {
        let loc = Location(l, r);
        let s = ast.push_then_stmt(loc, ThenStmt { atom });
        ast.push_stmt(loc, Stmt::Then(s))
    },
    <l: @L> "match" <tm: Term> "{" <cases: MatchCase*> "}" <r: @R> => {
        let loc = Location(l, r);
        let m = ast.push_match_stmt(loc, MatchStmt { term: tm, cases });
        ast.push_stmt(loc, Stmt::Match(m))
    },
}

RuleDecl: RuleDeclId = {
    <l: @L> "rule" <name: Id?> "{" <body: Stmt*> "}" <r: @R> =>
        ast.push_rule_decl(Location(l, r), RuleDecl { name, body }),
}

TypeDecl: TypeDeclId = {
    <l: @L> "type" <name: Id> ";" <r: @R> =>
        ast.push_type_decl(Location(l, r), TypeDecl { name }),
}

ArgDecl: ArgDeclId = {
    <l: @L> <name: PairFirst<Id, ":">?> <typ: TypeExpr> <r: @R> =>
        ast.push_arg_decl(Location(l, r), ArgDecl { name, typ }),
}

ArgDeclList: ArgDeclListId = {
    <l: @L> "(" <args: Intersperse<ArgDecl, ",">> ")" <r: @R> =>
        ast.push_arg_decl_list(Location(l, r), ArgDeclList { args }),
}

PredDecl: PredDeclId = {
    <l: @L> "pred" <name: Id> <args: ArgDeclList> ";" <r: @R> =>
        ast.push_pred_decl(Location(l, r), PredDecl { name, args }),
}

FuncDecl: FuncDeclId = {
    <l: @L> "func" <name: Id> <args: ArgDeclList> "->" <result: TypeExpr> ";" <r: @R> =>
        ast.push_func_decl(Location(l, r), FuncDecl { name, args, result }),
}

CtorDecl: CtorDeclId = {
    <l: @L> <name: Id> <args: ArgDeclList> <r: @R> =>
        ast.push_ctor_decl(Location(l, r), CtorDecl { name, args }),
}

EnumDecl: EnumDeclId = {
    // TODO: Let's also allow an optional comma after the last constructor. As of this writing,
    // adding a ","? there causes a shift-reduce conflict.
    <l: @L> "enum" <name: Id> "{" <ctors: Intersperse<CtorDecl, ",">> "}" <r: @R> =>
        ast.push_enum_decl(Location(l, r), EnumDecl { name, ctors }),
}

ModelDecl: ModelDeclId = {
    <l: @L> "model" <name: Id> "{" <body: Decl*> "}" <r: @R> =>
        ast.push_model_decl(Location(l, r), ModelDecl { name, body }),
}

Decl: DeclId = {
    <l: @L> <t: TypeDecl> <r: @R> => ast.push_decl(Location(l, r), Decl::Type(t)),
    <l: @L> <p: PredDecl> <r: @R> => ast.push_decl(Location(l, r), Decl::Pred(p)),
    <l: @L> <f: FuncDecl> <r: @R> => ast.push_decl(Location(l, r), Decl::Func(f)),
    <l: @L> <r_: RuleDecl> <r: @R> => ast.push_decl(Location(l, r), Decl::Rule(r_)),
    <l: @L> <e: EnumDecl> <r: @R> => ast.push_decl(Location(l, r), Decl::Enum(e)),
    <l: @L> <m: ModelDecl> <r: @R> => ast.push_decl(Location(l, r), Decl::Model(m)),
}

pub Module: ModuleId = {
    <l: @L> <decls: Decl*> <r: @R> =>
        ast.push_module(Location(l, r), Module { decls }),
}