// Reverse-Polish calculator for BigInteger // // R. Perry, May 2016 // // added echo command to control whether commands are displayed // when executing from a file // // changed StringBuffer to StringBuilder (no sync ok) // added base-256 conversions (ASCII), toString and BigInteger // - December 2015 // added new methods from jdk1.8.0_51: // byteValueExact, shortValueExact, intValueExact, longValueExact // - July 2015 // added sqrt command, binary search for square root // - July 2014 // added signum=1 to "64 BigInteger" command to force positive. // using new Base64 encode_skip_leading_zero() routine for consistency // with other base conversions. // - November 2013 // changed B64Decoder to reset before throwing an exception // - September 2013 // added ECadd, ECmul, ECmul2 Elliptic Curve methods over prime field // - June 2012 // changed process_word() nested if..else's to switch on String (JDK 7) // - May 22 // added byteValue, shortValue, nextProbablePrime, and probablePrime methods // - May 20, 2012 // added Base64 Encode/Decode for radix 64 used with toString // and BigInteger constructor // - April 2005 // added \ line continuation when reading commands from a file // - April 2003 // added font, tic, toc commands // - March 2003 // // with modifications by Shireesh Thanneru, to get rid of the // deprecation warning caused by the handleEvent() method of JDK1.0. // Now Uses JDK1.1/JDK1.2 event model, which is different from the // JDK1.0 event model. // // Operators and Commands: // // clear clear the stack // Clear clear the stack and output TextArea // clean clear the stack and symbol table // Clean clear the stack, symbol table, command history, and output TextArea // // dup duplicate the top of the stack // pop remove the top of the stack // size push size of stack onto the stack // swap swap the top two elements of the stack // save write the output window to file ~/Calc.out // // exit exit the calculator // quit exit the calculator // // history print the command history // !! recall previous command; must be the first and only command on an interactive input line // ! recall command by number // // < <= == != >= > Comparison operators // // + - * / Arithmetic operators // // ++ -- add or subtract one from top of stack // // % mod // // ** pow // // ^ & | ~ xor, and, or, not // // << shiftLeft // >> shiftRight // // milk order a keg of milk over the Internet; credit-card number is pop'd from the stack // // factorial factorial // // Euclid Euclid's algorithm for gcd and modInverse // // cubeRoot cube root, if one exists, otherwise 0 // // sqrt square root, if one exists, otherwise 0 // // font set display font size // // tic start a stopwatch timer // toc read the stopwatch timer, i.e. elapsed time in milliseconds // // = assign a name to the top of the stack // who display the symbol table // ; print the stack; but at the end of a line skips stack printing // // repeat ... until repeat commands until top of stack is non-zero // // while ... end repeat commands while top of stack is non-zero // // if ... [else ...] end conditionally execute commands // // run execute commands from a file // echo control whether commands are displayed when executing from a file // // # comment; remainder of input line skipped // // rand generate a random BigInteger with the specified number of bits // prime generate a random prime BigInteger with the specified number of bits and certainty // // BigInteger BigInteger( String val, int radix) constructor // // IDEAmul IDEA multiplication mod 2**16 + 1 // IDEAadd IDEA addition mod 2**16 // IDEAmulInverse IDEA multiplicative inverse // IDEAaddInverse IDEA additive inverse // // plus all of the methods from java.math.BigInteger, by name // // Example: // // Input Resulting Stack // ----- --------------- // 1 2 3 add 1 // 5 import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; import java.math.BigInteger; import java.security.spec.ECPoint; public class Calc extends Frame implements Runnable, ActionListener { protected TextField input; protected TextArea output; protected Thread listener; protected String newline; // "\n" ? protected Stack S; // the stack protected Stack T; // the symbol table for named values protected Vector C; // the history of command strings protected Random R; // random number generator (should really use SecureRandom) protected long tic; // stopwatch timer for tic and toc commands protected boolean echo; // control whether commands are displayed // IDEA constants // protected static BigInteger IDEA_N = BigInteger.valueOf( 65536); // 2**16 protected static BigInteger IDEA_N1 = BigInteger.valueOf( 65537); // 2**16 + 1 // 0,1,2,3 constants // protected static BigInteger ZERO = BigInteger.ZERO; protected static BigInteger ONE = BigInteger.ONE; protected static BigInteger TWO = BigInteger.valueOf( 2); protected static BigInteger THREE = BigInteger.valueOf( 3); // Base64 Encode/Decode // protected B64Encoder b64e; protected B64Decoder b64d; // Base256 Encode/Decode // protected B256Encoder b256e; protected B256Decoder b256d; // constructor // public Calc() { // GUI setup // super( "BigInteger Calculator"); setLayout( new BorderLayout()); output = new TextArea( 30, 70); output.setEditable( false); output.setBackground( Color.white); add( "Center", output); add( "South", input = new TextField()); pack(); validate(); setVisible(true); input.requestFocus(); listener = new Thread( this); listener.start(); newline = System.getProperty("line.separator"); // stack setup // S = new Stack(); T = new Stack(); C = new Vector(); R = new Random(); // from Shireesh // input.addActionListener( this); // for handling Text Field events addWindowListener( new WindowAdapter() { // for window closing public void windowClosing( WindowEvent e) { dispose(); try { System.exit(0); } catch( Exception ex) {} } } ); tic = System.currentTimeMillis(); echo = true; } public void run () { // new Thread/window is already running, so do nothing here } // pop an Element, and return the BigInteger part // BigInteger pop() { return (S.pop()).value; } // push a BigInteger with no name // void push( BigInteger a) { S.push( new Element( "", a)); } // Euclid's algorithm for gcd and modInverse // void Euclid() { BigInteger x, y, q = null; BigInteger[] r, u, v, t; y = pop(); x = pop(); r = new BigInteger[3]; u = new BigInteger[3]; v = new BigInteger[3]; r[0] = x; r[1] = y; u[0] = ONE; u[1] = ZERO; v[0] = ZERO; v[1] = ONE; output.append( "q, r, u, v" + newline); output.append( "[], " + r[0] + ", " + u[0] + ", " + v[0] + newline); output.append( "[], " + r[1] + ", " + u[1] + ", " + v[1] + newline); while( !r[1].equals( ZERO)) { t = r[0].divideAndRemainder(r[1]); q = t[0]; r[2] = t[1]; u[2] = u[0].subtract( q.multiply( u[1])); v[2] = v[0].subtract( q.multiply( v[1])); output.append( q + ", " + r[2] + ", " + u[2] + ", " + v[2] + newline); // // shift for next iteration: // r[0] = r[1]; r[1] = r[2]; u[0] = u[1]; u[1] = u[2]; v[0] = v[1]; v[1] = v[2]; } push( u[0]); push( v[0]); } // binary search for cube root // void cubeRoot() { BigInteger x = pop(); BigInteger a, b; BigInteger r, r3; int t, sign = x.signum(); if( sign == 0) // x == 0 { push( ZERO); return; } x = x.abs(); a = ONE; // lower bound b = x; // upper bound do { r = (a.add(b)).shiftRight(1); // (a+b)/2 r3 = r.pow(3); if( (t=r3.compareTo(x)) < 0) a = r.add( ONE); else if( t > 0) b = r.subtract( ONE); } while( t != 0 && a.compareTo(b) <= 0); if( t != 0) // no exact cube root { push( ZERO); return; } push( new BigInteger( sign, r.toByteArray())); } // binary search for square root // void sqrt() { BigInteger x = pop(); BigInteger a, b; BigInteger r, r2; int t, sign = x.signum(); if( sign <= 0) // x <= 0 { push( ZERO); return; } a = ONE; // lower bound b = x; // upper bound do { r = (a.add(b)).shiftRight(1); // (a+b)/2 r2 = r.multiply(r); if( (t=r2.compareTo(x)) < 0) a = r.add( ONE); else if( t > 0) b = r.subtract( ONE); } while( t != 0 && a.compareTo(b) <= 0); if( t != 0) // no exact square root { push( ZERO); return; } push( r); } // ECadd - Elliptic Curve point addition over prime field // // s = (y2-y1)/(x2-x1) // xr = s*s-x1-x2 // yr = s*(x1-xr)-y1 // // Note that ``a'' is not needed but is included as an argument // for consistency with the multiplication routines. // ECPoint ECadd( BigInteger x1, BigInteger y1, BigInteger x2, BigInteger y2, BigInteger p, BigInteger a) throws ArithmeticException { BigInteger s, num, den, xr, yr; if( x1.equals(x2)) { if( y1.equals(y2)) // adding a point to itself, use ECmul2 return ECmul2( x1, y1, p, a); else throw new ArithmeticException( "ECadd can't add points with x1 = x2"); } num = y2.subtract(y1); den = x2.subtract(x1); s = num.multiply(den.modInverse(p)).mod(p); xr = s.multiply(s).subtract(x1).subtract(x2).mod(p); yr = s.multiply(x1.subtract(xr)).subtract(y1).mod(p); return new ECPoint( xr, yr); } // ECmul2 - Elliptic Curve point multiply by 2 over prime field (double a point) // // s = (3*x*x+a)/(2*y) // xr = s*s-2*x // yr = s(x-xr)-y // ECPoint ECmul2( BigInteger x, BigInteger y, BigInteger p, BigInteger a) throws ArithmeticException { BigInteger s, num, den, xr, yr; if( y.equals(ZERO)) throw new ArithmeticException( "ECmul2 can't double point with y = 0"); num = THREE.multiply(x).multiply(x).mod(p).add(a); den = TWO.multiply(y); s = num.multiply(den.modInverse(p)).mod(p); xr = s.multiply(s).subtract(TWO.multiply(x)).mod(p); yr = s.multiply(x.subtract(xr)).subtract(y).mod(p); return new ECPoint( xr, yr); } // ECmul - Elliptic Curve point multiply by scalar over prime field // // Uses doubling and addition, which is not very efficient. // ECPoint ECmul( BigInteger x, BigInteger y, BigInteger d, BigInteger p, BigInteger a) throws ArithmeticException { BigInteger s, num, den, xr, yr; d = d.mod(p); // in case d is out-of-range or negative if( d.equals(ZERO)) throw new ArithmeticException( "ECmul can't multiply by 0"); ECPoint r = new ECPoint( x, y); BigInteger mask = ONE.shiftLeft( d.bitLength() - 1); while( mask.compareTo(ONE) > 0) { r = ECmul2( r.getAffineX(), r.getAffineY(), p, a); mask = mask.shiftRight(1); if( !mask.and(d).equals(ZERO)) r = ECadd( r.getAffineX(), r.getAffineY(), x, y, p, a); } return r; } // process one word of the user input // void process_word( StringTokenizer line, String word) { BigInteger a, b, c, d, e, f; Element x, y; try { switch( word) { case "clear": { S.clear(); break; } case "Clear": { S.clear(); output.setText( ""); break; } case "clean": { S.clear(); T.clear(); break; } case "Clean": { S.clear(); T.clear(); C.clear(); output.setText( ""); break; } case "dup": { x = S.pop(); S.push( x); S.push( x); break; } case "pop": { a = pop(); break; } case "size": { push( BigInteger.valueOf(S.size())); break; } case "swap": { x = S.pop(); y = S.pop(); S.push( x); S.push( y); break; } case "save": { String fname = System.getProperty( "user.home") + "/Calc.out"; BufferedWriter fout = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( fname))); fout.write( output.getText()); fout.close(); output.append( "output saved to file: " + fname + newline); break; } case "exit": case "quit": { dispose(); try { System.exit(0); } catch( Exception ex) {} break; } case "history": { output.append( "---" + newline); for( int i = 0; i < C.size(); ++i) output.append( i + "\t" + C.elementAt(i) + newline); break; } case "milk": { a = pop(); // need some more work here to convert 'a' to credit-card // number format and contact milk-distributer web site // over the net. For now, the "milk" command is equivalent // to "pop". output.append( "milk: credit-card rejected; drink water instead" + newline); break; } case "factorial": { a = pop(); c = b = ONE; while( a.compareTo(b) > 0) { c = c.multiply(a); a = a.subtract(b); } push( c); break; } case "Euclid": { Euclid(); break; } case "cubeRoot": { cubeRoot(); break; } case "sqrt": { sqrt(); break; } case "font": { Font font = new Font( "", Font.PLAIN, pop().intValue()); output.setFont( font); input.setFont( font); break; } case "tic": { tic = System.currentTimeMillis(); break; } case "toc": { push( BigInteger.valueOf( System.currentTimeMillis() - tic)); break; } case "=": { if( line.hasMoreTokens()) { word = line.nextToken(); x = S.pop(); // remove existing name from symbol table if( (y = lookup( T, word)) != null) T.removeElement( y); // unname existing copies on the stack if( (y = lookup( S, word)) != null) y.name = ""; // make a copy x = (Element) x.clone(); x.name = word; T.push( x); } else output.append( "Error: missing name after =" + newline); break; } case "who": { list( T); break; } case ";": { if( line.hasMoreTokens()) list( S); break; } case "until": { output.append( "Error: missing repeat before until" + newline); break; } case "repeat": // repeat ... until|EOLN { StringBuilder buf = new StringBuilder(); int repeat = 1; while( line.hasMoreTokens()) { word = line.nextToken(); if( word.equals( "repeat")) ++repeat; else if( word.equals( "until") && --repeat == 0) break; buf.append( ' '); buf.append( word); } String cmd = buf.toString(); do { process_cmd( cmd, false, -1); a = pop(); } while( a.equals(ZERO)); break; } case "end": { output.append( "Error: missing if or while before end" + newline); break; } case "while": // while ... end|EOLN { StringBuilder buf = new StringBuilder(); int nif = 1; while( line.hasMoreTokens()) { word = line.nextToken(); if( word.equals( "while") || word.equals( "if") ) ++nif; else if( word.equals( "end") && --nif == 0) break; buf.append( ' '); buf.append( word); } String cmd = buf.toString(); while( !(a = pop()).equals( ZERO) ) process_cmd( cmd, false, -1); break; } case "else": { output.append( "Error: missing if before else" + newline); break; } case "if": // if ... [else ...] end|EOLN { a = pop(); int nif = 1; if( !a.equals(ZERO)) // do if part { while( line.hasMoreTokens() && !(word = line.nextToken()).equals("else") && !word.equals("end")) process_word( line, word); // skip else part if( word.equals("else")) while( line.hasMoreTokens()) { word = line.nextToken(); if( word.equals( "if") || word.equals( "while") ) ++nif; else if( word.equals( "end") && --nif == 0) break; } } else // do else part { // skip if part while( line.hasMoreTokens()) { word = line.nextToken(); if( word.equals( "if") || word.equals( "while") ) ++nif; else if( word.equals( "end") && --nif == 0) break; else if( word.equals( "else") && nif == 1) break; } if( word.equals("else")) while( line.hasMoreTokens() && !(word = line.nextToken()).equals("end")) process_word( line, word); } break; } case "run": { if( line.hasMoreTokens()) { word = line.nextToken(); process_file( word); } else output.append( "Error: missing file name after run command" + newline); break; } case "echo": { if( line.hasMoreTokens()) { word = line.nextToken(); if( word.equals( "on")) echo = true; else if( word.equals( "off")) echo = false; else output.append( "Error: bad echo option" + newline); } else output.append( "echo is " + (echo ? "on" : "off") + newline); break; } case "#": { while( line.hasMoreTokens()) line.nextToken(); // skip rest of line break; } case "rand": { a = pop(); push( new BigInteger( a.intValue(), R)); break; } case "prime": { b = pop(); a = pop(); push( new BigInteger( a.intValue(), b.intValue(), R)); break; } case "BigInteger": { b = pop(); if( line.hasMoreTokens()) { int b_int = b.intValue(); word = line.nextToken(); if( b_int == 64) // Base64 special case { if( b64d == null) b64d = new B64Decoder(); b64d.decode( word); push( new BigInteger( 1, b64d.flush())); // positive } else if( b_int == 256) // Base256 (ASCII) special case { if( b256d == null) b256d = new B256Decoder(); b256d.decode( word); push( new BigInteger( 1, b256d.flush())); // positive } else { push( new BigInteger( word, b_int)); } } else output.append( "Error: missing String after BigInteger command" + newline); break; } case "abs": { a = pop(); push( a.abs()); break; } case "add": case "+": { b = pop(); a = pop(); push( a.add(b)); break; } case "++": { x = S.pop(); x.value = x.value.add(ONE); S.push( x); break; } case "--": { x = S.pop(); x.value = x.value.subtract(ONE); S.push( x); break; } case "and": case "&": { b = pop(); a = pop(); push( a.and(b)); break; } case "andNot": { b = pop(); a = pop(); push( a.andNot(b)); break; } case "bitCount": { a = pop(); push( BigInteger.valueOf(a.bitCount())); break; } case "bitLength": { a = pop(); push( BigInteger.valueOf(a.bitLength())); break; } case "byteValue": { a = pop(); push( BigInteger.valueOf(a.byteValue())); break; } case "byteValueExact": { a = pop(); push( BigInteger.valueOf(a.byteValueExact())); break; } case "clearBit": { b = pop(); a = pop(); push( a.clearBit( b.intValue()) ); break; } case "compareTo": { b = pop(); a = pop(); push( BigInteger.valueOf(a.compareTo(b))); break; } case "divide": case "/": { b = pop(); a = pop(); push( a.divide(b)); break; } case "divideAndRemainder": { b = pop(); a = pop(); BigInteger[] z = a.divideAndRemainder(b); push( z[0]); push( z[1]); break; } case "doubleValue": { a = pop(); double val = a.doubleValue(); output.append( "doubleValue = " + val + newline); push( BigInteger.valueOf((long)val) ); break; } case "equals": case "==": { b = pop(); a = pop(); push( a.equals(b) ? ONE : ZERO); break; } case "!=": { b = pop(); a = pop(); push( a.equals(b) ? ZERO : ONE); break; } case "<": { b = pop(); a = pop(); push( (a.compareTo(b) < 0) ? ONE : ZERO); break; } case "<=": { b = pop(); a = pop(); push( (a.compareTo(b) <= 0) ? ONE : ZERO); break; } case ">": { b = pop(); a = pop(); push( (a.compareTo(b) > 0) ? ONE : ZERO); break; } case ">=": { b = pop(); a = pop(); push( (a.compareTo(b) >= 0) ? ONE : ZERO); break; } case "flipBit": { b = pop(); a = pop(); push( a.flipBit( b.intValue()) ); break; } case "floatValue": { a = pop(); float val = a.floatValue(); output.append( "floatValue = " + val + newline); push( BigInteger.valueOf((long)val) ); break; } case "gcd": { b = pop(); a = pop(); push( a.gcd(b)); break; } case "getLowestSetBit": { a = pop(); push( BigInteger.valueOf(a.getLowestSetBit())); break; } case "hashCode": { a = pop(); push( BigInteger.valueOf(a.hashCode())); break; } case "intValue": { a = pop(); push( BigInteger.valueOf(a.intValue())); break; } case "intValueExact": { a = pop(); push( BigInteger.valueOf(a.intValueExact())); break; } case "isProbablePrime": { b = pop(); a = pop(); push( a.isProbablePrime(b.intValue()) ? ONE : ZERO); break; } case "longValue": { a = pop(); push( BigInteger.valueOf(a.longValue())); break; } case "longValueExact": { a = pop(); push( BigInteger.valueOf(a.longValueExact())); break; } case "max": { b = pop(); a = pop(); push( a.max(b)); break; } case "min": { b = pop(); a = pop(); push( a.min(b)); break; } case "mod": case "%": { b = pop(); a = pop(); push( a.mod(b)); break; } case "modInverse": { b = pop(); a = pop(); push( a.modInverse(b)); break; } case "modPow": { c = pop(); b = pop(); a = pop(); push( a.modPow(b,c)); break; } case "multiply": case "*": { b = pop(); a = pop(); push( a.multiply(b)); break; } case "negate": { a = pop(); push( a.negate()); break; } case "nextProbablePrime": { a = pop(); push( a.nextProbablePrime()); break; } case "not": case "~": { a = pop(); push( a.not()); break; } case "or": case "|": { b = pop(); a = pop(); push( a.or(b)); break; } case "pow": case "**": { b = pop(); a = pop(); push( a.pow( b.intValue()) ); break; } case "remainder": { b = pop(); a = pop(); push( a.remainder(b)); break; } case "probablePrime": { a = pop(); push( BigInteger.probablePrime( a.intValue(), R)); break; } case "setBit": { b = pop(); a = pop(); push( a.setBit( b.intValue()) ); break; } case "shiftLeft": case "<<": { b = pop(); a = pop(); push( a.shiftLeft( b.intValue()) ); break; } case "shiftRight": case ">>": { b = pop(); a = pop(); push( a.shiftRight( b.intValue()) ); break; } case "shortValue": { a = pop(); push( BigInteger.valueOf(a.shortValue())); break; } case "shortValueExact": { a = pop(); push( BigInteger.valueOf(a.shortValueExact())); break; } case "signum": { a = pop(); push( BigInteger.valueOf( a.signum())); break; } case "subtract": case "-": { b = pop(); a = pop(); push( a.subtract(b)); break; } case "testBit": { b = pop(); a = pop(); push( a.testBit(b.intValue()) ? ONE : ZERO); break; } case "toByteArray": { a = pop(); byte[] ba = a.toByteArray(); output.append( "toByteArray ="); for( int i = 0; i < ba.length; ++i) output.append( " " + ba[i]); output.append( newline); push( a); break; } case "toString": { b = pop(); int b_int = b.intValue(); x = S.pop(); if( b_int == 64) // Base64 special case { if( b64e == null) b64e = new B64Encoder(); b64e.encode_skip_leading_zero( x.value.toByteArray()); output.append( "toString = " + b64e.flush() + newline); } else if( b_int == 256) // Base256 (ASCII) special case { if( b256e == null) b256e = new B256Encoder(); b256e.encode_skip_leading_zero( x.value.toByteArray()); output.append( "toString = " + b256e.flush() + newline); } else { output.append( "toString = " + x.value.toString(b_int) + newline); } S.push( x); break; } case "valueOf": { // NOOP break; } case "xor": case "^": { b = pop(); a = pop(); push( a.xor(b)); break; } case "IDEAmul": { b = pop(); a = pop(); if( a.equals(ZERO)) a = IDEA_N; if( b.equals(ZERO)) b = IDEA_N; c = a.multiply(b).mod(IDEA_N1); if( c.equals(IDEA_N)) c = ZERO; push( c); break; } case "IDEAadd": { b = pop(); a = pop(); push( a.add(b).mod(IDEA_N)); break; } case "IDEAmulInverse": { a = pop(); if( a.equals(ZERO)) a = IDEA_N; a = a.modInverse(IDEA_N1); if( a.equals(IDEA_N)) a = ZERO; push( a); break; } case "IDEAaddInverse": { a = pop(); push( IDEA_N.subtract(a).mod(IDEA_N) ); break; } case "ECadd": // x1 y1 x2 y2 p a ECadd { f = pop(); e = pop(); d = pop(); c = pop(); b = pop(); a = pop(); ECPoint r = ECadd( a, b, c, d, e, f); push( r.getAffineX()); push( r.getAffineY()); break; } case "ECmul": // x y d p a ECmul { e = pop(); d = pop(); c = pop(); b = pop(); a = pop(); ECPoint r = ECmul( a, b, c, d, e); push( r.getAffineX()); push( r.getAffineY()); break; } case "ECmul2": // x y p a ECmul2 { d = pop(); c = pop(); b = pop(); a = pop(); ECPoint r = ECmul2( a, b, c, d); push( r.getAffineX()); push( r.getAffineY()); break; } default: { if( "+-0123456789".indexOf( word.charAt(0)) >= 0) // must be a number push( new BigInteger( word)); else { // must be a named result if( (x = lookup( T, word)) != null) S.push( x); else output.append( "Error: name not found: " + word + newline); } break; } } } catch( Exception ex) { output.append( "Error: " + ex + newline); } } // print the stack or symbol table // public void list( Stack s) { Enumeration e = s.elements(); Element x; if( e.hasMoreElements()) output.append( "---" + newline); while( e.hasMoreElements()) { x = e.nextElement(); output.append( "\t" + x.name + "\t" + x.value + newline); } } // lookup an element by name // public Element lookup( Stack s, String name) { Enumeration e = s.elements(); Element x; while( e.hasMoreElements()) { x = e.nextElement(); if( x.name.equals( name)) return x; } return null; } // process a file of commands // public void process_file( String fname) throws FileNotFoundException, IOException { BufferedReader fin = new BufferedReader( new InputStreamReader( new FileInputStream( fname))); String line, cmd; cmd = ""; while( (line = fin.readLine()) != null) { if( line.length() > 0) { if( line.charAt( line.length()-1) == '\\') // backslash at end means continue with next line { cmd = cmd + line.substring( 0, line.length()-1); } else { cmd = cmd + line; process_cmd( cmd, echo, -1); cmd = ""; } } } if( cmd.length() > 0) process_cmd( cmd, echo, -1); fin.close(); } // process one line of the user input // public void process_cmd( String cmd, boolean echo, int num) { StringTokenizer line = new StringTokenizer( cmd); String word; if( line.hasMoreTokens()) { if( echo) { if( num >= 0) output.append( "" + num); output.append( "> " + cmd + newline); } do { process_word( line, word = line.nextToken()); } while( line.hasMoreTokens()); if( echo && !word.equals(";")) list( S); } } // from Shireesh, replacing the deprecated handleEvent() method // public void actionPerformed( ActionEvent e) // for handling action events { Object source = e.getSource(); if (source == input) // for Text Field action events { int num; // command history number String cmd = input.getText(); input.setText (""); if( cmd.length() == 0) return; if( cmd.charAt(0) == '!') // command recall { output.append( "> " + cmd + newline); if( cmd.length() < 2) { output.append( "Error: bad ! command" + newline); list( S); return; } if( cmd.length() == 2 && cmd.charAt(1) == '!') // !! num = C.size() - 1; else try { num = (new Integer( cmd.substring(1))).intValue(); } catch( Exception ex) { output.append( "Error: " + ex + newline); list( S); return; } if( num < 0 || num >= C.size()) { output.append( "Error: bad ! argument" + newline); list( S); return; } input.setText( C.elementAt( num)); return; } C.addElement( cmd); num = C.size() - 1; process_cmd( cmd, true, num); return; } } public static void main( String args[]) { new Calc(); } }