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

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

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

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

stdout   : 's' 't' 'd' 'o' 'u' 't' ;
stderr   : 's' 't' 'd' 'e' 'r' 'r' ;

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

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

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

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

// ----------------------------------------------------------------
Redirector
  : ">"  RedirectTarget
    << dsl.NewASTNodeUnary($0, $1, dsl.NodeTypeRedirectWrite) >>
  | ">>" RedirectTarget
    << dsl.NewASTNodeUnary($0, $1, dsl.NodeTypeRedirectAppend) >>
  | "|"  RedirectTarget
    << dsl.NewASTNodeUnary($0, $1, dsl.NodeTypeRedirectPipe) >>
;

RedirectTarget
  : stdout
    << dsl.NewASTNodeZary($0, dsl.NodeTypeRedirectTargetStdout) >>
  | stderr
    << dsl.NewASTNodeZary($0, dsl.NodeTypeRedirectTargetStderr) >>
  | Rvalue
;

// ----------------------------------------------------------------
EmitStatement
  : emit Emittable
    <<
      dsl.NewASTNodeTernary(
        $0,
        $1,
        dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
        dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
        dsl.NodeTypeEmitStatement,
      )
    >>

  | emit Redirector "," Emittable
    <<
      dsl.NewASTNodeTernary(
      $0,
      $3,
      dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
      $1,
      dsl.NodeTypeEmitStatement,
    )
    >>

  | emit "(" EmittableList ")"
    <<
      dsl.NewASTNodeTernary(
        $0,
        $2,
        dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
        dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
        dsl.NodeTypeEmitStatement,
      )
    >>

  | emit Redirector "," "(" EmittableList ")"
    <<
      dsl.NewASTNodeTernary(
        $0,
        $4,
        dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
        $1,
        dsl.NodeTypeEmitStatement,
      )
    >>

  | emit Emittable "," EmitKeys
    <<
      dsl.NewASTNodeTernary(
        $0,
        $1,
        $3,
        dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
        dsl.NodeTypeEmitStatement,
      )
    >>

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

  | emit "(" EmittableList ")" "," EmitKeys
    <<
      dsl.NewASTNodeTernary(
        $0,
        $2,
        $5,
        dsl.NewASTNodeNestable(nil, dsl.NodeTypeNoOp),
        dsl.NodeTypeEmitStatement,
      )
    >>

  | emit Redirector "," "(" EmittableList ")" "," EmitKeys
    <<
      dsl.NewASTNodeTernary(
        $0,
        $4,
        $7,
        $1,
        dsl.NodeTypeEmitStatement,
      )
    >>
;

// ----------------------------------------------------------------
// Examples:
//   emitf @a
//   emitf @a, b, $c
// Each argument must be a non-indexed oosvar/localvar/fieldname, so we can use
// their names as keys in the emitted record.
EmitFStatement

  : emitf EmittableList
    << dsl.AdoptChildren(
      dsl.NewASTNodeNestable(
        $0,
        dsl.NodeTypeEmitFStatement,
      ),
      $1,
    ) >>

  | emitf Redirector "," EmittableList
    // TODO

;

// ----------------------------------------------------------------
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) >>
;
