import java.io.*; import java.util.*; public class Terp { public Terp() { //ctor pushVarFrame(); // global addBuiltins(); } // get and set variable, in current scope, or global if it startsWith "::" public boolean varExists(String v) { return null != (String) varScope(v).get( varKey(v) ); } public String getVar(String v) { String z= (String) varScope(v).get( varKey(v) ); if (null==z) Toss( "no such variable: "+v ); return z; } public String setVar(String v, String x) { if (null==x) Toss("null value assigned to var: "+v ); varScope(v).put( varKey(v), x ); return x; } // get and set commands with Cmd objects, or with "proc" definition public Cmd getCmd(String s) { return (Cmd) cmds.get(s); } public void addCmd( String name, Cmd c ) { cmds.put(name,c); } public void addProc( String name, String[] args, String body ) { cmds.put(name, new Proc(this,args,body) ); } public String call( String[] words ) { try { if (0==words.length) Toss( "call: empty wordlist" ); Cmd c= getCmd( words[0] ); if (null==c) Toss( "no such command: "+words[0] ); String z= c.call(words); if (null==z) Toss( "command returned null: "+words[0] ); return z; } catch (Return result) { throw result; } catch (Break b) { throw b; } catch (Continue c) { throw c; } catch (RuntimeException ex) { if (Verbose) ex.printStackTrace(); //String msg= (ex.getClass() == RuntimeException.class) ? ex.getMessage() : ex.toString(); String msg= ex.toString(); throw new Err( msg + "\n -- " + Human(ToString(words)) ); } } public String eval( String stmts ) { String z= ""; // default result is empty string String[][] ss= SplitStmts(stmts); // eval each statement in order for ( int i=0; i < ss.length; i++ ) { String[] ww= ss[i]; if (ww.length>0) { // substWord each word of the statement before calling it for ( int j=0; j < ww.length; j++ ) { ww[j]= substWord( ww[j] ); } z= call(ww); // keep as a potential result } } return z; // return last result set } //////////// static utilities public static RuntimeException Toss( Object msg ) { throw new Err( msg.toString() ); } public static int ToInt(String s) { int z= 0; try { z= Integer.parseInt(s); } catch (NumberFormatException ex) { if (Verbose) ex.printStackTrace(); Toss( "cannot convert to integer: "+s ); } return z; } /////////// Protected protected Hashtable cmds= new Hashtable(); protected Vector vars= new Vector(); public void pushVarFrame() { vars.addElement( new Hashtable() ); } public void popVarFrame() { vars.setSize( vars.size()-1 ); } // strips off optional initial :: to produce simple variable name protected String XvarKey(String v) { return v.startsWith("::") ? v.substring(2) : v; } protected Hashtable XvarScope(String v) { return v.startsWith("::") ? (Hashtable) vars.firstElement() : (Hashtable) vars.lastElement(); } protected String varKey(String v) { v= v.startsWith("::") ? v.substring(2) : v; if ( v.endsWith(")") ) { int p= v.indexOf('('); return v.substring(p+1, v.length()-1 ); } else { return v; } } protected Hashtable varScope(String v) { Hashtable ht= v.startsWith("::") ? (Hashtable) vars.firstElement() : (Hashtable) vars.lastElement(); if ( v.endsWith(")") ) { int p= v.indexOf('('); Hashtable z= (Hashtable) ht.get( v.substring(0,p) ); if (null==z) { z= new Hashtable(); ht.put( v.substring(0,p), z ); } return z; } else { return ht; } } //////////// Main public static String ReadWholeStream( InputStream is ) { StringBuffer sb= new StringBuffer(); final int SIZ = 0x10000; byte[] bbuf= new byte[SIZ]; char[] buf= new char[SIZ]; try { while (true) { int cc= is.read(bbuf); if (cc<1) break; for (int i=0; i " + ex ); if (Verbose) ex.printStackTrace(); } } public static boolean IsAlphanumeric(char x) { return '0'<=x && x<='9' || 'A'<=x && x<='Z' || 'a'<=x && x<='z' || x=='_' ; } public static boolean IsWhite(char x) { return x==' ' || x=='\t' || x=='\n' || x=='\r'; } public static boolean IsReallyWhite(char x) { return x==' ' || x=='\t' || x=='\r'; } public static String[] ToStringArray( Vector v ) { int n= v.size(); String[] z= new String[ n ]; for ( int i=0; i < n; i++ ) { z[i]= (String) v.elementAt(i); } return z; } public static String[][] ToStringArrayArray( Vector v ) { int n= v.size(); String[][] z= new String[n][]; for ( int i=0; i < n; i++ ) { z[i]= (String[]) v.elementAt(i); } return z; } public static String[] SplitWords( String s ) { Say("SplitWords `"+s+"'"); char[] a= s.toCharArray(); int N= a.length; Vector z= new Vector(); int i= 0; while (i0 && i0 || a[i]!='"' ) ) { if (a[i]=='[') ++level; if (a[i]==']') --level; ++i; } if (a[i]=='"') ++i; Say("2--- "+s.substring( start, i )); z.addElement( s.substring( start, i ) ); continue; ////////////////////// } else { // is a normalWord //++i; int level= 0; while (i0 || !IsWhite(a[i]) ) ) { if (a[i]=='[') ++level; if (a[i]==']') --level; ++i; } Say("start=" +start+ " i=" +i ); String w= s.substring( start, i ); Say("3--- "+w); z.addElement( w ); continue; ////////////////////// } } return ToStringArray( z ); } public static String[][] SplitStmts( String s ) { Vector zz= new Vector(); Say("SplitStmts `"+s+"'"); char[] a= s.toCharArray(); int N= a.length; int i= 0; while (i0 && i0 || a[i]!='"' ) ) { if (a[i]=='[') ++level; if (a[i]==']') --level; ++i; } if (a[i]=='"') ++i; Say("2+++ "+s.substring( start, i )); z.addElement( s.substring( start+1, i-1 ) ); continue; ////////////////////// } else { // is a normalWord //++i; int level= 0; while (i0 || !IsWhite(a[i]) && a[i]!=';' && a[i]!='\n' ) ) { if (a[i]=='[') ++level; if (a[i]==']') --level; ++i; //if (i0) zz.addElement( ss ); } // next cmd String[][]sss= ToStringArrayArray( zz ); Say(sss); return sss; } // Substitute [commands] in the string. private String substWord( String a ) { int N= a.length(); if ( 0==N ) return a; // quick return for empty string StringBuffer z= new StringBuffer(); // if hard quoted, return the insides if ( a.charAt(0)=='{' && a.charAt(N-1)=='}' ) return a.substring(1, N-1); int start= 0; int i=0; while (i0) { if (a.charAt(i)=='[') ++level; if (a.charAt(i)==']') --level; ++i; } z.append( eval( a.substring(start+1,i-1) ) ); } else if (a.charAt(i)=='$') { // substitute variable start= i; ++i; while (i0) buf.append(' '); buf.append( ListifyItem( ss[i] ) ); } return buf.toString(); } public static String Human(String s) { StringBuffer buf= new StringBuffer(); for (int i=0; i72) { buf.append("..."); break; } } return buf.toString(); } public static String ToString(String[] ss) { return ListifyItems(ss); } public static String ToString(String[][] sss) { String[] z= new String[ sss.length ]; for (int i=0; i1 ? a[1] : "" ); }}); addCmd( "break", new Cmd() { public String call (String[] a) { throw new Break(); }}); addCmd( "continue", new Cmd() { public String call (String[] a) { throw new Continue(); }}); addCmd( "==", new Cmd() { public String call (String[] a) { return ToInt(a[1]) == ToInt(a[2]) ? "1" : "0"; }}); addCmd( "!=", new Cmd() { public String call (String[] a) { return ToInt(a[1]) != ToInt(a[2]) ? "1" : "0"; }}); addCmd( "<", new Cmd() { public String call (String[] a) { return ToInt(a[1]) < ToInt(a[2]) ? "1" : "0"; }}); addCmd( "<=", new Cmd() { public String call (String[] a) { return ToInt(a[1]) <= ToInt(a[2]) ? "1" : "0"; }}); addCmd( ">", new Cmd() { public String call (String[] a) { return ToInt(a[1]) > ToInt(a[2]) ? "1" : "0"; }}); addCmd( ">=", new Cmd() { public String call (String[] a) { return ToInt(a[1]) >= ToInt(a[2]) ? "1" : "0"; }}); addCmd( "+", new Cmd() { public String call (String[] a) { int z= 0; for ( int i=1; i < a.length; i++ ) { z += ToInt( a[i] ); } return ""+z; }}); addCmd( "-", new Cmd() { public String call (String[] a) { if (a.length==3) return Integer.toString( ToInt(a[1]) - ToInt(a[2]) ); else if (a.length==2) return Integer.toString( - ToInt(a[1]) ); else throw Toss( "- needs 1 or two args" ); }}); addCmd( "*", new Cmd() { public String call (String[] a) { int z= 1; for ( int i=1; i < a.length; i++ ) { z *= ToInt( a[i] ); } return ""+z; }}); addCmd( "/", new Cmd() { public String call (String[] a) { return Integer.toString( ToInt(a[1]) / ToInt(a[2]) ); }}); addCmd( "range", new Cmd() { public String call (String[] a) { StringBuffer buf= new StringBuffer(); int max= ToInt( a[1] ); for ( int i=0; i < max; i++ ) { if (i>0) buf.append( ' ' ); buf.append( Integer.toString(i) ); } return buf.toString(); }}); addCmd( "lindex", new Cmd() { public String call (String[] a) { String[] ss= SplitWords(a[1]); int i= ToInt(a[2]); return i>=0 && i=ss.length ? ss.length-1 : j; return ListifyItems( Range( ss, i, j ) ); }}); addCmd( "lsort", new Cmd() { public String call (String[] a) { String[] ss= SplitWords(a[1]); Arrays.sort(ss); return ListifyItems( ss ); }}); addCmd( "implode", new Cmd() { public String call (String[] a) { StringBuffer buf= new StringBuffer(); String[] ss= SplitWords(a[1]); for ( int i=0; i < ss.length; i++ ) { buf.append( (char)( ToInt(ss[i]) ) ); } return buf.toString(); }}); addCmd( "explode", new Cmd() { public String call (String[] a) { StringBuffer buf= new StringBuffer(); int max= a[1].length(); for ( int i=0; i < max; i++ ) { if (i>0) buf.append( ' ' ); buf.append( Integer.toString( a[1].charAt(i) ) ); } return buf.toString(); }}); addCmd( "list", new Cmd() { public String call (String[] a) { return ListifyItems ( Range( a, 1, a.length-1 ) ); }}); addCmd( "proc", new Cmd() { public String call (String[] a) { if (a.length-1!=3) Toss("proc command requires 3 args. Usage: proc name arglist body"); addProc( a[1], SplitWords(a[2]), a[3] ); return ""; }}); addCmd( "names", new Cmd() { public String call (String[] a) { if (a.length-1!=1) Toss("names command requires 1 arg. Usage: names varName"); Hashtable ht= varScope( a[1] + "()" ); Vector v= new Vector(); Enumeration e= ht.keys(); while ( e.hasMoreElements() ) { v.addElement( e.nextElement() ); } String[] z= ToStringArray(v); Arrays.sort(z); return ListifyItems( z ); }}); addCmd( "get", new Cmd() { public String call (String[] a) { if (a.length-1!=1) Toss("get command requires 1 arg. Usage: get varName"); return getVar( a[1] ); }}); addCmd( "set", new Cmd() { public String call (String[] a) { if (a.length-1!=2) Toss("set command requires 2 args. Usage: set varName value"); return setVar( a[1], a[2] ); }}); addCmd( "incr", new Cmd() { public String call (String[] a) { if (a.length-1<2) Toss("Usage: incr varName ?increment?"); if ( ! varExists( a[1] ) ) { setVar( a[1], "0" ); } // create if necessary int incr= 1; if (a.length>2) incr= ToInt( a[2] ); return setVar( a[1], "" + (ToInt(getVar( a[1] )) + incr) ); }}); addCmd( "append", new Cmd() { public String call (String[] a) { if (a.length-1<2) Toss("Usage: append varName values..."); if ( ! varExists( a[1] ) ) { setVar( a[1], "" ); } // create if necessary for ( int i=2; i < a.length; i++ ) { setVar( a[1], getVar( a[1] ) + a[i] ); } return getVar( a[1] ); }}); addCmd( "lappend", new Cmd() { public String call (String[] a) { if (a.length-1<2) Toss("Usage: lappend varName values..."); if ( ! varExists( a[1] ) ) { setVar( a[1], "" ); } // create if necessary String link= getVar( a[1] ).length() > 0 ? " " : ""; for ( int i=2; i < a.length; i++ ) { setVar( a[1], getVar( a[1] ) + link + ListifyItem(a[i]) ); link= " "; } return getVar( a[1] ); }}); addCmd( "out", new Cmd() { public String call (String[] a) { System.out.println( a[1] ); System.out.flush(); return a[1]; }}); addCmd( "err", new Cmd() { public String call (String[] a) { System.err.println( a[1] ); return a[1]; }}); addCmd( "if", new Cmd() { public String call (String[] a) { int cond= ToInt( eval(a[1]) ); int p= 2; if ( "then".equals( a[2] ) ) ++p; if (0!=cond) { return eval(a[p]); } else if (a.length>p+1) { ++p; if ( "else".equals( a[p] ) ) ++p; return eval(a[p]); } else { return ""; } }}); addCmd( "while", new Cmd() { public String call (String[] a) { StringBuffer buf= new StringBuffer(); try { while ( 0 != ToInt( eval( a[1] ) ) ) { try { if (buf.length()>0) buf.append( " " ); buf.append( ListifyItem( eval( a[2] ) ) ); } catch (Continue ex) { continue; } } } catch (Break ex) { } return buf.toString(); }}); addCmd( "foreach", new Cmd() { public String call (String[] a) { if (a.length-1!=3) Toss("foreach command requires 3 args. Usage: foreach varname valuelist body"); String[] ww= SplitWords( a[2] ); StringBuffer buf= new StringBuffer(); try { for ( int i=0; i < ww.length; i++ ) { try { setVar( a[1], ww[i] ); if (buf.length()>0) buf.append( " " ); buf.append( ListifyItem( eval( a[3] ) ) ); } catch (Continue ex) { continue; } } } catch (Break ex) { } return buf.toString(); }}); addCmd( "must", new Cmd() { public String call (String[] a) { if (a.length-1!=2) Toss("must command requires 2 args. Usage: must expectedValue command"); String z= eval(a[2]); if ( ! a[1].equals(z) ) Toss("MUST expected `" +a[1]+ "' but got `" +z+ "'"); return ""; }}); } } // $Id: Terp.java,v 1.11 2004/08/24 06:28:22 strick Exp strick $