claimr 0.5.0

Claimr — a constraint logic programming language, Prolog III inspired, parsed with a rustemo-generated LR parser.
// 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]*/;