org.spf4j.zel.vm.Program.java Source code

Java tutorial

Introduction

Here is the source code for org.spf4j.zel.vm.Program.java

Source

/*
 * Copyright (c) 2001, Zoltan Farkas All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.spf4j.zel.vm;

import com.google.common.base.Charsets;
import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import org.spf4j.base.Pair;
import org.spf4j.zel.instr.Instruction;
import org.spf4j.zel.instr.LValRef;
import org.spf4j.zel.instr.var.ARRAY;
import org.spf4j.zel.instr.var.INT;
import org.spf4j.zel.instr.var.LOG;
import org.spf4j.zel.instr.var.MAX;
import org.spf4j.zel.instr.var.MIN;
import org.spf4j.zel.instr.var.OUT;
import org.spf4j.zel.instr.var.RANDOM;
import org.spf4j.zel.instr.var.SQRT;
import org.spf4j.zel.vm.ParsingContext.Location;
import org.spf4j.zel.vm.gen.ParseException;
import org.spf4j.zel.vm.gen.TokenMgrError;
import org.spf4j.zel.vm.gen.ZCompiler;

/**
 * <p>
 * Title: Program</p>
 *
 * @author zoly
 * @version 1.0
 *
 * This is a Turing machine a Program will always be pretty much an array of operations.
 */
@Immutable
public final class Program implements Serializable {

    private static final long serialVersionUID = 748365748433474932L;

    public enum Type {
        DETERMINISTIC, NONDETERMINISTIC
    };

    public enum ExecutionType {
        SYNC, ASYNC
    }

    private final Type type;
    private final ExecutionType execType;
    private final int id; // program ID, unique ID identifying the program

    private final Instruction[] instructions;
    private final Location[] debug;
    private final String source;
    private final boolean hasDeterministicFunctions;
    private final Object[] globalMem;
    private final int localMemSize;
    private final Map<String, Integer> localSymbolTable;
    private final Map<String, Integer> globalSymbolTable;
    private final String name;

    //CHECKSTYLE:OFF
    Program(final String name, final Map<String, Integer> globalTable, final Object[] globalMem,
            final Map<String, Integer> localTable, @Nonnull final Instruction[] objs, final Location[] debug,
            final String source, @Nonnegative final int start, @Nonnegative final int end, final Type progType,
            final ExecutionType execType, final boolean hasDeterministicFunctions, final String... parameterNames)
            throws CompileException {
        //CHECKSTYLE:ON
        this.globalMem = globalMem;
        int length = end - start;
        this.instructions = new Instruction[length];
        System.arraycopy(objs, start, instructions, 0, length);
        this.type = progType;
        this.id = ProgramBuilder.generateID();
        this.execType = execType;
        this.hasDeterministicFunctions = hasDeterministicFunctions;
        this.localSymbolTable = buildLocalSymTable(objs, parameterNames, length, globalTable, localTable);
        this.localMemSize = localSymbolTable.size();
        this.globalSymbolTable = globalTable;
        this.debug = debug;
        this.source = source;
        this.name = name;
    }

    //CHECKSTYLE:OFF
    Program(final String name, final Map<String, Integer> globalTable, final Object[] globalMem,
            final Map<String, Integer> localTable, @Nonnull final Instruction[] instructions,
            final Location[] debug, final String source, final Type progType, final ExecutionType execType,
            final boolean hasDeterministicFunctions) throws CompileException {
        //CHECKSTYLE:ON
        this.globalMem = globalMem;
        this.instructions = instructions;
        this.type = progType;
        this.id = ProgramBuilder.generateID();
        this.execType = execType;
        this.hasDeterministicFunctions = hasDeterministicFunctions;
        this.localSymbolTable = localTable;
        this.localMemSize = localSymbolTable.size();
        this.globalSymbolTable = globalTable;
        this.debug = debug;
        this.source = source;
        this.name = name;
    }

    Location[] getDebug() {
        return debug;
    }

    public String getSource() {
        return source;
    }

    public String getName() {
        return name;
    }

