Input:
import de.haupz.basicode.ast.ProgramNode;
import de.haupz.basicode.interpreter.Configuration;
import de.haupz.basicode.interpreter.InterpreterState;
import de.haupz.basicode.io.BasicInput;
import de.haupz.basicode.io.BasicOutput;
import de.haupz.basicode.io.BufferedReaderInput;
import de.haupz.basicode.io.PrintStreamOutput;
import de.haupz.basicode.parser.BasicParser;
import de.haupz.basicode.parser.ParseException;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import static org.junit.jupiter.api.Assertions.*;

Context:
src/test/java/de/haupz/basicode/InterpreterTest.java
package de.haupz.basicode;

public abstract class InterpreterTest {

    ByteArrayOutputStream bytesOut;

    private InterpreterState state;

    void setUpState(ProgramNode prog, String input) {
        bytesOut = new ByteArrayOutputStream();
        PrintStream ps = new PrintStream(bytesOut, true);
        BasicOutput out = new PrintStreamOutput(ps);
        BufferedReader br = new BufferedReader(new StringReader(input));
        BasicInput in = new BufferedReaderInput(br);
        state = new InterpreterState(prog, in, out, new Configuration());
    }

    private ProgramNode buildProgram(String source) {

src/main/java/de/haupz/basicode/io/BasicInput.java
public interface BasicInput {

    /**
     * Read a line of input terminated by a newline. This is a blocking operation.
     *
     * @return the line that was entered.
     * @throws IOException in case anything goes wrong with the input.
     */
    String readLine() throws IOException;

    /**
     * Read a single character from the input. This is a blocking operation.
     *
     * @return the character that was entered.
     * @throws IOException in case anything goes wrong witn the input.
     */
    int readChar() throws IOException;

    /**
     * If a character is available from the input at the time this method is called, return that character. This is a
     * non-blocking operation.
     *
     * @return the character available from the input at the time this method is called, or 0 if no character is
     * available.
     */
    default int lastChar() { return 0; }

    /**
     * Instruct the input to note that a waiting operation is under way, and that the input can interrupt this if a key
     * is pressed.
     *
     * @param ready if {@code true}, input will be ready to interrupt a waiting operation; if {@code false}, it won't.
     */
    default void setReadyToInterrupt(boolean ready) {}

    /**
     * Control whether pressing a stop key (e.g., ESC) will terminate program execution.
     *
     * @param acceptStopKey if {@code trye}, the stop key will terminate program execution; if {@code false}, it won't.
     */
    default void toggleAcceptStopKey(boolean acceptStopKey) {}

    /**
     * Register a handler to be invoked when the stop key is pressed.
     *
     * @param stopKeyHandler the handler.
     */
    default void registerStopKeyHandler(StopKeyHandler stopKeyHandler) {}

}

src/main/java/de/haupz/basicode/parser/ParseException.java
public class ParseException extends Exception {

  /**
   * The version identifier for this Serializable class.
   * Increment only if the serialized form of the
   * class changes.
   */
  private static final long serialVersionUID = 1L;

  /**
   * The end of line string for this machine.
   */
  protected static String EOL = System.getProperty("line.separator", "\n");

  /**
   * This constructor is used by the method "generateParseException"
   * in the generated parser.  Calling this constructor generates
   * a new object of this type with the fields "currentToken",
   * "expectedTokenSequences", and "tokenImage" set.
   */
  public ParseException(Token currentTokenVal,
                        int[][] expectedTokenSequencesVal,
                        String[] tokenImageVal
                       )
  {
    super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
    currentToken = currentTokenVal;
    expectedTokenSequences = expectedTokenSequencesVal;
    tokenImage = tokenImageVal;
  }

  /**
   * The following constructors are for use by you for whatever
   * purpose you can think of.  Constructing the exception in this
   * manner makes the exception behave in the normal way - i.e., as
   * documented in the class "Throwable".  The fields "errorToken",
   * "expectedTokenSequences", and "tokenImage" do not contain
   * relevant information.  The JavaCC generated code does not use
   * these constructors.
   */

  public ParseException() {
    super();
  }

  /** Constructor with message. */
  public ParseException(String message) {
    super(message);
  }

  /**
   * This is the last token that has been consumed successfully.  If
   * this object has been created due to a parse error, the token
   * following this token will (therefore) be the first error token.
   */
  public Token currentToken;

  /**
   * Each entry in this array is an array of integers.  Each array
   * of integers represents a sequence of tokens (by their ordinal
   * values) that is expected at this point of the parse.
   */
  public int[][] expectedTokenSequences;

  /**
   * This is a reference to the "tokenImage" array of the generated
   * parser within which the parse error occurred.  This array is
   * defined in the generated ...Constants interface.
   */
  public String[] tokenImage;

  /**
   * It uses "currentToken" and "expectedTokenSequences" to generate a parse
   * error message and returns it.  If this object has been created
   * due to a parse error, and you do not catch it (it gets thrown
   * from the parser) the correct error message
   * gets displayed.
   */
  private static String initialise(Token currentToken,
                           int[][] expectedTokenSequences,
                           String[] tokenImage) {

    StringBuilder expected = new StringBuilder();
    int maxSize = 0;
    for (int i = 0; i < expectedTokenSequences.length; i++) {
      if (maxSize < expectedTokenSequences[i].length) {
        maxSize = expectedTokenSequences[i].length;
      }
      for (int j = 0; j < expectedTokenSequences[i].length; j++) {
        expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
      }
      if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
        expected.append("...");
      }
      expected.append(EOL).append("    ");
    }
    String retval = "Encountered \"";
    Token tok = currentToken.next;
    for (int i = 0; i < maxSize; i++) {
      if (i != 0) retval += " ";
      if (tok.kind == 0) {
        retval += tokenImage[0];
        break;
      }
      retval += " " + tokenImage[tok.kind];
      retval += " \"";
      retval += add_escapes(tok.image);
      retval += " \"";
      tok = tok.next;
    }
    if (currentToken.next != null) {
      retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
    }
    retval += "." + EOL;

    if (expectedTokenSequences.length == 0) {
        // Nothing to add here
    } else {
	    if (expectedTokenSequences.length == 1) {
	      retval += "Was expecting:" + EOL + "    ";
	    } else {
	      retval += "Was expecting one of:" + EOL + "    ";
	    }
	    retval += expected.toString();
    }

    return retval;
  }

  /**
   * Used to convert raw characters to their escaped version
   * when these raw version cannot be used as part of an ASCII
   * string literal.
   */
  static String add_escapes(String str) {
      StringBuilder retval = new StringBuilder();
      char ch;
      for (int i = 0; i < str.length(); i++) {
        switch (str.charAt(i))
        {
           case '\b':
              retval.append("\\b");
              continue;
           case '\t':
              retval.append("\\t");
              continue;
           case '\n':
              retval.append("\\n");
              continue;
           case '\f':
              retval.append("\\f");
              continue;
           case '\r':
              retval.append("\\r");
              continue;
           case '\"':
              retval.append("\\\"");
              continue;
           case '\'':
              retval.append("\\\'");
              continue;
           case '\\':
              retval.append("\\\\");
              continue;
           default:
              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
                 String s = "0000" + Integer.toString(ch, 16);
                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
              } else {
                 retval.append(ch);
              }
              continue;
        }
      }
      return retval.toString();
   }

}

