/* * Statement.java - BASIC Statement object * * Copyright (c) 1996 Chuck McManis, All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes and without * fee is hereby granted provided that this copyright notice * appears in all copies. * * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT * OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ package basic; import java.io.PrintStream; import java.io.OutputStream; import java.io.InputStream; import java.io.DataInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ByteArrayOutputStream; import java.util.Vector; import util.RedBlackTree; import java.util.Enumeration; /** * This class defines BASIC statements. As with expressions, there is a * subclass of Statement that does parsing called ParseStatement. This * separation allows the statement object to be half as large as it might * otherwise be. * * The execute interface defines what the BASIC statements *do*. * These are all called by the containing Program. */ abstract class Statement { protected int keyword; // type of statement protected int line; private String orig; // original string that was parsed into this statement. /** * These are all of the statement keywords we can parse. */ final static String keywords[] = { "*NONE*", "goto", "gosub", "return", "print", "if", "then", "end", "data", "restore", "read", "on", "rem", "for", "to", "next", "step", "gosub", "goto", "let", "input", "stop", "dim", "randomize", "tron", "troff", "timer", }; /** * This constants should match the indexes of the above keywords. */ final static int NONE = 0; // invalid statement final static int GOTO = 1; final static int GOSUB = 2; final static int RETURN = 3; final static int PRINT = 4; final static int IF = 5; final static int THEN = 6; final static int END = 7; final static int DATA = 8; final static int RESTORE = 9; final static int READ = 10; final static int ON = 11; final static int REM = 12; final static int FOR = 13; final static int TO = 14; final static int NEXT = 15; final static int STEP = 16; final static int ON_GOSUB = 17; final static int ON_GOTO = 18; final static int LET = 19; final static int INPUT = 20; final static int STOP = 21; final static int DIM = 22; final static int RANDOMIZE = 23; final static int TRON = 24; final static int TROFF = 25; final static int TIMER = 26; // not a real statement Statement nxt; // if there are chained statements /** * Construct a new statement object with a valid key. */ protected Statement(int key) { keyword = key; } /** * This method does the actual statement execution. It works by calling the * abstract function 'doit' which is defined in each statement subclass. The * runtime error (if any) is caught so that the line number and statement can * be attached to the result and then it is re-thrown. */ Statement execute(Program pgm, InputStream in, PrintStream out) throws BASICRuntimeError { Statement nxt = null; try { nxt = doit(pgm, in, out); } catch (BASICRuntimeError e) { throw new BASICRuntimeError(this, e.getMsg()); } return nxt; } /** * Return a string representation of this statement. If the * original text was set then use that, otherwise reconstruct * the string from the parse tree. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("BASIC Statement :"); if (orig != null) { sb.append(orig); } else { sb.append(unparse()); } return sb.toString(); } /** * Put a reference to the original string from which this statement was parsed. * this can be used for listing out the program in the native case style of the user. */ void addText(String t) { orig = t; } /** * Return the statement as a string. */ String asString() { return orig; } /** * Update line number information in this statement. Used to determine the next * line to execute. */ void addLine(int l) { line = l; if (nxt != null) nxt.addLine(l); } /** * Return this statements line number. */ int lineNo() { return line; } /** * reconstruct the statement from the parse tree, this is most useful for * diagnosing parsing problems. */ abstract String unparse(); /** * This method "runs" this statement and returns a reference on * the next statement to run or null if there is no next statement. * * @throws BASICRuntimeError if there is a problem during statement * execution such as divide by zero etc. */ abstract Statement doit(Program pgm, InputStream in, PrintStream out) throws BASICRuntimeError; protected RedBlackTree vars; // variables used by this statement. /** * The trace method can be used during execution to print out what * the program is doing. */ void trace(Program pgm, PrintStream ps) { StringBuffer sb = new StringBuffer(); String n; sb.append("**:"); if (vars == null) vars = getVars(); /* * Print the line we're executing on the output stream. */ n = line+""; for (int zz = 0; zz < 5 - n.length(); zz++) sb.append(' '); sb.append(n); sb.append(':'); sb.append(unparse()); ps.println(sb.toString()); if (vars != null) { for (Enumeration e = vars.elements(); e.hasMoreElements(); ) { VariableExpression vi = (VariableExpression) e.nextElement(); String t = null; try { t = vi.stringValue(pgm); } catch (BASICRuntimeError bse) { t = "Not yet defined."; } ps.println(" :"+vi.unparse()+" = "+(vi.isString() ? "\""+t+"\"" : t)); } } } /** * Can be overridden by statements that use variables in their execution. */ RedBlackTree getVars() { return null; } }