    private static Map<String, Integer> buildLocalSymTable(final Instruction[] instructions,
            final String[] parameterNames1, final int length, final Map<String, Integer> globalTable,
            final Map<String, Integer> addTo) throws CompileException {
        final int addToSize = addTo.size();
        Map<String, Integer> symbolTable = new HashMap<>(addToSize + parameterNames1.length);
        symbolTable.putAll(addTo);
        // allocate program params
        int i = addToSize;
        for (String param : parameterNames1) {
            Integer existing = symbolTable.put(param, i++);
            if (existing != null) {
                throw new CompileException("Duplicate parameter defined: " + param);
            }
        }
        // allocate variables used in Program
        for (int j = 0; j < length; j++) {
            Instruction code = instructions[j];
            if (code instanceof LValRef) {
                String ref = ((LValRef) code).getSymbol();
                Integer idxr = symbolTable.get(ref);
                if (idxr == null) {
                    idxr = globalTable.get(ref);
                    if (idxr == null) {
                        idxr = i++;
                        symbolTable.put(ref, idxr);
                    }
                }
            }
        }
        return symbolTable;
    }

    public Map<String, Integer> getGlobalSymbolTable() {
        return globalSymbolTable;
    }

    public Map<String, Integer> getLocalSymbolTable() {
        return localSymbolTable;
    }

    public int getLocalMemSize() {
        return localMemSize;
    }

    Object[] getGlobalMem() {
        return globalMem;
    }

