
// ================================================================
// LEXICAL ELEMENTS
// ================================================================

// ----------------------------------------------------------------
// CHARACTER CLASSES
// ----------------------------------------------------------------

_letter : 'a'-'z' | 'A'-'Z' ;
_decdig : '0'-'9' ;
_hexdig : '0'-'9' | 'a'-'f' | 'A'-'F';
_bindig : '0'-'1' ;
_idchar : _letter | _decdig | '_' ;
!whitespace : ' ' | '\t' | '\n' | '\r' ;

// ----------------------------------------------------------------
// STRING/INT/FLOAT/BOOLEAN LITERALS
// ----------------------------------------------------------------

// Notes on string literals:
// * " isn't included here -- need \" handling to put that inside strings
// * GOCC seems to lack a '[^"] notation ...
_string_literal_element
  : 'A'-'Z' | 'a'-'z' | '0'-'9'
  | ' ' | '!' | '#' | '$' | '%' | '&' | '\'' | '\\'
  | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/'
  | ':' | ';' | '<' | '=' | '>' | '?' | '@' | '['
  | ']' | '^' | '_' | '`' | '{' | '|' | '}' | '~'
  | ( '\\' '"' )
  | '\u0100'-'\U0010FFFF'
;
md_token_string_literal : '"' {_string_literal_element} '"' ;

// md_token_regexi : \"([^\\\"]|\\.)*\"\i ;

// Notes on int literals:
// * Leading minus sign is handled via the unary-minus operator, not here.
md_token_int_literal
  : _decdig { _decdig }
  | '0' 'x' _hexdig { _hexdig }
  | '0' 'b' _bindig { _bindig }
;

// Notes on float literals:
// * Leading minus sign is handled via the unary-minus operator, not here.
// * The various shapes are for scientific notation. Examples:
//   123
//   123.
//   123.4
//   .234
//   1e2
//   1e-2
//   1.2e3 1.e3
//   1.2e-3 1.e-3
//   .2e3
//   .2e-3 1.e-3
_scinotE : 'e' | 'E' ;
md_token_float_literal
  : _decdig { _decdig} '.' { _decdig }
  | _decdig { _decdig}                        _scinotE     _decdig { _decdig}
  | _decdig { _decdig}                        _scinotE '-' _decdig { _decdig}
  | _decdig { _decdig} '.'         { _decdig} _scinotE     _decdig { _decdig}
  | _decdig { _decdig} '.'         { _decdig} _scinotE '-' _decdig { _decdig}
  |         { _decdig} '.' _decdig { _decdig} _scinotE     _decdig { _decdig}
  |         { _decdig} '.' _decdig { _decdig} _scinotE '-' _decdig { _decdig}
;

md_token_M_PI : 'M' '_' 'P' 'I' ;
md_token_M_E  : 'M' '_' 'E' ;

// Notes on boolean literals:
// * true and false should be defined here rather than as "true" / "false"
//   within the grammar below -- this forces them to be keywords, not legal as
//   variable names. We want them as keywords -- we don't want to allow things
//   like 'true = 3'.
_md_token_true : 't' 'r' 'u' 'e' ;
_md_token_false : 'f' 'a' 'l' 's' 'e';
md_token_boolean_literal : ( _md_token_true | _md_token_false );

// ----------------------------------------------------------------
// MILLER CONTEXT VARIABLES
// ----------------------------------------------------------------

md_token_IPS : 'I' 'P' 'S' ;
md_token_IFS : 'I' 'F' 'S' ;
md_token_IRS : 'I' 'R' 'S' ;

md_token_OPS : 'O' 'P' 'S' ;
md_token_OFS : 'O' 'F' 'S' ;
md_token_ORS : 'O' 'R' 'S' ;

md_token_NF  : 'N' 'F' ;
md_token_NR  : 'N' 'R' ;
md_token_FNR : 'F' 'N' 'R' ;

md_token_FILENAME : 'F' 'I' 'L' 'E' 'N' 'A' 'M' 'E' ;
md_token_FILENUM  : 'F' 'I' 'L' 'E' 'N' 'U' 'M' ;

