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
© 2015 - 2025 Weber Informatics LLC | Privacy Policy