grammar Dejavu { } entry class Root { Element* } union Element { | TextMany #Text | TemplateExport #Export | TemplateIf #If | TemplateFor #For } // === text area === --------------------------------------------------------------------------------------------------- atomic class TextMany { TextElement+ } union TextElement { | ^TEMPLATE_E #Escape | TEXT_SPACE #TextSpace | TEXT_WORD #TextWord } @style(escape) class TEMPLATE_E { '<%!' } text token { TEXT_SPACE: /\p{White_Space}+/ TEXT_WORD: /[^<\p{White_Space}]+/ } // === template token === ---------------------------------------------------------------------------------------------- @style(operator) atomic class TEMPLATE_L { // !'<%!' '<%' SpaceControl? } atomic class TEMPLATE_R { SpaceControl? '%>' } union SpaceControl { | '=' #Nothing | '~' #Break0 | '-' #Break1 | '_' #Delete0 | '.' #Delete1 } @style(keyword) token { KW_END: 'end' } // === template export === ------------------------------------------------------------------------------------------------- class TemplateExport ^ { TEMPLATE_L exports:ExportItem* TEMPLATE_R } class ExportItem ^ { KW_EXPORT name:Identifier (KW_TO language:Identifier)? (KW_BY KW_CLASS class :NamepathFree)? (KW_BY KW_TRAIT trait:NamepathFree?)? } @style(keyword) token { KW_EXPORT: 'export' KW_CLASS: 'class' KW_TRAIT: 'trait' KW_TO: 'to' KW_BY: 'by' } // === template if === ------------------------------------------------------------------------------------------------- atomic class TemplateIf { IfBegin IfElseIf* IfElse? IfEnd } atomic class IfBegin { TEMPLATE_L ~ ^KW_IF ~ Expression ~ TEMPLATE_R Element* } atomic class IfElse { TEMPLATE_L ~ ^KW_ELSE ~ TEMPLATE_R Element* } atomic class IfElseIf { TEMPLATE_L ~ ^KW_ELSE ~ ^KW_IF ~ Expression ~ TEMPLATE_R Element* } atomic class IfEnd { TEMPLATE_L ~ ^KW_END ~ ^KW_IF? ~ TEMPLATE_R } @style(keyword) token { KW_IF: 'if' KW_ELSE: 'else' } // === template for === ------------------------------------------------------------------------------------------------ atomic class TemplateFor { ForBegin ForElse? ForEnd } atomic class ForBegin { TEMPLATE_L ~ ^KW_FOR ~ (!KW_IN Pattern) ~ (^KW_IN ~ iterator:Expression) ~ (^KW_IF ~ condition:Expression)? ~ TEMPLATE_R Element* } atomic class ForElse { TEMPLATE_L ~ ^KW_ELSE ~ TEMPLATE_R Element* } atomic class ForEnd { TEMPLATE_L ~ ^KW_END ~ ^KW_FOR? ~ TEMPLATE_R } @style(keyword) token { KW_FOR: 'for' KW_IN: 'in' } // === pattern === --------------------------------------------------------------------------------------------------- union Pattern { | BarePattern } class BarePattern { Identifier (',' Identifier)* ','? } // === text area === --------------------------------------------------------------------------------------------------- class Expression { Term ExpressionRest* } class ExpressionRest { Infix Term } union Infix { | '+' #Add | '-' #Mul } class Term { Prefix* Atomic Suffix* } union Prefix { | '!' #Not } union Suffix { | '?' #Null | '.' Identifier #DotCall } // === string === ------------------------------------------------------------------------------------------------------ union Atomic { // | '(' @box(Expression) ')' #Group | Boolean #Boolean | Identifier #Identifier | Number #Number } // === string === ------------------------------------------------------------------------------------------------------ union String { | "'" "'" #DoubleQuote | '"' '"' #SingleQuote } // === number === ------------------------------------------------------------------------------------------------------ atomic union Number { | Digits #Dec | /0b/ BIN #Bin | /0o/ OCT #Oct | /0x/ HEX #Hex } text class Digits { /(0|[1-9][0-9])(.[0-9]+)?/ } class Unit { Identifier } token { BIN: /[0-1]+/ OCT: /[0-7]+/ HEX: /[0-9a-fA-F]+/ } // === identifier === -------------------------------------------------------------------------------------------------- class NamepathFree -> Namepath { Identifier (('.' | '::') Identifier)* } class Namepath { Identifier ('::' Identifier)* } class Identifier { /[_\p{XID_start}]\p{XID_continue}*/ } union Boolean { | 'true' #True | 'false' #False } // === ignores === ----------------------------------------------------------------------------------------------------- ignore class WhiteSpace { /\p{White_Space}+/ } //@style(comment) //ignore union Comment { // | @comment_line('//') // | @comment_line('#') // | @comment_block('/*', '*/', nest: false) //}