// md_token_ENV   : 'E' 'N' 'V' ;

// ----------------------------------------------------------------
// MILLER KEYWORDS
// ----------------------------------------------------------------

// Notes on keywords:
// * Any new keywords defined here should also be documented
//   in dsl/mlr_dsl_cst.c's mlr_dsl_keyword_usage() et al.
// * true and false (boolean literals) are also keywords, defined above.

md_token_begin    : 'b' 'e' 'g' 'i' 'n' ;
md_token_do       : 'd' 'o' ;
md_token_dump     : 'd' 'u' 'm' 'p' ;
md_token_edump    : 'e' 'd' 'u' 'm' 'p' ;
md_token_elif     : 'e' 'l' 'i' 'f' ;
md_token_else     : 'e' 'l' 's' 'e' ;
md_token_emit     : 'e' 'm' 'i' 't' ;
md_token_end      : 'e' 'n' 'd' ;
md_token_filter   : 'f' 'i' 'l' 't' 'e' 'r' ;
md_token_for      : 'f' 'o' 'r' ;
md_token_if       : 'i' 'f' ;
md_token_in       : 'i' 'n' ;
md_token_while    : 'w' 'h' 'i' 'l' 'e' ;

md_token_func     : 'f' 'u' 'n' 'c' ;
//md_token_subr     : 's' 'u' 'b' 'r' ;

//md_token_break    : 'b' 'r' 'e' 'a' 'k' ;
//md_token_call     : 'c' 'a' 'l' 'l' ;
//md_token_continue : 'c' 'o' 'n' 't' 'i' 'n' 'u' 'e' ;
//md_token_return   : 'r' 'e' 't' 'u' 'r' 'n' ;

//md_token_unset    : 'u' 'n' 's' 'e' 't' ;

//md_token_eprint   : 'e' 'p' 'r' 'i' 'n' 't' ;
//md_token_eprintn  : 'e' 'p' 'r' 'i' 'n' 't' 'n' ;
//md_token_print    : 'p' 'r' 'i' 'n' 't' ;
//md_token_printn   : 'p' 'r' 'i' 'n' 't' 'n' ;
//md_token_stderr   : 's' 't' 'd' 'e' 'r' 'r' ;
//md_token_stdout   : 's' 't' 'd' 'o' 'u' 't' ;
//md_token_tee      : 't' 'e' 'e' ;

//md_token_all      : 'a' 'l' 'l' ;

md_token_int_type     : 'i' 'n' 't' ;
md_token_float_type   : 'f' 'l' 'o' 'a' 't' ;
//md_token_num_type   : 'n' 'u' 'm' ;
//md_token_bool_type  : 'b' 'o' 'o' 'l' ;
//md_token_str_type   : 's' 't' 'r' ;
//md_token_map_type   : 'm' 'a' 'p' ;
//md_token_array_type : 'a' 'r' 'r' 'a' 'y';

// ----------------------------------------------------------------
// FIELD NAMES, OUT-OF-STREAM VARIABLES, LOCAL VARIABLES
// ----------------------------------------------------------------

// Note: the parser depends on the dollar sign being here. If this is changed,
// that needs to be changed as well.
//
// Also note: if we omit the '$' here and include it in the parser section
// below as "$", then we get an LR-1 conflict. So this must be dealt with at
// the AST level.
md_token_field_name : '$' _idchar { _idchar } ;

// This is for literal strings but where the field name might have spaces in it
// or somesuch.
_braced_char
  : 'A'-'Z' | 'a'-'z' | '0'-'9'
  | ' ' | '!' | '#' | '$' | '%' | '&' | '\'' | '\\'
  | '(' | ')' | '*' | '+' | ',' | '-' | '.' | '/'
  | ':' | ';' | '<' | '=' | '>' | '?' | '@' | '['
  | ']' | '^' | '_' | '`'       | '|'       | '~'
  | ( '\\' '{' ) | ( '\\' '}' )
  | '\u0100'-'\U0010FFFF'
