// 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 | Directive;
// ---------------------------------------------------------------------------
// 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 '.';
// CLM-0027 (docs/design/2026-11-10-tabling-for-recursive-rules.md): a
// directive — `:- table ancestor/2.` declares tabled predicates. The
// directive name is any identifier here and is checked in the compile
// step; a rule's `:-` follows an atom, a directive's begins the clause.
Directive: ':-' name=Ident indicators=Indicator+[Comma] '.';
Indicator: name=Name '/' arity=Number;
// ---------------------------------------------------------------------------
// Bodies and goals
// ---------------------------------------------------------------------------
// CLM-0022 (docs/design/2026-11-09-negation-disjunction-and-if-then-else.md):
// ISO control in bodies. `,` binds tightest, then `->`, then `;`, which is
// right-associative; a `;` whose left operand is `C -> T` is an
// if-then-else, any other `;` a disjunction; `C -> T` alone is an if-then
// whose else fails. `\+` applies to one goal; parentheses group a body as a
// goal and leave no trace. Semantics in the record: a condition's external
// variables must be ground when the construct is reached.
Body: alt=Alt ';' rest=Body {Or}
| alt=Alt {One}
;
Alt: cond=Conj '->' then=Conj {IfThen}
| conj=Conj {Plain}
;
Conj: goals=Goal+[Comma];
Goal: atom=Atom {AtomGoal}
| '{' constraints=ConstraintExpr '}' {ConstraintGoal}
| NotOp goal=Goal {NotGoal}
| '(' body=Body ')' {GroupGoal}
;
// ---------------------------------------------------------------------------
// Constraints
// ---------------------------------------------------------------------------
ConstraintExpr: terms=Constraint+[Comma];
Constraint: left=Expr op=RelOp right=Expr;
RelOp: '=' {Eq}
| '!=' {Neq}
| '<' {Lt}
| '>' {Gt}
| '<=' {Le}
| '>=' {Ge}
;
// ---------------------------------------------------------------------------
// Atoms and terms
// ---------------------------------------------------------------------------
// CLM-0011: a name is a plain identifier or a quoted atom — identity
// is content, quoting is spelling (docs/design/2026-09-10-quoted-atoms.md).
Name: ident=Ident {PlainName}
| quoted=QuotedIdent {QuotedName}
;
Atom: name=Name '(' args=Args? ')';
Args: exprs=Expr+[Comma];
// Terms, including arithmetic. Precedence: unary minus > `* /` > `+ -` >
// list concatenation `.`; arithmetic operators are left-associative, `.` is
// right-associative. Arithmetic is admitted wherever a term is (atom
// arguments, constraint operands); linearity is a solver concern, not a
// grammar one.
// CLM-0014: Prolog III lists (docs/design/2026-11-09-lists-as-a-constraint-
// domain.md, stage 1). `<>` is the empty list, `<a, b, c>` a list, and
// `<a, b> . T` the chain a, b then T. CLM-0015 (stage 2): `|L|` is the
// length of the list L, a numeric term; the bar opens and closes, and nests
// (`||L||` is legal). CLM-0016 (stage 3): `X . Y` is the concatenation of
// two lists, right-associative and looser than arithmetic; with a literal
// tuple on the left it is the chain of stage 1. CLM-0017 (docs/design/
// 2026-11-09-strings-as-character-lists-and-type-relations.md): a string
// literal "abc" denotes the list of one-character atoms <a, b, c>; it is not
// a term kind of its own. CLM-0025 (docs/design/2026-11-09-aggregation-
// list-comprehensions-and-sum.md): `<T | Body>` is a list comprehension,
// the list of the instances of T for each solution of Body; Expr and Body
// are mutually recursive through it, and the bar after a complete
// template inside an unclosed `<` can only open the body. `<` `>` keep
// their relational meaning in Constraint and `.` its terminator meaning
// after a clause: an Expr never meets either in the same LR state (zero
// conflicts, checked with rcomp).
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=Name {IdentExpr}
| tuple=Tuple {ListExpr}
| left=Expr '.' right=Expr {ConcatExpr, right, 0}
| '|' inner=Expr '|' {LenExpr}
| string=StringLit {StringExpr}
| '<' template=Expr '|' body=Body '>' {ComprehensionExpr}
;
// A literal tuple (its own rule since CLM-0014; kept so the actions see the
// tuple as a unit).
Tuple: '<' items=Args? '>';
// ---------------------------------------------------------------------------
// 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 | "_" } | "_"
// CLM-0020: `_` is the anonymous variable (docs/design/2026-11-09-anonymous-
// variable.md): a fresh variable at every occurrence, never an answer
// variable. `_foo` is not a name: it lexes as `_` then an identifier the
// grammar does not expect there.
Ident: /[a-z][A-Za-z0-9_]*/;
// CLM-0011: quoted atom — any content, backslash escapes the next
// character (\' is a quote, \\ a backslash).
QuotedIdent: /'(?:[^'\\]|\\.)*'/;
Var: /(?:[A-Z][A-Za-z0-9_]*|_)/;
// CLM-0017: string literal — same escapes as a quoted atom (\" is a quote,
// \\ a backslash); denotes the list of its characters as atoms.
StringLit: /"(?:[^"\\]|\\.)*"/;
// 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: '/';
Bar: '|';
// CLM-0022: control in bodies.
Semi: ';';
Arrow: '->';
NotOp: /\\\+/;
// Layout terminals
WS: /\s+/;
Comment: /%[^\n]*/;