| Program |

    package ij.macro;
    import ij.*;
    import java.util.Hashtable;

    /** An object of this type is a tokenized macro file and the associated symbol table. */
    public class Program implements MacroConstants {

        private int maxSymbols = 800; // will be increased as needed
        private int maxProgramSize = 2000;  // well be increased as needed
        private int pc = -1;

        int stLoc = -1;
        int symTabLoc;
        Symbol[] table = new Symbol[maxSymbols];
        static Symbol[] systemTable;
        int[] code = new int[maxProgramSize];
        Variable[] globals;
        boolean hasVars;
        int macroCount;
        Hashtable menus;
        // run keyboard shortcut macros on event dispatch thread?
        boolean queueCommands;

        public Program() {
            if (systemTable!=null) {
                stLoc = systemTable.length - 1;
                for (int i=0; i<=stLoc; i++)
                table[i] = systemTable[i];
            } else {
                //IJ.log("make table");
                addKeywords();
                addFunctions();
                addNumericFunctions();
                addStringFunctions();
                addArrayFunctions();
                systemTable = new Symbol[stLoc+1];
                for (int i=0; i<=stLoc; i++)
                    systemTable[i] = table[i];
                IJ.register(Program.class);
            }
        }

        public int[] getCode() {
            return code;
        }

        public Symbol[] getSymbolTable() {
            return table;
        }

        void addKeywords() {
            for (int i=0; i<keywords.length; i++)
                addSymbol(new Symbol(keywordIDs[i], keywords[i]));
        }

        void addFunctions() {
            for (int i=0; i<functions.length; i++)
                addSymbol(new Symbol(functionIDs[i], functions[i]));
        }

        void addNumericFunctions() {
            for (int i=0; i<numericFunctions.length; i++)
                addSymbol(new Symbol(numericFunctionIDs[i], numericFunctions[i]));
        }

        void addStringFunctions() {
            for (int i=0; i<stringFunctions.length; i++)
                addSymbol(new Symbol(stringFunctionIDs[i], stringFunctions[i]));
        }

        void addArrayFunctions() {
            for (int i=0; i<arrayFunctions.length; i++)
                addSymbol(new Symbol(arrayFunctionIDs[i], arrayFunctions[i]));
        }

        void addSymbol(Symbol sym) {
            stLoc++;
            if (stLoc==table.length) {
                Symbol[] tmp = new Symbol[maxSymbols*2];
                System.arraycopy(table, 0, tmp, 0, maxSymbols);
                table = tmp;
                maxSymbols *= 2;
            }
            table[stLoc] = sym;
        }

        void addToken(int tok) {
            pc++;
            if (pc==code.length) {
                int[] tmp = new int[maxProgramSize*2];
                System.arraycopy(code, 0, tmp, 0, maxProgramSize);
                code = tmp;
                maxProgramSize *= 2;
            }
            code[pc] = tok;
        }

        /** Looks up a word in the symbol table. Returns null if the word is not found. */
        Symbol lookupWord(String str) {
            //IJ.log("lookupWord: "+str);
            Symbol symbol;
            String symStr;
            for (int i=0; i<=stLoc; i++) {
                symbol = table[i];
                if (symbol.type!=STRING_CONSTANT && str.equals(symbol.str)) {
                    symTabLoc = i;
                    return symbol;
                }
            }
            return null;
        }

        void saveGlobals(Interpreter interp) {
            //IJ.log("saveGlobals: "+interp.topOfStack);
            if (interp.topOfStack==-1)
                return;
            int n = interp.topOfStack+1;
            globals = new Variable[n];
            for (int i=0; i<n; i++)
                globals[i] = interp.stack[i];
        }

        public void dumpSymbolTable() {
            IJ.log("");
            IJ.log("Symbol Table");
            for (int i=0; i<=maxSymbols; i++) {
                Symbol symbol = table[i];
                if (symbol==null)
                    break;
                IJ.log(i+" "+symbol);
            }
        }

        public void dumpProgram() {
            IJ.log("");
            IJ.log("Tokenized Program");
            String str;
            int token, address;
            for (int i=0; i<=pc; i++)
                IJ.log(i+"  "+(code[i]&TOK_MASK)+"  "+decodeToken(code[i]));
        }

        public Variable[] getGlobals() {
            return globals;
        }

        public boolean hasVars() {
            return hasVars;
        }

        public int macroCount() {
            return macroCount;
        }

        public String decodeToken(int token) {
            return decodeToken(token&TOK_MASK, token>>TOK_SHIFT);
        }

        String decodeToken(int token, int address) {
            String str;
            switch (token) {
                case WORD:
                case PREDEFINED_FUNCTION:
                case NUMERIC_FUNCTION:
                case STRING_FUNCTION:
                case ARRAY_FUNCTION:
                case USER_FUNCTION:
                    str = table[address].str;
                    break;
                case STRING_CONSTANT:
                    str = "\""+table[address].str+"\"";
                    break;
                case NUMBER:
                    double v = table[address].value;
                    if ((int)v==v)
                        str = IJ.d2s(v,0);
                    else
                        str = ""+v;
                    break;
                case EOL:
                    str = "EOL";
                    break;
                case EOF:
                    str = "EOF";
                    break;
                default:
                    if (token<32) {
                        switch (token) {
                        case PLUS_PLUS:
                            str="++";
                            break;
                        case MINUS_MINUS:
                            str="--";
                            break;
                        case PLUS_EQUAL:
                            str="+=";
                            break;
                        case MINUS_EQUAL:
                            str="-=";
                            break;
                        case MUL_EQUAL:
                            str="*=";
                            break;
                        case DIV_EQUAL:
                            str="/=";
                            break;
                        case LOGICAL_AND:
                            str="&&";
                            break;
                        case LOGICAL_OR:
                            str="||";
                            break;
                        case EQ:
                            str="==";
                            break;
                        case NEQ:
                            str="!=";
                            break;
                        case GT:
                            str=">";
                            break;
                        case GTE:
                            str=">=";
                            break;
                        case LT:
                            str="<";
                            break;
                        case LTE:
                            str="<=";
                            break;
                        default:
                            str="";
                            break;
                        }
                    } else if (token>=200) {
                        str = table[address].str;
                    } else {
                        char s[] = new char[1];
                        s[0] = (char)token;
                        str = new String(s);
                    }
                    break;
            }
            return str;
        }

        public Hashtable getMenus() {
            return menus;
        }

    } // Program

| Program |