;
md_token_braced_field_name: '$' '{' _braced_char { _braced_char } '}' ;

md_token_full_srec : '$' '*' ;

md_token_oosvar_name : '@' _idchar { _idchar } ;

// This is for literal strings but where the oosvar name might have spaces in it
// or somesuch.
md_token_braced_oosvar_name: '@' '{' _braced_char { _braced_char } '}' ;

md_token_full_oosvar : '@' '*' ;

// ----------------------------------------------------------------
// FUNCTIONS AND LOCAL VARIABLES

md_token_non_sigil_name : _idchar { _idchar } ;

// ----------------------------------------------------------------
// PANIC TOKEN
// ----------------------------------------------------------------

// This is for testing short-circuiting of "&&", "||", etc in the CST.  The
// sole job of the CST evaluator for this token is to panic the process -- so
// we'll know if we're evaluating something we should not.
md_token_panic : '%' '%' '%' 'p' 'a' 'n' 'i' 'c' '%' '%' '%' ;


// ================================================================
// SYNTAX ELEMENTS
// ================================================================

// ================================================================
// Parsing goes through three formats:
//
// (1) Source code which is a string of characters.
//
// (2) Abstract syntax tree (AST):
//
//    * Parentheses, commas, semicolons, line endings, whitespace are all stripped away
//    * Variable names and literal values remain as leaf nodes of the AST
//    * = + - * / ** {function names} remain as non-leaf nodes of the AST
//
// (3) Concrete syntax tree (CST): a reshaping of the AST with pre-processed
//     setup of function pointers to handle each type of statement on a
//     per-record basis. The if/else and/or switch statements to decide what to
//     do with each AST node are done at CST-build time, so they don't need to
//     be re-done when the syntax tree is executed once on every data record.
//
// The job of this parser is to turn (1) into (2).
//
// Note: This parser accepts many things that are invalid, e.g.
// * begin{end{}} -- begin/end not at top level
// * begin{$x=1} -- references to stream records at begin/end (there is no $x when
//   there is no input record yet)
// * break/continue outside of for/while/do-while
// * return outside of a function definition
// * $x=x -- boundvars outside of for-loop variable bindings
//
// All of the above are enforced by the CST builder's semantic-analysis logic,
// which takes this parser's output AST as input.  This is done (a) to keep
// this grammar from being overly complex, and (b) so we can get more
// informative error messages.
//
// For clearer visuals on what the ASTs look like, you can do 
//
//   mlr -n put -v 'your expression goes here'
//
// Also see reg_test/run's filter -v and put -v outputs, e.g. in
// reg_test/expected/out.
// ================================================================

// Import the AST/ASTNode types and functions
<< import "miller/dsl" >>

// ================================================================
// TOP-LEVEL PRODUCTION RULE FOR THE MILLER DSL

Root :
  FuncBlock
;

// ================================================================
StatementBlockInBraces
  // Defining StatementBlock to be emptyable would have been more elegant ...
  // I got LR-1 conflicts though. :^/
  : "{" "}"
    << dsl.NewASTNodeZary(nil, dsl.NodeTypeStatementBlock) >>

  | "{" StatementBlock "}"
    << dsl.Wrap($1) >>
  ;

StatementBlock: "fooblock";

// ================================================================
// FUNCTION AND SUBROUTINE DEFINITIONS

// ----------------------------------------------------------------
FuncBlock
  : md_token_func
	  md_token_non_sigil_name
    "("
    FuncOrSubrParameterList
    ")"
    StatementBlockInBraces
  <<
    dsl.NewASTNodeTernary(
      $0, // "func"
      $1, // function name
      $3, // parameter list
      $5, // { ... }
      dsl.NodeTypeFunctionDefinition,
 	 );
  >>
;

// ----------------------------------------------------------------
FuncOrSubrParameterList
  : empty
  | FuncOrSubrParameterList ","
  | FuncOrSubrParameterList "," FuncOrSubrParameter
;

FuncOrSubrParameter : md_token_non_sigil_name;