src/main/java/de/haupz/basicode/io/BufferedReaderInput.java
public class BufferedReaderInput implements BasicInput {

    private final BufferedReader br;

    public BufferedReaderInput(BufferedReader br) {
        this.br = br;
    }

    @Override
    public String readLine() throws IOException {
        return br.readLine();
    }

    @Override
    public int readChar() throws IOException {
        return br.read();
    }

}

src/main/java/de/haupz/basicode/ast/ProgramNode.java
public class ProgramNode extends BasicNode {

    /**
     * All lines of the BASICODE program.
     */
    private final List<LineNode> lines;

    /**
     * The flattened representation of all statements of the program, for easier execution.
     */
    private List<StatementNode> statements = new ArrayList<>();

    /**
     * Map BASIC line numbers to indices in the flattened {@link #statements} list. The statement index for any line
     * number is the index of the first statement on the respective line.
     */
    private Map<Integer, Integer> lineNumberToStatementIndex = new HashMap<>();

    /**
     * A "code address", comprising of a line and statement index on that line.
     *
     * @param line a BASIC line number.
     * @param statement a 0-based statement index into the respective BASIC line.
     */
    record LineAndStatement(int line, int statement) {}

    /**
     * Map statement indices from the {@link #statements} list back to line numbers and statements. This is used for
     * debugging support.
     */
    private Map<Integer, LineAndStatement> statementIndexToLineNumberAndStatement = new HashMap<>();

    /**
     * The flattened list of all elements from {@code DATA} lines, in the order they appear in in the BASIC source code.
     */
    private final List<Object> dataList;

    /**
     *
Construct a program from a list of line nodes and data elements.

     *
     *
Aside from initialising the {@link #lines} and {@link #dataList}, the constructor also builds the flattened
     * {@link #statements} list and populates the {@link #lineNumberToStatementIndex} and
     * {@link #statementIndexToLineNumberAndStatement} maps.

     *
     * @param lines the {@link LineNode}s representing the source code.
     * @param dataList the {@code DATA} elements from the source code.
     */
    public ProgramNode(List<LineNode> lines, List<Object> dataList) {
        this.lines = List.copyOf(lines);
        lines.forEach(line -> {
            // A new line: the index of its first statement is the current size of the statements array.
            lineNumberToStatementIndex.put(line.getLineNumber(), statements.size());
            statements.addAll(List.copyOf(line.getStatements()));
            int nStatements = statements.size();
            int nStatementsOnLine = line.getStatements().size();
            int lineNum = line.getLineNumber();
            for (int s = 0; s < nStatementsOnLine; ++s) {
                statementIndexToLineNumberAndStatement.put(
                        nStatements - nStatementsOnLine + s, new LineAndStatement(lineNum, s));
            }
        });
        this.dataList = List.copyOf(dataList);
    }

    /**
     *
The main interpreter loop.

     *
     *
Until the interpreter state is {@linkplain InterpreterState#shouldEnd() notified about termination}, the
     * interpreter fetches the {@linkplain InterpreterState#getStatementIndex() next statement} from the
     * {@link #statements} list and runs it.

     *
     *
Statements may set specific flags in the {@linkplain InterpreterState interpreter state}. After each statement
     * execution, the interpreter checks if any of the flags is set, and handles it accordingly before resetting it
     * again. The flags are as follows:

     *
{@link InterpreterState#isLineJumpNext()}: a jump to another line needs to be performed, e.g., because a
     *     {@code GOTO} statement was encountered. The interpreter will set the target statement using
     *     {@link #resolveJump(InterpreterState)}.

     *
{@link InterpreterState#isReturnNext()}: the interpreter needs to {@code RETURN} from a {@code GOSUB}
     *     call. It will {@linkplain InterpreterState#getReturnIndex() retrieve the return address from the call stack}
     *     and continue execution there.

     *
{@link InterpreterState#isBackedgeNext()}: the interpreter needs to jump from a {@code NEXT} statement to
     *     the head of the corresponding loop (a {@code FOR} statement). It will
     *     {@linkplain InterpreterState#getBackedgeTarget() retrieve the loop head's statement index} and continue
     *     execution there.

     *
{@link InterpreterState#isSkipLine()}: the remainder of a line needs to be skipped, and execution needs
     *     to proceed at the beginning of the next line.

     *
If no flag is set, the interpreter will simply move on to the next statement, if there is one. If there
     * isn't, it will {@linkplain InterpreterState#terminate() mark the interpreter state for termination}.

     *
     *
In case the execution of a statement throws an exception, the interpreter will provide some helpful details
     * about the location in the BASIC source code where the exception originated. It will also print the BASIC call
     * stack (in case any {@code GOSUB} routines were being run). Finally, it will dump the Java call stack of the
     * interpreter itself.

     *
     * @param state the interpreter state.
     */
    @Override
    public void run(InterpreterState state) {
        StatementNode statement;
        while (!state.shouldEnd()) {
            statement = statements.get(state.getStatementIndex());
            try {
                statement.run(state);
            } catch (Exception e) {
                Stack<Integer> stack = state.getCallStack();
                String stackDump = "";
                LineAndStatement las = statementIndexToLineNumberAndStatement.get(state.getStatementIndex());
                stackDump = String.format("at line %d, statement %d", las.line, las.statement);
                if (!stack.isEmpty()) {
                    stackDump += '\n' + stack.reversed().stream().map(stmt -> {
                        LineAndStatement sdlas = statementIndexToLineNumberAndStatement.get(stmt - 1);
                        return String.format("at line %d, statement %d", sdlas.line, sdlas.statement);
                    }).collect(Collectors.joining("\n"));
                }
                throw new IllegalStateException(stackDump, e);
            }
            if (state.isLineJumpNext()) {
                resolveJump(state);
            } else if (state.isReturnNext()) {
                state.setNextStatement(state.getReturnIndex());
                state.returnDone();
            } else if (state.isBackedgeNext()) {
                state.setNextStatement(state.getBackedgeTarget());
                state.backedgeDone();
            } else if (state.isSkipLine()) {
                int stmt = state.getStatementIndex();
                int lineToSkip = statementIndexToLineNumberAndStatement.get(stmt).line;
                do {
                    ++stmt;
                } while (lineToSkip == statementIndexToLineNumberAndStatement.get(stmt).line);
                state.setNextStatement(stmt);
                state.skipLineDone();
            } else {
                state.incStatementIndex();
                if (state.getStatementIndex() >= statements.size()) {
                    state.terminate();
                }
            }
        }
    }

    /**
     * Resolve a jump by {@linkplain #lineNumberToStatementIndex retrieving the index of the first statement} of the
     * {@linkplain InterpreterState#getLineJumpTarget() target line}, and
     * {@linkplain InterpreterState#setNextStatement(int) setting that to be the next statement to execute}.
     *
     * @param state the interpreter state.
     */
    private void resolveJump(InterpreterState state) {
        try {
            state.setNextStatement(lineNumberToStatementIndex.get(state.getLineJumpTarget()));
        } catch (NullPointerException npe) {
            throw new IllegalStateException("line not found: " + state.getLineJumpTarget());
        }
        state.lineJumpDone();
    }

    /**
     * This method is overridden to throw an exception, as programs aren't expressions.
     *
     * @param state the interpreter state.
     * @return nothing; this implementation will throw an exception.
     */
    @Override
    public Object eval(InterpreterState state) {
        throw new IllegalStateException("a ProgramNode should not be evaluated");
    }

