// Claimr grammar — the AUTHORITATIVE definition of the language.
// ================================================================
// Parsed by rustemo (LR(1)); the parser is generated at build time (see
// build.rs). `docs/reference/grammar.md` is a rendered view of this file —
// change the grammar here first, then the actions, then add an example under
// examples/ (the test suite parses every `.claimr` file there).
//
// Faithful transcription of the original EBNF (Prolog III inspired). Two
// notational differences from the EBNF, neither changing the language:
// * `rule` and `constraint_rule` share one syntactic shape (a body whose
// goals may include `{ … }` constraint blocks); the action classifies the
// clause as Rule or ConstraintRule by whether the body contains a
// constraint goal.
// * a query's `"{" constraint_expr "}"` alternative is a body with a single
// constraint goal, so `Query` simply takes a `Body`.
// Whitespace and `%` line comments are layout (see the Layout rule) and may
// appear anywhere between tokens.
// Arithmetic operators are term constructors admissible anywhere a term goes
// (Prolog III); see docs/design/2026-08-17-arithmetic-in-terms.md.
// ---------------------------------------------------------------------------
// Top-level structure
// ---------------------------------------------------------------------------
Program: clauses=Clause*;
Clause: Fact | Rule | ConstraintFact | Implication | Query;
// ---------------------------------------------------------------------------
// Clauses
// ---------------------------------------------------------------------------
Fact: head=Atom '.';
Rule: head=Atom ':-' body=Body '.';
ConstraintFact: '{' constraints=ConstraintExpr '}' '.';
// Implication syntax (optional sugar): { c } => head.
Implication: '{' constraints=ConstraintExpr '}' '=>' head=Atom '.';
Query: '?-' body=Body '.';
// ---------------------------------------------------------------------------
// Bodies and goals
// ---------------------------------------------------------------------------
Body: goals=Goal+[Comma];
Goal: atom=Atom {AtomGoal}
| '{' constraints=ConstraintExpr '}' {ConstraintGoal}
;
// ---------------------------------------------------------------------------
// Constraints
// ---------------------------------------------------------------------------
ConstraintExpr: terms=Constraint+[Comma];
Constraint: left=Expr op=RelOp right=Expr;
RelOp: '=' {Eq}
| '!=' {Neq}
| '<' {Lt}
| '>' {Gt}
| '<=' {Le}
| '>=' {Ge}
;
// ---------------------------------------------------------------------------
// Atoms and terms
// ---------------------------------------------------------------------------
Atom: name=Ident '(' args=Args? ')';
Args: exprs=Expr+[Comma];
// Terms, including arithmetic. Precedence: unary minus > `* /` > `+ -`;
// binary operators are left-associative. Arithmetic is admitted wherever a
// term is (atom arguments, constraint operands); linearity is a solver
// concern, not a grammar one.
Expr: left=Expr '+' right=Expr {Add, left, 1}
| left=Expr '-' right=Expr {Sub, left, 1}
| left=Expr '*' right=Expr {Mul, left, 2}
| left=Expr '/' right=Expr {Div, left, 2}
| '-' operand=Expr {Neg, 3}
| '(' inner=Expr ')' {Paren}
| atom=Atom {AtomExpr}
| var=Var {VarExpr}
| number=Number {NumberExpr}
| name=Ident {IdentExpr}
;
// ---------------------------------------------------------------------------
// Layout: whitespace and line comments (`% ...` to end of line)
// ---------------------------------------------------------------------------
Layout: LayoutItem*;
LayoutItem: WS | Comment;
// ---------------------------------------------------------------------------
// Terminals
// ---------------------------------------------------------------------------
terminals
// identifier ::= letter { letter | digit | "_" }, lowercase-initial;
// variable ::= uppercase_letter { letter | digit | "_" }
Ident: /[a-z][A-Za-z0-9_]*/;
Var: /[A-Z][A-Za-z0-9_]*/;
// number ::= digit { digit } [ "." digit { digit } ]
// Decimal literals denote EXACT rationals (18.5 is 37/2); there are no floats.
Number: /[0-9]+(\.[0-9]+)?/;
Neck: ':-';
QueryPrefix: '?-';
Implies: '=>';
Dot: '.';
Comma: ',';
LParen: '(';
RParen: ')';
LBrace: '{';
RBrace: '}';
Eq: '=';
Neq: '!=';
Lt: '<';
Gt: '>';
Le: '<=';
Ge: '>=';
Plus: '+';
Minus: '-';
Star: '*';
Slash: '/';
// Layout terminals
WS: /\s+/;
Comment: /%[^\n]*/;