@@grammar::TwolCSyntax
@@left_recursion :: False

expr_start = expression $ ;

rul_start = rule $ ;

def_start = define $ ;

define = left:identifier op:'=' ~ right:expression ';' ;

identifier = /\b[^\W_][^\W_]+\b/ ;

rule = double_arrow_rule | right_arrow_rule | exclusion_rule | output_coercion_rule | input_coercion_rule ;

double_arrow_rule = left:expression op:'<=>' ~ right:contexts ';' ;

right_arrow_rule = left:expression op:'=>' ~ right:contexts ';' ;

exclusion_rule = left:expression op:'/<=' ~ right:contexts ';' ;

output_coercion_rule = left:expression op:'<=' ~ right:contexts ';' ;

input_coercion_rule = left:expression op:'<--' ~ right:contexts ';' ;

contexts = lst:context_lst | lst:context ;

context_lst = left:context ',' right:contexts ;

context = left:[expression] '_' right:[expression] ;

##expression = composition | term ;
expression = term ;

##composition = left:term '.o.' ~ right:expression ;

term = union | difference | intersection | factor ;

union = left:factor op:'|' ~ right:term ;

difference = left:factor op:'-' ~ right:term ;

intersection = left:factor op:'&' ~ right:term ;

factor = concatenation | unit ;

concatenation = left:unit right:factor ;

unit =
    | Kleene_plus
    | Kleene_star
    ##| Upper
    ##| Lower
    | Morphophonemic
    | Surface
    | One_but_not
    | atom
    ;

Kleene_plus = expr:atom '+' ;

Kleene_star = expr:atom '*' ;

Morphophonemic = expr:atom '.m' ;

Surface = expr:atom '.s' ;

One_but_not = '\\' expr:atom ;

atom
    = '[' @:subexpression ~ ']'
    | '(' @:optexpression ~ ')'
    | pair
    | outsym
    | defined
    | boundary ;

subexpression = expr:expression ;

optexpression = expr:expression ;

pair
#    = /(\{[^ _{}]+\}|):([^\W0-9_]\b|['§]|%\W|)/
#    = /(\{[^ {}]+\}|%_)?:([^\W0-9_]|['§_]|%\W)/
#    = /(?:\{[^ _{}]+\}|%_)?:(?:[^\W0-9_]\b|['§]|%_|%\W|)?/
#    = /(\{(?:[^ {}]+)\}|)/
#      /:/
#      /([^\W0-9_]|[']|%[\W_]|)?/
#      /(?![\w_0-9])/
#
    = /(\{(?:[^ {}]+)\}|):([^\W0-9_]|['§]|%[\W_]|)(?![\w%0-9])/
    ;
defined
    = /\b[^\W_][^\W_]+\b/
    ;
outsym
#    = /\b[^\W0-9_]\b|[']|%[-%_]/
    = /(?:[^\W0-9_]|[']|%[-:,;+*&.()%_])(?!\w)/
    ;

boundary = '.#.' ;