    /**
     * @return the list of {@ode DATA} elements.
     */
    public List<Object> getDataList() {
        return dataList;
    }

}

src/main/java/de/haupz/basicode/io/PrintStreamOutput.java
public class PrintStreamOutput implements BasicOutput {

    private int col = 0;

    private int row = 0;

    private final PrintStream ps;

    public PrintStreamOutput(PrintStream ps) {
        this.ps = ps;
    }

    @Override
    public void print(String s) {
        ps.print(s);
        col += s.length();
    }

    @Override
    public void println(String s) {
        ps.println(s);
        col = 0;
        ++row;
    }

    @Override
    public void println() {
        ps.println();
        col = 0;
        ++row;
    }

    @Override
    public void flush() {
        ps.flush();
    }

    @Override
    public TextCursor getTextCursor() {
        return new TextCursor(col, row);
    }

}

src/main/java/de/haupz/basicode/parser/BasicParser.java
public class BasicParser implements BasicParserConstants {
    List<Object> dataList = new ArrayList<>();

// Parser.

//
// basic structures
//
  final public
ProgramNode program() throws ParseException {List<LineNode> lines = new ArrayList<>();
    LineNode l;
    label_1:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case NUMBER:{
        ;
        break;
        }
      default:
        jj_la1[0] = jj_gen;
        break label_1;
      }
      l = line();
lines.add(l);
    }
{if ("" != null) return new ProgramNode(lines, dataList);}
    throw new Error("Missing return statement in function");
}

  final public LineNode line() throws ParseException {List<StatementNode> statements = new ArrayList<>();
    int num;
    StatementNode st;
    num = lineNumber();
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case DATA:{
      st = data_line();
statements.add(st);
      break;
      }
    case DEF:
    case DIM:
    case END:
    case FOR:
    case GOSUB:
    case GOTO:
    case IF:
    case INPUT:
    case LET:
    case NEXT:
    case ON:
    case PRINT:
    case READ:
    case REM:
    case RESTORE:
    case RETURN:
    case RUN:
    case STOP:
    case IDENTIFIER:{
      st = statement();
statements.add(st);
      label_2:
      while (true) {
        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
        case 52:{
          ;
          break;
          }
        default:
          jj_la1[1] = jj_gen;
          break label_2;
        }
        jj_consume_token(52);
        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
        case DEF:
        case DIM:
        case END:
        case FOR:
        case GOSUB:
        case GOTO:
        case IF:
        case INPUT:
        case LET:
        case NEXT:
        case ON:
        case PRINT:
        case READ:
        case REM:
        case RESTORE:
        case RETURN:
        case RUN:
        case STOP:
        case IDENTIFIER:{
          st = statement();
statements.add(st);
          break;
          }
        default:
          jj_la1[2] = jj_gen;
          ;
        }
      }
      break;
      }
    default:
      jj_la1[3] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case EOL:{
      jj_consume_token(EOL);
      break;
      }
    case 0:{
      jj_consume_token(0);
      break;
      }
    default:
      jj_la1[4] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
{if ("" != null) return new LineNode(num, statements);}
    throw new Error("Missing return statement in function");
}

  final public int lineNumber() throws ParseException {Token n;
    n = jj_consume_token(NUMBER);
{if ("" != null) return Integer.parseInt(n.image);}
    throw new Error("Missing return statement in function");
}

//
// statements
//
  final public
StatementNode statement() throws ParseException {Token t;
    ExpressionNode e;
    StatementNode s;
    int n;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case REM:{
      t = jj_consume_token(REM);
{if ("" != null) return new RemNode(t.image);}
      break;
      }
    case LET:
    case IDENTIFIER:{
      s = assignment();
{if ("" != null) return s;}
      break;
      }
    case PRINT:{
      s = print_statement();
{if ("" != null) return s;}
      break;
      }
    case GOTO:{
      jj_consume_token(GOTO);
      n = lineNumber();
{if ("" != null) return new GotoNode(n);}
      break;
      }
    case GOSUB:{
      jj_consume_token(GOSUB);
      n = lineNumber();
{if ("" != null) return new GosubNode(n);}
      break;
      }
    case RETURN:{
      jj_consume_token(RETURN);
{if ("" != null) return new ReturnNode();}
      break;
      }
    case ON:{
      s = dependent_jump();
{if ("" != null) return s;}
      break;
      }
    case DIM:{
      s = dim_statement();
{if ("" != null) return s;}
      break;
      }
    case FOR:{
      s = for_statement();
{if ("" != null) return s;}
      break;
      }
    case NEXT:{
      jj_consume_token(NEXT);
      t = jj_consume_token(IDENTIFIER);
{if ("" != null) return new NextNode(t.image);}
      break;
      }
    case IF:{
      s = if_statement();
{if ("" != null) return s;}
      break;
      }
    case READ:{
      s = read_statement();
{if ("" != null) return s;}
      break;
      }
    case RESTORE:{
      jj_consume_token(RESTORE);
{if ("" != null) return new RestoreNode();}
      break;
      }
    case END:
    case STOP:{
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case END:{
        jj_consume_token(END);
        break;
        }
      case STOP:{
        jj_consume_token(STOP);
        break;
        }
      default:
        jj_la1[5] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
{if ("" != null) return new EndNode();}
      break;
      }
    case INPUT:{
      jj_consume_token(INPUT);
      t = jj_consume_token(IDENTIFIER);
{if ("" != null) return new InputNode(t.image);}
      break;
      }
    case RUN:{
      jj_consume_token(RUN);
{if ("" != null) return new RunNode();}
      break;
      }
    case DEF:{
      s = def_fn();
{if ("" != null) return s;}
      break;
      }
    default:
      jj_la1[6] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public StatementNode data_line() throws ParseException {Object l;
    List<Object> items = new ArrayList<>();
    jj_consume_token(DATA);
    l = data_literal();
items.add(l);
    label_3:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 53:{
        ;
        break;
        }
      default:
        jj_la1[7] = jj_gen;
        break label_3;
      }
      jj_consume_token(53);
      l = data_literal();
items.add(l);
    }
