// Joseph Bergin // Pace University // April, 1996 // Updated for Java 2 events April 2002 import java.util.Random; import java.lang.Math; import java.awt.*; import java.applet.*; import java.util.*; import java.io.*; import java.awt.event.*; class SemanticRecord { private int _kind; private int _value; private SpreadsheetCell _which; private static final int num = 0; private static final int cell = 1; private static final int empty = 2; SemanticRecord(int kind, SpreadsheetCell which, int value) { _kind = kind; _which = which; _value = value; } SpreadsheetCell which() { return _which; } int evaluate() { if(_kind == num ) return _value; else if (_kind == cell) return _which.evaluate(); else return 0; // should never be executed actually. } } class SpreadsheetCell { // instructions static final int pushi = 0; static final int pushv = 1; static final int pop = 2; static final int sum = 3; static final int diff = 4; static final int prod = 5; static final int quot = 6; // Semantic Kind static final int num = 0; static final int cell = 1; static final int empty = 2; public SpreadsheetCell(SpreadsheetModel m, int col, int row, Scanner scanner) { this._sheet = m; this._row = row; this._col = col; this.scanner = scanner; } static boolean isLetter(char c) { return (c>='a' && c<='z') || (c>='A' && c<='Z'); } static boolean isDigit(char c) { return (c>='0' && c<='9'); } static boolean legalCellSpelling(String s) { int i; if(!isLetter(s.charAt(0))) return false; if(s.length() < 2) return false; for(i = 1; i < s.length(); ++i) { if(!isDigit(s.charAt(i))) return false; } return true; } static int extractColumn(String s) // assumed to be legal { return s.charAt(0) - 'a'; } static int extractRow(String s) // assumed to be legal { int i, result = 0; for(i=1; i< s.length(); ++i) { if(!isDigit(s.charAt(i))) break; result = 10*result + (s.charAt(i) - '0'); } return result; } int evaluate() { Integer Rtemp,Ltemp; if(!_isEvaluated) { //System.out.println("eval"+_row+" "+_col+" "+_programLength); for(int i = 0; i<_programLength; ++i) { //System.out.println(((Integer)((Pair)_program.elementAt(i)).first).intValue()); switch (((Integer)((Pair)_program.elementAt(i)).first).intValue()) { case pushi: case pushv: SemanticRecord rec = (SemanticRecord)((Pair)_program.elementAt(i)).second; int val; //if(rec.which() == this) // val = _value; //else val = rec.evaluate(); // Will recurse infinitely if a cell self references. evaluationStack.push(new Integer(val)); break; case pop: // never used here??? break; case sum: Rtemp = (Integer)evaluationStack.pop(); Ltemp = (Integer)evaluationStack.pop(); evaluationStack.push(new Integer(Ltemp.intValue() + Rtemp.intValue())); break; case diff: Rtemp = (Integer)evaluationStack.pop(); Ltemp = (Integer)evaluationStack.pop(); evaluationStack.push(new Integer(Ltemp.intValue() - Rtemp.intValue())); break; case prod: Rtemp = (Integer)evaluationStack.pop(); Ltemp = (Integer)evaluationStack.pop(); evaluationStack.push(new Integer(Ltemp.intValue() * Rtemp.intValue())); break; case quot: Rtemp = (Integer)evaluationStack.pop(); Ltemp = (Integer)evaluationStack.pop(); evaluationStack.push(new Integer(Ltemp.intValue() / Rtemp.intValue())); break; } } Integer temp = (Integer)evaluationStack.pop(); _value = temp.intValue(); } //System.out.println("value "+_value); _isEvaluated = true; return _value; } String name() { return String.valueOf('a' + _col).concat(String.valueOf(_row)); } void mark() { _isEvaluated = _hasValue; } void parse() // parses rhs of an assignment command. { _programLength = 0; // start with a new program _hasValue = false; // no value yet parseexpression(); scanner.match(scanner.semicolon); // Ths parser is recursive descent, based on the grammar below. } void parseexpression() { parseterm(); parseaot(); } void parseterm() { parsefactor(); parsemof(); } void parseaot() { int op; if(scanner.nextToken() == scanner.plusop || scanner.nextToken() == scanner.minusop) { op = sum; if (scanner.nextToken() == Scanner.minusop) op = diff; scanner.toss(); parseterm(); SemanticRecord none = new SemanticRecord(empty, null, 0); _programLength++; _program.addElement( new Pair(new Integer(op), none)); _hasValue = false; parseaot(); } } void parsemof() { int op; if(scanner.nextToken() == scanner.timesop || scanner.nextToken() == scanner.divideop) { op = prod; if (scanner.nextToken() == scanner.divideop) op = quot; scanner.toss(); parsefactor(); SemanticRecord none = new SemanticRecord(empty, null, 0); _programLength++; _program.addElement( new Pair(new Integer(op), none)); _hasValue = false; parsemof(); } } void parsefactor() { SemanticRecord result; SemanticRecord none; switch (scanner.nextToken()) { case Scanner.lparen: scanner.match(scanner.lparen); parseexpression(); scanner.match(scanner.rparen); break; case Scanner.letter: int row, col; row = extractRow(scanner.which()); col = extractColumn(scanner.which()); //extractCell(scanner.tokenbuffer.which, col, row); if(!_sheet.legalCell(col,row))scanner.parseError("Illegal cell. "+col+" "+row); result = new SemanticRecord(cell, _sheet.cell(col,row) ,0); scanner.toss(); _programLength++; _program.addElement( new Pair(new Integer(pushv), result)); _hasValue = false; break; case Scanner.number: result = new SemanticRecord(num, null, scanner.value()); scanner.toss(); _programLength++; _program.addElement( new Pair(new Integer(pushi), result)); _hasValue = true; _value = result.evaluate(); break; case Scanner.sumfunc: case Scanner.prodfunc: int op = sum; if(scanner.nextToken() == scanner.prodfunc)op = prod; scanner.toss(); scanner.match(scanner.lparen); int lr=-1,lc=-1,rr=-1,rc=-1; // leftrow... if(scanner.nextToken() == scanner.letter && legalCellSpelling(scanner.which())) { lc = extractColumn(scanner.which()); lr = extractRow(scanner.which()); scanner.toss(); } else scanner.parseError("Illegal range "+lc+" "+lr); scanner.match(scanner.rangeop); if(scanner.nextToken() == scanner.letter && legalCellSpelling(scanner.which())) { rc = extractColumn(scanner.which()); rr = extractRow(scanner.which()); scanner.toss(); } else scanner.parseError("Illegal range "+rc+" "+rr); scanner.match(scanner.rparen); int temp; if(lc > rc){temp = lc; lc = rc; rc = temp;} if(lr > rr){temp = lr; lr = rr; rr = temp;} if(!_sheet.legalCell(lc,lr))scanner.parseError("Illegal range "+lc+" "+lr); if(!_sheet.legalCell(rc,rr))scanner.parseError("Illegal range "+rc+" "+rr); none = new SemanticRecord(empty,null, 0); result = new SemanticRecord(cell, _sheet.cell(lc,lr) , 0); int cellcount = (rc - lc +1) * (rr - lr + 1); _programLength++; _program.addElement( new Pair(new Integer(pushv), result)); for(int i = lr; i<= rr; i++) for(int j = lc; j<=rc; j++) { if(i!=lr || j != lc) { result = new SemanticRecord(cell, _sheet.cell(j,i) , 0); _programLength++; _program.addElement( new Pair(new Integer(pushv), result)); _programLength++; _program.addElement( new Pair(new Integer(op), none)); } } break; } } private boolean _hasValue = true; // ifFalse: hasProgram private boolean _isEvaluated = false; private Vector _program = new Vector(10); // The vector contains pairs(Integer, SemanticRecord) private static Stack evaluationStack = new Stack(); private Scanner scanner; private int _programLength = 0; // Current number of instructions stored private int _value = 0; private SpreadsheetModel _sheet = null; private int _row, _col; // Where this cell is in the Sheet. public int value() { return _value; } public boolean isEvaluated() { return _isEvaluated; } } class SpreadsheetModel { public SpreadsheetModel( int columns, int rows, Scanner scan ) //up to 26 COLUMNS { scanner = scan; _rows = rows; _columns = columns; _cells = new SpreadsheetCell[columns][rows]; if(columns > 26) throw (new IllegalArgumentException("Too many columns in Spreadsheet.")); for(int i = 0; i ::= ";" | ::= "cell" "=" ::= ::= "" | ("+" | "-") ::= ::= "" | ("*" | "/") ::= "number" | "cell" | "(" ")" | "prod" "(" ")" ::= "cell" ".." "cell" ::= "eval" | "print" | "dump" | "done" "cell" is a single letter followed by an integer (both in legal range): e.g. a4. "number" is an unsigned integer. To get -5 use 0-5. There is no unary minus. Sorry. */ // example commands // A6 = 5; // The letter names the column and the integer the row. // B8 = A6 + 1; // also elaborate expressions with operators +-*/ and parens. // A9 = sum(B1..C4); // also prod(range) where range == cell..cell // NOTE: A recursive command (e.g. A0=A0+1) is illegal. There is no check for it in this // program however. Your program will eventually crash if you try it. It is also possible // to write mutually recursive sets of commands. These are just as illegal. /* Exercises: (In approximate increasing order of difficulty ) Add a unary minus. Use ~ for this operator, not -. ~5 means minus 5. Add the modulo operator to the language. Use % for this operator. 12 % 5 should yield 2. Give the program a prettier interface. Add an additional range function. Choose a good one. Add an exponentiation operator. Use ^ for this operator. 2^5 should yield 32. The precidence level of ^ should be higher than that of * and /, but lower than parens. Note that if you permit 2^5^3, that it should group from the right as 2^(5^3). The included operators all group from the left within precidence levels. Add a logic operator similar to the C++ conditional expression operator ?:. To the left of the ? you need a comparison like a4<3. To the right you need two expressions separated by the colon. What improvements can you make that will increase the total code size by no more than one page? Guarantee that recursive programs are caught before they are evaluated. */