// Phenotyper v2 Grammar
// ====================
// GLR grammar for the Phenotyper structural artifact definition language.
// Parsed by Rustemo (rustemo.rs) — generates parser + default AST types.
//
// v2 changes from v1:
// - Namespace syntax: 'namespace path;' → 'path:' ... '.'
// - GLR mode: resolves Ident ':' ambiguity (namespace vs phenotype)
// - Removed 'namespace' keyword
// ---------------------------------------------------------------------------
// Top-level structure
// ---------------------------------------------------------------------------
// GLR: the parser forks on 'Ident :' — NamespaceScope terminates with '.',
// TypeDef terminates with ';'. The wrong branch is pruned structurally.
File: ns=NamespaceScope;
NamespaceScope: path=NamespacePath ':' uses=UsesDecl* decls=TopLevelDecl* '.';
NamespacePath: Ident+[Slash];
UsesDecl: 'uses' path=NamespacePath ';';
TopLevelDecl: TypeDecl | TypeDef;
// ---------------------------------------------------------------------------
// Type declarations (aliases and enums)
// ---------------------------------------------------------------------------
TypeDecl: 'type' name=Ident ':' body=TypeDeclBody ';';
TypeDeclBody: alias=TypeExpr {Alias}
| '[' members=EnumMembers ']' {Enum}
;
EnumMembers: first=Ident ',' rest=EnumMembers {Cons}
| last=Ident {Single}
;
// ---------------------------------------------------------------------------
// Phenotype (type definition) declarations
// ---------------------------------------------------------------------------
TypeDef: name=Ident plural_clause=PluralClause? ':' items=BodyItem+[Comma] ';';
PluralClause: 'plural' plural_name=Ident;
// ---------------------------------------------------------------------------
// Body items (fields and render expressions)
// ---------------------------------------------------------------------------
BodyItem: name=Ident plural_clause=PluralClause ':' items=BodyItem+[Comma] ';' {NestedTypePlural}
| name=Ident ':' items=BodyItem+[Comma] ';' {NestedType}
| name=Ident ':' req=Requiredness type_expr=TypeExpr {Field}
| render=RenderExpr {Render}
;
Requiredness: 'required' {Req} | 'optional' {Opt};
// ---------------------------------------------------------------------------
// Type expressions
// ---------------------------------------------------------------------------
TypeExpr: base=TypeName card=CardinalityOp {Cardinalized}
| '{' members=TypeName+[Comma] '}' {Union}
| name=TypeName {Simple}
;
CardinalityOp: '+' {Plus} | '*' {Star};
TypeName: name=Ident {UserDefined}
| 'string' {String}
| 'int64' {Int64}
| 'real64' {Real64}
| 'bool' {Bool}
| 'date' {Date}
| 'time' {Time}
| 'datetime' {DateTime}
;
// ---------------------------------------------------------------------------
// Render expressions
// ---------------------------------------------------------------------------
RenderExpr: '@' name=Ident suffix=DirectiveSuffix '?' block=BlockBody? {ConditionalDirective}
| '@' name=Ident suffix=DirectiveSuffix {Directive}
| '@' name=Ident {BareDirective}
| '@' '(' ref_path=FieldPath ')' '?' block=BlockBody? {ConditionalRef}
| '@' '(' ref_path=FieldPath ')' {FieldRef}
| value=StringLiteral {StringLit}
;
FieldPath: segments=Ident+[Slash];
DirectiveSuffix: '(' args=Argument+[Comma] ')' block=BlockBody? {WithArgs}
| '(' ')' block=BlockBody? {EmptyParen}
;
BlockBody: '{' items=RenderExpr+[Comma] '}';
Argument: val=Ident {IdentArg}
| val=StringLiteral {LiteralArg}
;
// ---------------------------------------------------------------------------
// Layout: whitespace and comments
// ---------------------------------------------------------------------------
Layout: LayoutItem*;
LayoutItem: WS | Comment;
Comment: '/*' Corncs '*/' | CommentLine;
Corncs: Cornc*;
Cornc: Comment | NotComment | WS;
// ---------------------------------------------------------------------------
// Terminals
// ---------------------------------------------------------------------------
terminals
Ident: /[A-Za-z_][A-Za-z0-9_]*/;
StringLiteral: /"([^"\\]|\\.)*"/;
// Language keywords (v2: 'namespace' removed)
Uses: 'uses';
Type: 'type';
Plural: 'plural';
Required: 'required';
Optional: 'optional';
// Primitive type keywords (lexically reserved per DEC-007)
KwString: 'string';
KwInt64: 'int64';
KwReal64: 'real64';
KwBool: 'bool';
KwDate: 'date';
KwTime: 'time';
KwDatetime: 'datetime';
// Punctuation
Slash: '/';
Comma: ',';
Colon: ':';
Semicolon: ';';
Dot: '.';
Question: '?';
At: '@';
Plus: '+';
Star: '*';
LBrace: '{';
RBrace: '}';
LBracket: '[';
RBracket: ']';
LParen: '(';
RParen: ')';
// Block comment delimiters
BlockCommentOpen: '/*';
BlockCommentClose: '*/';
// Layout terminals
WS: /\s+/;
CommentLine: /\/\/.*/;
NotComment: /((\*[^\/])|[^\s*\/]|\/[^\*])+/;