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

objectos.code.internal.InternalCompiler Maven / Gradle / Ivy

There is a newer version: 0.8.2
Show newest version
/*
 * Copyright (C) 2014-2023 Objectos Software LTDA.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package objectos.code.internal;

import java.util.function.IntPredicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import objectos.util.IntArrays;

class InternalCompiler extends InternalApi {

  @FunctionalInterface
  private interface Action {
    void execute();
  }

  @FunctionalInterface
  private interface SwitchAction {
    void execute(int proto);
  }

  static final int TRUE = 1, FALSE = 0;

  private static final int _START = 0,
      _ANNOTATION = 1,
      _BLOCK = 2,
      _COMMA = 3,
      _ENUM_CONSTANT = 4,
      _IDENTIFIER = 5,
      _KEYWORD = 6,
      _NEW_LINE = 7,
      _PRIMARY = 8,
      _PRIMARY_NL = 9,
      _SYMBOL = 10,
      _TYPE = 11;

  private static final int _CTX_OTHER = -1, _CTX_IFACE = -2;

  protected final void compile() {
    codeIndex = 0;

    // simpleName
    stackArray[1] = NULL;
    // public found
    stackArray[2] = NULL;
    // slot
    stackArray[3] = NULL;
    // topLevel
    stackArray[4] = NULL;
    // error
    stackArray[5] = 0;
    // abstract found
    stackArray[6] = NULL;
    // last
    stackArray[7] = NULL;

    stackIndex = 7;

    // do not change
    // - objectIndex

    try {
      compilationUnit();
    } catch (RuntimeException e) {
      codeAdd(Whitespace.NEW_LINE);
      codeAdd(Whitespace.NEW_LINE);

      var collector = Collectors.joining(
        System.lineSeparator(),
        e.getMessage() + System.lineSeparator() + System.lineSeparator(),
        ""
      );

      var stackTrace = Stream.of(e.getStackTrace())
          .map(Object::toString)
          .collect(collector);

      codeAdd(ByteCode.RAW, object(stackTrace));

      e.printStackTrace();
    }

    codeAdd(ByteCode.EOF);
  }

  final void noopState(String element, String state) {
    int proto = protoNext();

    errorRaise(
      "no-op item '%s' at '%s' (State '%s')".formatted(protoName(proto), element, state)
    );
  }

  final int topLevel() { return stackArray[4]; }

  final void topLevel(int value) { stackArray[4] = value; }

  private boolean abstractFound() { return stackArray[6] != NULL; }

  private void abstractFound(int value) { stackArray[6] = value; }

  private void annotationValue() {
    // TODO name

    lang();
  }

  private void arg() {
    lang();
  }

  private void argList() {
    argumentStart();

    int nl = countNewLine();

    if (protoIs(ByteProto.ARGUMENT)) {
      nl = newLine(nl);

      execute(this::arg);

      nl = countNewLine();

      while (protoIs(ByteProto.ARGUMENT)) {
        nl = newLine(nl);

        argumentComma();

        execute(this::arg);

        nl = countNewLine();
      }
    }

    argumentEnd();

    nl = newLine(nl);
  }

  private void argument() {
    if (!lastIs(_START)) {
      argumentComma();
    }

    lang();
  }

  private void argumentComma() {
    int nl = 0;

    int index = codeIndex;

    while (index >= 0) {
      int value = codeArray[--index];

      if (value != Whitespace.NEW_LINE.ordinal()) {
        break;
      }

      int code = codeArray[--index];

      if (code != ByteCode.WHITESPACE) {
        break;
      }

      nl++;

      codeIndex = index;
    }

    codeAdd(Symbol.COMMA);

    last(_COMMA);

    for (int i = 0; i < nl; i++) {
      codeAdd(Whitespace.NEW_LINE);

      last(_NEW_LINE);
    }
  }

  private void argumentEnd() {
    codeAdd(Indentation.EXIT_PARENTHESIS);

    if (lastIs(_NEW_LINE)) {
      codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);
    }

    codeAdd(Symbol.RIGHT_PARENTHESIS);
  }

  private void argumentList(int offset) {
    argumentStart();

    listExecute(offset, this::argument);

    argumentEnd();
  }

  private void argumentStart() {
    codeAdd(Symbol.LEFT_PARENTHESIS);

    codeAdd(Indentation.ENTER_PARENTHESIS);

    last(_START);
  }

  private void arrayAccess() {
    codeAdd(Symbol.LEFT_SQUARE_BRACKET);

    last(_START);

    lang();

    codeAdd(Symbol.RIGHT_SQUARE_BRACKET);

    last(_PRIMARY);
  }

  private void arrayDimension() {
    if (itemIs(ByteProto.ARRAY_DIMENSION)) {
      execute(this::arrayDimensionAction);
    } else {
      errorRaise("invalid array dimension");
    }
  }

  private void arrayDimensionAction() {
    codeAdd(Symbol.LEFT_SQUARE_BRACKET);
    codeAdd(Symbol.RIGHT_SQUARE_BRACKET);
  }

  private void arrayInitializer() {
    switch (last()) {
      case _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }
  }

  private void arrayInitializerValueList() {
    codeAdd(Symbol.LEFT_CURLY_BRACKET);
    codeAdd(Indentation.ENTER_BLOCK);
    last(_START);

    consumeWs();

    if (protoIs(ByteProto.VALUE)) {
      execute(this::arg);

      consumeWs();

      while (protoIs(ByteProto.VALUE)) {
        argumentComma();

        execute(this::arg);

        consumeWs();
      }
    }

    codeAdd(Indentation.EXIT_BLOCK);
    if (lastIs(_NEW_LINE)) {
      codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);
    }
    codeAdd(Symbol.RIGHT_CURLY_BRACKET);
  }

  private void arrayType() {
    int item = itemPeek();

    switch (item) {
      case ByteProto.CLASS_TYPE -> execute(this::classType);

      case ByteProto.PRIMITIVE_TYPE -> execute(this::primitiveType);

      default -> errorRaise("'%s' invalid array type".formatted(protoName(item)));
    }

    while (itemMore()) {
      arrayDimension();
    }

    last(_TYPE);
  }

  private void autoImports() {
    switch (last()) {
      default -> codeAdd(ByteCode.AUTO_IMPORTS0);

      case _SYMBOL -> codeAdd(ByteCode.AUTO_IMPORTS1);
    }
  }

  private void beforeTopLevelTypeDeclaration() {
    switch (last()) {
      case _ANNOTATION -> codeAdd(Whitespace.AFTER_ANNOTATION);

      case _KEYWORD -> codeAdd(Whitespace.MANDATORY);
    }
  }

  private void block() {
    switch (last()) {
      case _KEYWORD, _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }

    codeAdd(Symbol.LEFT_CURLY_BRACKET);

    last(_START);

    if (protoIs(ByteProto.STATEMENT)) {
      codeAdd(Indentation.ENTER_BLOCK);

      execute(this::blockStatement);

      while (elemMore()) {
        execute(this::blockStatement);
      }

      codeAdd(Indentation.EXIT_BLOCK);
      codeAdd(Whitespace.BEFORE_NON_EMPTY_BLOCK_END);
    } else {
      codeAdd(Whitespace.BEFORE_EMPTY_BLOCK_END);
    }

    codeAdd(Symbol.RIGHT_CURLY_BRACKET);

    last(_BLOCK);
  }

  private void blockStatement() {
    codeAdd(Whitespace.BEFORE_NEXT_STATEMENT);

    last(_START);

    lang();

    if (!lastIs(_BLOCK)) {
      codeAdd(Symbol.SEMICOLON);

      last(_SYMBOL);
    }
  }

  private void body() {
    codeAdd(Symbol.LEFT_CURLY_BRACKET);

    if (itemMore()) {
      codeAdd(Indentation.ENTER_BLOCK);
      codeAdd(Whitespace.BEFORE_FIRST_MEMBER);

      bodyMember();

      while (itemMore()) {
        codeAdd(Whitespace.BEFORE_NEXT_MEMBER);

        bodyMember();
      }

      if (lastIs(_ENUM_CONSTANT)) {
        slotSemicolon();
      }

      codeAdd(Indentation.EXIT_BLOCK);
      codeAdd(Whitespace.BEFORE_NON_EMPTY_BLOCK_END);
    } else {
      codeAdd(Whitespace.BEFORE_EMPTY_BLOCK_END);
    }

    codeAdd(Symbol.RIGHT_CURLY_BRACKET);

    last(_START);
  }

  private void body(int enumConstants, int members) {
    preSymbol();

    codeAdd(Symbol.LEFT_CURLY_BRACKET);

    last(_START);

    var empty = true;

    if (enumConstants != NULL) {
      empty = false;

      codeAdd(Indentation.ENTER_BLOCK);

      listExecute(enumConstants, this::enumConstant);

      semicolon();
    }

    if (members != NULL) {
      if (empty) {
        empty = false;

        codeAdd(Indentation.ENTER_BLOCK);
      }

      listSwitch(members, this::bodyMember);
    }

    if (!empty) {
      codeAdd(Indentation.EXIT_BLOCK);

      codeAdd(Whitespace.BEFORE_NON_EMPTY_BLOCK_END);
    } else {
      codeAdd(Whitespace.BEFORE_EMPTY_BLOCK_END);
    }

    codeAdd(Symbol.RIGHT_CURLY_BRACKET);

    last(_SYMBOL);
  }

  private void bodyMember() {
    topLevel(FALSE);

    if (itemIs(ByteProto.FIELD_DECLARATION)) {
      execute(this::fieldDeclaration);

      return;
    }

    if (itemIs(ByteProto.CONSTRUCTOR_DECLARATION)) {
      execute(this::constructorDeclaration);

      return;
    }

    if (itemIs(ByteProto.METHOD_DECLARATION)) {
      execute(this::methodDeclaration);

      return;
    }

    var wasEnumConstant = lastIs(_ENUM_CONSTANT);

    last(_START);

    oldDeclarationAnnotationList();

    oldModifierList();

    int item = itemPeek();

    if (wasEnumConstant) {
      if (item == ByteProto.ENUM_CONSTANT) {
        slotComma();
      } else {
        slotSemicolon();
      }
    }

    switch (item) {
      case ByteProto.ARRAY_TYPE,
           ByteProto.CLASS_TYPE,
           ByteProto.PARAMETERIZED_TYPE,
           ByteProto.PRIMITIVE_TYPE,
           ByteProto.TYPE_VARIABLE -> {
        executeSwitch(this::oldType);

        fieldOrMethodDeclaration();
      }

      case ByteProto.BLOCK -> {
        if (lastIs(_KEYWORD)) {
          codeAdd(Whitespace.MANDATORY);
        }

        execute(this::oldBlock);
      }

      case ByteProto.CLASS -> {
        if (lastIs(_KEYWORD)) {
          codeAdd(Whitespace.MANDATORY);
        }

        oldClassDeclaration();
      }

      case ByteProto.ENUM -> oldEnumDeclaration();

      case ByteProto.ENUM_CONSTANT -> execute(this::oldEnumConstant);

      case ByteProto.INTERFACE -> {
        if (lastIs(_KEYWORD)) {
          codeAdd(Whitespace.MANDATORY);
        }

        oldInterfaceDeclaration();
      }

      case ByteProto.TYPE_PARAMETER -> {
        oldTypeParameterList();

        oldMethodDeclarationAfterTypeParameterList();
      }

      case ByteProto.VOID -> {
        execute(this::voidKeyword);

        errorRaise(
          "method declarator not found"
        );
      }

      default -> errorRaise(
        "invalid or no-op body member '%s'".formatted(protoName(item))
      );
    }
  }

  private void bodyMember(int proto) {
    topLevel(FALSE);

    switch (last()) {
      case _START -> codeAdd(Whitespace.BEFORE_FIRST_MEMBER);

      case _SYMBOL -> codeAdd(Whitespace.BEFORE_NEXT_MEMBER);
    }

    switch (proto) {
      case ByteProto.CLASS_DECLARATION -> classDeclaration();

      case ByteProto.CONSTRUCTOR_DECLARATION -> constructorDeclaration();

      case ByteProto.ENUM_DECLARATION -> enumDeclaration();

      case ByteProto.FIELD_DECLARATION -> fieldDeclaration();

      case ByteProto.INTERFACE_DECLARATION -> interfaceDeclaration();

      case ByteProto.METHOD_DECLARATION -> methodDeclaration();

      default -> errorRaise(
        "no-op body member '%s'".formatted(protoName(proto))
      );
    }
  }

  private void classDeclaration() {
    typeDeclaration(Keyword.CLASS);
  }

  private void classDeclarationExtends() {
    execute(this::extendsKeyword);

    if (itemTest(this::isClassOrParameterizedType)) {
      executeSwitch(this::oldType);
    } else {
      error();
    }
  }

  private void classType() {
    preType();

    var packageIndex = protoNext();

    var packageName = (String) objectget(packageIndex);

    autoImports.classTypePackageName(packageName);

    var count = protoNext();

    switch (count) {
      case 1 -> {
        var n1Index = protoNext();

        var n1 = (String) objectget(n1Index);

        autoImports.classTypeSimpleName(n1);

        int instruction = autoImports.classTypeInstruction();

        switch (instruction) {
          case 1 -> {
            codeAdd(ByteCode.IDENTIFIER, n1Index);
          }

          default -> {
            codeAdd(ByteCode.IDENTIFIER, packageIndex);
            codeAdd(Symbol.DOT);
            codeAdd(ByteCode.IDENTIFIER, n1Index);
          }
        }
      }

      case 2 -> {
        var n1Index = protoNext();
        var n2Index = protoNext();

        var n1 = (String) objectget(n1Index);
        var n2 = (String) objectget(n2Index);

        autoImports.classTypeSimpleName(n1);
        autoImports.classTypeSimpleName(n2);

        int instruction = autoImports.classTypeInstruction();

        switch (instruction) {
          case 1 -> {
            codeAdd(ByteCode.IDENTIFIER, n2Index);
          }

          case 2 -> {
            codeAdd(ByteCode.IDENTIFIER, n1Index);
            codeAdd(Symbol.DOT);
            codeAdd(ByteCode.IDENTIFIER, n2Index);
          }

          default -> {
            codeAdd(ByteCode.IDENTIFIER, packageIndex);
            codeAdd(Symbol.DOT);
            codeAdd(ByteCode.IDENTIFIER, n1Index);
            codeAdd(Symbol.DOT);
            codeAdd(ByteCode.IDENTIFIER, n2Index);
          }
        }
      }

      default -> {
        throw new UnsupportedOperationException(
          "Implement me :: count=" + count);
      }
    }

    last(_TYPE);
  }

  private void clause(Keyword keyword) {
    if (elemMore()) {
      switch (last()) {
        case _IDENTIFIER -> keyword(keyword);

        case _TYPE -> comma();
      }

      int nl = countNewLine();

      nl = newLine(nl);

      executeSwitch(this::type);

      nl = countNewLine();

      while (elemMore()) {
        nl = newLine(nl);

        argumentComma();

        executeSwitch(this::type);

        nl = countNewLine();
      }

      nl = newLine(nl);
    }
  }

  private void codeAdd(Indentation value) { codeAdd(ByteCode.INDENTATION, value.ordinal()); }

  private void codeAdd(int v0) {
    codeArray = IntArrays.growIfNecessary(codeArray, codeIndex + 0);

    codeArray[codeIndex++] = v0;
  }

  private void codeAdd(int v0, int v1) {
    codeArray = IntArrays.growIfNecessary(codeArray, codeIndex + 1);

    codeArray[codeIndex++] = v0;
    codeArray[codeIndex++] = v1;
  }

  private void codeAdd(Keyword value) { codeAdd(ByteCode.KEYWORD, value.ordinal()); }

  private void codeAdd(Symbol value) { codeAdd(ByteCode.SYMBOL, value.ordinal()); }

  private void codeAdd(Whitespace value) { codeAdd(ByteCode.WHITESPACE, value.ordinal()); }

  private void comma() {
    codeAdd(Symbol.COMMA);

    last(_COMMA);
  }

  private void compilationUnit() {
    last(_START);

    while (elemMore()) {
      int proto = protoPeek();

      switch (proto) {
        case ByteProto.ANNOTATION -> {
          oldDeclarationAnnotationList();

          if (itemIs(ByteProto.PACKAGE)) {
            ordinaryCompilationUnit();
          } else {
            topLevelDeclarationList();
          }
        }

        case ByteProto.AUTO_IMPORTS -> {
          execute(this::autoImports);

          importDeclarationList();

          topLevelDeclarationList();
        }

        case ByteProto.CLASS,
             ByteProto.ENUM,
             ByteProto.INTERFACE,
             ByteProto.MODIFIER -> topLevelDeclarationList();

        case ByteProto.CLASS_DECLARATION -> {
          preTopLevel();

          execute(this::classDeclaration);
        }

        case ByteProto.END_ELEMENT -> {}

        case ByteProto.ENUM_DECLARATION -> {
          preTopLevel();

          execute(this::enumDeclaration);
        }

        case ByteProto.INTERFACE_DECLARATION -> {
          preTopLevel();

          execute(this::interfaceDeclaration);
        }

        case ByteProto.PACKAGE -> ordinaryCompilationUnit();

        case ByteProto.PACKAGE_DECLARATION -> {
          execute(this::packageDeclaration);
        }

        default -> errorRaise(
          "no-op item @ compilation unit '%s'".formatted(protoName(proto))
        );
      }
    }
  }

  private void constructorDeclaration() {
    int start = stackArray[0];

    abstractFound(NULL);

    last(_START);

    int annotations = NULL,
        modifier = NULL,
        typeParameters = NULL,
        //receiverParameter = NULL,
        parameters = NULL,
        statements = NULL;

    while (protoMore()) {
      int proto = protoPeek();

      if (proto == ByteProto.END_ELEMENT) {
        break;
      }

      switch (proto) {
        case ByteProto.ANNOTATION -> annotations = listAdd(annotations);

        case ByteProto.MODIFIER -> modifier = singleSet(modifier);

        case ByteProto.PARAMETER_DECLARATION -> parameters = listAdd(parameters);

        case ByteProto.STATEMENT -> statements = listAdd(statements);

        case ByteProto.TYPE_PARAMETER -> typeParameters = listAdd(typeParameters);

        default -> {
          throw new UnsupportedOperationException(
            "Implement me :: " + protoName(proto)
          );
        }
      }
    }

    if (annotations != NULL) {
      listExecute(annotations, this::declarationAnnotation);
    }

    if (modifier != NULL) {
      singleExecute(modifier, this::modifier);
    }

    if (typeParameters != NULL) {
      typeParameterList(typeParameters);
    }

    int name = simpleName();
    if (name == NULL) {
      name = object("Constructor");
    }
    preIdentifier();
    codeAdd(ByteCode.IDENTIFIER, name);

    codeAdd(Symbol.LEFT_PARENTHESIS);

    if (parameters != NULL) {
      last(_START);

      listExecute(parameters, this::parameterDeclaration);
    }

    executableBody(statements);

    stackArray[0] = start;
  }

  private void consumeWs() {
    while (protoMore() && protoPeek() == ByteProto.NEW_LINE) {
      execute(this::newLine);
    }
  }

  private int countNewLine() {
    int count = 0;

    while (protoMore() && protoPeek() == ByteProto.NEW_LINE) {
      count++;

      protoIndex += 2;
    }

    return count;
  }

  private void declarationAnnotation() {
    switch (last()) {
      case _ANNOTATION -> {
        codeAdd(Whitespace.NEW_LINE);
        codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);
        last(_START);
      }
    }

    codeAdd(Symbol.COMMERCIAL_AT);

    execute(this::classType);

    if (elemMore()) {
      codeAdd(Symbol.LEFT_PARENTHESIS);

      last(_START);

      execute(this::annotationValue);

      while (elemMore()) {
        comma();

        execute(this::annotationValue);
      }

      codeAdd(Symbol.RIGHT_PARENTHESIS);
    }

    last(_ANNOTATION);
  }

  private void declarationName() {
    preIdentifier();

    codeAdd(ByteCode.IDENTIFIER, protoNext());

    last(_IDENTIFIER);
  }

  private boolean elemMore() {
    if (protoMore()) {
      int proto = protoPeek();

      return proto != ByteProto.END_ELEMENT;
    } else {
      return false;
    }
  }

  private void ellipsis() {
    codeAdd(Symbol.ELLIPSIS);

    last(_SYMBOL);
  }

  private void elseKeyword() {
    preKeyword();

    codeAdd(Keyword.ELSE);

    last(_KEYWORD);
  }

  private void enumConstant() {
    switch (last()) {
      case _ENUM_CONSTANT -> {
        codeAdd(Symbol.COMMA);

        codeAdd(Whitespace.BEFORE_NEXT_MEMBER);

        last(_START);
      }

      case _START -> codeAdd(Whitespace.BEFORE_FIRST_MEMBER);

      case _SYMBOL -> codeAdd(Whitespace.BEFORE_NEXT_MEMBER);
    }

    int annotations = NULL,
        name = NULL,
        arguments = NULL;

    while (protoMore()) {
      int proto = protoPeek();

      if (proto == ByteProto.END_ELEMENT) {
        break;
      }

      switch (proto) {
        case ByteProto.ARGUMENT -> arguments = listAdd(arguments);

        case ByteProto.ANNOTATION -> annotations = listAdd(annotations);

        case ByteProto.DECLARATION_NAME -> name = singleSet(name);

        default -> {
          throw new UnsupportedOperationException(
            "Implement me :: " + protoName(proto)
          );
        }
      }
    }

    if (annotations != NULL) {
      listExecute(annotations, this::declarationAnnotation);
    }

    if (name != NULL) {
      singleExecute(name, this::declarationName);
    } else {
      unnamedConstant();
    }

    if (arguments != NULL) {
      argumentList(arguments);
    }

    last(_ENUM_CONSTANT);
  }

  private void enumDeclaration() {
    typeDeclaration(Keyword.ENUM);
  }

  private void enumKeyword() {
    typeKeyword(Keyword.ENUM);
  }

  private boolean error() {
    var result = stackArray[5] == 1;

    stackArray[5] = 0;

    return result;
  }

  private void errorRaise() { stackArray[5] = 1; }

  private void errorRaise(String message) {
    errorRaise();

    codeAdd(ByteCode.COMMENT, object(message));
  }

  private void executableBody(int statements) {
    codeAdd(Symbol.RIGHT_PARENTHESIS);

    var iface = stackPeek() == _CTX_IFACE;

    if (iface && statements == NULL) {
      semicolon();

      return;
    }

    if (!abstractFound()) {
      codeAdd(Whitespace.OPTIONAL);

      codeAdd(Symbol.LEFT_CURLY_BRACKET);

      if (statements != NULL) {
        codeAdd(Indentation.ENTER_BLOCK);

        listExecute(statements, this::blockStatement);

        codeAdd(Indentation.EXIT_BLOCK);

        codeAdd(Whitespace.BEFORE_NON_EMPTY_BLOCK_END);
      } else {
        codeAdd(Whitespace.BEFORE_EMPTY_BLOCK_END);
      }

      codeAdd(Symbol.RIGHT_CURLY_BRACKET);

      last(_SYMBOL);
    } else {
      semicolon();
    }
  }

  private int execute(Action action) {
    int proto = protoNext();

    int location = protoNext();

    int returnTo = protoIndex;

    protoIndex = location;

    action.execute();

    protoIndex = returnTo;

    return proto;
  }

  private int executeSwitch(SwitchAction action) {
    int proto = protoNext();

    int location = protoNext();

    int returnTo = protoIndex;

    protoIndex = location;

    action.execute(proto);

    protoIndex = returnTo;

    return proto;
  }

  private void expressionName() {
    preDot();

    codeAdd(ByteCode.IDENTIFIER, protoNext());

    last(_IDENTIFIER);
  }

  private void extendsClause() {
    clause(Keyword.EXTENDS);
  }

  private void extendsKeyword() {
    preKeyword();

    codeAdd(Keyword.EXTENDS);

    last(_KEYWORD);
  }

  private void fieldDeclaration() {
    int start = stackArray[0];

    last(_START);

    int annotations = NULL,
        modifiers = NULL,
        type = NULL,
        declarators = NULL;

    loop: while (protoMore()) {
      int proto = protoPeek();

      switch (proto) {
        case ByteProto.ANNOTATION -> annotations = listAdd(annotations);

        case ByteProto.ARRAY_TYPE,
             ByteProto.CLASS_TYPE,
             ByteProto.PARAMETERIZED_TYPE,
             ByteProto.PRIMITIVE_TYPE,
             ByteProto.TYPE_VARIABLE -> type = singleSet(type);

        case ByteProto.MODIFIER -> modifiers = listAdd(modifiers);

        default -> {
          if (proto != ByteProto.END_ELEMENT) {
            declarators = protoIndex;
          }

          break loop;
        }
      }
    }

    if (annotations != NULL) {
      listExecute(annotations, this::declarationAnnotation);
    }

    if (modifiers != NULL) {
      listExecute(modifiers, this::modifier);
    }

    if (type != NULL) {
      singleSwitch(type, this::type);
    } else {
      voidKeyword();
    }

    if (declarators != NULL) {
      protoIndex = declarators;

      fieldDeclarationVariableList();
    } else {
      unnamed();
    }

    semicolon();

    stackArray[0] = start;
  }

  private void fieldDeclarationVariableItem() {
    if (protoIs(ByteProto.DECLARATION_NAME)) {
      execute(this::declarationName);
    } else {
      unnamed();
    }

    int proto = protoPeek();

    if (proto == ByteProto.END_ELEMENT) {
      return;
    }

    if (proto == ByteProto.DECLARATION_NAME) {
      return;
    }

    preSymbol();
    codeAdd(Symbol.ASSIGNMENT);
    last(_SYMBOL);

    langItem(proto);

    while (elemMore()) {
      proto = protoPeek();

      if (proto == ByteProto.DECLARATION_NAME) {
        return;
      }

      codeAdd(Indentation.ENTER_CONTINUATION);

      langItem(proto);

      codeAdd(Indentation.EXIT_CONTINUATION);
    }
  }

  private void fieldDeclarationVariableList() {
    if (elemMore()) {
      fieldDeclarationVariableItem();

      while (elemMore()) {
        comma();

        fieldDeclarationVariableItem();
      }
    }
  }

  private void fieldOrMethodDeclaration() {
    int item = itemPeek();

    switch (item) {
      case ByteProto.IDENTIFIER -> {
        oldFieldDeclarationVariableList();
      }

      default -> errorRaise(
        "found '%s' in field or method".formatted(protoName(item))
      );
    }
  }

  private void identifier() {
    preIdentifier();

    codeAdd(ByteCode.IDENTIFIER, protoNext());

    last(_IDENTIFIER);
  }

  private void ifKeyword() {
    preKeyword();

    codeAdd(Keyword.IF);

    last(_KEYWORD);
  }

  private void ifStatement() {
    execute(this::oldIfCondition);

    if (itemTest(ByteProto::isStatementStart)) {
      codeAdd(Whitespace.OPTIONAL);

      oldStatement();
    } else {
      errorRaise("no statement after if condition");
    }

    if (itemIs(ByteProto.ELSE)) {
      codeAdd(Whitespace.OPTIONAL);

      execute(this::elseKeyword);

      if (itemTest(ByteProto::isStatementStart)) {
        codeAdd(Whitespace.MANDATORY);

        oldStatement();
      } else {
        errorRaise("no statement after the `else` keyword");
      }
    }
  }

  private void implementsClause() {
    clause(Keyword.IMPLEMENTS);
  }

  private void implementsKeyword() {
    preKeyword();

    codeAdd(Keyword.IMPLEMENTS);

    last(_KEYWORD);
  }

  private void importDeclarationList() {
    if (itemIs(ByteProto.AUTO_IMPORTS)) {
      execute(this::autoImports);
    }
  }

  private void interfaceDeclaration() {
    typeDeclaration(Keyword.INTERFACE);
  }

  private void interfaceDeclarationExtends() {
    execute(this::extendsKeyword);

    if (itemTest(this::isClassOrParameterizedType)) {
      executeSwitch(this::oldType);

      while (itemTest(this::isClassOrParameterizedType)) {
        codeAdd(Symbol.COMMA);
        codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

        executeSwitch(this::oldType);
      }
    }
  }

  private void interfaceKeyword() {
    typeKeyword(Keyword.INTERFACE);
  }

  private void invoke() {
    preDot();

    last(_START);

    execute(this::identifier);

    oldArgumentList();

    last(_PRIMARY);
  }

  private boolean isArgumentStart(int proto) {
    return ByteProto.isExpressionStart(proto)
        || proto == ByteProto.CLASS_TYPE;
  }

  private boolean isClassOrParameterizedType(int proto) {
    return proto == ByteProto.CLASS_TYPE || proto == ByteProto.PARAMETERIZED_TYPE;
  }

  private boolean isExpressionStartOrClassType(int proto) {
    return ByteProto.isExpressionStart(proto)
        || proto == ByteProto.CLASS_TYPE;
  }

  private boolean isModifierOrAnnotation(int proto) {
    return proto == ByteProto.MODIFIER || proto == ByteProto.ANNOTATION;
  }

  private boolean isVariableInitializerOrClassType(int proto) {
    if (ByteProto.isExpressionStart(proto) || proto == ByteProto.ARRAY_INITIALIZER) {
      return true;
    }

    if (proto == ByteProto.CLASS_TYPE) {
      int next = itemPeekAhead();

      return next != ByteProto.IDENTIFIER;
    }

    return false;
  }

  private boolean itemIs(int condition) {
    if (!error()) {
      consumeWs();

      return protoPeek() == condition;
    } else {
      return false;
    }
  }

  private boolean itemMore() {
    if (!error()) {
      consumeWs();

      return protoPeek() != ByteProto.END_ELEMENT;
    } else {
      return false;
    }
  }

  private int itemPeek() {
    consumeWs();

    return protoPeek();
  }

  private int itemPeekAhead() {
    for (int i = protoIndex + 2; i < protoArray.length; i += 2) {
      int proto = protoArray[i];

      if (ByteProto.isWhitespace(proto)) {
        continue;
      }

      return proto;
    }

    return ByteProto.NOOP;
  }

  private boolean itemTest(IntPredicate predicate) {
    consumeWs();

    int proto = protoPeek();

    return predicate.test(proto);
  }

  private void keyword(Keyword value) {
    preKeyword();

    codeAdd(value);

    last(_KEYWORD);
  }

  private void lang() {
    if (elemMore()) {
      int proto = protoPeek();

      langItem(proto);

      var continuation = ByteProto.isContinuation(proto);

      while (elemMore()) {
        proto = protoPeek();

        if (continuation) {
          codeAdd(Indentation.ENTER_CONTINUATION);

          langItem(proto);

          codeAdd(Indentation.EXIT_CONTINUATION);
        } else {
          langItem(proto);
        }
      }
    }
  }

  private void langItem(int proto) {
    switch (proto) {
      case ByteProto.ARRAY_ACCESS -> execute(this::arrayAccess);

      case ByteProto.ARRAY_INITIALIZER -> {
        execute(this::arrayInitializer);
        arrayInitializerValueList();
      }

      case ByteProto.ARGUMENT -> execute(this::arg);

      case ByteProto.ASSIGNMENT_OPERATOR -> execute(this::operator);

      case ByteProto.BLOCK -> execute(this::block);

      case ByteProto.CLASS_TYPE -> {
        execute(this::classType);
        maybeLocalVariable();
      }

      case ByteProto.CONDITION -> execute(this::arg);

      case ByteProto.DECLARATION_NAME -> execute(this::declarationName);

      case ByteProto.ELSE -> execute(this::elseKeyword);

      case ByteProto.EQUALITY_OPERATOR -> execute(this::operator);

      case ByteProto.EXPRESSION_NAME -> execute(this::expressionName);

      case ByteProto.IF -> {
        execute(this::ifKeyword);
        consumeWs();

        if (protoIs(ByteProto.CONDITION)) {
          codeAdd(Whitespace.OPTIONAL);
          argumentStart();
          consumeWs();
          execute(this::arg);
          consumeWs();
          argumentEnd();
          last(_SYMBOL);
        }
      }

      case ByteProto.NEW -> {
        execute(this::newKeyword);
        consumeWs();
        if (protoTest(ByteProto::isClassOrParameterizedType)) {
          executeSwitch(this::type);
          argList();
          last(_PRIMARY);
        }
      }

      case ByteProto.NEW_LINE -> execute(this::newLine);

      case ByteProto.NULL_LITERAL -> execute(this::nullLiteral);

      case ByteProto.PARAMETERIZED_TYPE -> execute(this::parameterizedType);

      case ByteProto.PRIMITIVE_LITERAL -> execute(this::primitiveLiteral);

      case ByteProto.PRIMITIVE_TYPE -> {
        execute(this::primitiveType);
        maybeLocalVariable();
      }

      case ByteProto.RETURN -> execute(this::returnKeyword);

      case ByteProto.STRING_LITERAL -> execute(this::stringLiteral);

      case ByteProto.SUPER -> {
        execute(this::superKeyword);

        consumeWs();

        if (protoIs(ByteProto.END_ELEMENT)) {
          codeAdd(Symbol.LEFT_PARENTHESIS);
          codeAdd(Symbol.RIGHT_PARENTHESIS);
        } else if (protoIs(ByteProto.ARGUMENT)) {
          argList();
        }
      }

      case ByteProto.THIS -> execute(this::thisKeyword);

      case ByteProto.THROW -> execute(this::throwKeyword);

      case ByteProto.V -> {
        execute(this::v);
        argList();
        if (lastIs(_NEW_LINE)) {
          last(_PRIMARY_NL);
        } else {
          last(_PRIMARY);
        }
      }

      case ByteProto.VAR -> {
        execute(this::varKeyword);
        maybeLocalVariable();
      }

      default -> errorRaise(
        "no-op statement part '%s'".formatted(protoName(proto))
      );
    }
  }

  private int last() { return stackArray[7]; }

  private void last(int value) { stackArray[7] = value; }

  private boolean lastIs(int value) { return last() == value; }

  private int listAdd(int list) {
    var index = stackArray[0];

    if (list == NULL) {
      list = index;
      protoArray = IntArrays.growIfNecessary(protoArray, index + 3);
      protoArray[index++] = NULL;
    } else {
      int jmpLocation = protoArray[list];
      protoArray[jmpLocation] = index;
      protoArray = IntArrays.growIfNecessary(protoArray, index + 2);
    }

    int proto = protoNext();
    int value = protoNext();

    protoArray[index++] = proto;
    protoArray[index++] = value;
    int tail = index;
    protoArray[index++] = NULL;
    protoArray[list] = tail;

    stackArray[0] = index;

    return list;
  }

  private void listExecute(Action action) {
    protoNext();

    int location = protoNext();

    int returnTo = protoIndex;

    protoIndex = location;

    action.execute();

    protoIndex = returnTo;

    int maybeNext = protoArray[protoIndex];

    if (maybeNext != NULL) {
      protoIndex = maybeNext;
    }
  }

  private void listExecute(int offset, Action action) {
    protoIndex = offset + 1;

    while (listMore()) {
      listExecute(action);
    }
  }

  private boolean listMore() {
    return protoArray[protoIndex] != NULL;
  }

  private void listSwitch(int offset, SwitchAction action) {
    protoIndex = offset + 1;

    while (listMore()) {
      listSwitch(action);
    }
  }

  private void listSwitch(SwitchAction action) {
    int proto = protoNext();

    int location = protoNext();

    int returnTo = protoIndex;

    protoIndex = location;

    action.execute(proto);

    protoIndex = returnTo;

    int maybeNext = protoArray[protoIndex];

    if (maybeNext != NULL) {
      protoIndex = maybeNext;
    }
  }

  private void localVariableDeclaration() {
    int type = itemPeek();

    last(_START);

    if (ByteProto.isType(type)) {
      executeSwitch(this::oldType);
    } else if (type == ByteProto.VAR) {
      execute(this::varKeyword);
    } else {
      errorRaise(
        "invalid local var: expected var or type but found '%s'"
            .formatted(protoName(type))
      );
    }

    if (itemIs(ByteProto.IDENTIFIER)) {
      oldVariableDeclarator();

      while (itemIs(ByteProto.IDENTIFIER)) {
        codeAdd(Symbol.COMMA);
        codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

        oldVariableDeclarator();
      }
    } else {
      errorRaise("invalid local var: variable name not found");
    }

    codeAdd(Symbol.SEMICOLON);
  }

  private void maybeLocalVariable() {
    if (protoIs(ByteProto.DECLARATION_NAME)) {
      execute(this::declarationName);

      codeAdd(Whitespace.OPTIONAL);
      codeAdd(Symbol.ASSIGNMENT);
      last(_SYMBOL);
    }
  }

  private void methodDeclaration() {
    int start = stackArray[0];

    abstractFound(NULL);

    last(_START);

    int annotations = NULL,
        modifiers = NULL,
        typeParameters = NULL,
        result = NULL,
        name = NULL,
        //receiverParameter = NULL,
        parameters = NULL,
        statements = NULL;

    while (protoMore()) {
      int proto = protoPeek();

      if (proto == ByteProto.END_ELEMENT) {
        break;
      }

      switch (proto) {
        case ByteProto.ANNOTATION -> annotations = listAdd(annotations);

        case ByteProto.ARRAY_TYPE,
             ByteProto.CLASS_TYPE,
             ByteProto.PARAMETERIZED_TYPE,
             ByteProto.PRIMITIVE_TYPE,
             ByteProto.TYPE_VARIABLE -> result = singleSet(result);

        case ByteProto.DECLARATION_NAME -> name = singleSet(name);

        case ByteProto.MODIFIER -> modifiers = listAdd(modifiers);

        case ByteProto.PARAMETER_DECLARATION -> parameters = listAdd(parameters);

        case ByteProto.STATEMENT -> statements = listAdd(statements);

        case ByteProto.TYPE_PARAMETER -> typeParameters = listAdd(typeParameters);

        case ByteProto.VOID -> result = singleSet(result);

        default -> {
          throw new UnsupportedOperationException(
            "Implement me :: " + protoName(proto)
          );
        }
      }
    }

    if (annotations != NULL) {
      listExecute(annotations, this::declarationAnnotation);
    }

    if (modifiers != NULL) {
      listExecute(modifiers, this::modifier);
    }

    if (typeParameters != NULL) {
      typeParameterList(typeParameters);
    }

    if (result != NULL) {
      singleSwitch(result, this::methodResult);
    } else {
      voidKeyword();
    }

    if (name != NULL) {
      singleExecute(name, this::declarationName);
    } else {
      unnamed();
    }

    codeAdd(Symbol.LEFT_PARENTHESIS);

    if (parameters != NULL) {
      last(_START);

      listExecute(parameters, this::parameterDeclaration);
    }

    executableBody(statements);

    stackArray[0] = start;
  }

  private void methodResult(int proto) {
    switch (proto) {
      case ByteProto.ARRAY_TYPE -> arrayType();

      case ByteProto.CLASS_TYPE -> classType();

      case ByteProto.PARAMETERIZED_TYPE -> parameterizedType();

      case ByteProto.PRIMITIVE_TYPE -> primitiveType();

      case ByteProto.TYPE_VARIABLE -> typeVariable();

      case ByteProto.VOID -> voidKeyword();

      default -> errorRaise(
        "no-op method result '%s'".formatted(protoName(proto))
      );
    }
  }

  private void modifier() {
    switch (last()) {
      case _ANNOTATION -> codeAdd(Whitespace.AFTER_ANNOTATION);

      case _COMMA -> codeAdd(Whitespace.OPTIONAL);

      case _KEYWORD -> codeAdd(Whitespace.MANDATORY);
    }

    int proto = protoNext();

    var keyword = Keyword.get(proto);

    switch (keyword) {
      case ABSTRACT -> abstractFound(1);

      case PUBLIC -> publicFound(1);

      default -> {}
    }

    codeAdd(ByteCode.KEYWORD, proto);

    last(_KEYWORD);
  }

  private void newKeyword() {
    preKeyword();

    codeAdd(Keyword.NEW);

    last(_KEYWORD);
  }

  private void newLine() {
    codeAdd(Whitespace.NEW_LINE);

    last(_NEW_LINE);
  }

  private int newLine(int count) {
    for (int i = 0; i < count; i++) {
      newLine();
    }

    return 0;
  }

  private void noop() {}

  private void nullLiteral() {
    preKeyword();

    codeAdd(Keyword.NULL);

    last(_KEYWORD);
  }

  private Object objectget(int index) {
    return objectArray[index];
  }

  private void oldAnnotation() {
    codeAdd(Symbol.COMMERCIAL_AT);

    last(_START);

    execute(this::classType);

    if (itemMore()) {
      codeAdd(Symbol.LEFT_PARENTHESIS);

      last(_START);

      oldAnnotationValuePair();

      while (itemMore()) {
        codeAdd(Symbol.COMMA);
        codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

        oldAnnotationValuePair();
      }

      codeAdd(Symbol.RIGHT_PARENTHESIS);
    }

    last(_ANNOTATION);
  }

  private void oldAnnotationValuePair() {
    // check for value name

    oldExpression();
  }

  private void oldArgumentList() {
    codeAdd(Symbol.LEFT_PARENTHESIS);
    codeAdd(Indentation.ENTER_PARENTHESIS);

    if (itemTest(this::isArgumentStart)) {
      if (lastIs(_NEW_LINE)) {
        codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);
      }

      last(_START);

      oldExpression();

      while (itemTest(this::isArgumentStart)) {
        slotComma();

        var ws = lastIs(_NEW_LINE)
            ? Whitespace.BEFORE_FIRST_LINE_CONTENT
            : Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM;

        codeAdd(ws);

        last(_START);

        oldExpression();
      }
    }

    argumentEnd();
  }

  private void oldArrayAccess() {
    codeAdd(Symbol.LEFT_SQUARE_BRACKET);

    last(_START);

    oldExpression();

    codeAdd(Symbol.RIGHT_SQUARE_BRACKET);
  }

  private void oldArrayInitializer() {
    if (lastIs(_SYMBOL)) {
      codeAdd(Whitespace.OPTIONAL);
    }

    codeAdd(Symbol.LEFT_CURLY_BRACKET);
    codeAdd(Indentation.ENTER_BLOCK);

    last(_START);

    if (itemMore()) {
      oldVariableInitializer();

      while (itemMore()) {
        slotComma();

        oldVariableInitializer();
      }
    }

    codeAdd(Indentation.EXIT_BLOCK);

    if (lastIs(_NEW_LINE)) {
      codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);
    }

    codeAdd(Symbol.RIGHT_CURLY_BRACKET);
  }

  private void oldBlock() {
    codeAdd(Symbol.LEFT_CURLY_BRACKET);

    if (itemMore()) {
      codeAdd(Indentation.ENTER_BLOCK);
      codeAdd(Whitespace.BEFORE_NEXT_STATEMENT);

      oldBlockStatement();

      while (itemMore()) {
        codeAdd(Whitespace.BEFORE_NEXT_STATEMENT);

        oldBlockStatement();
      }

      codeAdd(Indentation.EXIT_BLOCK);
      codeAdd(Whitespace.BEFORE_NON_EMPTY_BLOCK_END);
    } else {
      codeAdd(Whitespace.BEFORE_EMPTY_BLOCK_END);
    }

    codeAdd(Symbol.RIGHT_CURLY_BRACKET);

    last(_START);
  }

  private void oldBlockStatement() {
    int start = itemPeek();
    // TODO local class
    // TODO local variable decl

    oldStatement0(start);
  }

  private void oldClassDeclaration() {
    execute(this::oldClassKeyword);

    if (itemIs(ByteProto.EXTENDS)) {
      classDeclarationExtends();
    }

    if (itemIs(ByteProto.IMPLEMENTS)) {
      oldImplementsClause();
    }

    if (itemIs(ByteProto.BODY)) {
      codeAdd(Whitespace.OPTIONAL);

      execute(this::body);
    }
  }

  private void oldClassInstanceCreation() {
    preKeyword();

    codeAdd(Keyword.NEW);

    last(_KEYWORD);

    executeSwitch(this::oldType);

    oldArgumentList();
  }

  private void oldClassKeyword() {
    typeKeyword(Keyword.CLASS);
  }

  private void oldDeclarationAnnotationList() {
    if (itemIs(ByteProto.ANNOTATION)) {
      execute(this::oldAnnotation);

      while (itemIs(ByteProto.ANNOTATION)) {
        codeAdd(Whitespace.AFTER_ANNOTATION);

        execute(this::oldAnnotation);
      }
    }
  }

  private void oldEnumConstant() {
    last(_START);

    execute(this::identifier);

    if (itemMore()) {
      oldArgumentList();
    }

    slot();

    last(_ENUM_CONSTANT);
  }

  private void oldEnumDeclaration() {
    execute(this::enumKeyword);

    if (itemIs(ByteProto.IMPLEMENTS)) {
      oldImplementsClause();
    }

    if (itemIs(ByteProto.BODY)) {
      codeAdd(Whitespace.OPTIONAL);

      execute(this::body);
    }
  }

  private void oldExpression() {
    int part = executeSwitch(this::oldExpressionBegin);

    slot();

    if (stop()) {
      return;
    }

    switch (part) {
      case ByteProto.CLASS_INSTANCE_CREATION,
           ByteProto.CLASS_TYPE,
           ByteProto.EXPRESSION_NAME,
           ByteProto.INVOKE,
           ByteProto.STRING_LITERAL,
           ByteProto.THIS -> {
        int next = itemPeek();

        switch (next) {
          case ByteProto.EXPRESSION_NAME,
               ByteProto.INVOKE -> {
            if (lastIs(_NEW_LINE)) {
              codeAdd(Indentation.CONTINUATION);

              last(_START);
            }

            oldExpression();
          }

          case ByteProto.ARRAY_ACCESS -> {
            execute(this::oldArrayAccess);

            while (itemIs(ByteProto.ARRAY_ACCESS)) {
              execute(this::oldArrayAccess);
            }

            slot();
          }
        }
      }
    }

    if (stop()) {
      return;
    }

    while (itemTest(ByteProto::isOperator)) {
      execute(this::operator);

      if (itemTest(ByteProto::isExpressionStart)) {
        oldExpression();
      } else {
        errorRaise("expected expression after operator");
      }
    }
  }

  private void oldExpressionBegin(int proto) {
    switch (proto) {
      case ByteProto.ARRAY_ACCESS -> oldArrayAccess();

      case ByteProto.CLASS_INSTANCE_CREATION -> oldClassInstanceCreation();

      case ByteProto.CLASS_TYPE -> classType();

      case ByteProto.EXPRESSION_NAME -> expressionName();

      case ByteProto.INVOKE -> invoke();

      case ByteProto.NULL_LITERAL -> nullLiteral();

      case ByteProto.PRIMITIVE_LITERAL -> primitiveLiteral();

      case ByteProto.STRING_LITERAL -> stringLiteral();

      case ByteProto.THIS -> thisKeyword();

      default -> errorRaise(
        "no-op expression part '%s'".formatted(protoName(proto))
      );
    }
  }

  private void oldFieldDeclarationVariableList() {
    oldVariableDeclarator();

    while (itemIs(ByteProto.IDENTIFIER)) {
      codeAdd(Symbol.COMMA);
      last(_COMMA);

      oldVariableDeclarator();
    }

    codeAdd(Symbol.SEMICOLON);

    last(_SYMBOL);
  }

  private void oldIfCondition() {
    codeAdd(Keyword.IF);

    codeAdd(Whitespace.OPTIONAL);

    codeAdd(Symbol.LEFT_PARENTHESIS);

    oldExpression();

    if (itemMore()) {
      int proto = itemPeek();

      errorRaise(
        "expected expression end but found '%s'".formatted(protoName(proto))
      );
    }

    codeAdd(Symbol.RIGHT_PARENTHESIS);
  }

  private void oldImplementsClause() {
    execute(this::implementsKeyword);

    if (itemTest(ByteProto::isClassOrParameterizedType)) {
      executeSwitch(this::oldType);

      while (itemTest(ByteProto::isClassOrParameterizedType)) {
        codeAdd(Symbol.COMMA);
        last(_COMMA);

        executeSwitch(this::oldType);
      }
    }
  }

  private void oldInterfaceDeclaration() {
    execute(this::interfaceKeyword);

    if (itemIs(ByteProto.EXTENDS)) {
      interfaceDeclarationExtends();
    }

    if (itemIs(ByteProto.BODY)) {
      codeAdd(Whitespace.OPTIONAL);

      execute(this::body);
    }
  }

  private void oldMethodDeclarationAfterTypeParameterList() {
    int returnType = itemPeek();

    if (ByteProto.isType(returnType)) {
      executeSwitch(this::oldType);
    } else if (returnType == ByteProto.VOID) {
      execute(this::voidKeyword);
    } else {
      errorRaise(
        "Method declaration: expected 'Return Type' but found '%s'".formatted(protoName(returnType))
      );

      return;
    }

    int next = itemPeek();

    errorRaise(
      "Method declaration: expected 'Declarator' but found '%s'".formatted(protoName(next))
    );
  }

  private void oldModifier() {
    int proto = protoNext();

    var keyword = Keyword.get(proto);

    switch (keyword) {
      case ABSTRACT -> abstractFound(1);

      case PUBLIC -> publicFound(1);

      default -> {}
    }

    codeAdd(ByteCode.KEYWORD, proto);

    last(_KEYWORD);
  }

  private void oldModifierList() {
    publicFound(NULL);

    if (itemIs(ByteProto.MODIFIER)) {
      if (lastIs(_ANNOTATION)) {
        codeAdd(Whitespace.AFTER_ANNOTATION);
      }

      execute(this::oldModifier);

      while (itemTest(this::isModifierOrAnnotation)) {
        codeAdd(Whitespace.MANDATORY);

        executeSwitch(this::oldModifierOrAnnotation);
      }
    }
  }

  private void oldModifierOrAnnotation(int proto) {
    switch (proto) {
      case ByteProto.ANNOTATION -> oldAnnotation();

      case ByteProto.MODIFIER -> oldModifier();
    }

    last(_KEYWORD);
  }

  private void oldReturnStatement() {
    execute(this::returnKeyword);

    if (itemTest(ByteProto::isExpressionStart)) {
      oldExpression();

      codeAdd(Symbol.SEMICOLON);
    } else {
      errorRaise("expected start of expression");
    }
  }

  private void oldStatement() {
    int start = itemPeek();

    oldStatement0(start);
  }

  private void oldStatement0(int start) {
    last(_START);

    switch (start) {
      case ByteProto.ARRAY_TYPE,
           ByteProto.PARAMETERIZED_TYPE,
           ByteProto.PRIMITIVE_TYPE,
           ByteProto.TYPE_VARIABLE -> localVariableDeclaration();

      case ByteProto.BLOCK -> execute(this::oldBlock);

      case ByteProto.CLASS_INSTANCE_CREATION,
           ByteProto.EXPRESSION_NAME,
           ByteProto.INVOKE,
           ByteProto.THIS -> {
        oldStatementPrimary();

        codeAdd(Symbol.SEMICOLON);
      }

      case ByteProto.CLASS_TYPE -> {
        int next = itemPeekAhead();

        if (next != ByteProto.IDENTIFIER) {
          oldStatementPrimary();

          codeAdd(Symbol.SEMICOLON);
        } else {
          localVariableDeclaration();
        }
      }

      case ByteProto.IF_CONDITION -> ifStatement();

      case ByteProto.RETURN -> oldReturnStatement();

      case ByteProto.SUPER -> superInvocationWithKeyword();

      case ByteProto.SUPER_INVOCATION -> superInvocation();

      case ByteProto.THROW -> throwStatement();

      case ByteProto.VAR -> localVariableDeclaration();

      default -> errorRaise(
        "no-op statement start '%s'".formatted(protoName(start))
      );
    }
  }

  private void oldStatementPrimary() {
    oldExpression();
  }

  private void oldType(int proto) {
    switch (proto) {
      case ByteProto.ARRAY_TYPE -> arrayType();

      case ByteProto.CLASS_TYPE -> classType();

      case ByteProto.PARAMETERIZED_TYPE -> parameterizedType();

      case ByteProto.PRIMITIVE_TYPE -> primitiveType();

      case ByteProto.TYPE_VARIABLE -> typeVariable();

      default -> errorRaise(
        "no-op type '%s'".formatted(protoName(proto))
      );
    }
  }

  private void oldTypeParameter() {
    execute(this::identifier);

    if (itemMore()) {
      codeAdd(Whitespace.MANDATORY);
      codeAdd(Keyword.EXTENDS);
      codeAdd(Whitespace.MANDATORY);

      executeSwitch(this::oldType);

      while (itemMore()) {
        codeAdd(Whitespace.OPTIONAL);
        codeAdd(Symbol.AMPERSAND);
        codeAdd(Whitespace.OPTIONAL);

        executeSwitch(this::oldType);
      }
    }
  }

  private void oldTypeParameterList() {
    if (lastIs(_KEYWORD)) {
      codeAdd(Whitespace.OPTIONAL);
    }

    last(_START);

    codeAdd(Symbol.LEFT_ANGLE_BRACKET);

    execute(this::oldTypeParameter);

    while (itemIs(ByteProto.TYPE_PARAMETER)) {
      codeAdd(Symbol.COMMA);
      last(_COMMA);

      execute(this::oldTypeParameter);
    }

    codeAdd(Symbol.RIGHT_ANGLE_BRACKET);

    last(_SYMBOL);
  }

  private void oldVariableDeclarator() {
    execute(this::identifier);

    if (itemTest(this::isVariableInitializerOrClassType)) {
      preSymbol();
      codeAdd(Symbol.ASSIGNMENT);
      last(_SYMBOL);

      oldVariableInitializer();
    }
  }

  private void oldVariableInitializer() {
    if (itemTest(this::isExpressionStartOrClassType)) {
      oldExpression();
    } else if (itemIs(ByteProto.ARRAY_INITIALIZER)) {
      execute(this::oldArrayInitializer);
    } else {
      errorRaise();
    }
  }

  private void operator() {
    preSymbol();

    codeAdd(ByteCode.SYMBOL, protoNext());

    last(_SYMBOL);
  }

  private void ordinaryCompilationUnit() {
    execute(this::packageKeyword);

    importDeclarationList();

    topLevelDeclarationList();
  }

  private void packageDeclaration() {
    execute(this::packageKeyword);
  }

  private void packageKeyword() {
    codeAdd(Keyword.PACKAGE);

    codeAdd(Whitespace.MANDATORY);

    codeAdd(ByteCode.IDENTIFIER, protoNext());

    codeAdd(Symbol.SEMICOLON);

    last(_SYMBOL);
  }

  private void parameterDeclaration() {
    switch (last()) {
      case _IDENTIFIER -> comma();
    }

    int annotations = NULL,
        modifiers = NULL,
        type = NULL,
        name = NULL;

    var varargs = false;

    while (protoMore()) {
      int proto = protoPeek();

      if (proto == ByteProto.END_ELEMENT) {
        break;
      }

      switch (proto) {
        case ByteProto.ANNOTATION -> annotations = listAdd(annotations);

        case ByteProto.ARRAY_TYPE,
             ByteProto.CLASS_TYPE,
             ByteProto.PARAMETERIZED_TYPE,
             ByteProto.PRIMITIVE_TYPE,
             ByteProto.TYPE_VARIABLE -> type = singleSet(type);

        case ByteProto.DECLARATION_NAME -> name = singleSet(name);

        case ByteProto.ELLIPSIS -> {
          varargs = true;
          protoNext();
          protoNext();
        }

        case ByteProto.MODIFIER -> modifiers = listAdd(modifiers);

        default -> {
          throw new UnsupportedOperationException(
            "Implement me :: " + protoName(proto)
          );
        }
      }
    }

    if (annotations != NULL) {
      listExecute(annotations, this::declarationAnnotation);
    }

    if (modifiers != NULL) {
      listExecute(modifiers, this::modifier);
    }

    if (type != NULL) {
      singleSwitch(type, this::type);
    } else {
      voidKeyword();
    }

    if (varargs) {
      ellipsis();
    }

    if (name != NULL) {
      singleExecute(name, this::declarationName);
    } else {
      unnamed();
    }
  }

  private void parameterizedType() {
    execute(this::classType);

    codeAdd(Symbol.LEFT_ANGLE_BRACKET);

    if (itemMore()) {
      executeSwitch(this::type);

      while (itemMore()) {
        codeAdd(Symbol.COMMA);
        codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

        executeSwitch(this::type);
      }
    }

    codeAdd(Symbol.RIGHT_ANGLE_BRACKET);

    last(_TYPE);
  }

  private void permitsClause() {
    clause(Keyword.PERMITS);
  }

  private void preDot() {
    switch (last()) {
      case _COMMA -> codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

      case _IDENTIFIER,
           _PRIMARY,
           _TYPE -> codeAdd(Symbol.DOT);

      case _KEYWORD -> codeAdd(Whitespace.MANDATORY);

      case _NEW_LINE -> codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);

      case _PRIMARY_NL -> {
        codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);
        codeAdd(Symbol.DOT);
      }

      case _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }
  }

  private void preIdentifier() {
    switch (last()) {
      case _ANNOTATION -> codeAdd(Whitespace.AFTER_ANNOTATION);

      case _COMMA -> codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

      case _IDENTIFIER,
           _KEYWORD,
           _TYPE -> codeAdd(Whitespace.MANDATORY);

      case _NEW_LINE -> codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);

      case _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }
  }

  private void preKeyword() {
    switch (last()) {
      case _ANNOTATION -> codeAdd(Whitespace.AFTER_ANNOTATION);

      case _BLOCK -> codeAdd(Whitespace.OPTIONAL);

      case _IDENTIFIER,
           _KEYWORD,
           _PRIMARY,
           _TYPE -> codeAdd(Whitespace.MANDATORY);

      case _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }
  }

  private void preSymbol() {
    switch (last()) {
      case _IDENTIFIER,
           _KEYWORD,
           _PRIMARY,
           _SYMBOL,
           _TYPE -> codeAdd(Whitespace.OPTIONAL);
    }
  }

  private void preTopLevel() {
    switch (last()) {
      case _SYMBOL -> codeAdd(Whitespace.BEFORE_NEXT_MEMBER);
    }

    simpleName(NULL);

    topLevel(TRUE);
  }

  private void preType() {
    switch (last()) {
      case _ANNOTATION -> codeAdd(Whitespace.AFTER_ANNOTATION);

      case _COMMA -> codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

      case _KEYWORD -> codeAdd(Whitespace.MANDATORY);

      case _NEW_LINE -> codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);

      case _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }
  }

  private void primitiveLiteral() {
    switch (last()) {
      case _COMMA -> codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

      case _KEYWORD -> codeAdd(Whitespace.MANDATORY);

      case _NEW_LINE -> codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);

      case _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }

    codeAdd(ByteCode.PRIMITIVE_LITERAL, protoNext());
  }

  private void primitiveType() {
    preType();

    codeAdd(ByteCode.KEYWORD, protoNext());

    last(_TYPE);
  }

  private boolean protoIs(int value) {
    if (protoMore()) {
      int proto = protoPeek();

      return proto == value;
    } else {
      return false;
    }
  }

  private boolean protoMore() {
    if (!error()) {
      return protoIndex < protoArray.length;
    } else {
      return false;
    }
  }

  private String protoName(int proto) {
    return switch (proto) {
      case ByteProto.ARRAY_TYPE -> "Array Type";

      case ByteProto.CLASS -> "Class Keyword";

      case ByteProto.DECLARATION_NAME -> "Declaration Name";

      case ByteProto.IF_CONDITION -> "If Condition";

      case ByteProto.INTERFACE -> "Interface";

      case ByteProto.INVOKE -> "Method invocation";

      case ByteProto.MODIFIER -> "Modifier";

      case ByteProto.PARAMETER -> "Formal Parameter";

      case ByteProto.PRIMITIVE_LITERAL -> "Primitive Literal";

      case ByteProto.TYPE_PARAMETER -> "Type Parameter";

      case ByteProto.V -> "V";

      default -> Integer.toString(proto);
    };
  }

  private int protoNext() { return protoArray[protoIndex++]; }

  private int protoPeek() {
    return protoArray[protoIndex];
  }

  private boolean protoTest(IntPredicate predicate) {
    if (protoMore()) {
      int proto = protoPeek();

      return predicate.test(proto);
    } else {
      return false;
    }
  }

  private int publicFound() { return stackArray[2]; }

  private void publicFound(int value) { stackArray[2] = value; }

  private void returnKeyword() {
    preKeyword();

    codeAdd(Keyword.RETURN);

    last(_KEYWORD);
  }

  private void semicolon() {
    codeAdd(Symbol.SEMICOLON);

    last(_SYMBOL);
  }

  private int simpleName() { return stackArray[1]; }

  private void simpleName(int value) { stackArray[1] = value; }

  private void singleExecute(int offset, Action action) {
    int location = protoArray[offset + 1];

    int returnTo = protoIndex;

    protoIndex = location;

    action.execute();

    protoIndex = returnTo;
  }

  private int singleSet(int property) {
    int proto = protoNext();
    int value = protoNext();

    if (property == NULL) {
      var index = stackArray[0];

      property = index;
      protoArray = IntArrays.growIfNecessary(protoArray, index + 1);
      protoArray[index++] = proto;
      protoArray[index++] = value;

      stackArray[0] = index;
    } else {
      protoArray[property + 0] = proto;
      protoArray[property + 1] = value;
    }

    return property;
  }

  private void singleSwitch(int offset, SwitchAction action) {
    protoIndex = offset;

    int proto = protoArray[protoIndex++];

    int location = protoArray[protoIndex++];

    int returnTo = protoIndex;

    protoIndex = location;

    action.execute(proto);

    protoIndex = returnTo;
  }

  private void slot() {
    stackArray[3] = codeIndex;

    codeAdd(ByteCode.NOP1);

    codeAdd(-1);
  }

  private void slotComma() {
    int index = stackArray[3];

    codeArray[index + 0] = ByteCode.SYMBOL;

    codeArray[index + 1] = Symbol.COMMA.ordinal();
  }

  private void slotSemicolon() {
    int index = stackArray[3];

    codeArray[index + 0] = ByteCode.SYMBOL;

    codeArray[index + 1] = Symbol.SEMICOLON.ordinal();
  }

  private int stackPeek() { return stackArray[stackIndex]; }

  private boolean stop() {
    if (itemIs(ByteProto.STOP)) {
      execute(this::noop);

      while (itemIs(ByteProto.STOP)) {
        execute(this::noop);
      }

      return true;
    } else {
      return false;
    }
  }

  private void stringLiteral() {
    switch (last()) {
      case _COMMA -> codeAdd(Whitespace.BEFORE_NEXT_COMMA_SEPARATED_ITEM);

      case _KEYWORD -> codeAdd(Whitespace.OPTIONAL);

      case _NEW_LINE -> codeAdd(Whitespace.BEFORE_FIRST_LINE_CONTENT);

      case _SYMBOL -> codeAdd(Whitespace.OPTIONAL);
    }

    codeAdd(ByteCode.STRING_LITERAL, protoNext());

    last(_PRIMARY);
  }

  private void superInvocation() {
    superKeyword();

    execute(this::oldArgumentList);

    codeAdd(Symbol.SEMICOLON);
  }

  private void superInvocationWithKeyword() {
    execute(this::superKeyword);

    codeAdd(Symbol.LEFT_PARENTHESIS);

    codeAdd(Symbol.RIGHT_PARENTHESIS);

    codeAdd(Symbol.SEMICOLON);
  }

  private void superKeyword() {
    preKeyword();

    codeAdd(Keyword.SUPER);

    last(_KEYWORD);
  }

  private void symbol(Symbol symbol) {
    preSymbol();

    codeAdd(symbol);

    last(_SYMBOL);
  }

  private void thisKeyword() {
    preKeyword();

    codeAdd(Keyword.THIS);

    last(_PRIMARY);
  }

  private void throwKeyword() {
    preKeyword();

    codeAdd(Keyword.THROW);

    last(_KEYWORD);
  }

  private void throwStatement() {
    execute(this::throwKeyword);

    if (itemTest(ByteProto::isExpressionStart)) {
      oldExpression();

      codeAdd(Symbol.SEMICOLON);
    } else {
      errorRaise("expected start of expression");
    }
  }

  private void topLevelDeclaration() {
    topLevel(TRUE);

    oldDeclarationAnnotationList();

    oldModifierList();

    int next = itemPeek();

    switch (next) {
      case ByteProto.CLASS -> {
        beforeTopLevelTypeDeclaration();

        oldClassDeclaration();
      }

      case ByteProto.CLASS_DECLARATION -> execute(this::classDeclaration);

      case ByteProto.ENUM -> {
        beforeTopLevelTypeDeclaration();

        oldEnumDeclaration();
      }

      case ByteProto.ENUM_DECLARATION -> execute(this::enumDeclaration);

      case ByteProto.INTERFACE -> {
        beforeTopLevelTypeDeclaration();

        oldInterfaceDeclaration();
      }

      case ByteProto.INTERFACE_DECLARATION -> execute(this::interfaceDeclaration);

      default -> errorRaise(
        "no-op top level declaration '%s'".formatted(protoName(next))
      );
    }
  }

  private void topLevelDeclarationList() {
    simpleName(NULL);

    if (itemMore()) {
      switch (last()) {
        case _ANNOTATION,
             _START -> {}

        default -> codeAdd(Whitespace.BEFORE_NEXT_MEMBER);
      }

      topLevelDeclaration();

      while (itemMore()) {
        codeAdd(Whitespace.BEFORE_NEXT_MEMBER);

        topLevelDeclaration();
      }
    }
  }

  private void type(int proto) {
    switch (proto) {
      case ByteProto.ARRAY_TYPE -> arrayType();

      case ByteProto.CLASS_TYPE -> classType();

      case ByteProto.PARAMETERIZED_TYPE -> parameterizedType();

      case ByteProto.PRIMITIVE_TYPE -> primitiveType();

      case ByteProto.TYPE_VARIABLE -> typeVariable();

      default -> errorRaise(
        "no-op type '%s'".formatted(protoName(proto))
      );
    }
  }

  private void typeDeclaration(Keyword keyword) {
    if (keyword == Keyword.INTERFACE) {
      stackPush(_CTX_IFACE);
    } else {
      stackPush(_CTX_OTHER);
    }

    int start = stackArray[0];

    last(_START);

    int annotations = NULL,
        modifiers = NULL,
        typeParameters = NULL,
        name = NULL,
        extendsClause = NULL,
        implementsClause = NULL,
        permitsClause = NULL,
        enumConstants = NULL,
        body = NULL;

    while (protoMore()) {
      int proto = protoPeek();

      if (proto == ByteProto.END_ELEMENT) {
        break;
      }

      switch (proto) {
        case ByteProto.ANNOTATION -> annotations = listAdd(annotations);

        case ByteProto.CLASS_DECLARATION -> body = listAdd(body);

        case ByteProto.CONSTRUCTOR_DECLARATION -> body = listAdd(body);

        case ByteProto.DECLARATION_NAME -> name = singleSet(name);

        case ByteProto.ENUM_CONSTANT -> enumConstants = listAdd(enumConstants);

        case ByteProto.ENUM_DECLARATION -> body = listAdd(body);

        case ByteProto.EXTENDS_CLAUSE -> {
          if (keyword == Keyword.CLASS) {
            extendsClause = singleSet(extendsClause);
          } else {
            extendsClause = listAdd(extendsClause);
          }
        }

        case ByteProto.FIELD_DECLARATION -> body = listAdd(body);

        case ByteProto.IMPLEMENTS_CLAUSE -> implementsClause = listAdd(implementsClause);

        case ByteProto.INTERFACE_DECLARATION -> body = listAdd(body);

        case ByteProto.METHOD_DECLARATION -> body = listAdd(body);

        case ByteProto.MODIFIER -> modifiers = listAdd(modifiers);

        case ByteProto.PERMITS_CLAUSE -> permitsClause = listAdd(permitsClause);

        case ByteProto.TYPE_PARAMETER -> typeParameters = listAdd(typeParameters);

        default -> {
          throw new UnsupportedOperationException(
            "Implement me :: " + protoName(proto)
          );
        }
      }
    }

    if (annotations != NULL) {
      listExecute(annotations, this::declarationAnnotation);
    }

    if (modifiers != NULL) {
      listExecute(modifiers, this::modifier);
    }

    keyword(keyword);

    if (name != NULL) {
      singleExecute(name, this::typeDeclarationName);
    } else {
      unnamedType();
    }

    if (typeParameters != NULL) {
      typeParameterList(typeParameters);
    }

    if (extendsClause != NULL) {
      last(_IDENTIFIER);

      if (keyword == Keyword.CLASS) {
        singleExecute(extendsClause, this::extendsClause);
      } else {
        codeAdd(Indentation.ENTER_CONTINUATION);

        listExecute(extendsClause, this::extendsClause);

        codeAdd(Indentation.EXIT_CONTINUATION);
      }
    }

    if (implementsClause != NULL) {
      last(_IDENTIFIER);

      codeAdd(Indentation.ENTER_CONTINUATION);

      listExecute(implementsClause, this::implementsClause);

      codeAdd(Indentation.EXIT_CONTINUATION);
    }

    if (permitsClause != NULL) {
      last(_IDENTIFIER);

      listExecute(permitsClause, this::permitsClause);
    }

    body(enumConstants, body);

    stackArray[0] = start;

    stackPop();
  }

  private void typeDeclarationName() {
    int proto = protoNext();

    typeDeclarationNameValue(proto);

    preIdentifier();

    codeAdd(ByteCode.IDENTIFIER, proto);

    last(_IDENTIFIER);
  }

  private void typeDeclarationNameValue(int proto) {
    simpleName(proto);

    var topLevel = topLevel() == TRUE;

    if (topLevel) {
      var publicFound = publicFound() != NULL;

      var fileName = (String) objectget(proto);

      autoImports.fileName(publicFound, fileName);
    }
  }

  private void typeKeyword(Keyword keyword) {
    codeAdd(keyword);

    codeAdd(Whitespace.MANDATORY);

    int proto = protoNext();

    codeAdd(ByteCode.IDENTIFIER, proto);

    typeDeclarationNameValue(proto);

    last(_IDENTIFIER);
  }

  private void typeParameter() {
    switch (last()) {
      case _IDENTIFIER,
           _TYPE -> comma();
    }

    execute(this::identifier);

    if (elemMore()) {
      extendsKeyword();

      executeSwitch(this::type);

      while (elemMore()) {
        symbol(Symbol.AMPERSAND);

        executeSwitch(this::type);
      }
    }
  }

  private void typeParameterList(int typeParameters) {
    switch (last()) {
      case _KEYWORD -> codeAdd(Whitespace.OPTIONAL);
    }

    codeAdd(Symbol.LEFT_ANGLE_BRACKET);

    last(_START);

    listExecute(typeParameters, this::typeParameter);

    codeAdd(Symbol.RIGHT_ANGLE_BRACKET);

    last(_SYMBOL);
  }

  private void typeVariable() {
    preType();

    codeAdd(ByteCode.IDENTIFIER, protoNext());

    last(_TYPE);
  }

  private void unnamed() {
    preIdentifier();

    codeAdd(ByteCode.IDENTIFIER, object("unnamed"));
  }

  private void unnamedConstant() {
    if (lastIs(_KEYWORD)) {
      codeAdd(Whitespace.MANDATORY);
    }

    codeAdd(ByteCode.IDENTIFIER, object("UNNAMED"));
  }

  private void unnamedType() {
    if (lastIs(_KEYWORD)) {
      codeAdd(Whitespace.MANDATORY);
    }

    int proto = object("Unnamed");

    typeDeclarationNameValue(proto);

    codeAdd(ByteCode.IDENTIFIER, proto);
  }

  private void v() {
    preDot();

    codeAdd(ByteCode.IDENTIFIER, protoNext());
  }

  private void varKeyword() {
    preKeyword();

    codeAdd(Keyword.VAR);

    last(_KEYWORD);
  }

  private void voidKeyword() {
    preType();

    codeAdd(Keyword.VOID);

    last(_KEYWORD);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy