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

!whitespace : ' ' | '\t' | '\n' | '\r' ;
!comment : '#'  {.} '\n' ;

_letter : 'a'-'z' | 'A'-'Z' ;
_decdig : '0'-'9' ;
_idchar : _letter | _decdig | '_' ;

emit : 'e' 'm' 'i' 't' ;

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

<< import "two/src/dsl" >>

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

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

// ----------------------------------------------------------------
// Examples:
//   emit @a
//   emit (@a, @b)
//   emit @a, "x", "y"
//   emit (@a, @b), "x", "y"
// First argument (single or in parentheses) must be non-indexed
// oosvar/localvar/fieldname, so we can use their names as keys in the emitted
// record.  These restrictions are enforced in the CST logic, to keep this
// parser/AST logic simpler.

EmitStatement
  : emit Emittable
    <<
      dsl.NewASTNodeUnary($0, $1, dsl.NodeTypeEmitStatement)
      /*
      dsl.NewASTNodeUnary(
        $0,
        dsl.NewASTNodeNestable(
          $1,
        ),
        dsl.NodeTypeEmitStatement,
      )
      */
    >>

  | emit "(" EmittableList ")"
    << dsl.NewASTNodeUnary($0, $2, dsl.NodeTypeEmitStatement) >>

  | emit Emittable "," EmitKeys
    << dsl.NewASTNodeBinary($0, $1, $3, dsl.NodeTypeEmitStatement) >>

  | emit "(" EmittableList ")" "," EmitKeys
    << dsl.NewASTNodeBinary($0, $2, $5, dsl.NodeTypeEmitStatement) >>
;

//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EmittableList
  : Emittable
    << dsl.NewASTNodeUnary(
      nil,
      $0,
      dsl.NodeTypeEmittableList,
    ) >>

  // Allow trailing final comma, especially for multiline statements
  | Emittable "," EmittableList
    << dsl.PrependChild(
      $2,
      $0,
    ) >>
;

Emittable
  : Literal
;

//  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EmitKeys

  : Rvalue
    << dsl.NewASTNodeUnary(
      nil,
      $0,
      dsl.NodeTypeEmitKeys,
    ) >>

  | Rvalue "," EmitKeys
    << dsl.PrependChild(
      $2,
      $0,
    ) >>
;


// ----------------------------------------------------------------
Rvalue
  : Literal
    << dsl.NewASTNodeUnary(nil, $0, dsl.NodeTypeStringLiteral) >>
  | "(" Literal ")"
    << dsl.NewASTNodeUnary(nil, $1, dsl.NodeTypeStringLiteral) >>
  | "[" Literal "]"
    << dsl.NewASTNodeUnary(nil, $1, dsl.NodeTypeStringLiteral) >>
  | "[" Literal "," Literal "]"
    << dsl.NewASTNodeBinary(nil, $1, $2, dsl.NodeTypeStringLiteral) >>
;

Literal
  : "x" << dsl.NewASTNodeZary($0, dsl.NodeTypeStringLiteral) >>
  | "y" << dsl.NewASTNodeZary($0, dsl.NodeTypeStringLiteral) >>
  | "z" << dsl.NewASTNodeZary($0, dsl.NodeTypeStringLiteral) >>
;
