rulelist       =  1*( rule/ (*c-wsp c-nl)) :
    #rulelist{rules=[Rule || Rule <- _YY, is_tuple(Rule)]}.

rule           =  rulename defined-as elements [*WSP ":" *WSP erlangcode] c-nl :
    #rule{type=_YY2, name=element(2,_YY1), body=_YY3,
          code=case _YY4 of
                   [[_,_,_,Code]] -> Code;
                   [] -> nocode
               end}.

rulename       =  ALPHA *(ALPHA/ DIGIT/ "-") :
    #rulename{name=list_to_atom(lists:flatten(_YY))}.

defined-as     =  *c-wsp ("="/ "=/") *c-wsp :
    case _YY2 of
        $= -> def_rule;
        _ -> app_rule
    end.

elements       =  alternation *c-wsp : _YY1.

c-wsp          =  WSP/ (c-nl WSP) : 'c-wsp'.

c-nl           =  comment/ CRLF : 'c-nl'.

comment        =  ";" *(WSP/ VCHAR) CRLF : comment.

alternation    =  concatenation *(*c-wsp "/" *c-wsp concatenation) :
    case [Alt || [_,_,_,Alt] <- _YY2] of
        [] -> _YY1;
        Alts -> #alt{alts=[_YY1 | Alts]}
    end.

concatenation  =  repetition *(1*c-wsp repetition) :
    case [Rule || [_,Rule] <- _YY2] of
        [] -> _YY1;
        More -> #seq{elements=[_YY1 | More]}
    end.

repetition     =  [repeat] element :
    case _YY1 of
        [{Min,Max}] -> #repeat{min=Min,max=Max,body=_YY2};
        [] -> _YY2
    end.

repeat         =  (*DIGIT "*" *DIGIT)/ 1*DIGIT :
    case _YY of
        [[],$*,[]] -> {0, infinity};
        [Min, $*, []] -> {list_to_integer(Min), infinity};
        [[], $*, Max] -> {0, list_to_integer(Max)};
        [Min, $*, Max] -> {list_to_integer(Min), list_to_integer(Max)};
        Number -> {list_to_integer(Number), list_to_integer(Number)}
    end.

element        =  rulename/ group/ option/ char-val/ num-val/ prose-val

group          =  "(" *c-wsp alternation *c-wsp ")" : _YY3.

option         =  "[" *c-wsp alternation *c-wsp "]" :
    {repeat, 0, 1, _YY3}.

char-val       =  DQUOTE *(%x20-21/ %x23-7E) DQUOTE :
    F = fun (Char) ->
                  case {string:to_lower(Char),string:to_upper(Char)} of
                      {Char,Char} -> #char_val{value=Char};
                      {Low,Up} -> #char_alt{alts=[#char_val{value=Low},
                                                  #char_val{value=Up}]}
                  end
         end,
    case _YY2 of
        [C] -> F(C);
        Chars -> #char_seq{elements=[F(C)||C<-Chars]}
    end.

num-val        =  "%" (bin-val/ dec-val/ hex-val) : _YY2.

bin-val        =  "b" 1*BIT [ 1*("." 1*BIT)/ ("-" 1*BIT)] :
    First = bin_to_int(_YY2),
    case _YY3 of
        [] -> #char_val{value=First};
        [[$-,To]] -> #char_range{from=First, to=bin_to_int(To)};
        [Vals] -> #char_seq{elements=[#char_val{value=First}|[#char_val{value=bin_to_int(Val)}||[$.,Val] <- Vals]]}
    end.

dec-val        =  "d" 1*DIGIT [ 1*("." 1*DIGIT)/ ("-" 1*DIGIT)] :
    First = list_to_integer(_YY2),
    case _YY3 of
        [] -> #char_val{value=First};
        [[$-,To]] -> #char_range{from=First, to=list_to_integer(To)};
        [Vals] -> #char_seq{elements=[#char_val{value=First}|[#char_val{value=list_to_integer(Val)}||[$.,Val] <- Vals]]}
    end.

hex-val        =  "x" 1*HEXDIG [ 1*("." 1*HEXDIG)/ ("-" 1*HEXDIG)] :
    First = hex_to_int(_YY2),
    case _YY3 of
        [] -> #char_val{value=First};
        [[$-,To]] -> #char_range{from=First, to=hex_to_int(To)};
        [Vals] -> #char_seq{elements=[#char_val{value=First}|[#char_val{value=hex_to_int(Val)}||[$.,Val] <- Vals]]}
    end.


prose-val      =  "<" *(%x20-3D/ %x3F-7E) ">" :
    {'prose-val', lists:flatten(_YY2)}.
