; ABNFA Definition of ABNFA

ABNF-Actions =
  ; Custom configuration
  to-language     'ABNFA'
  to-fileTypes    ['abnf']
  to-scopeName    'source.abnf'
  to-description  'ABNFA to AST'

  ; Specific configuration for meta
  to-typefield 'type'
  to-locfield  'loc'

  ; AST node structure described
  meta  (
    mode      'string'
    typefield 'type'
    locfield  'loc'
    crlf      ''
    indent    ''
    infix     <infix>
    custom    OBJECT<BOOL, STRING, stringList>
    nullable  UNIQUE<STRING>
    typenames UNIQUE<STRING>
    types     ARRAY<type>
    formnames UNIQUE<STRING>
    formulas  ARRAY<list, action, string, codes, bits, index>
    comments  ARRAY<comment>
  )

  stringList ARRAY<STRING>

  type  (
    kind        ''
    fields      OBJECT<field>
    annotation  ARRAY<STRING, INT>
  )

  field (
    kind        ''
    value       <INT,STRING,BOOL>
    annotation  ARRAY<STRING, INT>
  )

  infix  (
    node        ''
    left        ''
    operator    ''
    right       ''
    priority    ARRAY<stringList>
  )

  repeat  (
    min %d1
    max %d1
  )

  list (
    repeat  mixins
    choice  false
    factor  ARRAY<list, action, string, codes, bits, index>
  )

  codes (
    repeat  mixins
    value   ARRAY<RUNE>
    isRange false
  )

  bits   (
    repeat  mixins
    value   ''
  )

  string (
    repeat    mixins
    value     ''
    sensitive true
  )

  action (
    repeat    mixins
    refer     ''
    name      ''
    factor    ARRAY<STRING, INT, FLOAT>
  )

  index (
    repeat    mixins
    refer     %d0
    action    %d0
  )

  comment (
    value     ''
  )

result = grammar--meta

grammar =
  *c-nl 'ABNF-Actions' defined-as meta *(1*c-wsp meta)
  1*(*WSP c-nl) rulelist *(WSP / comment--comment(/comments) / to--eol)

meta =
    'to-' (
        'mode' 1*WSP (
              modes--STRING(mode)
            / to--fault('Unsupported %s mode')
          )
      / 'crlf' 1*WSP "'" *quotes-vchar--STRING(crlf, unescape) "'"
      / 'indent' 1*WSP "'" *quotes-vchar--STRING(indent, unescape) "'"
      / 'locfield' 1*WSP "'" *ALPHA--STRING(locfield) "'"
      / 'typefield' 1*WSP "'" *ALPHA--STRING(typefield) "'"
      / 'nullable' 1*WSP '<' nullable--UNIQUE(nullable) '>'
      / 'infix' 1*WSP infix--infix(infix)
      / custom--OBJECT(custom)
      / to--fault('Illegal configuration %q', -3)
    )
  / rulename--STRING(typenames) 1*WSP
    (
        type-declare--type(types)
      / to--fault('Illegal type annotation %q', typenames)
    )

modes = 'string' / 'byte' / 'bits'

nullable = rulename--STRING *(',' [SP] rulename--STRING)

custom =
  rulename--STRING(key) 1*WSP (
      'true' to--true(val)
    / 'false' to--false(val)
    / string
    / '[' *WSP strings--stringList(val) *WSP ']'
    / to--fault('Invalid custom configuration %q', key)
  )

or = 1*c-wsp '/' 1*c-wsp

strings = string *(or string)

string =
  "'" *quotes-vchar--STRING(val, unescape) "'"

quotes-vchar =
    %x20-21 / %x23-26 / %x28 / %x2A-5B / %x5D-7E
  / '\' (
      '"' /          ; quotation mark  U+0022
      "'" /          ; quotation mark  U+0027
      '\' /          ; reverse solidus U+005C
      'x' 2HEXDIG /  ; xXX             U+XX
      'u' (          ; uXXXX           U+XXXXXX
        '{' 1*6HEXDIG '}' /
        4HEXDIG
      )
    )

infix =
  '(' c-wsp
    'node'      1*WSP "'" rulename--STRING(node) "'" 1*c-wsp
    'left'      1*WSP "'" 1*ALPHA--STRING(left) "'" 1*c-wsp
    'operator'  1*WSP "'" 1*ALPHA--STRING(operator) "'" 1*c-wsp
    'right'     1*WSP "'" 1*ALPHA--STRING(right) "'" 1*c-wsp
    'priority'  1*WSP '[' *c-wsp priority--ARRAY(priority) *c-wsp ']'

  c-wsp ')'