dataList.addAll(items);
        {if ("" != null) return new DataNode(items);}
    throw new Error("Missing return statement in function");
}

  final public Object data_literal() throws ParseException {int sgn = 1;
    Token t;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case NUMBER:
    case FLOAT:
    case 54:{
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 54:{
        jj_consume_token(54);
sgn = -1;
        break;
        }
      default:
        jj_la1[8] = jj_gen;
        ;
      }
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case NUMBER:{
        t = jj_consume_token(NUMBER);
        break;
        }
      case FLOAT:{
        t = jj_consume_token(FLOAT);
        break;
        }
      default:
        jj_la1[9] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
{if ("" != null) return sgn * Double.parseDouble(t.image);}
      break;
      }
    case STRING:{
      t = jj_consume_token(STRING);
{if ("" != null) return t.image.substring(1, t.image.length() - 1);}
      break;
      }
    default:
      jj_la1[10] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public StatementNode assignment() throws ParseException {LetNode.LHS l;
    ExpressionNode e = null;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case LET:{
      jj_consume_token(LET);
      break;
      }
    default:
      jj_la1[11] = jj_gen;
      ;
    }
    l = lhs();
    jj_consume_token(55);
    e = expression();
{if ("" != null) return new LetNode(l, e);}
    throw new Error("Missing return statement in function");
}

  final public LetNode.LHS lhs() throws ParseException {Token t;
    ExpressionNode e = null;
    ExpressionNode f = null;
    t = jj_consume_token(IDENTIFIER);
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case 56:{
      jj_consume_token(56);
      e = expression();
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 53:{
        jj_consume_token(53);
        f = expression();
        break;
        }
      default:
        jj_la1[12] = jj_gen;
        ;
      }
      jj_consume_token(57);
{if ("" != null) return new LetNode.Array(t.image, e, f);}
      break;
      }
    default:
      jj_la1[13] = jj_gen;
      ;
    }
{if ("" != null) return new LetNode.Variable(t.image);}
    throw new Error("Missing return statement in function");
}

  final public StatementNode print_statement() throws ParseException {Token t;
    PrintNode.Element elem;
    List<PrintNode.Element> elements = new ArrayList<>();
    jj_consume_token(PRINT);
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case NUMBER:
    case FLOAT:
    case STRING:
    case ABS:
    case ASC:
    case ATN:
    case CHRS:
    case COS:
    case FN:
    case INT:
    case LEFTS:
    case LEN:
    case LOG:
    case MIDS:
    case NOT:
    case RIGHTS:
    case SGN:
    case SIN:
    case SQR:
    case TAB:
    case TAN:
    case VAL:
    case IDENTIFIER:
    case FNIDENTIFIER:
    case 54:
    case 56:{
      elem = print_element();
elements.add(elem);
      label_4:
      while (true) {
        if (jj_2_1(2)) {
          ;
        } else {
          break label_4;
        }
        switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
        case 53:{
          t = jj_consume_token(53);
          break;
          }
        case 58:{
          t = jj_consume_token(58);
          break;
          }
        default:
          jj_la1[14] = jj_gen;
          jj_consume_token(-1);
          throw new ParseException();
        }
elements.add(new PrintNode.Element(PrintNode.ElementType.SEPARATOR, t.image));
        elem = print_element();
elements.add(elem);
      }
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 58:{
        t = jj_consume_token(58);
elements.add(new PrintNode.Element(PrintNode.ElementType.SEPARATOR, t.image));
        break;
        }
      default:
        jj_la1[15] = jj_gen;
        ;
      }
      break;
      }
    default:
      jj_la1[16] = jj_gen;
      ;
    }
{if ("" != null) return new PrintNode(elements);}
    throw new Error("Missing return statement in function");
}

  final public PrintNode.Element print_element() throws ParseException {ExpressionNode e;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case NUMBER:
    case FLOAT:
    case STRING:
    case ABS:
    case ASC:
    case ATN:
    case CHRS:
    case COS:
    case FN:
    case INT:
    case LEFTS:
    case LEN:
    case LOG:
    case MIDS:
    case NOT:
    case RIGHTS:
    case SGN:
    case SIN:
    case SQR:
    case TAN:
    case VAL:
    case IDENTIFIER:
    case FNIDENTIFIER:
    case 54:
    case 56:{
      e = expression();
{if ("" != null) return new PrintNode.Element(PrintNode.ElementType.EXPRESSION, e);}
      break;
      }
    case TAB:{
      jj_consume_token(TAB);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new PrintNode.Element(PrintNode.ElementType.TAB, e);}
      break;
      }
    default:
      jj_la1[17] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public StatementNode dim_statement() throws ParseException {DimCreateNode d;
    List<DimCreateNode> dims = new ArrayList<>();
    jj_consume_token(DIM);
    d = one_dim();
dims.add(d);
    label_5:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 53:{
        ;
        break;
        }
      default:
        jj_la1[18] = jj_gen;
        break label_5;
      }
      jj_consume_token(53);
      d = one_dim();
dims.add(d);
    }
{if ("" != null) return new DimNode(dims);}
    throw new Error("Missing return statement in function");
}

  final public DimCreateNode one_dim() throws ParseException {Token t;
    ExpressionNode d1 = null;
    ExpressionNode d2 = null;
    t = jj_consume_token(IDENTIFIER);
    jj_consume_token(56);
    d1 = expression();
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case 53:{
      jj_consume_token(53);
      d2 = expression();
      break;
      }
    default:
      jj_la1[19] = jj_gen;
      ;
    }
    jj_consume_token(57);
{if ("" != null) return new DimCreateNode(t.image, d1, d2);}
    throw new Error("Missing return statement in function");
}

  final public StatementNode dependent_jump() throws ParseException {ExpressionNode e;
    boolean isGosub = false;
    List<Integer> targets = new ArrayList<>();
    int n;
    jj_consume_token(ON);
    e = expression();
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case GOTO:{
      jj_consume_token(GOTO);
      break;
      }
    case GOSUB:{
      jj_consume_token(GOSUB);
isGosub = true;
      break;
      }
    default:
      jj_la1[20] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    n = lineNumber();
targets.add(n);
    label_6:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 53:{
        ;
        break;
        }
      default:
        jj_la1[21] = jj_gen;
        break label_6;
      }
      jj_consume_token(53);
      n = lineNumber();
targets.add(n);
    }
{if ("" != null) return isGosub ? new OnGosubNode(e, targets) : new OnGotoNode(e, targets);}
    throw new Error("Missing return statement in function");
}

  final public StatementNode for_statement() throws ParseException {Token t;
    ExpressionNode e;
    ExpressionNode f;
    ExpressionNode g = null;
    jj_consume_token(FOR);
    t = jj_consume_token(IDENTIFIER);
    jj_consume_token(55);
    e = expression();
    jj_consume_token(TO);
    f = expression();
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case STEP:{
      jj_consume_token(STEP);
      g = expression();
      break;
      }
    default:
      jj_la1[22] = jj_gen;
      ;
    }
{if ("" != null) return new ForNode(t.image, e, f, g);}
    throw new Error("Missing return statement in function");
}

  final public StatementNode if_statement() throws ParseException {ExpressionNode e;
    int l;
    StatementNode s = null;
    jj_consume_token(IF);
    e = expression();
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case THEN:{
      jj_consume_token(THEN);
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case NUMBER:{
        l = lineNumber();
s = new GotoNode(l);
        break;
        }
      case DEF:
      case DIM:
      case END:
      case FOR:
      case GOSUB:
      case GOTO:
      case IF:
      case INPUT:
      case LET:
      case NEXT:
      case ON:
      case PRINT:
      case READ:
      case REM:
      case RESTORE:
      case RETURN:
      case RUN:
      case STOP:
      case IDENTIFIER:{
        s = statement();
        break;
        }
      default:
        jj_la1[23] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
      break;
      }
    case GOTO:{
      jj_consume_token(GOTO);
      l = lineNumber();
s = new GotoNode(l);
      break;
      }
    default:
      jj_la1[24] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
{if ("" != null) return new IfThenNode(e, s);}
    throw new Error("Missing return statement in function");
}

  final public StatementNode read_statement() throws ParseException {LetNode.LHS l;
    List<LetNode.LHS> lhss = new ArrayList<>();
    jj_consume_token(READ);
    l = lhs();
lhss.add(l);
    label_7:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 53:{
        ;
        break;
        }
      default:
        jj_la1[25] = jj_gen;
        break label_7;
      }
      jj_consume_token(53);
      l = lhs();
