All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.spf4j.zel.vm.Program Maven / Gradle / Ivy

/*
 * Copyright (c) 2001-2017, 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.
 *
 * Additionally licensed with:
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.spf4j.zel.vm;

import com.google.common.base.Strings;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
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 java.util.function.Function;
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.CharSequences;
import org.spf4j.base.Pair;
import org.spf4j.base.Throwables;
import org.spf4j.base.TimeSource;
import org.spf4j.zel.instr.Instruction;
import org.spf4j.zel.instr.var.ARRAY;
import org.spf4j.zel.instr.var.DECODE;
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.NVL;
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.instr.SymbolRef;
import org.spf4j.zel.instr.var.LIST;
import org.spf4j.zel.instr.var.MAP;

/**
 * 

* A ZEL program (function)

* * This is a Turing machine a Program will always be pretty much an array of operations (instructions). * * @author zoly * @version 1.0 * */ @Immutable public final class Program implements Serializable { private static final long serialVersionUID = 1L; private static final MemoryBuilder ZEL_GLOBAL_FUNC; private static volatile boolean terminated = false; 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("list", LIST.INSTANCE); ZEL_GLOBAL_FUNC.addSymbol("map", MAP.INSTANCE); ZEL_GLOBAL_FUNC.addSymbol("random", RANDOM.INSTANCE); ZEL_GLOBAL_FUNC.addSymbol("channel", Channel.Factory.INSTANCE); ZEL_GLOBAL_FUNC.addSymbol("EOF", Channel.EOF); ZEL_GLOBAL_FUNC.addSymbol("decode", DECODE.INSTANCE); ZEL_GLOBAL_FUNC.addSymbol("nvl", NVL.INSTANCE); } 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 localSymbolTable; private final Map globalSymbolTable; private final String name; private final String[] parameterNames; //CHECKSTYLE:OFF Program(final String name, final Map globalTable, final Object[] globalMem, final Map 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(name, globalTable, globalMem, buildLocalSymTable(objs, parameterNames, end - start, globalTable, localTable), java.util.Arrays.copyOfRange(objs, start, end), debug, source, progType, execType, hasDeterministicFunctions, parameterNames); } //CHECKSTYLE:OFF Program(final String name, final Map globalTable, final Object[] globalMem, final Map localTable, @Nonnull final Instruction[] instructions, final Location[] debug, final String source, final Type progType, final ExecutionType execType, final boolean hasDeterministicFunctions, final String... parameterNames) { //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; this.parameterNames = parameterNames; } public Program async() { return new Program(name, globalSymbolTable, globalMem, localSymbolTable, instructions, debug, source, type, ExecutionType.ASYNC, hasDeterministicFunctions, parameterNames); } public static MemoryBuilder getGlobalMemoryBuilder() { return ZEL_GLOBAL_FUNC.copy(); } Location[] getDebug() { return debug; } public String getSource() { return source; } public String getName() { return name; } public String[] getParameterNames() { return parameterNames.clone(); } String[] getParameterNamesInternal() { return parameterNames.clone(); } private static Map buildLocalSymTable(final Instruction[] instructions, final String[] parameterNames1, final int length, final Map globalTable, final Map addTo) throws CompileException { final int addToSize = addTo.size(); Map 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 SymbolRef) { String ref = ((SymbolRef) 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; } @SuppressFBWarnings("EI_EXPOSE_REP") public Map getGlobalSymbolTable() { return globalSymbolTable; } @SuppressFBWarnings("EI_EXPOSE_REP") public Map getLocalSymbolTable() { return localSymbolTable; } public int getLocalMemSize() { return localMemSize; } @SuppressFBWarnings("EI_EXPOSE_REP") public 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; } /** * @param i - inst address. * @return the instruction. */ @CheckReturnValue public Instruction get(final int i) { return instructions[i]; } @CheckReturnValue public Instruction[] getCode() { return instructions.clone(); } @CheckReturnValue Instruction[] getCodeInternal() { return instructions; } @CheckReturnValue Location[] getDebugInfoInternal() { return debug; } @CheckReturnValue public Location[] getDebugInfo() { return debug.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 { return compile("anonFunc", "String", new StringReader(zExpr), varNames); } @Nonnull public static Program compile(@Nonnull final String source, @Nonnull final String name, @Nonnull final Reader zExpr, @Nonnull final String... varNames) throws CompileException { ParsingContext cc = new CompileContext(ZEL_GLOBAL_FUNC.copy()); try { ZCompiler.compile(source, zExpr, cc); } catch (TokenMgrError | ParseException err) { throw new CompileException(err); } return RefOptimizer.INSTANCE.apply(cc.getProgramBuilder().toProgram(name, source, varNames)); } @Nonnull public static ZelPredicate compilePredicate(@Nonnull final CharSequence zExpr, @Nonnull final String varName) throws CompileException { ParsingContext cc = new CompileContext(ZEL_GLOBAL_FUNC.copy()); try { ZCompiler.compilePredicate("CharSquence", CharSequences.reader(zExpr), cc); } catch (TokenMgrError | ParseException err) { throw new CompileException(err); } Program result = RefOptimizer.INSTANCE.apply(cc.getProgramBuilder().toProgram("anonPredicate", "CharSquence", varName)); return result.toPredicate(zExpr.toString()); } public static Program compile(@Nonnull final String zExpr, final Map localTable, final Object[] globalMem, final Map globalTable, @Nonnull final String... varNames) throws CompileException { return compile("String", "anonFunc", new StringReader(zExpr), localTable, globalMem, globalTable, varNames); } public static Program compile(@Nonnull final String source, @Nonnull final String name, @Nonnull final Reader zExpr, final Map localTable, final Object[] globalMem, final Map globalTable, @Nonnull final String... varNames) throws CompileException { ParsingContext cc = new CompileContext(new MemoryBuilder( new ArrayList<>(Arrays.asList(globalMem)), globalTable)); try { ZCompiler.compile(source, zExpr, cc); } catch (TokenMgrError | ParseException err) { throw new CompileException(err); } return cc.getProgramBuilder().toProgram(name, source, varNames, localTable); } public Object execute() throws ExecutionException, InterruptedException { return execute(ProcessIOStreams.DEFAULT); } public Object execute(final Object... args) throws ExecutionException, InterruptedException { return execute(ProcessIOStreams.DEFAULT, args); } public ZelPredicate toPredicate(final String toString) { if (parameterNames.length != 1) { throw new UnsupportedOperationException("Not a predicate " + this); } String paramName = parameterNames[0]; return new ZelPredicate() { @Override public boolean test(final T arg) { try { return (Boolean) execute((Object) arg); } catch (ExecutionException | InterruptedException ex) { throw new RuntimeException(ex); } } @Override public String toString() { return toString; } @Override public String getZelExpression() { return toString; } @Override public String getParameterId() { return paramName; } }; } public Object execute(@Nonnull final ExecutorService execService, final Object... args) throws ExecutionException, InterruptedException { return execute(new VMExecutor(execService), ProcessIOStreams.DEFAULT, args); } public Object executeSingleThreaded(final Object... args) throws ExecutionException, InterruptedException { return execute(null, ProcessIOStreams.DEFAULT, args); } public Object execute(@Nullable final VMExecutor execService, @Nullable final ProcessIO io, final Object... args) throws ExecutionException, InterruptedException { Object[] localMem = allocMem(args); final ExecutionContext ectx = new ExecutionContext(this, globalMem, localMem, io, execService); return execute(ectx); } Object[] allocMem(final Object... args) { Object[] localMem; final int lms = this.getLocalMemSize(); if (args.length == lms) { localMem = args; } else { localMem = new Object[lms]; System.arraycopy(args, 0, localMem, 0, args.length); } return localMem; } public Pair execute(@Nullable final VMExecutor execService, @Nullable final ProcessIO io, final ResultCache resultCache, final Object... args) throws ExecutionException, InterruptedException { Object[] localMem = allocMem(args); final ExecutionContext ectx = new ExecutionContext(this, globalMem, localMem, resultCache, io, execService); return Pair.of(execute(ectx), ectx); } public static Object executeSync(@Nonnull final ExecutionContext ectx) throws ExecutionException, InterruptedException { try { return ectx.call(); } catch (SuspendedException ex) { throw new ExecutionException("Suspension not supported in sync calls " + ectx, ex); } } public static Object execute(@Nonnull final ExecutionContext ectx) throws ExecutionException, InterruptedException { Object result = ectx.executeSyncOrAsync(); if (result instanceof Future) { return ((Future) result).get(); } else { return result; } } public Object execute(final ProcessIO io, final Object... args) throws ExecutionException, InterruptedException { if (execType == ExecutionType.SYNC) { return execute((VMExecutor) null, io, args); } else { return execute(VMExecutor.Lazy.DEFAULT, io, args); } } /** * * This allows to run ZEL in an interactive mode * * @param args */ @SuppressWarnings("checkstyle:regexp") public static void main(final String[] args) throws IOException, InterruptedException { System.out.println("ZEL Shell"); Map localSymTable = Collections.emptyMap(); Pair> gmemPair = ZEL_GLOBAL_FUNC.build(); Map globalSymTable = gmemPair.getSecond(); Object[] mem = new Object[]{}; Object[] gmem = gmemPair.getFirst(); ResultCache resCache = new SimpleResultCache(); InputStreamReader inp = new InputStreamReader(System.in, StandardCharsets.UTF_8); BufferedReader br = new BufferedReader(inp); org.spf4j.base.Runtime.queueHookAtBeginning(new Runnable() { @Override public void run() { terminated = true; try { System.in.close(); } catch (IOException ex) { // ignore. } } }); System.out.println("zel>\n"); while (!terminated) { String line = br.readLine(); if (line == null) { break; } line = line.trim(); if ("quit".equals(line)) { terminated = true; } else { try { final Program prog = Program.compile(line, localSymTable, gmem, globalSymTable).async(); localSymTable = prog.getLocalSymbolTable(); globalSymTable = prog.getGlobalSymbolTable(); gmem = prog.getGlobalMem(); long startTime = TimeSource.nanoTime(); Pair res = prog.execute( VMExecutor.Lazy.DEFAULT, ProcessIOStreams.DEFAULT, resCache, mem); long elapsed = TimeSource.nanoTime() - startTime; final Object result = res.getFirst(); System.out.println("result> " + result); System.out.println("type> " + (result == null ? "none" : result.getClass())); System.out.println("executed in> " + elapsed + " ns"); final ExecutionContext execCtx = res.getSecond(); mem = execCtx.getMem(); resCache = execCtx.getResultCache(); } catch (CompileException ex) { System.out.println("Syntax Error:"); Throwables.writeTo(ex, System.out, Throwables.PackageDetail.SHORT); System.out.println(); } catch (ExecutionException ex) { System.out.println("Execution Error:"); Throwables.writeTo(ex, System.out, Throwables.PackageDetail.SHORT); System.out.println(); } System.out.println("zel>"); } } } public String toAssemblyString() { StringBuilder result = new StringBuilder(); result.append("Program: \n"); int toPad = Integer.toString(instructions.length).length(); for (int i = 0; i < instructions.length; i++) { Object obj = instructions[i]; result.append(Strings.padEnd(Integer.toString(i), toPad, ' ')); result.append(':'); result.append(obj); result.append(','); } result.append("execType = ").append(this.execType).append('\n'); result.append("type = ").append(this.type).append('\n'); return result.toString(); } @Override public String toString() { return name; } /** * @return the type */ public Program.Type getType() { return type; } public boolean contains(final Class instr) { Boolean res = itterate(new HasClass(instr)); if (res == null) { return false; } return res; } @Nullable public T itterate(final Function 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; } static final class HasClass implements Function { private final Class instr; HasClass(final Class instr) { this.instr = instr; } @Override @SuppressFBWarnings({"TBP_TRISTATE_BOOLEAN_PATTERN", "NP_BOOLEAN_RETURN_NULL"}) @Nullable public Boolean apply(@Nonnull final Object input) { if (input.getClass() == instr) { return Boolean.TRUE; } return null; } } }