    @Override
    @CheckReturnValue
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Program other = (Program) obj;
        return (this.id == other.id);
    }

    @Override
    @CheckReturnValue
    public int hashCode() {
        return this.id;
    }

    public boolean hasDeterministicFunctions() {
        return hasDeterministicFunctions;
    }

    /**
     * @return the instructions
     */
    @CheckReturnValue
    public Object get(final int i) {
        return instructions[i];
    }

    @CheckReturnValue
    Object[] toArray() {
        return instructions.clone();
    }

    @CheckReturnValue
    public int size() {
        return instructions.length;
    }

    public ExecutionType getExecType() {
        return execType;
    }

    @Nonnull
    public static Program compile(@Nonnull final String zExpr, @Nonnull final String... varNames)
            throws CompileException {

        ParsingContext cc = new CompileContext(ZEL_GLOBAL_FUNC.copy());
        final String srcId = ZelFrame.newSource(zExpr);
        try {
            ZCompiler.compile(srcId, zExpr, cc);
        } catch (TokenMgrError | ParseException err) {
            throw new CompileException(err);
        }
        return RefOptimizer.INSTANCE.apply(cc.getProgramBuilder().toProgram("anon@root", srcId, varNames));
    }

    static Program compile(@Nonnull final String zExpr, final Map<String, Integer> localTable,
            final Object[] globalMem, final Map<String, Integer> globalTable, @Nonnull final String... varNames)
            throws CompileException {

        ParsingContext cc = new CompileContext(
                new MemoryBuilder(new ArrayList<>(Arrays.asList(globalMem)), globalTable));
        final String srcId = ZelFrame.newSource(zExpr);
        try {
            ZCompiler.compile(srcId, zExpr, cc);
        } catch (TokenMgrError | ParseException err) {
            throw new CompileException(err);
        }
        return cc.getProgramBuilder().toProgram("anon@root", srcId, varNames, localTable);
    }

    public Object execute() throws ExecutionException, InterruptedException {
        return execute(System.in, System.out, System.err);
    }

    public Object execute(final Object... args) throws ExecutionException, InterruptedException {
        return execute(System.in, System.out, System.err, args);
    }

    public Object execute(@Nonnull final ExecutorService execService, final Object... args)
            throws ExecutionException, InterruptedException {
        return execute(new VMExecutor(execService), System.in, System.out, System.err, args);
    }

    public Object executeSingleThreaded(final Object... args) throws ExecutionException, InterruptedException {
        return execute(null, System.in, System.out, System.err, args);
    }

    private static final MemoryBuilder ZEL_GLOBAL_FUNC;

    static {
        ZEL_GLOBAL_FUNC = new MemoryBuilder();
        ZEL_GLOBAL_FUNC.addSymbol("out", OUT.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("sqrt", SQRT.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("int", INT.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("log", LOG.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("log10", LOG.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("min", MIN.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("max", MAX.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("array", ARRAY.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("random", RANDOM.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("channel", Channel.Factory.INSTANCE);
        ZEL_GLOBAL_FUNC.addSymbol("EOF", Channel.EOF);
    }

    public Object execute(@Nullable final VMExecutor execService, @Nullable final InputStream in,
            @Nullable final PrintStream out, @Nullable final PrintStream err, final Object... args)
            throws ExecutionException, InterruptedException {
        final ExecutionContext ectx = new ExecutionContext(this, globalMem, in, out, err, execService);
        System.arraycopy(args, 0, ectx.mem, 0, args.length);
        return execute(ectx);
    }

    public Pair<Object, ExecutionContext> executeX(@Nullable final VMExecutor execService,
            @Nullable final InputStream in, @Nullable final PrintStream out, @Nullable final PrintStream err,
            final ResultCache resultCache, final Object... args) throws ExecutionException, InterruptedException {
        final ExecutionContext ectx = new ExecutionContext(this, globalMem, resultCache, in, out, err, execService);
        System.arraycopy(args, 0, ectx.mem, 0, args.length);
        return Pair.of(execute(ectx), ectx);
    }

    // TODO: Need to employ Either here
    @SuppressFBWarnings("URV_UNRELATED_RETURN_VALUES")
    public static Object executeSyncOrAsync(@Nonnull final ExecutionContext ectx)
            throws ExecutionException, InterruptedException {
        final VMExecutor.Suspendable<Object> execution = ectx.getCallable();
        if (ectx.execService != null && ectx.code.getExecType() == ExecutionType.ASYNC) {
            if (ectx.isChildContext()) {
                return ectx.execService.submitInternal(VMExecutor.synchronize(execution));
            } else {
                return ectx.execService.submit(VMExecutor.synchronize(execution));
            }
        } else {
            try {
                return execution.call();
            } catch (SuspendedException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    @SuppressFBWarnings("URV_UNRELATED_RETURN_VALUES")
    // TODO: Need to employ Either here
    public static Object executeAsync(@Nonnull final ExecutionContext ectx)
            throws ExecutionException, InterruptedException {
        final VMExecutor.Suspendable<Object> execution = ectx.getCallable();
        if (ectx.execService != null) {
            if (ectx.isChildContext()) {
                return ectx.execService.submitInternal(VMExecutor.synchronize(execution));
            } else {
                return ectx.execService.submit(VMExecutor.synchronize(execution));
            }
        } else {
            try {
                return execution.call();
            } catch (SuspendedException ex) {
                throw new RuntimeException(ex);
            }
        }
    }

    public static Object executeSync(@Nonnull final ExecutionContext ectx)
            throws ExecutionException, InterruptedException {
        try {
            return ectx.getCallable().call();
        } catch (SuspendedException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static Object execute(@Nonnull final ExecutionContext ectx)
            throws ExecutionException, InterruptedException {
        Object result = executeSyncOrAsync(ectx);
        if (result instanceof Future) {
            return ((Future<Object>) result).get();
        } else {
            return result;
        }
    }

    public Object execute(@Nonnull final InputStream in, @Nonnull final PrintStream out,
            @Nonnull final PrintStream err, final Object... args) throws ExecutionException, InterruptedException {
        if (execType == ExecutionType.SYNC) {
            return execute((VMExecutor) null, in, out, err, args);
        } else {
            return execute(VMExecutor.Lazy.DEFAULT, in, out, err, args);
        }
    }

    /**
     * get a value of a variable from memory, this function is syntax safe
     *
     * @param mem Map
     * @param name String
     * @throws Exception
     * @return Object
     */
    public static Object getValue(@Nonnull final java.util.Map mem, @Nonnull final String name)
            throws CompileException, InterruptedException, ExecutionException {
        return Program.compile(name + ";").execute(mem);
    }

    /**
     * Load a value into memory Have to go through the VM so that the assignement is acurate
     *
     * @param mem
     * @param name String
     * @param value Object
     * @throws net.sf.zel.vm.ParseException
     * @throws net.sf.zel.vm.ZExecutionException
     * @throws java.lang.InterruptedException
     */
    public static void addValue(@Nonnull final java.util.Map mem, @Nonnull final String name, final Object value)
            throws CompileException, InterruptedException, ExecutionException {
        Program.compile(name + "=" + value + ";").execute(mem);
    }

    /**
     * build indentation string
     *
     * @param indent
     * @return
     */
    @CheckReturnValue
    public static String strIndent(@Nonnegative final int indent) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < indent; i++) {
            result.append(' ');
        }
        return result.toString();
    }

    /**
     * Output Core, in hierarchical tab indented mode
     *
     * @param name
     * @param mem
     * @param indent
     * @param maxIndent
     * @return
     */
    @SuppressWarnings("unchecked")
    @CheckReturnValue
    public static String dumpCore(final String name, final Object mem, final int indent, final int maxIndent) {
        if (mem == null) {
            return "";
        }
        if (maxIndent > 0 && indent > maxIndent) {
            return "";
        }
        StringBuilder result = new StringBuilder();
        if (mem instanceof java.util.Map) {
            result.append(strIndent(indent)).append(name).append('\n');
            for (Map.Entry<Object, Object> elem : ((Map<Object, Object>) mem).entrySet()) {
                result.append(dumpCore(elem.getKey().toString(), elem.getValue(), indent + 1, maxIndent));
            }
        } else {
            result.append(strIndent(indent)).append(name).append('=').append(mem).append('\n');
        }
        return result.toString();
    }

    /**
     * *
     * This allows to run ZEL in an interactive mode
     *
     * @param args
     */
    public static void main(final String[] args) throws IOException, ZExecutionException, InterruptedException {
        System.out.println("ZEL Shell");
        boolean terminated = false;
        Map<String, Integer> localSymTable = Collections.EMPTY_MAP;
        Pair<Object[], Map<String, Integer>> gmemPair = ZEL_GLOBAL_FUNC.build();
        Map<String, Integer> globalSymTable = gmemPair.getSecond();
        Object[] mem = new Object[] {};
        Object[] gmem = gmemPair.getFirst();
        ResultCache resCache = new SimpleResultCache();
        InputStreamReader inp = new InputStreamReader(System.in, Charsets.UTF_8);
        BufferedReader br = new BufferedReader(inp);
        while (!terminated) {
            System.out.print("zel>");
            String line = br.readLine();
            if (line != null) {
                if ("QUIT".equalsIgnoreCase(line)) {
                    terminated = true;
                } else {
                    try {
                        final Program prog = Program.compile(line, localSymTable, gmem, globalSymTable);
                        localSymTable = prog.getLocalSymbolTable();
                        globalSymTable = prog.getGlobalSymbolTable();
                        gmem = prog.getGlobalMem();
                        Pair<Object, ExecutionContext> res = prog.executeX(VMExecutor.Lazy.DEFAULT, System.in,
                                System.out, System.err, resCache, mem);
                        System.out.println("result>" + res.getFirst());
                        final ExecutionContext execCtx = res.getSecond();
                        mem = execCtx.mem;
                        resCache = execCtx.resultCache;
                    } catch (CompileException ex) {
                        System.out.println("Syntax Error: " + Throwables.getStackTraceAsString(ex));
                    } catch (ExecutionException ex) {
                        System.out.println("Execution Error: " + Throwables.getStackTraceAsString(ex));
                    }
                }
            }
        }
    }

    public String toAssemblyString() {
        StringBuilder result = new StringBuilder();
        result.append("Program: \n");
        for (int i = 0; i < instructions.length; i++) {
            Object obj = instructions[i];
            result.append(Strings.padEnd(Integer.toString(i), 8, ' '));
            result.append(':');
            result.append(obj);
            result.append('\n');
        }
        result.append("execType = ").append(this.execType).append("\n");
        result.append("type = ").append(this.type).append("\n");
        return result.toString();
    }

    @Override
    public String toString() {
        return source;
    }

    /**
     * @return the type
     */
    public Program.Type getType() {
        return type;
    }

    public boolean contains(final Class<? extends Instruction> instr) {
        Boolean res = itterate(new HasClass(instr));
        if (res == null) {
            return false;
        }
        return res;
    }

    @Nullable
    public <T> T itterate(final Function<Object, T> func) {
        for (Instruction code : instructions) {
            T res = func.apply(code);
            if (res != null) {
                return res;
            }
            for (Object param : code.getParameters()) {
                res = func.apply(param);
                if (res != null) {
                    return res;
                }
                if (param instanceof Program) {
                    res = ((Program) param).itterate(func);
                }
                if (res != null) {
                    return res;
                }
            }
        }
        return null;
    }

    Instruction[] getInstructions() {
        return instructions;
    }

    public static final class HasClass implements Function<Object, Boolean> {

        private final Class<? extends Instruction> instr;

        public HasClass(final Class<? extends Instruction> instr) {
            this.instr = instr;
        }

        @Override
        @SuppressFBWarnings("TBP_TRISTATE_BOOLEAN_PATTERN")
        public Boolean apply(@Nonnull final Object input) {
            if (input.getClass() == instr) {
                return Boolean.TRUE;
            }
            return null;
        }
    }

}