// ================================================================
// LEXER

!whitespace : ' ' | '\t' | '\n' | '\r' ;

// ================================================================
// IMPORT

<< import "miller/src/dsl" >>

// ================================================================
// PARSER

// ----------------------------------------------------------------
Root
  : StatementBlock
   << dsl.NewAST($0) >>
;

// ================================================================
StatementBlock

  // Empty statement. This allows for 'mlr put ""', as well as repeated semicolons.
  : empty
    << dsl.NewASTNodeZary(nil, dsl.NodeTypeStatementBlock) >>

  | NonEmptyStatementBlock
    << dsl.Wrap($0) >>
;

// ----------------------------------------------------------------
NonEmptyStatementBlock
  // ---------------------- Terminal rules

  // Things not ending in a curly brace, like assignments -- and also do-while.
  : BracelessStatement
    << dsl.NewASTNodeUnary(nil, $0, dsl.NodeTypeStatementBlock) >>

  // Things ending in a curly brace, like for/do/while, begin/end, and pattern-action blocks
  | BracefulStatement
    << dsl.NewASTNodeUnary(nil, $0, dsl.NodeTypeStatementBlock) >>

  // ---------------------- Recursive rules

  // So statements can start with a semicolon
  | ";" StatementBlock
    << dsl.Wrap($1) >>

  // Normal case for sequential statements like '$x=1; $y=2'
  | BracelessStatement ";" StatementBlock
    <<dsl.PrependChild($2, $0) >>

  // For 'begin {...} ; $x=1'
  | BracefulStatement ";" StatementBlock
    <<dsl.PrependChild($2, $0) >>

  // These are for things like 'begin {...} begin {...} ...' -- where people
  // shouldn't have to put semicolons after the closing curly braces.
  //
  // We get LR-1 conflicts with the following, so we need a pair of more
  // explicit lookahead-by-more production rules instead. (By using ternaries
  // we are effectively getting lookahead-by-two.)
  //
  // | BracefulStatement StatementBlock
  //    <<dsl.PrependChild($1, $0) >>

  // E.g. 'begin {...} begin {...} $x=1'
  | BracefulStatement BracefulStatement StatementBlock
    <<dsl.PrependTwoChildren($2, $0, $1) >>

  // E.g. 'begin {...} $x=1'
  | BracefulStatement BracelessStatement
    << dsl.NewASTNodeBinary(nil, $0, $1, dsl.NodeTypeStatementBlock) >>

  // E.g. 'begin {...} $x=1 ;'
  | BracefulStatement BracelessStatement ";"
    << dsl.NewASTNodeBinary(nil, $0, $1, dsl.NodeTypeStatementBlock) >>

  | BracefulStatement BracelessStatement ";" NonEmptyStatementBlock
    <<dsl.PrependTwoChildren($3, $0, $1) >>
;

// ----------------------------------------------------------------
BracelessStatement
  : "x"
    << dsl.NewASTNodeZary($0, dsl.NodeTypeBareBoolean) >>
;

// ----------------------------------------------------------------
BracefulStatement
  : "{" StatementBlock "}"
    << dsl.Wrap($1) >>
;