priority = infixes--stringList *(1*c-wsp infixes--stringList)

infixes = '[' *c-wsp strings *c-wsp ']'

type-declare =
  '(' 1*(1*c-wsp field-declare--OBJECT(fields)) 1*c-wsp ')'
                                    to--STRING(kind, struct)
  / type-annotation

field-declare =
  rulename--STRING(key) 1*WSP (
      field-annotation--field(val)
    / to--fault('Invalid type-annotation: %s', key)
  )

type-annotation =
    type-kinds--STRING(kind) annotation--ARRAY(annotation)
  / annotation--ARRAY(annotation)

field-annotation =
    '%d' 1*DIGIT--INT(value)   to--STRING(kind, INT)
  / "'" *quotes-vchar--STRING(value, unescape) "'"
                                  to--STRING(kind, STRING)
  / 'true'  to--true(value)       to--STRING(kind, BOOL)
  / 'false' to--false(value)      to--STRING(kind, BOOL)
  / mixins--STRING(kind)
  / field-kinds--STRING(kind) annotation--ARRAY(annotation)
  / annotation--ARRAY(annotation)

type-kinds = field-kinds / 'interface'
field-kinds = 'ARRAY' / 'UNIQUE' / 'OBJECT'
mixins = 'mixins'

annotation =
  '<'
    rulename--STRING
    *(',' [SP] rulename--STRING)
  '>'

rulename    = ALPHA *(['-'] (ALPHA / DIGIT ))

defined-as  = *c-wsp '=' *c-wsp

c-wsp       = WSP / c-nl WSP

c-nl        = [comment--comment(/comments)] 1*to--eol
                ; comment or newline

comment     = ';' *comment-val--STRING(value)
comment-val = WSP / VCHAR

rulelist = rule *( *WSP c-nl [rule] )

rule =
  rulename--STRING(formnames) defined-as
  alternation--pending(formulas)

alternation =
  concatenation--pending(factor) (
      1*(or concatenation--pending(factor))
      to--true(choice) to--type(list)
    / to--discard
  )

concatenation =
  repetition--pending(factor) (
    1*(1*c-wsp repetition--pending(factor)) to--type(list)
    / to--discard
  )

repetition = option / [repeat] (group / element)

repeat =
    '*' to--INT(min,0) (1*DIGIT--INT(max) / to--INT(max, -1))
  / 1*DIGIT--INT(min) (
        '*' ( 1*DIGIT--INT(max) / to--INT(max, -1) )
      / to--copy(min, max)
    )

element =
    num-val
  / char-val to--type(string)
  / action to--type(action)

option =
  '[' *c-wsp (
      element *c-wsp ']'
    / alternation--pending(factor) *c-wsp ']' to--type(list)
  ) to--INT(min,0)

group =
  '(' *c-wsp alternation--pending(factor) *c-wsp ')' to--type(list)

action =
  rulename--STRING(refer) ['--' (
      1*ALPHA--STRING(name) [
        '(' *SP argument *(*SP ',' *SP argument ) *SP ')'
      ]
    / to--fault('Invalid arguments of %s', refer)
  )]

argument =
    "'" *quotes-vchar--STRING(factor, unescape) "'"
  / number-val--pending(factor)
  / field--STRING(factor)
  / to--fault('Invalid arguments on %s', refer)

number-val =
  ['-'] 1*DIGIT (
      '.' 1*DIGIT to--type(FLOAT)
    / to--type(INT)
  )

field = ['/' / '?'] ['+' / '!'] rulename

char-val =
    "'" 1*ex27--STRING(value) "'"
  / '"' 1*ex22--STRING(value) '"' to--false(sensitive)

ex22 = %x20-21 / %x23-7E
ex27 = %x20-26 / %x28-7E

num-val =
  '%' (
      'x' hex-val to--type(codes)
    / 'b' 1*53BIT--STRING(value) to--type(bits)
    / to--fault('Unsupport formula %q', -1)
  )

hex-val =
  1*HEXDIG--RUNE(value,'16') (
      '-' 1*HEXDIG--RUNE(value,'16') to--true(isRange)
    / *('.' 1*HEXDIG--RUNE(value,'16'))
  )

ALPHA   = %x61-7A / %x41-5A

BIT     = %x30-31

DIGIT   = %x30-39 ; 0-9

HEXDIG  = DIGIT / %x41-46

SP      = %x20

WSP     = %x20 / %x09 ; space and horizontal tab

VCHAR   = %x21-7E ; visible (printing) characters