@top Source { Statement* } Statement { FieldDefinition semicolon? | Expression semicolon? } Expression { DefaultExpression } DefaultExpression { PatchExpression | PatchExpression !default default DefaultExpression } PatchExpression { ComposeExpression | PatchExpression !patch slashSlash ComposeExpression } ComposeExpression { LogicalOrExpression | ComposeExpression !compose amp LogicalOrExpression } LogicalOrExpression { LogicalAndExpression | LogicalOrExpression !or pipePipe LogicalAndExpression } LogicalAndExpression { ComparisonExpression | LogicalAndExpression !and ampAmp ComparisonExpression } ComparisonExpression { ConcatExpression | ConcatExpression !compare CompareOperator ConcatExpression } ConcatExpression { AdditiveExpression | ConcatExpression !concat plusPlus AdditiveExpression } AdditiveExpression { MultiplicativeExpression | AdditiveExpression !add (plus | minus) MultiplicativeExpression } MultiplicativeExpression { UnaryExpression | MultiplicativeExpression !multiply (star | slash) UnaryExpression } UnaryExpression { PostfixExpression | (bang | minus) !unary UnaryExpression } PostfixExpression { PrimaryExpression | PostfixExpression !call CallSuffix | PostfixExpression !path dot identifier } CallSuffix { lParen ArgumentList? rParen } ArgumentList { Expression (comma Expression)* comma? } PrimaryExpression { Literal | identifier | ComparisonConstraint | Object | Array | LetExpression | FunctionExpression | MatchExpression | ImportExpression | lParen Expression rParen } Literal { string | integer | float | true | false | regex } CompareOperator { equalEqual | bangEqual | lt | lte | gt | gte } ComparisonConstraint { (lt | lte | gt | gte) Expression } Object { lBrace (FieldDefinition (semicolon FieldDefinition)* semicolon?)? rBrace } FieldDefinition { FieldPath equal Expression } FieldPath { identifier !fieldPath (dot identifier)* } Array { lBracket (Expression (comma Expression)* comma?)? rBracket } LetExpression { let FieldDefinitionList in Expression } FieldDefinitionList { (FieldDefinition semicolon)* } FunctionExpression { lParen ParameterList? rParen arrow Expression } ParameterList { Parameter (comma Parameter)* comma? } Parameter { identifier colon Expression } MatchExpression { match Expression lBrace (MatchArm (semicolon MatchArm)* semicolon?)? rBrace } MatchArm { Pattern colon Expression } Pattern { underscore | Expression } ImportExpression { import string } @precedence { fieldPath @left, default @right, patch @left, compose @left, or @left, and @left, compare @left, concat @left, add @left, multiply @left, unary, call @left, path @left } @tokens { let { "let" } in { "in" } match { "match" } import { "import" } default { "default" } true { "true" } false { "false" } identifier { $[A-Za-z_] $[A-Za-z0-9_]* } integer { $[0-9]+ } float { $[0-9]+ "." $[0-9]+ } string { '"' (!["\\\n] | "\\" _)* '"' } regex { "/" (![/\\\n] | "\\" _)+ "/" } comment { "#" ![\n]* } lBrace { "{" } rBrace { "}" } lBracket { "[" } rBracket { "]" } lParen { "(" } rParen { ")" } semicolon { ";" } comma { "," } dot { "." } colon { ":" } equal { "=" } equalEqual { "==" } bang { "!" } bangEqual { "!=" } arrow { "=>" } amp { "&" } ampAmp { "&&" } pipePipe { "||" } plus { "+" } plusPlus { "++" } minus { "-" } star { "*" } slash { "/" } slashSlash { "//" } gt { ">" } gte { ">=" } lt { "<" } lte { "<=" } underscore { "_" } whitespace { @whitespace+ } @precedence { let, in, match, import, default, true, false, float, integer, underscore, identifier } } @skip { whitespace | comment }