lhss.add(l);
    }
{if ("" != null) return new ReadNode(lhss);}
    throw new Error("Missing return statement in function");
}

  final public StatementNode def_fn() throws ParseException {String f;
    Token u;
    ExpressionNode e;
    jj_consume_token(DEF);
    f = fn_id();
    jj_consume_token(56);
    u = jj_consume_token(IDENTIFIER);
    jj_consume_token(57);
    jj_consume_token(55);
    e = expression();
{if ("" != null) return new DefFnNode(f, u.image, e);}
    throw new Error("Missing return statement in function");
}

  final public String fn_id() throws ParseException {Token t;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case FN:{
      jj_consume_token(FN);
      t = jj_consume_token(IDENTIFIER);
{if ("" != null) return t.image;}
      break;
      }
    case FNIDENTIFIER:{
      t = jj_consume_token(FNIDENTIFIER);
{if ("" != null) return t.image.substring(2, t.image.length());}
      break;
      }
    default:
      jj_la1[26] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

//
// expressions
//
  final public
ExpressionNode expression() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    e = and_expression();
    label_8:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case OR:{
        ;
        break;
        }
      default:
        jj_la1[27] = jj_gen;
        break label_8;
      }
      jj_consume_token(OR);
      f = and_expression();
e = new OrNode(e, f);
    }
{if ("" != null) return e;}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode and_expression() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    e = equality_expression();
    label_9:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case AND:{
        ;
        break;
        }
      default:
        jj_la1[28] = jj_gen;
        break label_9;
      }
      jj_consume_token(AND);
      f = equality_expression();
e = new AndNode(e, f);
    }
{if ("" != null) return e;}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode equality_expression() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    e = relational_expression();
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case 55:
    case 59:{
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 55:{
        jj_consume_token(55);
        f = relational_expression();
{if ("" != null) return new EqNode(e, f);}
        break;
        }
      case 59:{
        jj_consume_token(59);
        f = relational_expression();
{if ("" != null) return new NeqNode(e, f);}
        break;
        }
      default:
        jj_la1[29] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
      break;
      }
    default:
      jj_la1[30] = jj_gen;
      ;
    }
{if ("" != null) return e;}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode relational_expression() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    e = additive_expression();
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case 60:
    case 61:
    case 62:
    case 63:{
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 60:{
        jj_consume_token(60);
        f = additive_expression();
{if ("" != null) return new LtNode(e, f);}
        break;
        }
      case 61:{
        jj_consume_token(61);
        f = additive_expression();
{if ("" != null) return new LeqNode(e, f);}
        break;
        }
      case 62:{
        jj_consume_token(62);
        f = additive_expression();
{if ("" != null) return new GtNode(e, f);}
        break;
        }
      case 63:{
        jj_consume_token(63);
        f = additive_expression();
{if ("" != null) return new GeqNode(e, f);}
        break;
        }
      default:
        jj_la1[31] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
      break;
      }
    default:
      jj_la1[32] = jj_gen;
      ;
    }
{if ("" != null) return e;}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode additive_expression() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    e = multiplicative_expression();
    label_10:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 54:
      case 64:{
        ;
        break;
        }
      default:
        jj_la1[33] = jj_gen;
        break label_10;
      }
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 64:{
        jj_consume_token(64);
        f = multiplicative_expression();
e = new AddNode(e, f);
        break;
        }
      case 54:{
        jj_consume_token(54);
        f = multiplicative_expression();
e = new SubtractNode(e, f);
        break;
        }
      default:
        jj_la1[34] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    }
{if ("" != null) return e;}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode multiplicative_expression() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    e = power_expression();
    label_11:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 65:
      case 66:{
        ;
        break;
        }
      default:
        jj_la1[35] = jj_gen;
        break label_11;
      }
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 65:{
        jj_consume_token(65);
        f = power_expression();
e = new MultiplyNode(e, f);
        break;
        }
      case 66:{
        jj_consume_token(66);
        f = power_expression();
e = new DivideNode(e, f);
        break;
        }
      default:
        jj_la1[36] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
    }
{if ("" != null) return e;}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode power_expression() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    e = unary_expression();
    label_12:
    while (true) {
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 67:{
        ;
        break;
        }
      default:
        jj_la1[37] = jj_gen;
        break label_12;
      }
      jj_consume_token(67);
      f = unary_expression();
