grammar DGDGrammar

rule source_file
  ws (inherit ws)* (top_level_decl ws)*
end

rule inherit
  ("private" WS)? "inherit" WS (ident WS)? ('object' WS)? string_exp ws ';' <Inherit>
end

rule top_level_decl
  data_decl / func_decl
end

rule ident
  [a-zA-Z_] [a-zA-Z_0-9]* <Identifier>
end

rule c_comment
  '/*'
  (
    !'*/'
    (. / "\n")
  )*
  '*/'
end

rule preproc_hash_line
  "\n"? "# " decimal " "+ simple_string (" "+ decimal)? "\n"
end

rule simple_string
  '"' (
    !["\n\\]
    .
    )*
  '"' <StringLiteral>
end

rule complex_string
  '"' ( ((!["\n\\] ) .) / . )+ '"' <StringLiteral>
end

rule simple_char
  "'" ((!['\n\\]) .)+ "'" <CharLiteral>
end

rule complex_char
  "'" (((!['\n\\]) .) / ("\\" .))+ "'" <CharLiteral>
end

rule decimal
  [1-9] [0-9]* <DecimalLiteral>
end

rule octal
  '0' [0-7]* <OctalLiteral>
end

rule hexadecimal
  '0' [xX] [a-fA-F0-9]+ <HexadecimalLiteral>
end

rule float
  ([0-9]+ "." [0-9]+ ([eE] [-+]? [0-9]+)?) <FloatLiteral> /
  ("." [0-9]+ ([eE] [-+]? [0-9]+)?) <FloatLiteral> /
  ([0-9]+ [eE] [-+]? [0-9]+) <FloatLiteral>
end

rule single_WS
  [ \t\v\f\r\n] / c_comment / preproc_hash_line
end

rule ws
  single_WS*
end

rule WS
  single_WS+
end

rule string
  simple_string /
  complex_string
end

rule string_exp
  string /
  '(' ws composite_string ws ')'
end

rule composite_string
  string_exp / string_exp '+' composite_string
end

rule klass
  (class_spec WS)* class_spec
end

rule class_spec
  'private' / 'static' / 'atomic' / 'nomask' / 'varargs'
end

rule type_spec
  'int' / 'float' / 'string' / 'object' / 'mapping' / 'mixed' / 'void'
end

rule stars
  ('*' ws)*
end

rule formals
  # If you put these in a different order with '' and 'void' first, parses start failing. That seems bad.
  formal_list (ws '...')? / '' / 'void'
end

rule formal_list
  formal (ws ',' ws formal)*
end

rule formal
  class_type data_declarator /
  ident
end

rule data_decl
  class_type WS declarators ws ';' <DataDecl>
end

rule func_decl
  class_type WS function_declarator ws compound_statement <FuncDecl> /
  klass WS function_name ws '(' ws formals ws ')' ws compound_statement <FuncDecl>
end

rule class_type
  (class_spec WS)* type_spec /
  (class_spec WS)* 'object' list_exp
end

rule data_declarator
  stars ws ident
end

rule function_name
  ident /
  'operator' ws '+' /
  'operator' ws '-' /
  'operator' ws '*' /
  'operator' ws '/' /
  'operator' ws '%' /
  'operator' ws '&' /
  'operator' ws '^' /
  'operator' ws '|' /
  'operator' ws '<' /
  'operator' ws '>' /
  'operator' ws '>=' /
  'operator' ws '<=' /
  'operator' ws '<<' /
  'operator' ws '>>' /
  'operator' ws '~' /
  'operator' ws '++' /
  'operator' ws '--' /
  'operator' ws '[' ws ']' /
  'operator' ws '[' ws ']' ws '=' /
  'operator' ws '[' ws '..' ws ']'
end

rule function_declarator
  stars ws function_name ws '(' ws formals ws ')'
end

rule declarator
  function_declarator / data_declarator
end

rule declarators
  declarator (ws ',' ws declarator)*
end

rule locals
  (ws data_decl ws)*
end

rule statements
  (ws statement ws)*
end

rule statement
  'if' ws '(' ws list_exp ws ')' ws statement (WS 'else' statement)? /
  'do' WS statement WS 'while' ws '(' ws list_exp ws ')' ws ';' /
  'while' ws '(' ws list_exp ws ')' ws statement /
  'for' ws '(' ws list_exp? ws ';' ws list_exp? ws ';' ws list_exp? ws ')' ws statement /
  'rlimits' ws '(' ws list_exp ws ';' ws list_exp ws ')' ws compound_statement /
  'catch' ws compound_statement ws ':' statement /
  'catch' ws compound_statement /
  'switch' ws '(' ws list_exp ws ')' ws compound_statement /
  'case' WS exp ws ':' ws statement /
  'case' WS exp ws '..' ws exp ws ':' statement /
  'default' ws ':' ws statement /
  'goto' WS ident ws ';' /
  'break' ws ';' /
  'continue' ws ';' /
  'return' WS list_exp WS ';' /
  'return' ws ';' /
  ident ws ':' ws statement /  # label statement
  list_exp /
  compound_statement /
  ';'
end

rule compound_statement
  '{' ws locals ws statements ws '}'
end

rule function_call
  ('::' ws function_name) /
  (ident ws '::' ws function_name) /
  function_name
end

rule list_exp
  exp ws ',' ws list_exp /
  exp
end

rule exp
  cond_exp ws '=' ws exp /
  cond_exp ws '+=' ws exp /
  cond_exp ws '-=' ws exp /
  cond_exp ws '*=' ws exp /
  cond_exp ws '/=' ws exp /
  cond_exp ws '%=' ws exp /
  cond_exp ws '<<=' ws exp /
  cond_exp ws '>>=' ws exp /
  cond_exp ws '&=' ws exp /
  cond_exp ws '^=' ws exp /
  cond_exp ws '|=' ws exp /
  cond_exp
end

rule cond_exp
  or_exp ws '?' ws list_exp ws ':' ws cond_exp / or_exp
end

rule or_exp
  and_exp ws '||' ws or_exp / and_exp
end

rule and_exp
  bitor_exp ws '&&' ws and_exp / bitor_exp
end

rule bitor_exp
  bitxor_exp ws '|' ws bitor_exp / bitxor_exp
end

rule bitxor_exp
  bitand_exp ws '^' ws bitxor_exp / bitand_exp
end

rule bitand_exp
  equ_exp ws '&' bitand_exp / equ_exp
end

rule equ_exp
  rel_exp ws '==' ws equ_exp /
  rel_exp ws '!=' ws equ_exp /
  rel_exp
end

rule rel_exp
  shift_exp ws '<' ws rel_exp /
  shift_exp ws '>' ws rel_exp /
  shift_exp ws '<=' ws rel_exp /
  shift_exp ws '>=' ws rel_exp /
  shift_exp
end

rule shift_exp
  add_exp ws '<<' ws shift_exp /
  add_exp ws '>>' ws shift_exp /
  add_exp
end

rule add_exp
  mult_exp ws '+' ws add_exp /
  mult_exp ws '-' ws add_exp /
  mult_exp
end

rule mult_exp
  cast_exp ws '*' ws mult_exp /
  cast_exp ws '/' ws mult_exp /
  cast_exp ws '%' ws mult_exp /
  cast_exp
end

rule cast_exp
  '(' ws class_type ws stars ws ')' ws cast_exp /
  prefix_exp
end

rule prefix_exp
  '++' ws cast_exp /
  '--' ws cast_exp /
  '+' ws cast_exp /
  '-' ws cast_exp /
  '!' ws cast_exp /
  '~' ws cast_exp /
  postfix_exp
end

rule postfix_exp
  exp2 (ws ('++' / '--'))?
end

rule exp2
  exp1 ws '->' ws ident ws '(' ws opt_arg_list ws ')' /
  exp1 ws '<-' ws string_exp /
  exp1 ((ws '[' ws list_exp ws ']') / (ws '[' ws (list_exp ws)? '..' ws (list_exp ws)? ']'))*
end

rule exp1
  # This works (for now) with function_call up front, but not when it was lower down...
  # There's some kind of bad stuff going on with precedence that's causing parse failures.
  # That's weird, since I assume it should try all combinations/precedences before giving up...
  function_call ws '(' ws opt_arg_list ws ')' /
  'catch' ws '(' list_exp ws ')' /
  'new' (WS 'object')? ws string_exp (ws '(' ws opt_arg_list ws ')')? /
  'nil' /
  '(' ws '{' ws opt_arg_list_comma ws '}' ws ')' /
  '(' ws '[' ws opt_assoc_list_comma ws ']' ws ')' /
  '::' ws ident /
  decimal /
  octal /
  hexadecimal /
  simple_char /
  complex_char /
  float /
  string /
  ident /
  '(' ws list_exp ws ')'
end

rule arg_list
  exp ws ',' ws arg_list /
  exp
end

rule opt_arg_list
  arg_list ws '...' /
  arg_list?
end

rule opt_arg_list_comma
  arg_list ws ',' /
  arg_list?
end

rule assoc_list
  assoc_pair ws ',' ws assoc_list /
  assoc_pair
end

rule assoc_pair
  exp ws ':' ws exp
end

rule opt_assoc_list_comma
  assoc_list ws ',' /
  assoc_list?
end

end