All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.deephaven.lang.generated.Chunker.jj Maven / Gradle / Ivy

There is a newer version: 0.36.1
Show newest version
/*@bgen(jjtree) Generated By:JJTree: Do not edit this line. Chunker.jj */
/*@egen*/options {
    STATIC = false;

    DEBUG_PARSER=false;
    DEBUG_LOOKAHEAD=false;
    DEBUG_TOKEN_MANAGER=false;

                                               
               
                          
                          
                                            
                         
                 
    SUPPORT_CLASS_VISIBILITY_PUBLIC=true;

                                                

    JAVA_TEMPLATE_TYPE = "modern";

                       
    TOKEN_EXTENDS ="BaseToken";
    COMMON_TOKEN_ACTION=true;
}

PARSER_BEGIN(Chunker)
package io.deephaven.lang.generated;

import static io.deephaven.lang.shared.lsp.DiagnosticCode.*;
import static java.util.Collections.singletonList;

import io.deephaven.lang.api.ChunkerInvokable;
import io.deephaven.lang.api.IsScope;
import io.deephaven.lang.api.ParseState;
import io.deephaven.lang.api.ParseCancelled;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.IdentityHashMap;
import java.io.IOException;

public class Chunker implements/*@bgen(jjtree)*/ ChunkerTreeConstants, /*@egen*/ ChunkerMixin {/*@bgen(jjtree)*/
  protected JJTChunkerState jjtree = new JJTChunkerState();

/*@egen*/

    private final ParseState root = new ParseState(null);
    private volatile ParseState current = root;
    private final Stack stack = new Stack();
    private final IdentityHashMap states = new IdentityHashMap();
    private ChunkerInvokable invoker;

    public static void main(String[] args) throws Exception {
        final Chunker ast;
        if (args.length == 0) {
            ast = new Chunker(new StreamProvider(System.in));
        } else {
            ast = new Chunker(new StreamProvider(new java.io.StringReader(args[0])));
        }
        ast.Document();
    }

    void report(int code) {
        // no-op
    }

    void jjtreeOpenNodeScope(Node n){
        ParseState state = new ParseState(n);
        ParseState was = states.put(n, state);
        assert was == null : "State map is screwy; " + was + " and " + state + " both added for " + n;
        current.addChild(state);
        current = state;
    }

    void jjtreeCloseNodeScope(Node n){
        ParseState is = states.get(n);
        assert is != null : "State map is screwy; no entry for " + n + " found in " + states;
        current = is.finish();
    }


  public boolean isLookingAhead() {
    return jj_lookingAhead;
  }

  public Token curToken() {
    return jj_lookingAhead ? jj_scanpos : token;
  }

  @Override
  public char next() throws IOException {
    // next() is much nicer to type....
    return jj_input_stream.readChar();
  }

  @Override
  public void back(int backup, int tokenBegin) {
    jj_input_stream.backup(backup);
    if (tokenBegin != -1) {
      jj_input_stream.tokenBegin = tokenBegin;
    }
  }

  void putBackTokens(boolean clearCurrent) {
    // instead of trying to mess with the insanity of the jjt runtime state,
    // lets instead make our peeking-methods look at existing token images;
    // if there are cases where a peek method matches and an existing token is of the wrong type,
    // then we may need to fix on a case-by-case basis (adding extra "recovery productions")

    // This should at least be simplified by exposing a simple character stream,
    // which first reads the existing tokens, and then looks at the underlying input stream.
    // We may also want to behave differently when jj_lookingAhead is true. i.e. only actually
    // put tokens back when we aren't looking ahead, so we don't attempt to mutate the stack
    // until the moment we are actually creating the tokens protected by a peeker method.


    jj_ntk = -1; // erase knowledge of any existing tokens.

      jj_lastpos = jj_scanpos;
      jj_la = 1;
    if (token == null) {
      return;
    }

    final Token toPutBack = clearCurrent ? token : token.next;
    int begin;
    if (toPutBack != null) {
        begin = toPutBack.tokenBegin;
    } else {
        begin = -1;
    }
    int backup = putBack(clearCurrent ? token : token.next);
    if (clearCurrent) {
      token.next = null;
    }
    back(backup, begin);
  }

  int putBack(Token token) {
    if (token == null) {
      return 0;
    }
    // Only put back tokens we haven't already put back.
    // We want to leave the tokens attached, because
    // we may accidentally detach a legitimate token if a lookahead fails.
    if (token.detached) {
      return 0;
    }
    token.detached = true;
    int backup = putBack(token.specialToken);
    backup += token.image.length();
    Token putBack = token.next;
    token.next = null;
    return backup + putBack(putBack);
  }

  void fixState(Token token) {
    ParseState p = current;
    while (p.getSrc() != null) {
      if (p.getSrc().jjtGetLastToken() == null) {
        p.getSrc().addJunk(token);
      }
      p = p.getParent();
    }
  }

  public Token token() {
    return token;
  }

  public void checkInterrupts() {
    if (Thread.interrupted()) {
      throw new ParseCancelled();
    }
  }
}

PARSER_END(Chunker)

TOKEN_MGR_DECLS : {
    Stack stateStack = new Stack();
    boolean first;

  void backup(int n) { input_stream.backup(n); }

  void putBack(Token token, StringBuilder image, int amt) {
      input_stream.backup(amt);
      assert image.length() - amt >= 0;
      image.setLength(image.length()-amt);
      token.image = image.toString();
      token.endColumn -= amt;
  }

  public void CommonTokenAction(Token t) {
       // hm... perhaps we should also -- the specialToken length from startIndex here?
       // we handle it manually already, but will need to do more testing on comments
       // (i.e. our only SpecialTokens) later...
       t.startIndex =  getCurrentTokenAbsolutePosition();
       t.endIndex = t.startIndex + t.image.length();
       t.tokenBegin = input_stream.tokenBegin;
  }

  public int getCurrentTokenAbsolutePosition() {
      final SimpleCharStream in = input_stream;
      return jjmatchedKind == EOF && in.getTotalCharsRead() + 1 == in.getMaxNextCharInd()
          ? in.getMaxNextCharInd()
          : in.getAbsoluteTokenBegin();
  }

  public SimpleCharStream stream() {
      return input_stream;
  }

}


// we ignore slash newlines (for python's benefit)
// however, we will match actual newlines,
// since those are semantically significant in python and groovy
SKIP:
{
  "\\\n"
| "\\\r"
| "\\\r\n"
}


TOKEN:
{
  // An internal token for any newline character
   < #NL: "\n"|"\r"|"\r\n"  >
}


TOKEN :
{
   <#ESCAPED_NL: "\\" [ "\n", "\r" ] > // for python, allow `\` to end a line; we will want to filter these out when appropriate...
}


TOKEN :
{
  <#ESCAPED_APOS: "\\'" >
|  |  | ~["'", "\n", "\r"] )+ >
|  > : DEFAULT
}


TOKEN :
{
  <#ESCAPED_QUOTE: "\\\"" >
|   |  | ~["\"", "\n", "\r" ] )+ >
|  > : DEFAULT
}


// Whitespace tokens; only considered in DEFAULT mode (collecting up assignment / statements).

TOKEN:
{
   // We leave whitespace and newline tokens in the parsed ast;
   // places where whitespace can be ignored, we will simply discard the tokens.
   // This is done in preparation to properly understand python structures (where leading whitespace is significant)
   < WHITESPACE: (" " | "\t")+ >
}

// Newline must be checked for inside of strings as well...

TOKEN:
{
  // Eat whitespace before a newline, but not after (thx python)
   < NEWLINE: ( " " | "\t" )* (  )+  >
}


// Handle comments; for now, we'll just totally ignore them,
// however, in the future, we may want to offer some kind of intelligent completion there as well.

< DEFAULT >
SPECIAL_TOKEN :
{
  // Ugh...  need to pick "//" or "#" based on groovy or python.
  // double slashes IS a valid operator in python (floor division)
  // so...  not really sure how to handle this at the tokenizer level...
  // probably makes the most sense to bifurcate DEFAULT into GROOVY and PYTHON states.
  // This is fairly straight forward for the special tokens, but we'll need to ensure
  // other token semantics are updated / possibly duplicated as well...
  )? >
}

< DEFAULT >
SPECIAL_TOKEN :
{
  < "/**" ~["/"] > { input_stream.backup(1); } : IN_JAVA_DOC_COMMENT
|
  < "/*" > : IN_MULTI_LINE_COMMENT
}

// Once in a multi-line comment, gobble up everything that isn't */

MORE :
{
  < ~["*"] | ( "*" ~["/"] ) >
}

// Exiting from multi-line comments

SPECIAL_TOKEN :
{
   : DEFAULT
}


SPECIAL_TOKEN :
{
   : DEFAULT
}

// Alright, done with whitespace and comments; now for strings:

< DEFAULT >
TOKEN:
{
  < QUOTE: "\"" > : STR_QUOTE
| < APOS: "'" > : STR_APOS
}

< DEFAULT > // TODO(later) python-only....
TOKEN:
{
  < FROM_LOCAL: "from" (  )+ (".")+ (  )* >
}