e = new PowerNode(e, f);
    }
{if ("" != null) return e;}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode unary_expression() throws ParseException {ExpressionNode e;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case 54:{
      jj_consume_token(54);
      e = unary_expression();
{if ("" != null) return new NegateNode(e);}
      break;
      }
    case NUMBER:
    case FLOAT:
    case STRING:
    case ABS:
    case ASC:
    case ATN:
    case CHRS:
    case COS:
    case FN:
    case INT:
    case LEFTS:
    case LEN:
    case LOG:
    case MIDS:
    case NOT:
    case RIGHTS:
    case SGN:
    case SIN:
    case SQR:
    case TAN:
    case VAL:
    case IDENTIFIER:
    case FNIDENTIFIER:
    case 56:{
      e = unary_expression_not_plus_minus();
{if ("" != null) return e;}
      break;
      }
    default:
      jj_la1[38] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode unary_expression_not_plus_minus() throws ParseException {ExpressionNode e;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case NOT:{
      jj_consume_token(NOT);
      e = unary_expression();
{if ("" != null) return new NotNode(e);}
      break;
      }
    case NUMBER:
    case FLOAT:
    case STRING:
    case ABS:
    case ASC:
    case ATN:
    case CHRS:
    case COS:
    case FN:
    case INT:
    case LEFTS:
    case LEN:
    case LOG:
    case MIDS:
    case RIGHTS:
    case SGN:
    case SIN:
    case SQR:
    case TAN:
    case VAL:
    case IDENTIFIER:
    case FNIDENTIFIER:
    case 56:{
      e = primary_expression();
{if ("" != null) return e;}
      break;
      }
    default:
      jj_la1[39] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode primary_expression() throws ParseException {ExpressionNode e;
    Token t;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case NUMBER:
    case FLOAT:
    case STRING:{
      e = literal();
{if ("" != null) return e;}
      break;
      }
    case 56:{
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return e;}
      break;
      }
    case ABS:
    case ASC:
    case ATN:
    case CHRS:
    case COS:
    case INT:
    case LEFTS:
    case LEN:
    case LOG:
    case MIDS:
    case RIGHTS:
    case SGN:
    case SIN:
    case SQR:
    case TAN:
    case VAL:{
      e = builtin_call();
{if ("" != null) return e;}
      break;
      }
    case FN:
    case FNIDENTIFIER:{
      e = fn_call();
{if ("" != null) return e;}
      break;
      }
    case IDENTIFIER:{
      e = varOrDimAccess();
{if ("" != null) return e;}
      break;
      }
    default:
      jj_la1[40] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode literal() throws ParseException {Token t;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case NUMBER:
    case FLOAT:{
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case NUMBER:{
        t = jj_consume_token(NUMBER);
        break;
        }
      case FLOAT:{
        t = jj_consume_token(FLOAT);
        break;
        }
      default:
        jj_la1[41] = jj_gen;
        jj_consume_token(-1);
        throw new ParseException();
      }
{if ("" != null) return new DoubleNode(Double.parseDouble(t.image));}
      break;
      }
    case STRING:{
      t = jj_consume_token(STRING);
{if ("" != null) return new StringNode(t.image.substring(1, t.image.length() - 1));}
      break;
      }
    default:
      jj_la1[42] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode varOrDimAccess() throws ParseException {Token t;
    ExpressionNode e = null;
    ExpressionNode f = null;
    t = jj_consume_token(IDENTIFIER);
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case 56:{
      jj_consume_token(56);
      e = expression();
      switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
      case 53:{
        jj_consume_token(53);
        f = expression();
        break;
        }
      default:
        jj_la1[43] = jj_gen;
        ;
      }
      jj_consume_token(57);
{if ("" != null) return new DimAccessNode(t.image, e, f);}
      break;
      }
    default:
      jj_la1[44] = jj_gen;
      ;
    }
{if ("" != null) return new VarNode(t.image, false);}
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode builtin_call() throws ParseException {ExpressionNode e;
    ExpressionNode f;
    ExpressionNode g;
    switch ((jj_ntk==-1)?jj_ntk_f():jj_ntk) {
    case ABS:{
      jj_consume_token(ABS);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new AbsNode(e);}
      break;
      }
    case ASC:{
      jj_consume_token(ASC);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new AscNode(e);}
      break;
      }
    case ATN:{
      jj_consume_token(ATN);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new AtnNode(e);}
      break;
      }
    case CHRS:{
      jj_consume_token(CHRS);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new ChrsNode(e);}
      break;
      }
    case COS:{
      jj_consume_token(COS);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new CosNode(e);}
      break;
      }
    case INT:{
      jj_consume_token(INT);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new IntNode(e);}
      break;
      }
    case LEFTS:{
      jj_consume_token(LEFTS);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(53);
      f = expression();
      jj_consume_token(57);
{if ("" != null) return new LeftsNode(e, f);}
      break;
      }
    case LEN:{
      jj_consume_token(LEN);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new LenNode(e);}
      break;
      }
    case LOG:{
      jj_consume_token(LOG);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new LogNode(e);}
      break;
      }
    case MIDS:{
      jj_consume_token(MIDS);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(53);
      f = expression();
      jj_consume_token(53);
      g = expression();
      jj_consume_token(57);
{if ("" != null) return new MidsNode(e, f, g);}
      break;
      }
    case RIGHTS:{
      jj_consume_token(RIGHTS);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(53);
      f = expression();
      jj_consume_token(57);
{if ("" != null) return new RightsNode(e, f);}
      break;
      }
    case SGN:{
      jj_consume_token(SGN);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new SgnNode(e);}
      break;
      }
    case SIN:{
      jj_consume_token(SIN);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new SinNode(e);}
      break;
      }
    case SQR:{
      jj_consume_token(SQR);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new SqrNode(e);}
      break;
      }
    case TAN:{
      jj_consume_token(TAN);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new TanNode(e);}
      break;
      }
    case VAL:{
      jj_consume_token(VAL);
      jj_consume_token(56);
      e = expression();
      jj_consume_token(57);
{if ("" != null) return new ValNode(e);}
      break;
      }
    default:
      jj_la1[45] = jj_gen;
      jj_consume_token(-1);
      throw new ParseException();
    }
    throw new Error("Missing return statement in function");
}

  final public ExpressionNode fn_call() throws ParseException {String f;
    ExpressionNode e;
    f = fn_id();
    jj_consume_token(56);
    e = expression();
    jj_consume_token(57);
{if ("" != null) return new FnCallNode(f, e);}
    throw new Error("Missing return statement in function");
}

  private boolean jj_2_1(int xla)
 {
    jj_la = xla; jj_lastpos = jj_scanpos = token;
    try { return (!jj_3_1()); }
    catch(LookaheadSuccess ls) { return true; }
    finally { jj_save(0, xla); }
  }

  private boolean jj_3R_builtin_call_469_5_55()
 {
    if (jj_scan_token(TAN)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_468_5_54()
 {
    if (jj_scan_token(SQR)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_467_5_53()
 {
    if (jj_scan_token(SIN)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_466_5_52()
 {
    if (jj_scan_token(SGN)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_465_5_51()
 {
    if (jj_scan_token(RIGHTS)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_464_5_50()
 {
    if (jj_scan_token(MIDS)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_463_5_49()
 {
    if (jj_scan_token(LOG)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_462_5_48()
 {
    if (jj_scan_token(LEN)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_461_5_47()
 {
    if (jj_scan_token(LEFTS)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_460_5_46()
 {
    if (jj_scan_token(INT)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_459_5_45()
 {
    if (jj_scan_token(COS)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_458_5_44()
 {
    if (jj_scan_token(CHRS)) return true;
    return false;
  }

  private boolean jj_3R_additive_expression_362_5_20()
 {
    if (jj_3R_multiplicative_expression_376_5_21()) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_457_5_43()
 {
    if (jj_scan_token(ATN)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_456_5_42()
 {
    if (jj_scan_token(ASC)) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_455_5_36()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_builtin_call_455_5_41()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_456_5_42()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_457_5_43()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_458_5_44()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_459_5_45()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_460_5_46()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_461_5_47()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_462_5_48()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_463_5_49()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_464_5_50()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_465_5_51()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_466_5_52()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_467_5_53()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_468_5_54()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_469_5_55()) {
    jj_scanpos = xsp;
    if (jj_3R_builtin_call_470_5_56()) return true;
    }
    }
    }
    }
    }
    }
    }
    }
    }
    }
    }
    }
    }
    }
    }
    return false;
  }

  private boolean jj_3R_builtin_call_455_5_41()
 {
    if (jj_scan_token(ABS)) return true;
    return false;
  }

  private boolean jj_3R_varOrDimAccess_444_5_38()
 {
    if (jj_scan_token(IDENTIFIER)) return true;
    return false;
  }

  private boolean jj_3R_relational_expression_346_5_19()
 {
    if (jj_3R_additive_expression_362_5_20()) return true;
    return false;
  }

  private boolean jj_3R_literal_434_5_40()
 {
    if (jj_scan_token(STRING)) return true;
    return false;
  }

  private boolean jj_3R_literal_433_5_35()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_literal_433_5_39()) {
    jj_scanpos = xsp;
    if (jj_3R_literal_434_5_40()) return true;
    }
    return false;
  }

  private boolean jj_3R_literal_433_5_39()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_scan_token(3)) {
    jj_scanpos = xsp;
    if (jj_scan_token(4)) return true;
    }
    return false;
  }

  private boolean jj_3R_equality_expression_332_5_18()
 {
    if (jj_3R_relational_expression_346_5_19()) return true;
    return false;
  }

  private boolean jj_3R_primary_expression_425_5_34()
 {
    if (jj_3R_varOrDimAccess_444_5_38()) return true;
    return false;
  }

  private boolean jj_3R_primary_expression_424_5_33()
 {
    if (jj_3R_fn_call_479_5_37()) return true;
    return false;
  }

  private boolean jj_3R_primary_expression_423_5_32()
 {
    if (jj_3R_builtin_call_455_5_36()) return true;
    return false;
  }

  private boolean jj_3R_primary_expression_422_5_31()
 {
    if (jj_scan_token(56)) return true;
    return false;
  }

  private boolean jj_3R_primary_expression_421_5_29()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_primary_expression_421_5_30()) {
    jj_scanpos = xsp;
    if (jj_3R_primary_expression_422_5_31()) {
    jj_scanpos = xsp;
    if (jj_3R_primary_expression_423_5_32()) {
    jj_scanpos = xsp;
    if (jj_3R_primary_expression_424_5_33()) {
    jj_scanpos = xsp;
    if (jj_3R_primary_expression_425_5_34()) return true;
    }
    }
    }
    }
    return false;
  }

  private boolean jj_3R_primary_expression_421_5_30()
 {
    if (jj_3R_literal_433_5_35()) return true;
    return false;
  }

  private boolean jj_3R_and_expression_322_5_17()
 {
    if (jj_3R_equality_expression_332_5_18()) return true;
    return false;
  }

  private boolean jj_3R_unary_expression_not_plus_minus_412_5_28()
 {
    if (jj_3R_primary_expression_421_5_29()) return true;
    return false;
  }

  private boolean jj_3R_unary_expression_not_plus_minus_411_5_27()
 {
    if (jj_scan_token(NOT)) return true;
    return false;
  }

  private boolean jj_3R_unary_expression_not_plus_minus_411_5_26()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_unary_expression_not_plus_minus_411_5_27()) {
    jj_scanpos = xsp;
    if (jj_3R_unary_expression_not_plus_minus_412_5_28()) return true;
    }
    return false;
  }

  private boolean jj_3R_expression_312_5_16()
 {
    if (jj_3R_and_expression_322_5_17()) return true;
    return false;
  }

  private boolean jj_3R_unary_expression_403_5_25()
 {
    if (jj_3R_unary_expression_not_plus_minus_411_5_26()) return true;
    return false;
  }

  private boolean jj_3R_unary_expression_402_5_24()
 {
    if (jj_scan_token(54)) return true;
    return false;
  }

  private boolean jj_3R_unary_expression_402_5_23()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_unary_expression_402_5_24()) {
    jj_scanpos = xsp;
    if (jj_3R_unary_expression_403_5_25()) return true;
    }
    return false;
  }

  private boolean jj_3R_fn_id_298_9_59()
 {
    if (jj_scan_token(FNIDENTIFIER)) return true;
    return false;
  }

  private boolean jj_3R_fn_id_297_9_58()
 {
    if (jj_scan_token(FN)) return true;
    return false;
  }

  private boolean jj_3R_print_element_202_5_15()
 {
    if (jj_scan_token(TAB)) return true;
    return false;
  }

  private boolean jj_3R_fn_id_296_5_57()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_fn_id_297_9_58()) {
    jj_scanpos = xsp;
    if (jj_3R_fn_id_298_9_59()) return true;
    }
    return false;
  }

  private boolean jj_3R_print_element_201_5_13()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_3R_print_element_201_5_14()) {
    jj_scanpos = xsp;
    if (jj_3R_print_element_202_5_15()) return true;
    }
    return false;
  }

  private boolean jj_3R_print_element_201_5_14()
 {
    if (jj_3R_expression_312_5_16()) return true;
    return false;
  }

  private boolean jj_3R_power_expression_390_5_22()
 {
    if (jj_3R_unary_expression_402_5_23()) return true;
    return false;
  }

  private boolean jj_3R_fn_call_479_5_37()
 {
    if (jj_3R_fn_id_296_5_57()) return true;
    return false;
  }

  private boolean jj_3_1()
 {
    Token xsp;
    xsp = jj_scanpos;
    if (jj_scan_token(53)) {
    jj_scanpos = xsp;
    if (jj_scan_token(58)) return true;
    }
    if (jj_3R_print_element_201_5_13()) return true;
    return false;
  }

  private boolean jj_3R_multiplicative_expression_376_5_21()
 {
    if (jj_3R_power_expression_390_5_22()) return true;
    return false;
  }

  private boolean jj_3R_builtin_call_470_5_56()
 {
    if (jj_scan_token(VAL)) return true;
    return false;
  }

  /** Generated Token Manager. */
  public BasicParserTokenManager token_source;
  SimpleCharStream jj_input_stream;
  /** Current token. */
  public Token token;
  /** Next token. */
  public Token jj_nt;
  private int jj_ntk;
  private Token jj_scanpos, jj_lastpos;
  private int jj_la;
  private int jj_gen;
  final private int[] jj_la1 = new int[46];
  static private int[] jj_la1_0;
  static private int[] jj_la1_1;
  static private int[] jj_la1_2;
  static {
	   jj_la1_init_0();
	   jj_la1_init_1();
	   jj_la1_init_2();
	}
	private static void jj_la1_init_0() {
	   jj_la1_0 = new int[] {0x8,0x0,0xa4ddc000,0xa4dde000,0x5,0x10000,0xa4ddc000,0x0,0x0,0x18,0x58,0x4000000,0x0,0x0,0x0,0x0,0x5b221ed8,0x5b221ed8,0x0,0x0,0x180000,0x0,0x0,0xa4ddc008,0x100000,0x0,0x20000,0x0,0x100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x5b221ed8,0x5b221ed8,0x1b221ed8,0x18,0x58,0x0,0x0,0x1b201e80,};
	}
	private static void jj_la1_init_1() {
	   jj_la1_1 = new int[] {0x0,0x100000,0x410be,0x410be,0x0,0x1000,0x410be,0x200000,0x400000,0x0,0x400000,0x0,0x200000,0x1000000,0x4200000,0x4000000,0x14e6740,0x14e6740,0x200000,0x200000,0x0,0x200000,0x800,0x410be,0x8000,0x200000,0x80000,0x1,0x0,0x8800000,0x8800000,0xf0000000,0xf0000000,0x400000,0x400000,0x0,0x0,0x0,0x14e4740,0x10e4740,0x10e4740,0x0,0x0,0x200000,0x1000000,0x24740,};
	}
	private static void jj_la1_init_2() {
	   jj_la1_2 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x6,0x6,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
	}
  final private JJCalls[] jj_2_rtns = new JJCalls[1];
  private boolean jj_rescan = false;
  private int jj_gc = 0;

  /** Constructor with InputStream. */
  public BasicParser(java.io.InputStream stream) {
	  this(stream, null);
  }
  /** Constructor with InputStream and supplied encoding */
  public BasicParser(java.io.InputStream stream, String encoding) {
	 try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
	 token_source = new BasicParserTokenManager(jj_input_stream);
	 token = new Token();
	 jj_ntk = -1;
	 jj_gen = 0;
	 for (int i = 0; i < 46; i++) jj_la1[i] = -1;
	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  /** Reinitialise. */
  public void ReInit(java.io.InputStream stream) {
	  ReInit(stream, null);
  }
  /** Reinitialise. */
  public void ReInit(java.io.InputStream stream, String encoding) {
	 try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
	 token_source.ReInit(jj_input_stream);
	 token = new Token();
	 jj_ntk = -1;
	 jj_gen = 0;
	 for (int i = 0; i < 46; i++) jj_la1[i] = -1;
	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  /** Constructor. */
  public BasicParser(java.io.Reader stream) {
	 jj_input_stream = new SimpleCharStream(stream, 1, 1);
	 token_source = new BasicParserTokenManager(jj_input_stream);
	 token = new Token();
	 jj_ntk = -1;
	 jj_gen = 0;
	 for (int i = 0; i < 46; i++) jj_la1[i] = -1;
	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  /** Reinitialise. */
  public void ReInit(java.io.Reader stream) {
	if (jj_input_stream == null) {
	   jj_input_stream = new SimpleCharStream(stream, 1, 1);
	} else {
	   jj_input_stream.ReInit(stream, 1, 1);
	}
	if (token_source == null) {
 token_source = new BasicParserTokenManager(jj_input_stream);
	}

	 token_source.ReInit(jj_input_stream);
	 token = new Token();
	 jj_ntk = -1;
	 jj_gen = 0;
	 for (int i = 0; i < 46; i++) jj_la1[i] = -1;
	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  /** Constructor with generated Token Manager. */
  public BasicParser(BasicParserTokenManager tm) {
	 token_source = tm;
	 token = new Token();
	 jj_ntk = -1;
	 jj_gen = 0;
	 for (int i = 0; i < 46; i++) jj_la1[i] = -1;
	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  /** Reinitialise. */
  public void ReInit(BasicParserTokenManager tm) {
	 token_source = tm;
	 token = new Token();
	 jj_ntk = -1;
	 jj_gen = 0;
	 for (int i = 0; i < 46; i++) jj_la1[i] = -1;
	 for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
  }

  private Token jj_consume_token(int kind) throws ParseException {
	 Token oldToken;
	 if ((oldToken = token).next != null) token = token.next;
	 else token = token.next = token_source.getNextToken();
	 jj_ntk = -1;
	 if (token.kind == kind) {
	   jj_gen++;
	   if (++jj_gc > 100) {
		 jj_gc = 0;
		 for (int i = 0; i < jj_2_rtns.length; i++) {
		   JJCalls c = jj_2_rtns[i];
		   while (c != null) {
			 if (c.gen < jj_gen) c.first = null;
			 c = c.next;
		   }
		 }
	   }
	   return token;
	 }
	 token = oldToken;
	 jj_kind = kind;
	 throw generateParseException();
  }

  @SuppressWarnings("serial")
  static private final class LookaheadSuccess extends java.lang.Error {
    @Override
    public Throwable fillInStackTrace() {
      return this;
    }
  }
  static private final LookaheadSuccess jj_ls = new LookaheadSuccess();
  private boolean jj_scan_token(int kind) {
	 if (jj_scanpos == jj_lastpos) {
	   jj_la--;
	   if (jj_scanpos.next == null) {
		 jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
	   } else {
		 jj_lastpos = jj_scanpos = jj_scanpos.next;
	   }
	 } else {
	   jj_scanpos = jj_scanpos.next;
	 }
	 if (jj_rescan) {
	   int i = 0; Token tok = token;
	   while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
	   if (tok != null) jj_add_error_token(kind, i);
	 }
	 if (jj_scanpos.kind != kind) return true;
	 if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
	 return false;
  }

/** Get the next Token. */
  final public Token getNextToken() {
	 if (token.next != null) token = token.next;
	 else token = token.next = token_source.getNextToken();
	 jj_ntk = -1;
	 jj_gen++;
	 return token;
  }

/** Get the specific Token. */
  final public Token getToken(int index) {
	 Token t = token;
	 for (int i = 0; i < index; i++) {
	   if (t.next != null) t = t.next;
	   else t = t.next = token_source.getNextToken();
	 }
	 return t;
  }

  private int jj_ntk_f() {
	 if ((jj_nt=token.next) == null)
	   return (jj_ntk = (token.next=token_source.getNextToken()).kind);
	 else
	   return (jj_ntk = jj_nt.kind);
  }

  private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
  private int[] jj_expentry;
  private int jj_kind = -1;
  private int[] jj_lasttokens = new int[100];
  private int jj_endpos;

  private void jj_add_error_token(int kind, int pos) {
	 if (pos >= 100) {
		return;
	 }

	 if (pos == jj_endpos + 1) {
	   jj_lasttokens[jj_endpos++] = kind;
	 } else if (jj_endpos != 0) {
	   jj_expentry = new int[jj_endpos];

	   for (int i = 0; i < jj_endpos; i++) {
		 jj_expentry[i] = jj_lasttokens[i];
	   }

	   for (int[] oldentry : jj_expentries) {
		 if (oldentry.length == jj_expentry.length) {
		   boolean isMatched = true;

		   for (int i = 0; i < jj_expentry.length; i++) {
			 if (oldentry[i] != jj_expentry[i]) {
			   isMatched = false;
			   break;
			 }

		   }
		   if (isMatched) {
			 jj_expentries.add(jj_expentry);
			 break;
		   }
		 }
	   }

	   if (pos != 0) {
		 jj_lasttokens[(jj_endpos = pos) - 1] = kind;
	   }
	 }
  }

  /** Generate ParseException. */
  public ParseException generateParseException() {
	 jj_expentries.clear();
	 boolean[] la1tokens = new boolean[68];
	 if (jj_kind >= 0) {
	   la1tokens[jj_kind] = true;
	   jj_kind = -1;
	 }
	 for (int i = 0; i < 46; i++) {
	   if (jj_la1[i] == jj_gen) {
		 for (int j = 0; j < 32; j++) {
		   if ((jj_la1_0[i] & (1<<j)) != 0) {
			 la1tokens[j] = true;
		   }
		   if ((jj_la1_1[i] & (1<<j)) != 0) {
			 la1tokens[32+j] = true;
		   }
		   if ((jj_la1_2[i] & (1<<j)) != 0) {
			 la1tokens[64+j] = true;
		   }
		 }
	   }
	 }
	 for (int i = 0; i < 68; i++) {
	   if (la1tokens[i]) {
		 jj_expentry = new int[1];
		 jj_expentry[0] = i;
		 jj_expentries.add(jj_expentry);
	   }
	 }
	 jj_endpos = 0;
	 jj_rescan_token();
	 jj_add_error_token(0, 0);
	 int[][] exptokseq = new int[jj_expentries.size()][];
	 for (int i = 0; i < jj_expentries.size(); i++) {
	   exptokseq[i] = jj_expentries.get(i);
	 }
	 return new ParseException(token, exptokseq, tokenImage);
  }

  private boolean trace_enabled;

/** Trace enabled. */
  final public boolean trace_enabled() {
	 return trace_enabled;
  }

  /** Enable tracing. */
  final public void enable_tracing() {
  }

  /** Disable tracing. */
  final public void disable_tracing() {
  }

  private void jj_rescan_token() {
	 jj_rescan = true;
	 for (int i = 0; i < 1; i++) {
	   try {
		 JJCalls p = jj_2_rtns[i];

		 do {
		   if (p.gen > jj_gen) {
			 jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
			 switch (i) {
			   case 0: jj_3_1(); break;
			 }
		   }
		   p = p.next;
		 } while (p != null);

		 } catch(LookaheadSuccess ls) { }
	 }
	 jj_rescan = false;
  }

  private void jj_save(int index, int xla) {
	 JJCalls p = jj_2_rtns[index];
	 while (p.gen > jj_gen) {
	   if (p.next == null) { p = p.next = new JJCalls(); break; }
	   p = p.next;
	 }

	 p.gen = jj_gen + xla - jj_la;
	 p.first = token;
	 p.arg = xla;
  }

  static final class JJCalls {
	 int gen;
	 Token first;
	 int arg;
	 JJCalls next;
  }

}

Answer: BasicParser parser = new BasicParser(new StringReader(source));

<think>
The buildProgram method needs to create a parser for the source string. The imports already include BasicParser and StringReader, so the next line should instantiate the parser with the source wrapped in a StringReader.
</think>
BasicParser parser = new BasicParser(new StringReader(source));