phenotyper 0.3.0

Core compiler library for the Phenotyper structural artifact definition language
// 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*\/]|\/[^\*])+/;