// Define some whitespace and universally recognized punctuation tokens
< DEFAULT >
TOKEN:
{
    // Tokens suitable for identifiers
   < NEW: "new" >
|  < EXTENDS: "extends" >
|  < IMPLEMENTS: "implements" >
|  < CLASS: ("class" | "interface" | "enum" | "@interface") >
|  < SUPER: "super" >
|  < ID:
    ["A"-"Z","a"-"z", "_", "$" ]
    (["A"-"Z","a"-"z","0"-"9", "_", "$" ])*
    >
| < #DIGIT: [ "0"-"9" ] ( [ "0"-"9", "_" ] )* >
| < #SCIENTIFIC: [ "e", "E" ] ( "-" )?   >
| < NUM:
    // see https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
    // we'll allow for all valid java formats; will consider python/groovy later
    ( "-" | "+" )?
    (
      // hex
      "0x" [ "0"-"9", "a"-"f", "A"-"F" ] ( [ "0"-"9", "a"-"f", "A"-"F", "_" ] )*
    )
    |
    (
      // binary
      "0b" ( [ "0"-"1" ] )+
    )
    |
    (
      // regular digits
      
      // optional suffixes
      (
          "l" | "L" |
          ( "."
              (  )?
              ( ["f", "F", "d", "D" ] |  )?
          )?
          | 
      )?
    ) >
| < #NUM_OP: "+" | "-" | "*" | "**" | "/" | "%" | "@" | "&" | "|" | "^" | ">>" | "<<" > // Consider //=
| < DOUBLE_EQ: "=" ( "=" | "~" | "=~" ) >
| < ASSIGN:  (  )? "=" ( ~[ "=", "~" ] )? > {
          // We gobble up an (optional) "extra char" to check for and avoid == and =~.
          // Here, we will put that extra char back, since we don't want it in our token
          if (image.charAt(image.length()-1) != '=') {
            putBack(matchedToken, image, 1);
          }
        }
| < INVOKE: (  (  |  )* )? "(" >
| < CLOSURE_START: "{" >
| < COLON: ":" >
| < CLOSURE_END: "}" >
| < AT: "@" >
| < ARRAY_START: "[" >
| < ARRAY_END: "]" >
| < ARROW: "->" >
| < LT: "<" >
| < GT: ">" >
| < QUEST: "?" >
| < AMP: "&" >
| < STARS: "*" ( "*" )? >
| < LOGIC:
    // we aren't concerned with correct structural use of any operators;
    // we just want to be able to gobble them up without producing syntax errors
     "!="
    | "!"
    // we may consider ternaries properly ...later.
    | ( "?"  ) // allow elvis operator (plain ? handled by , which we grab from productions manually)
    // explicitly not adding  as it gets special handling (b/c python and groovy use it differently)
    | (  ( "in" | "as" | "is" | "instanceof" ) (  |  ) ) // groovy uses some words as operators
    | "*:" // groovy spread map
    | ( ".." ( "<" )? ) // groovy ranges
    | "<=>" // groovy spaceship operator
    | ( ">" ( "=" | ">" | ">>" ) )
    | ( "<" ( "=" | "<" ) )
    | ( "+" ( "+" )? )
    | ( "-" ( "-" )? )
//    | ( "*" ( "*" )? )
    | "/"
    | "%"
    | "&&"
    | ( "|" ( "|" )? )
    | "^"
    | "~"
 >
| < ACCESS:
    (
      (  | "*" ) // groovy
      "."
    ) |
    (
      "."
      (  | "@" )? // more groovy
    )
>
| < TRIPLE_QUOTES:
  "\"\"\""
  (
      ( "\\\"" )
      |
      (
        ~["\""]
        | ( "\"" ~["\""] )
        | ("\"\"" ~["\""] )
      )
  )*
  ( "\"\"\"" )?
  >

| < TRIPLE_APOS:
  "'''"
  (
      ( "\\'" )
      |
      (
          ~["'"]
          | ( "'" ~["'"] )
          | ("''" ~["'"] )
      )
  )*
  ( "'''" )?
  >
| < COMMA: "," >
| < SEMI: ";" >
| < CLOSE_PAREN: ")" >
}

// Do not define new token regex here, unless they are for junk nodes!
// The actual AST nodes defined below

ChunkerDocument Document() :
{/*@bgen(jjtree) Document */
ChunkerDocument jjtn000 = new ChunkerDocument(this, JJTDOCUMENT);
boolean jjtc000 = true;
jjtree.openNodeScope(jjtn000);
jjtreeOpenNodeScope(jjtn000);
jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
Node n = jjtn000;
}
{/*@bgen(jjtree) Document */
   try {
/*@egen*/
   // Grab as many statements as we can.
   EatStatements(true)/*@bgen(jjtree)*/
    {
      jjtree.closeNodeScope(jjtn000, true);
      jjtc000 = false;
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
/*@egen*/
    {
      // document is done.  Fill in backlinks.
      jjtn000.jjtGetFirstToken().addBackLinks();
      return jjtn000;
    }/*@bgen(jjtree)*/
   } catch (Throwable jjte000) {
     if (jjtc000) {
       jjtree.clearNodeScope(jjtn000);
       jjtc000 = false;
     } else {
       jjtree.popNode();
     }
     if (jjte000 instanceof RuntimeException) {
       throw (RuntimeException)jjte000;
     }
     if (jjte000 instanceof ParseException) {
       throw (ParseException)jjte000;
     }
     throw (Error)jjte000;
   } finally {
     if (jjtc000) {
       jjtree.closeNodeScope(jjtn000, true);
       if (jjtree.nodeCreated()) {
        jjtreeCloseNodeScope(jjtn000);
       }
       jjtn000.jjtSetLastToken(getToken(0));
     }
   }
/*@egen*/
}

Node EatStatements(boolean persistent)      :
{Node n = null;}
{
  (
     try {
       (
         n = Newline() |
         // TODO: pass leading whitespace into Statement.
         // We do not let Statement() gather leading Whitespace(),
         // as that could result in it being matched eagerly,
         // and we don't want to pay for LL(2+).
         // for now, we can easily search to the left of Statement()'s first token,
         // so we'll avoid any leading whitespace in any production.
         n = Whitespace() |
         n = Statement()
       )
       } catch (ParseException e) {
         // Whenever there is an unhandled parse exception,
         // gobble up the rest of the line and attach to previous node.
         if (n != null) {
           token = n.addJunk(eatJunk());
         }
         fixState(token);
         if (!persistent) {
           return n;
         }
       }
     )*
  { return n; }
}


ChunkerStatement Statement() :
{/*@bgen(jjtree) Statement */
ChunkerStatement jjtn000 = new ChunkerStatement(this, JJTSTATEMENT);
boolean jjtc000 = true;
jjtree.openNodeScope(jjtn000);
jjtreeOpenNodeScope(jjtn000);
jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
Node expr = null; ChunkerAssign assignment = null;
Token ignore = null;
List annotations = Collections.emptyList();
checkInterrupts();
}
{/*@bgen(jjtree) Statement */
  try {
/*@egen*/
  try {
  [ annotations = Annotations() { expr = annotations.get(annotations.size() - 1); } [ Whitespace() ] ]
  (
    LOOKAHEAD( { isTypedAssign() } )
    (
      assignment = TypedAssign() { expr = assignment; }
      [ expr = Values() {
          assignment.setValue(expr);
      }]
    )
    |
    LOOKAHEAD( { isAssign() } )
    (
      assignment = Assign() { expr = assignment; }
      [ expr = Values() {
           assignment.setValue(expr);
      }] // optional to allow for `var =` as an "invalid, but likely thing we want to handle"
    )
    | LOOKAHEAD( { isClassDecl() } )
    (
      expr = JavaClassDecl()
    )
    | (
        // special handling for python `from .name` imports
        []
        expr = Values()
        [
          [ Whitespace() ]
          assignment = Assign() {
            // set the current expression as the scope to assign.
            assignment.addScope(singletonList((IsScope)expr));
          }
          [ Whitespace() ]
          [ expr = Values() ]
        ]
      )
  )
  [ Whitespace() {
    expr.adopt(jjtree.popNode());
   } ]
  (  {
    expr.addToken(token);
  } |  {
    // TODO: mark this node as something that should give type information, if any, to it's sibling.
    // when we are ready for it, finding a comma token at the end of a statement should suffice to figure it out.
    // This will be useful for groovy closure and python set detection.
    expr.addToken(token);
  } |  {
    // we'll want something better than this for python ... later.
    expr.addToken(token);
  } |  {
    ignore = token;
    back(1, token.tokenBegin);
  } |  {
    ignore = token;
    back(1, token.tokenBegin);
  } | Newline()
    | Eof() {
      // let's keep eof's for now, since "end of the script" is a very common and important cursor position,
      // deserving of special treatment / enhanced guesses.
      // jjtree.popNode();
    } ) // optional semi-colon delimiter
   } catch (ParseException e) {
     // Whenever there is an unhandled parse exception,
     // gobble up the rest of the line and attach to previous node.
     if (expr != null) {
       token = expr.addJunk(eatJunk());
       fixState(token);
     }
   }/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/

  {
      Token removed = jjtn000.removeToken(ignore);
      if (removed != null) {
        token = removed;
      }
      jjtn000.setAnnotations(annotations);
      return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerJavaClassDecl JavaClassDecl() :
{/*@bgen(jjtree) JavaClassDecl */
ChunkerJavaClassDecl jjtn000 = new ChunkerJavaClassDecl(this, JJTJAVACLASSDECL);
boolean jjtc000 = true;
jjtree.openNodeScope(jjtn000);
jjtreeOpenNodeScope(jjtn000);
jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
boolean wellFormed = false;
}
{/*@bgen(jjtree) JavaClassDecl */
  try {
/*@egen*/
  // This is brutish, but we don't inspect these ast nodes at all right now...
  // and likely not for quite a while... we'd be much better off to defer to native parsers here.
  
  [ 
    [
      // TODO: handle well-formed-ness and associated warnings / suggestions when that fails.
      TypeDecl()
      [  ]
      [
        
        [
          
          TypeDecl()
          (
            [  ]
            
            [  ]
            [ TypeDecl() ]
          )*
        ]
        [  ]
      ]
      [
        
        [
          
          TypeDecl()
          (
            [  ]
            
            [  ]
            [ TypeDecl() ]
          )*
        ]
        [  ]
      ]
    ]
  ]
  [  ]
  [
    (
      
      // TODO something which actually eats member declarations... and arbitrary `{ statementBlocks() }`
      EatStatements(false)
      [  {
        wellFormed = true;

      } ]
    )
    | Eof()
  ]/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/

  {
    jjtn000.setWellFormed(wellFormed);
    return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerAssign Assign() :
{/*@bgen(jjtree) Assign */
  ChunkerAssign jjtn000 = new ChunkerAssign(this, JJTASSIGN);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) Assign */
  try {
/*@egen*/
  Ident()
  [  ]
  
  [  ]/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerTypedAssign TypedAssign() :
{/*@bgen(jjtree) TypedAssign */
  ChunkerTypedAssign jjtn000 = new ChunkerTypedAssign(this, JJTTYPEDASSIGN);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) TypedAssign */
  try {
/*@egen*/
  TypeDecl()
  [  ]
  Ident()
  [  ]
  
  [  ]/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerTypeDecl TypeDecl():
{/*@bgen(jjtree) TypeDecl */
  ChunkerTypeDecl jjtn000 = new ChunkerTypeDecl(this, JJTTYPEDECL);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) TypeDecl */
  try {
/*@egen*/
  
  [  ]
  [ TypeParams() ]/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerTypeParams TypeParams() :
{/*@bgen(jjtree) TypeParams */
  ChunkerTypeParams jjtn000 = new ChunkerTypeParams(this, JJTTYPEPARAMS);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/ boolean wellFormed=false; }
{/*@bgen(jjtree) TypeParams */
  try {
/*@egen*/
  
  (
    (  |  )*
    TypeParam(true)
      ( (  |  )*  (  |  )* TypeParam(true) )*
  )?
  [  ]

  [  { wellFormed = true; } ]/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {
    jjtn000.setWellFormed(wellFormed);
    return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerTypeParam TypeParam(boolean canWildcard) :
{/*@bgen(jjtree) TypeParam */
  ChunkerTypeParam jjtn000 = new ChunkerTypeParam(this, JJTTYPEPARAM);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/ boolean wellFormed=false; }
{/*@bgen(jjtree) TypeParam */
  try {
/*@egen*/
  (
    ( 
      (  |  )*
      (
        
        (  |  )*
        [  ]
        (  |  )*
      )*
    )
    |
     {
      if (!canWildcard) {
        report(MALFORMED_TYPE_ARGUMENT);
      }
    }
  )
  (  |  )?
  (
    (  |  )
    [  |  ]
    (  (  [  ] )*  )
    [ TypeParams() ]
    (
      
      [  [ TypeParams() ] ]
    )*
  )?/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {
    jjtn000.setWellFormed(wellFormed);
    return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerIdent Ident() :
{/*@bgen(jjtree) Ident */
  ChunkerIdent jjtn000 = new ChunkerIdent(this, JJTIDENT);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) Ident */
  try {
/*@egen*/
  /*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerNum Num() :
{/*@bgen(jjtree) Num */
  ChunkerNum jjtn000 = new ChunkerNum(this, JJTNUM);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) Num */
  try {
/*@egen*/
  /*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerWhitespace Whitespace() :
{/*@bgen(jjtree) Whitespace */
  ChunkerWhitespace jjtn000 = new ChunkerWhitespace(this, JJTWHITESPACE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) Whitespace */
  try {
/*@egen*/
  /*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerMethodName MethodName() :
{/*@bgen(jjtree) MethodName */
  ChunkerMethodName jjtn000 = new ChunkerMethodName(this, JJTMETHODNAME);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) MethodName */
  try {
/*@egen*/
  /*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerNewline Newline() :
{/*@bgen(jjtree) Newline */
  ChunkerNewline jjtn000 = new ChunkerNewline(this, JJTNEWLINE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) Newline */
  try {
/*@egen*/
  /*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerNew New() :
{/*@bgen(jjtree) New */
ChunkerNew jjtn000 = new ChunkerNew(this, JJTNEW);
boolean jjtc000 = true;
jjtree.openNodeScope(jjtn000);
jjtreeOpenNodeScope(jjtn000);
jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
boolean wellFormed = false;
String name = null;
ChunkerInvokable parent = invoker;
List scope = null;
}
{/*@bgen(jjtree) New */
  try {
/*@egen*/
    [ TypeParams() ] [ scope = Scope() { jjtn000.addScope(scope); } ] [  ] [
   (
       LOOKAHEAD( { isTypedInvoke(true) } )
       (
         { name = token.image; }
        [  ]
        TypeParams()
        [  ]
        MethodName() {
           name += "." + token.image.substring(0, token.image.length()-1);
        }
       )
       |
       MethodName() {
         // shave off the opening (
         name = token.image.substring(0, token.image.length()-1);
       }
     )
     [ Whitespace() ]
     { invoker = jjtn000; }
     [ MethodArguments(jjtn000) ]
     { invoker = parent; }
     try {
     (
         { wellFormed = true; }
        | Newline() {
          // we want to let newline end the final statement of source,
          // but we don't actually want to keep the token; that will be needed
          // to finish a not-the-last statement in a block of source.
          jjtn000.setWellFormed(false);
          putBackTokens(true);
          token.next = null;
          jjtree.popNode();
        }
        | Eof() {
          wellFormed = false;
        }

     )
     } catch (ParseException e) {
       // This invocation is malformed; recover here so we can continue
       Node node = jjtree.nodeArity() > 0 ? jjtree.peekNode() : jjtn000;
       Token junk = eatJunk();
       node.addToken(junk);
     }
   ]/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {
  if (name == null) { wellFormed = false; }
  jjtn000.setName(name);
  jjtn000.setWellFormed(wellFormed);
  return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

Node maybeBin(Node src)      :
{
      ChunkerBinaryExpression result;
      assert src == jjtree.peekNode();
      jjtree.popNode();
}
{
  result = BinaryExpression(src)
  {
    if (result.getJoiner() == null && result.getRight() == null) {
      jjtree.pushNode(src);
      return src;
    }
    return result.rescope(null);
  }
}

// Forms a chain of whitespace-separated values;
// anything that actually matches in here is likely either
// malformed, or deserving of it's own production
// (but, for now, we're being extremely lax, so
// the parser can at least survive valid source that it
// simply does not understand well enough yet).
Node Values()      :
{
  Node value;
  ChunkerWhitespace ws = null;
}
{
  (
    value = Expression()
    [ ws = Whitespace() {
        jjtree.popNode();
    }]
    ( value = maybeBin(value) {
        if (ws != null) {
          value.insertChild(ws, 1);
          ws = null;
        }
     }
      [ ws = Whitespace() {
        jjtree.popNode();
      }]
    )*
  )
    {
    if (ws != null) {
      jjtree.pushNode(ws);
    }
    return value;
    }
}

Node MethodArg()      :
{ Node value; }
{

  (
    LOOKAHEAD( { isPythonAnnotated() } )
    ( value = Ident()  {
      value.addToken(token);
     } )
    |
    value = Values()
  )

  {
    return value;
  }
}
void MethodArguments(ChunkerInvokable invokable)      :
{ Node value; ChunkerAssign assign; }
{

    // handle argument lists
    [ 
      [ Whitespace() ]
    ]
    value = MethodArg() {
      invokable.addArgument(value);
    }
    // once there is an unmatched open paren, eat both newlines and whitespace
    ( Whitespace() | Newline() )*
    (
      LOOKAHEAD( { isAssign() } )
      assign = Assign() [ value = Values() {
          assign.setValue(value);
      }]
    )?
    ( Whitespace() | Newline() )*
    (
      
      ( Whitespace() | Newline() )*
      [  ]
      ( Whitespace() | Newline() )*
      [
        value = MethodArg() {
          invokable.addArgument(value);
        }
        ( Whitespace() | Newline() )*
      ]
   )*
}

List Scope()      :
{
  ChunkerIdent addTo;
  ChunkerTypeParams typeParams;
  List sub, result = new ArrayList();
}
{
    LOOKAHEAD( { isScope() } )
    (
      addTo = Ident() {
        // hm, should probably pop this node, and force callers to always stash the actual nodes on something.
       result.add(addTo);
       jjtree.popNode();
      }
      // handle type argument lists
      [ typeParams = TypeParams() {
        addTo.setTypeParams(typeParams);
        // we are eating these TypeParams, so remove them from the stack.
        jjtree.popNode();
      } ]
       {
        // add this to the Ident that we'll be returning.
        // We should maybe have a different subtype for this...
        addTo.addToken(token);
      }
      [ sub = Scope() {
        result.addAll(sub);
      }]
    )
    {
      return result;
    }

}

List Annotations()      :
{
  ChunkerAnnotation anno;
  List result = new ArrayList();
}
{
  (
    ( anno = Annotation() {
        result.add(anno);
    } )+
  )
  {
    return result;
  }
}

ChunkerAnnotation Annotation():
{/*@bgen(jjtree) Annotation */
  ChunkerAnnotation jjtn000 = new ChunkerAnnotation(this, JJTANNOTATION);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
  boolean wellFormed = false;
}
{/*@bgen(jjtree) Annotation */
  try {
/*@egen*/
  (
     [ Ident() {
      wellFormed = true;
    } ]
    [ Whitespace() ]
    [ Invoke() ]
    // TODO: if python, _require_ a newline for each decorator
    ( Whitespace() | Newline() )*
  )/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerInvoke Invoke():
{/*@bgen(jjtree) Invoke */
  ChunkerInvoke jjtn000 = new ChunkerInvoke(this, JJTINVOKE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
  boolean wellFormed = false;
  String name;
  ChunkerInvokable parent = invoker;
}
{/*@bgen(jjtree) Invoke */
  try {
/*@egen*/
  (
    LOOKAHEAD( { isTypedInvoke(false) } )
    (
     TypeParams()
     [  ]
     MethodName() { name = token.image.substring(0, token.image.length()-1); }
    )
    |
    MethodName() {
      // shave off the opening (
      name = token.image.substring(0, token.image.length()-1);
    }
  )
  ( Whitespace() | Newline() )*

  { invoker = jjtn000; }
  [ MethodArguments(jjtn000) ]
  {
    if (parent != null) {
   //   jjtThis.addScope(parent);
    }
    invoker = parent;
  }
  try {
    (
      (
         {
          wellFormed = true;
        }
        // technically, this should be reserved only for method declarations, not invocations,
        // but we currently are not differentiating, and are generally allowing all malformed syntax,
        // so, here it goes...
        [  ]
        [
          
          [ Values() ]
          {
            wellFormed = false;
          }
          [  {
            wellFormed = true;
          } ]
        ]
      )
      | Newline() {
        // we want to let newline end the final statement of source,
        // but we don't actually want to keep the token; that will be needed
        // to finish a not-the-last statement in a block of source.
        jjtn000.setWellFormed(false);
      }
      | Eof() {
       wellFormed = false;
     }
    )
  } catch (ParseException e) {
     // This invocation is malformed; recover here so we can continue
     Node node = jjtree.nodeArity() > 0 ? jjtree.peekNode() : jjtn000;
     Token junk = eatJunk();
     node.addToken(junk);
  }/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {
      jjtn000.setWellFormed(wellFormed);
      jjtn000.setName(name);
      return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerParam Param():
{/*@bgen(jjtree) Param */
  ChunkerParam jjtn000 = new ChunkerParam(this, JJTPARAM);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
  List scope;
  Node value; ChunkerAssign assign;

}
{/*@bgen(jjtree) Param */
  try {
/*@egen*/
  (
    [
      LOOKAHEAD( { isScope() } )
      scope = Scope() {
        for (IsScope ident : scope) {
          jjtn000.addChild(ident);
        }
      }
    ]
    Ident()
    [  ]
    [  // python support
        Values()
    ]
    [ TypeParams() ]
    [  ]
    [ Array() ]
    [
      
      [
         (
           LOOKAHEAD( { isAssign() } )
           // try to gobble up assigns first...
           assign = Assign()
           (  |  )*
           [ value = Expression() {
             assign.setValue(value);
           }]
         )
           // settle for a plain identifier
         | Ident()
      ]
    ]
    [  ]
  )/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {return jjtn000;}/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

ChunkerClosure Closure():
{/*@bgen(jjtree) Closure */
  ChunkerClosure jjtn000 = new ChunkerClosure(this, JJTCLOSURE);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
  boolean wellFormed = false, hasType = false, hadComma = false;
  Node n = null;
  List scope;
  List params;
}
{/*@bgen(jjtree) Closure */
  try {
/*@egen*/
  
    // TODO: consider python dictionaries before getting here (i.e. make a method that peeks for dictionary syntax)
  ( Whitespace() | Newline() )*
  [
    LOOKAHEAD({ isParamList() })
    n = ClosureParams() [  ]
  ]
  ( Whitespace() | Newline() )*
   EatStatements(false)
  ( Whitespace() | Newline() )*

  try {
    (
      (
         { wellFormed = true; }
        [ Whitespace() {
          jjtree.popNode();
         }]
        [ Invoke() ]
      )
      | Eof() {
        jjtn000.setWellFormed(false);
      }
    )
  } catch (ParseException e) {
     // This invocation is malformed; recover here so we can continue
     Node node = jjtree.nodeArity() > 0 ? jjtree.peekNode() : jjtn000;
     Token junk = eatJunk();
     node.addToken(junk);
  }/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {
      jjtn000.setWellFormed(wellFormed);
      return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

Node ClosureParams()      :
{Node n = null;}
{
    try {
      n = Param()
      (
        
        (  |  )*
        n = Param()
      )*
    } catch (ParseException ignored) {
      // any matched tokens should be put back for us...
    }

  { return n; }
}


ChunkerArray Array():
{/*@bgen(jjtree) Array */
  ChunkerArray jjtn000 = new ChunkerArray(this, JJTARRAY);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
  boolean wellFormed = false;
  Node n = null;
}
{/*@bgen(jjtree) Array */
  try {
/*@egen*/
  
  // TODO: consider python list comprehension here
  ( Whitespace() | Newline() )*
  [ MethodArguments(jjtn000) ]
  ( Whitespace() | Newline() )*

  try {
    (
       { wellFormed = true; }
      | Eof() {
        wellFormed = false;
      }
    )
  } catch (ParseException e) {
     // This invocation is malformed; recover here so we can continue
     Node node = jjtree.nodeArity() > 0 ? jjtree.peekNode() : jjtn000;
     Token junk = eatJunk();
     node.addToken(junk);
  }/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {
      jjtn000.setWellFormed(wellFormed);
      return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

Node Expression()      :
{
    Node expr = null, expr2 = null, anchor = null;
    String name;
    ChunkerBinaryExpression bin = null;
    ChunkerWhitespace ws = null;
    ChunkerAssign assign = null;
    boolean wellFormed = false;
}
{
  (
    (
      // | expr = ClassDef()
      // | expr = FuncDef() // more python-y stuff...
      (
        expr = Closure()
      ) | (
        expr = Array()
      ) | (
        expr = Invoke()
      ) | (
        expr = String()
      ) | (
        expr = Num()
      ) | (
        expr = New()
      ) | (
        expr = Ident()
          [
           ws = Whitespace() {
             jjtree.popNode();
           }
           // Space-delimited expressions; this is how we ignore "keywords" like return or println
           // We should do something special when these might actually be groovy "shorthand": method calls w/out ()
           [ expr2 = Expression() {
             ChunkerBinaryExpression b = new ChunkerBinaryExpression(this, JJTBINARYEXPRESSION);
             jjtree.popNode();
             jjtree.popNode();
             b.setLeft(expr);
             b.insertChild(ws, 1);
             b.setJoiner(ws.jjtGetFirstToken());
             b.setRight(expr2);
             expr = b.rescope(null);
             jjtree.pushNode(expr);

           }]
          ]
      )
    )

    [ ws = Whitespace() {
      jjtree.popNode();
    } ]

    // after a valid expression, there may be some things to chain into a more complex expression...
    [
      {
        jjtree.popNode();
      }
      bin = BinaryExpression(expr) {
        if (bin.getRight() == null && bin.getJoiner() == null) {
          if (ws != null) {
            jjtree.pushNode(ws);
          }
          bin = null;
        } else {
          if (ws != null) {
            bin.insertChild(ws, 1);
          }
          expr = bin.rescope(null);
          if (bin != expr) {
            jjtree.popNode();
            jjtree.pushNode(expr);
            bin = null;
          }
        }
      }
    ]
    [
      (
         {
          // If we saw an assign statement, then we need to make the current expression the scope of an Assign().
          assign = new ChunkerAssign(this, JJTASSIGN);
          jjtree.popNode();
          assign.addChild(bin == null ? expr : bin);
          bin = null;
          if (ws != null) {
            assign.addChild(ws);
          }
          assign.addToken(token);
          jjtree.pushNode(assign);
          expr = assign;
        }
      )
      [  {
          assign.addToken(token);
      }]
      [ expr2 = Values() {
        assign.setValue(expr2);
        jjtree.popNode();
        assign.addChild(expr2);
      }]
    ]
  )

  {
  if (assign != null) {
    // TODO: make addChild fix tokens so we can delete this workaround
    assign.jjtSetLastToken(token);
  }
  return bin == null ? expr : bin;
  }
}

void EatBinaryWhitespace()      :
{}
{
  LOOKAHEAD( { isBinExprAcrossNewline() } )
  (  |  )+
  {}
}

ChunkerBinaryExpression BinaryExpression(Node left) :
{/*@bgen(jjtree) BinaryExpression */
  ChunkerBinaryExpression jjtn000 = new ChunkerBinaryExpression(this, JJTBINARYEXPRESSION);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
  boolean wellFormed = left.isWellFormed();
  Node right = null;
  Token joiner;
  jjtn000.setLeft(left);
//  jjtree.popNode();
}
{/*@bgen(jjtree) BinaryExpression */
  try {
/*@egen*/
  (
    // TODO: handle newline followed by ACCESS or LOGIC
    [ EatBinaryWhitespace() ]
    (
       {
        if (!(token.image.equals("++") || token.image.equals("--"))) {
          wellFormed = false; // if there is an expression, it will be reset to true...
        }
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        wellFormed = false; // not legal to have this w/out a followup expression
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        wellFormed = false; // not legal to have this w/out a followup expression
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        wellFormed = false; // not legal to have this w/out a followup expression
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        wellFormed = false; // not legal to have this w/out a followup expression
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        wellFormed = false; // not legal to have this w/out a followup expression
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        wellFormed = false; // not legal to have this w/out a followup expression
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      |  {
        joiner = token;
        jjtn000.setJoiner(joiner);
      }
      | right = Array() {


        ChunkerBinaryExpression b = new ChunkerBinaryExpression(this, JJTBINARYEXPRESSION);
        jjtree.popNode();
        b.setLeft(left);
        // no joiner when we're mashing a trailing [] onto an expression (optional parens are troublesome)
        b.setRight(right);
        left = b;
        right = null;
        jjtn000.setLeft(b);
        wellFormed = left.isWellFormed();
      }
    )
    [  ]

    // recurse into the "any valid value" production.
    [ right = Expression() {
      wellFormed = left.isWellFormed();
      jjtn000.setRight(right);
      Node maybeWs = jjtree.popNode();
      if (maybeWs instanceof ChunkerWhitespace) {
        Node next = jjtree.popNode();
        assert next == right;
        jjtree.pushNode(maybeWs);
      }
    }]

    [  ]
  )/*@bgen(jjtree)*/
{
  jjtree.closeNodeScope(jjtn000, true);
  jjtc000 = false;
  if (jjtree.nodeCreated()) {
   jjtreeCloseNodeScope(jjtn000);
  }
  jjtn000.jjtSetLastToken(getToken(0));
}
/*@egen*/
{
    // TODO: transform  types to scopes...
    jjtn000.setWellFormed(wellFormed);
    return jjtn000;
}/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}


ChunkerString String() :
{/*@bgen(jjtree) String */
ChunkerString jjtn000 = new ChunkerString(this, JJTSTRING);
boolean jjtc000 = true;
jjtree.openNodeScope(jjtn000);
jjtreeOpenNodeScope(jjtn000);
jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/
boolean wellFormed = false;
StringBuilder b = new StringBuilder();
String quoteType;
}
{/*@bgen(jjtree) String */
  try {
/*@egen*/
  (
    (
      
      {
        quoteType = token.image;
        wellFormed = false;
      }
      [
        [  { b.append(token.image); } ]
        (
          Newline() {
            // TODO: take a parameter to this production that controls
            // whether we simply absorb the newlines, or form a malformed ast node.
            jjtn000.setWellFormed(false);
            jjtree.popNode();
            putBackTokens(true);
            token.next = null;
          }
          | Eof() {
            wellFormed = false;
            jjtree.popNode();
          }
          |  {
            wellFormed = "\"".equals(token.image);
            if (!wellFormed) {
              back(token.image.length(), -1);
            }
          }
        )
      ]
    )
    |
    (
       { quoteType = token.image; }
      {
       wellFormed = false;
       }
      [
        (  {
          b.append(token.image);
        } )*
        (
         Newline() {
           // TODO: take a parameter to this production that controls
           // whether we simply absorb the newlines, or form a malformed ast node.
           jjtn000.setWellFormed(false);
           jjtree.popNode();
           putBackTokens(true);
           token.next = null;
         }
         | Eof() {
           wellFormed = false;
           jjtree.popNode();
          }
         |  {
           wellFormed = "'".equals(token.image);
           if (!wellFormed) {
             back(token.image.length(), -1);
           }
         }
        )
      ]
    )
  )/*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  {
    jjtn000.initialize(quoteType, b.toString(), wellFormed);
    return jjtn000;
  }/*@bgen(jjtree)*/
  } catch (Throwable jjte000) {
    if (jjtc000) {
      jjtree.clearNodeScope(jjtn000);
      jjtc000 = false;
    } else {
      jjtree.popNode();
    }
    if (jjte000 instanceof RuntimeException) {
      throw (RuntimeException)jjte000;
    }
    if (jjte000 instanceof ParseException) {
      throw (ParseException)jjte000;
    }
    throw (Error)jjte000;
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}

// This is here so we can easily Eof() { jjtree.popNode(); } to discard eof tokens
ChunkerEof Eof():
{/*@bgen(jjtree) Eof */
  ChunkerEof jjtn000 = new ChunkerEof(this, JJTEOF);
  boolean jjtc000 = true;
  jjtree.openNodeScope(jjtn000);
  jjtreeOpenNodeScope(jjtn000);
  jjtn000.jjtSetFirstToken(getToken(1));
/*@egen*/}
{/*@bgen(jjtree) Eof */
  try {
/*@egen*/
  /*@bgen(jjtree)*/
  {
    jjtree.closeNodeScope(jjtn000, true);
    jjtc000 = false;
    if (jjtree.nodeCreated()) {
     jjtreeCloseNodeScope(jjtn000);
    }
    jjtn000.jjtSetLastToken(getToken(0));
  }
/*@egen*/
  { return jjtn000; }/*@bgen(jjtree)*/
  } finally {
    if (jjtc000) {
      jjtree.closeNodeScope(jjtn000, true);
      if (jjtree.nodeCreated()) {
       jjtreeCloseNodeScope(jjtn000);
      }
      jjtn000.jjtSetLastToken(getToken(0));
    }
  }
/*@egen*/
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy