/****************************************************************************** * * Copyright 2014 Paphus Solutions Inc. * * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/legal/epl-v10.html * * 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.botlibre.self; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import org.botlibre.api.knowledge.Network; import org.botlibre.api.knowledge.Relationship; import org.botlibre.api.knowledge.Vertex; import org.botlibre.knowledge.BinaryData; import org.botlibre.knowledge.Primitive; import org.botlibre.util.Utils; /** * Decompiler for the Self scripting language object code. * Self is compiled into Vertex objects, this converts the object code back into Self code text. */ public class Self4Decompiler extends SelfDecompiler { public static Map<Primitive, String> BINARY_OPERATORS; static { BINARY_OPERATORS = new HashMap<Primitive, String>(); BINARY_OPERATORS.put(Primitive.EQUALS, "=="); BINARY_OPERATORS.put(Primitive.NOTEQUALS, "!="); BINARY_OPERATORS.put(Primitive.OR, "||"); BINARY_OPERATORS.put(Primitive.AND, "&&"); BINARY_OPERATORS.put(Primitive.GREATERTHAN, ">"); BINARY_OPERATORS.put(Primitive.LESSTHAN, "<"); BINARY_OPERATORS.put(Primitive.GREATERTHANEQUAL, ">="); BINARY_OPERATORS.put(Primitive.LESSTHANEQUAL, "<="); BINARY_OPERATORS.put(Primitive.MINUS, "-"); BINARY_OPERATORS.put(Primitive.PLUS, "+"); BINARY_OPERATORS.put(Primitive.MULTIPLY, "*"); BINARY_OPERATORS.put(Primitive.DIVIDE, "/"); } /** * Print the Self code for the state machine. */ @Override public void printStateMachine(Vertex state, Writer writer, Network network, long start, long timeout) { try { if (!state.hasRelationship(Primitive.LANGUAGE) || state.hasRelationship(Primitive.LANGUAGE, Primitive.SELF2)) { new SelfDecompiler().printStateMachine(state, writer, network, start, timeout); return; } Set<Vertex> elements = new HashSet<Vertex>(); elements.add(state); printState(state, writer, "", elements, network, start, timeout); writer.write("\r\n"); } catch (IOException error) { network.getBot().log(this, error); return; } } /** * Print the state and any referenced states or variables that have not yet been printed. */ @Override public void printState(Vertex state, Writer writer, String indent, Set<Vertex> elements, Network network, long start, long timeout) throws IOException { if (state.getData() instanceof BinaryData) { Vertex detached = parseStateByteCode(state, (BinaryData)state.getData(), network); elements.add(detached); printState(detached, writer, indent, elements, network, start, timeout); return; } printComments(state, writer, indent, false, network); writer.write(indent); writer.write("state "); printElement(state, writer, indent, null, null, elements, network); writer.write(" {\r\n"); if ((System.currentTimeMillis() - start) > timeout) { writer.write(indent); writer.write("\t"); writer.write("** decompile timeout reached **\r\n"); writer.write(indent); writer.write("}\r\n"); return; } String childIndent = indent + "\t"; Collection<Relationship> expressions = state.orderedRelationships(Primitive.DO); List<Vertex> newFunctions = new ArrayList<Vertex>(); List<Vertex> newVariables = new ArrayList<Vertex>(); List<Vertex> newStates = new ArrayList<Vertex>(); if (expressions != null) { for (Relationship expression : expressions) { printComments(expression.getTarget(), writer, childIndent, false, network); if (expression.getTarget().instanceOf(Primitive.CASE)) { printCase(expression.getTarget(), writer, childIndent, elements, newVariables, newFunctions, newStates, network); } else if (expression.getTarget().instanceOf(Primitive.DO)) { printDo(expression.getTarget().getRelationship(Primitive.DO), writer, indent, elements, newVariables, newFunctions, newStates, network); } else if (expression.getTarget().instanceOf(Primitive.GOTO)) { printGoto(expression.getTarget(), writer, childIndent, elements, newVariables, newFunctions, newStates, network); } else if (expression.getTarget().instanceOf(Primitive.PUSH)) { printPush(expression.getTarget(), writer, childIndent, elements, newVariables, newFunctions, newStates, network); } else if (expression.getTarget().instanceOf(Primitive.RETURN)) { printReturn(expression.getTarget(), writer, childIndent, elements, newVariables, newFunctions, newStates, network); } } } for (Vertex variable : newVariables) { printVariable(variable, writer, childIndent, elements, network); } for (Vertex function : newFunctions) { printFunction(function, writer, childIndent, elements, network); } newFunctions = new ArrayList<Vertex>(); newVariables = new ArrayList<Vertex>(); Collection<Relationship> quotients = state.orderedRelationships(Primitive.QUOTIENT); if (quotients != null) { for (Relationship quotient : quotients) { writer.write(childIndent); writer.write("answer"); if (quotient.getCorrectness() < 1.0f) { writer.write(":"); writer.write(String.format("%.02f", quotient.getCorrectness())); } writer.write(" "); printElement(quotient.getTarget(), writer, indent, newFunctions, newVariables, elements, network); if (quotient.hasMeta()) { writer.write(" {\r\n"); Collection<Relationship> previousRelationships = quotient.getMeta().orderedRelationships(Primitive.PREVIOUS); if (previousRelationships != null) { for (Relationship previous : previousRelationships) { writer.write(childIndent); if (previous.getCorrectness() > 0) { writer.write("\tprevious "); } else { writer.write("\tprevious ! "); } printElement(previous.getTarget(), writer, indent + 1, newFunctions, newVariables, elements, network); writer.write(";\r\n"); } } writer.write(childIndent); writer.write("}"); } writer.write(";\r\n"); } writer.write("\r\n"); } Collection<Relationship> possibleQuotients = state.orderedRelationships(Primitive.POSSIBLE_QUOTIENT); if (possibleQuotients != null) { for (Relationship quotient : possibleQuotients) { writer.write(childIndent); writer.write("//Possible answer:"); printElement(quotient.getTarget(), writer, indent, newFunctions, newVariables, elements, network); writer.write(";\r\n"); } writer.write("\r\n"); } for (Vertex variable : newVariables) { printVariable(variable, writer, childIndent, elements, network); } for (Vertex function : newFunctions) { printFunction(function, writer, childIndent, elements, network); } for (Vertex element : newStates) { if (element.instanceOf(Primitive.STATE)) { printState(element, writer, childIndent, elements, network, start, timeout); } } writer.write(indent); writer.write("}\r\n"); writer.write("\r\n"); } /** * Print the vertex, either a state, variable, expression, or data. */ @Override public void printData(Vertex vertex, Writer writer) throws IOException { Object data = vertex.getData(); if (data instanceof Primitive) { if (!data.equals(Primitive.NULL) && !data.equals(Primitive.TRUE) && !data.equals(Primitive.FALSE)) { writer.write("#"); } writer.write(((Primitive)vertex.getData()).getIdentity()); } else if (data instanceof String) { writer.write("\""); String text = (String)vertex.getData(); if (text.indexOf('"') != -1) { text = text.replace("\"", "\\\""); } writer.write(text); writer.write("\""); } else if (data instanceof Number) { // TODO number type. writer.write(vertex.getData().toString()); } else { writer.write(vertex.getDataType()); writer.write("(\""); writer.write(vertex.getDataValue()); writer.write("\")"); } } /** * Print the vertex, either a state, variable, expression, or data. */ @Override public void printElement(Vertex vertex, Writer writer, String indent, List<Vertex> functions, List<Vertex> variables, Set<Vertex> elements, Network network) throws IOException { if (vertex == null) { writer.write("null"); return; } if (vertex.instanceOf(Primitive.STATE)) { // Just print name. } else if (vertex.instanceOf(Primitive.VARIABLE)) { // Just print name. } else if (vertex.instanceOf(Primitive.FORMULA)) { if (vertex.hasData()) { writer.write(String.valueOf(vertex.getData())); } else { printTemplate(vertex, writer, indent, functions, variables, elements, network); } return; } else if (vertex.instanceOf(Primitive.PATTERN)) { writer.write(String.valueOf(vertex.getData())); return; } else if (vertex.instanceOf(Primitive.FUNCTION)) { if (!elements.contains(vertex)) { functions.add(vertex); elements.add(vertex); } } else if (vertex.instanceOf(Primitive.EXPRESSION)) { if (vertex.getData() instanceof BinaryData) { Vertex detached = parseExpressionByteCode(vertex, (BinaryData)vertex.getData(), network); elements.add(detached); vertex = detached; } printOperator(vertex, writer, indent, functions, variables, elements, network); return; } else if (vertex.instanceOf(Primitive.PARAGRAPH)) { writer.write("\""); String text = vertex.printString(); if (text.indexOf('"') != -1) { text = text.replace("\"", "\\\""); } writer.write(text); writer.write("\""); return; } else if (vertex.hasData()) { Object data = vertex.getData(); if (data instanceof Primitive) { if (!data.equals(Primitive.NULL) && !data.equals(Primitive.TRUE) && !data.equals(Primitive.FALSE)) { writer.write("#"); } writer.write(((Primitive)vertex.getData()).getIdentity()); } else if (data instanceof String) { writer.write("\""); String text = (String)vertex.getData(); if (text.indexOf('"') != -1) { text = text.replace("\"", "\\\""); } writer.write(text); writer.write("\""); } else if (data instanceof Number) { // TODO number type. writer.write(vertex.getData().toString()); } else { writer.write(vertex.getDataType()); writer.write("(\""); writer.write(vertex.getDataValue()); writer.write("\")"); } return; } else if (vertex.instanceOf(Primitive.ARRAY)) { writer.write("["); List<Vertex> values = vertex.orderedRelations(Primitive.ELEMENT); if (values != null) { boolean first = true; for (Vertex value : values) { if (!first) { writer.write(", "); } else { first = false; } printElement(value, writer, indent, functions, variables, elements, network); } } writer.write("]"); return; } else { writer.write("Object("); writer.write(vertex.getId().toString()); writer.write(")"); return; } if (vertex.hasName()) { String name = Utils.compress(vertex.getName(), 100); writer.write(name); } else { writer.write("v"); writer.write(vertex.getId().toString()); } if (vertex.instanceOf(Primitive.FUNCTION)) { List<Relationship> arguments = vertex.orderedRelationships(Primitive.ARGUMENT); if (arguments == null) { writer.write("()"); } else { printArguments(vertex, Primitive.ARGUMENT, 0, null, false, false, false, true, writer, indent + "\t", variables, functions, elements, false, network); } } } @Override public void printTemplate(Vertex formula, Writer writer, String indent, List<Vertex> expressions, List<Vertex> variables, Set<Vertex> elements, Network network) throws IOException { writer.write("Template("); writer.write("\""); boolean first = true; List<Vertex> words = formula.orderedRelations(Primitive.WORD); if (words == null) { writer.write("\""); return; } boolean inferWhitespace = !formula.hasRelationship(Primitive.TYPE, Primitive.SPACE) && !formula.hasRelationship(Primitive.WORD, Primitive.SPACE); for (Vertex word : words) { if (inferWhitespace) { if (!first) { if (!word.instanceOf(Primitive.PUNCTUATION)) { writer.write(" "); } } else { first = false; } } else if (word.is(Primitive.SPACE)) { writer.write(" "); continue; } if (word.instanceOf(Primitive.WORD)) { String text = String.valueOf(word.getData()); if (text.equals("\"")) { writer.write("\\"); } writer.write(text); } else { writer.write("{"); printElement(word, writer, indent, expressions, variables, elements, network); writer.write("}"); } } writer.write("\")"); } /** * Print the variable and any variables it references that have not been printed. */ @Override public void printVariable(Vertex variable, Writer writer, String indent, Set<Vertex> elements, Network network) throws IOException { String name = variable.getName(); if (name != null && (name.equals("input") || name.equals("sentence") || name.equals("conversation") || name.equals("speaker") || name.equals("target"))) { // Don't print globals. return; } boolean hasComments = printComments(variable, writer, indent, false, network); Iterator<Relationship> iterator = variable.allRelationships(); if (!hasComments && variable.totalRelationships() <= 1) { return; // Nothing to print. } List<Vertex> localElements = new ArrayList<Vertex>(); writer.write(indent); writer.write("var "); printElement(variable, writer, indent, null, null, elements, network); writer.write(" {\r\n"); writer.write(indent); while (iterator.hasNext()) { Relationship relationship = iterator.next(); Vertex type = relationship.getType(); Vertex target = relationship.getTarget(); if ((type.is(Primitive.INSTANTIATION) && (target.is(Primitive.VARIABLE)))) { continue; } if (type.is(Primitive.COMMENT)) { continue; } if (target.instanceOf(Primitive.VARIABLE) && !elements.contains(target)) { localElements.add(target); elements.add(target); } if (type.is(Primitive.EQUALS)) { writer.write("\t"); if (relationship.isInverse()) { writer.write(" : ! "); } else { writer.write(" : "); } printElement(target, writer, indent, null, null, elements, network); } else { writer.write("\t"); if (type.isPrimitive()) { writer.write(((Primitive)type.getData()).getIdentity()); } else { printElement(type, writer, indent, null, null, elements, network); } if (relationship.isInverse()) { writer.write(" : ! "); } else { writer.write(" : "); } printElement(target, writer, indent, null, null, elements, network); } writer.write(";\r\n"); writer.write(indent); } writer.write("}\r\n"); writer.write("\r\n"); for (Vertex element : localElements) { printVariable(element, writer, indent, elements, network); } } /** * Print the operation arguments. */ public void printArguments(Vertex expression, Primitive type, int start, String[] tokens, boolean reverse, boolean newLine, boolean unravel, boolean brackets, Writer writer, String indent, List<Vertex> variables, List<Vertex> functions, Set<Vertex> elements, boolean space, Network network) throws IOException { List<Relationship> arguments = expression.orderedRelationships(type); if (brackets) { writer.write("("); } if (arguments != null) { boolean needsBrackets = !unravel && !brackets; if ((arguments.size() == 1) || (tokens != null)) { needsBrackets = false; } if (arguments.size() <= (start + 1)) { newLine = false; } else if (arguments.size() > (3 + start)) { newLine = true; } if (!unravel && space) { writer.write(" "); } if (needsBrackets) { writer.write("("); } int size = arguments.size(); boolean isDo = false; for (int index = start; index < size; index++) { if (newLine && (!unravel || (index > 0))) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("\t"); } Vertex argument = null; isDo = false; if (reverse) { argument = arguments.get(size - index - 1).getTarget(); } else { argument = arguments.get(index).getTarget(); } if (argument.instanceOf(Primitive.VARIABLE) && !elements.contains(argument)) { variables.add(argument); elements.add(argument); } boolean isExpression = argument.instanceOf(Primitive.EXPRESSION); if (!unravel && !needsBrackets && isExpression && !brackets) { writer.write("("); } printElement(argument, writer, indent, functions, variables, elements, network); if (!unravel && !needsBrackets && isExpression && !brackets) { writer.write(")"); } Vertex operator = argument.getRelationship(Primitive.OPERATOR); if (operator != null && !argument.instanceOf(Primitive.FUNCTION) && (operator.is(Primitive.DO) || operator.is(Primitive.IF) || operator.is(Primitive.WHILE) || operator.is(Primitive.FOR))) { isDo = true; } if (index < (size - 1)) { if (tokens != null) { writer.write(" "); writer.write(tokens[index]); writer.write(" "); } else if (!isDo) { if (unravel) { writer.write(";"); } else { writer.write(","); } if (!newLine) { writer.write(" "); } } } } if (unravel) { if (!isDo) { writer.write(";"); } writer.write("\r\n"); } if (!unravel && newLine) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); } if (needsBrackets) { writer.write(")"); } } if (brackets) { writer.write(")"); } } /** * Parse the operation arguments. */ @Override public void parseArgumentsByteCode(Vertex expression, DataInputStream dataStream, Vertex type, Network network) throws IOException { parseArgumentsByteCode(expression, dataStream, type, null, network); } /** * Parse the operation arguments. */ public void parseArgumentsByteCode(Vertex expression, DataInputStream dataStream, Vertex type, Vertex pop, Network network) throws IOException { long id = dataStream.readLong(); Object[] result = new Object[3]; result[0] = id; while (id > 0) { parseArgumentByteCode(result, dataStream, pop, network); Vertex argument = (Vertex)result[2]; id = (Long)result[0]; if (argument != null) { expression.addRelationship(type, argument, Integer.MAX_VALUE); } } } /** * Parse the operation argument. */ public void parseArgumentByteCode(Object[] result, DataInputStream dataStream, Vertex pop, Network network) throws IOException { Vertex last = null; Vertex next = (Vertex)result[1]; Long id = (Long)result[0]; if (id == 0l) { result[0] = id; result[1] = null; result[2] = null; return; } Vertex element = next; if (element == null) { element = network.findById(id); } if (element == null) { result[0] = dataStream.readLong(); result[1] = null; result[2] = null; return; } if (element.is(Primitive.EXPRESSION)) { element = parseOperatorByteCode(dataStream, network); } if (element.is(Primitive.POP)) { element = pop; } id = dataStream.readLong(); if (id == 0l) { result[0] = id; result[1] = null; result[2] = element; return; } last = element; next = network.findById(id); while ((next != null) && (next.is(Primitive.PUSH))) { element = parseOperatorByteCode(dataStream, last, network); id = dataStream.readLong(); if (id == 0l) { next = null; break; } last = element; next = network.findById(id); } result[0] = id; result[1] = next; result[2] = element; } /** * Check if the function is bytecode and decompile. */ @Override public Vertex decompileFunction(Vertex function, Network network) { if (function.getData() instanceof BinaryData) { try { return parseFunctionByteCode(function, (BinaryData)function.getData(), network); } catch (IOException exception) { throw new SelfExecutionException(function, exception); } } return function; } /** * Check if the expression is bytecode and decompile. */ @Override public Vertex decompileExpression(Vertex expression, Network network) { if (expression.getData() instanceof BinaryData) { try { return parseExpressionByteCode(expression, (BinaryData)expression.getData(), network); } catch (IOException exception) { throw new SelfExecutionException(expression, exception); } } return expression; } /** * Check if the state is bytecode and decompile. */ @Override public Vertex decompileState(Vertex state, Network network) { if (state.getData() instanceof BinaryData) { try { return parseStateByteCode(state, (BinaryData)state.getData(), network); } catch (Exception exception) { throw new SelfExecutionException(state, exception); } } return state; } /** * Print the function and any functions it references that have not been printed. */ public void printFunction(Vertex function, Writer writer, String indent, Set<Vertex> elements, Network network) throws IOException { if (function.getData() instanceof BinaryData) { Vertex detached = parseFunctionByteCode(function, (BinaryData)function.getData(), network); elements.add(detached); printFunction(detached, writer, indent, elements, network); return; } printComments(function, writer, indent, false, network); List<Vertex> functions = new ArrayList<Vertex>(); List<Vertex> variables = new ArrayList<Vertex>(); writer.write(indent); writer.write("function "); printElement(function, writer, indent, functions, variables, elements, network); writer.write(" {\r\n"); writer.write(indent); writer.write("\t"); printArguments(function, Primitive.DO, 0, null, false, true, true, false, writer, indent.substring(0, indent.length() - 1), variables, functions, elements, true, network); writer.write(indent); writer.write("}\r\n"); writer.write("\r\n"); for (Vertex element : variables) { printVariable(element, writer, indent, elements, network); } for (Vertex element : functions) { printFunction(element, writer, indent, elements, network); } } /** * Parse the function from bytecode. */ public Vertex parseFunctionByteCode(Vertex function, BinaryData data, Network network) throws IOException { if (data.getCache() != null) { return (Vertex)data.getCache(); } BinaryData bytes = data; if (!function.isTemporary()) { bytes = (BinaryData)network.findData(data); if (bytes == null) { bytes = data; } } ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes.getBytes()); DataInputStream dataStream = new DataInputStream(byteStream); Vertex cache = network.createTemporyVertex(); cache.setName(function.getName()); cache.addRelationship(Primitive.INSTANTIATION, Primitive.FUNCTION); cache.addRelationship(Primitive.OPERATOR, new Primitive(function.getName())); parseArgumentsByteCode(cache, dataStream, network.createVertex(Primitive.DO), network); data.setCache(cache); bytes.setCache(cache); return cache; } /** * Parse the expression from bytecode. */ @Override public Vertex parseExpressionByteCode(Vertex expression, BinaryData data, Network network) throws IOException { if (data.getCache() != null) { return (Vertex)data.getCache(); } BinaryData bytes = data; if (!expression.isTemporary()) { bytes = (BinaryData)network.findData(data); if (bytes == null) { bytes = data; } } if (bytes.getBytes() == null) { return network.createVertex(Primitive.NULL); } ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes.getBytes()); DataInputStream dataStream = new DataInputStream(byteStream); Object[] result = new Object[3]; result[0] = dataStream.readLong(); parseArgumentByteCode(result, dataStream, null, network); Vertex cache = (Vertex)result[2]; if (cache == null) { return expression; } cache.setName(expression.getName()); data.setCache(cache); bytes.setCache(cache); return cache; } /** * Parse the Self2 equation from bytecode. */ @Override public Vertex parseEquationByteCode(Vertex equation, BinaryData data, Network network) throws IOException { return new SelfDecompiler().parseEquationByteCode(equation, data, network); } /** * Parse the state from bytecode. */ @Override public Vertex parseStateByteCode(Vertex state, BinaryData data, Network network) throws IOException { if (data.getCache() != null) { return (Vertex)data.getCache(); } BinaryData bytes = data; if (!state.isTemporary()) { bytes = (BinaryData)network.findData(data); if (bytes == null) { bytes = data; } } ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes.getBytes()); DataInputStream dataStream = new DataInputStream(byteStream); long id = dataStream.readLong(); Vertex vertex = network.findById(id); if (vertex != null && !vertex.is(Primitive.SELF4)) { // Parse old state machine. return new SelfDecompiler().parseStateByteCode(state, data, network); } Vertex cache = parseStateByteCode(dataStream, network); // Add any dynamically added cases. Collection<Relationship> cases = state.getRelationships(Primitive.DO); if (cases != null) { for (Relationship expression : cases) { cache.addRelationship(expression, true); } } cache.setName(state.getName()); data.setCache(cache); bytes.setCache(cache); return cache; } /** * Parse the state and its cases from bytecode. */ @Override public Vertex parseStateByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex state = network.createTemporyVertex(); state.addRelationship(Primitive.INSTANTIATION, Primitive.STATE); try { long id = dataStream.readLong(); while (id > 0) { Vertex next = network.findById(id); Vertex vertex = null; if (next == null) { id = dataStream.readLong(); continue; } if (next.is(Primitive.CASE)) { vertex = parseCaseByteCode(dataStream, network); state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE); } else if (next.is(Primitive.QUOTIENT)) { parseQuotientByteCode(state, dataStream, network); } else if (next.is(Primitive.DO)) { vertex = parseDoByteCode(dataStream, network); state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE); } else if (next.is(Primitive.GOTO)) { vertex = parseGotoByteCode(dataStream, network); state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE); } else if (next.is(Primitive.PUSH)) { vertex = parsePushByteCode(dataStream, network); state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE); } else if (next.is(Primitive.RETURN)) { vertex = parseReturnByteCode(dataStream, network); state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE); } id = dataStream.readLong(); } } catch (Exception exception) { network.getBot().log(this, "Error parsing state bytecode", Level.WARNING, state); network.getBot().log(this, exception); } return state; } /** * Parse the CASE bytecode. */ @Override public Vertex parseCaseByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex vertex = network.createTemporyVertex(); vertex.addRelationship(Primitive.INSTANTIATION, Primitive.CASE); long id = dataStream.readLong(); if (id == 0) { return vertex; } Object[] result = new Object[3]; result[0] = id; parseArgumentByteCode(result, dataStream, null, network); id = (Long)result[0]; Vertex variable = (Vertex)result[2]; if (variable == null) { return vertex; } if (variable.is(Primitive.PATTERN)) { if (id == 0) { return vertex; } variable = network.findById(id); if (variable == null) { return vertex; } vertex.addRelationship(Primitive.PATTERN, variable); id = dataStream.readLong(); } else { vertex.addRelationship(Primitive.CASE, variable); } while (id > 0) { Vertex type = network.findById(id); if (type == null) { return vertex; } id = dataStream.readLong(); if (type.is(Primitive.GOTO) || type.is(Primitive.FOR)) { while (id > 0) { Vertex element = network.findById(id); if (element == null) { id = dataStream.readLong(); continue; } vertex.addRelationship(type, element); id = dataStream.readLong(); } id = dataStream.readLong(); continue; } else { result[0] = id; result[1] = null; result[2] = null; parseArgumentByteCode(result, dataStream, null, network); id = (Long)result[0]; Vertex argument = (Vertex)result[2]; if (argument != null) { vertex.addRelationship(type, argument, Integer.MAX_VALUE); } } } return vertex; } /** * Parse the PUSH bytecode. */ @Override public Vertex parsePushByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex expression = network.createTemporyVertex(); expression.addRelationship(Primitive.INSTANTIATION, Primitive.PUSH); long id = dataStream.readLong(); if (id == 0) { return expression; } Vertex element = network.findById(id); if (element != null) { expression.addRelationship(Primitive.ARGUMENT, element, Integer.MAX_VALUE); } return expression; } /** * Parse the DO bytecode. */ @Override public Vertex parseDoByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex expression = network.createTemporyVertex(); expression.addRelationship(Primitive.INSTANTIATION, Primitive.DO); Vertex operation = parseOperatorByteCode(dataStream, network); expression.addRelationship(Primitive.DO, operation, Integer.MAX_VALUE); return expression; } /** * Parse the RETURN bytecode. */ @Override public Vertex parseReturnByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex expression = network.createTemporyVertex(); expression.addRelationship(Primitive.INSTANTIATION, Primitive.RETURN); long id = dataStream.readLong(); if (id == 0) { return expression; } Vertex element = network.findById(id); if (element != null) { if (element.is(Primitive.ARGUMENT)) { expression.addRelationship(Primitive.RETURN, element); id = dataStream.readLong(); while (id > 0) { element = network.findById(id); if (element != null) { expression.addRelationship(Primitive.ARGUMENT, element, Integer.MAX_VALUE); } id = dataStream.readLong(); } } else { expression.addRelationship(Primitive.RETURN, element); } } return expression; } /** * Parse the GOTO bytecode. */ @Override public Vertex parseGotoByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex expression = network.createTemporyVertex(); expression.addRelationship(Primitive.INSTANTIATION, Primitive.GOTO); long id = dataStream.readLong(); if (id == 0) { return expression; } Vertex element = network.findById(id); if (element == null) { return expression; } if (element.is(Primitive.FINALLY)) { expression.addRelationship(Primitive.FINALLY, Primitive.FINALLY); id = dataStream.readLong(); if (id == 0) { return expression; } element = network.findById(id); if (element == null) { return expression; } } expression.addRelationship(Primitive.GOTO, element); id = dataStream.readLong(); if (id == 0) { return expression; } element = network.findById(id); if (element == null) { return expression; } if (element.is(Primitive.ARGUMENT)) { id = dataStream.readLong(); while (id > 0) { element = network.findById(id); if (element != null) { expression.addRelationship(Primitive.ARGUMENT, element, Integer.MAX_VALUE); } id = dataStream.readLong(); } id = dataStream.readLong(); } return expression; } /** * Parse the GOTO bytecode. */ @Override public void parseQuotientByteCode(Vertex state, DataInputStream dataStream, Network network) throws IOException { float correctness = dataStream.readFloat(); long id = dataStream.readLong(); if (id == 0) { return; } Object[] result = new Object[3]; result[0] = id; parseArgumentByteCode(result, dataStream, null, network); id = (Long)result[0]; Vertex element = (Vertex)result[2]; if (element == null) { return; } Relationship relationship = state.addWeakRelationship(Primitive.QUOTIENT, element, correctness); if (id == 0) { return; } result[0] = id; result[1] = null; result[2] = null; parseArgumentByteCode(result, dataStream, null, network); id = (Long)result[0]; element = (Vertex)result[2]; if (element == null) { return; } if (element.is(Primitive.PREVIOUS)) { Vertex meta = network.createTemporyVertex(); relationship.setMeta(meta); while (id > 0) { result[0] = id; result[1] = null; result[2] = null; parseArgumentByteCode(result, dataStream, null, network); id = (Long)result[0]; element = (Vertex)result[2]; if (element != null) { if (element.is(Primitive.NOT)) { if (id == 0) { return; } result[0] = id; result[1] = null; result[2] = null; parseArgumentByteCode(result, dataStream, null, network); id = (Long)result[0]; element = (Vertex)result[2]; if (element == null) { continue; } meta.removeRelationship(Primitive.PREVIOUS, element); } else { meta.addRelationship(Primitive.PREVIOUS, element); } } } } } /** * Print the expression and any expressions it references that have not been printed. */ @Override public void printOperator(Vertex expression, Writer writer, String indent, List<Vertex> functions, List<Vertex> variables, Set<Vertex> elements, Network network) throws IOException { printComments(expression, writer, indent + "\t\t", true, network); Vertex operator = expression.getRelationship(Primitive.OPERATOR); if (operator == null) { return; } List<Relationship> arguments = expression.orderedRelationships(Primitive.ARGUMENT); if (operator.is(Primitive.CALL)) { Vertex source = expression.getRelationship(Primitive.THIS); if (source != null) { printElement(source, writer, indent, functions, variables, elements, network); } Vertex function = expression.getRelationship(Primitive.FUNCTION); if (function != null) { if (source == null) { if (function.isPrimitive()) { writer.write(((Primitive)function.getData()).getIdentity()); } else { printElement(function, writer, indent, functions, variables, elements, network); } } else { if (function.isPrimitive() && !(source.getData() instanceof Number)) { writer.write("."); writer.write(((Primitive)function.getData()).getIdentity()); } else { writer.write("["); printElement(function, writer, indent, functions, variables, elements, network); writer.write("]"); } } } else { writer.write("** missing function **"); } printArguments(expression, Primitive.ARGUMENT, 0, null, false, false, false, true, writer, indent + "\t", variables, functions, elements, false, network); return; } else if (operator.is(Primitive.ASSIGN)) { if (arguments != null && !arguments.isEmpty()) { printElement(arguments.get(0).getTarget(), writer, indent, functions, variables, elements, network); } if (expression.hasRelationship(Primitive.NOT, Primitive.NOT)) { writer.write(" = ! "); } else { writer.write(" = "); } if (arguments != null && arguments.size() > 1) { printElement(arguments.get(1).getTarget(), writer, indent, functions, variables, elements, network); } return; } else if (operator.is(Primitive.INCREMENT)) { if (arguments != null && !arguments.isEmpty()) { printElement(arguments.get(0).getTarget(), writer, indent, functions, variables, elements, network); } writer.write(" ++"); return; } else if (operator.is(Primitive.DECREMENT)) { if (arguments != null && !arguments.isEmpty()) { printElement(arguments.get(0).getTarget(), writer, indent, functions, variables, elements, network); } writer.write(" --"); return; } else if (operator.is(Primitive.GET)) { Vertex source = null; if (arguments != null && !arguments.isEmpty()) { source = arguments.get(0).getTarget(); printElement(source, writer, indent, functions, variables, elements, network); } if (arguments != null && arguments.size() > 1) { Vertex variable = arguments.get(1).getTarget(); if (variable.isPrimitive()) { writer.write("."); writer.write(((Primitive)variable.getData()).getIdentity()); Vertex index = expression.getRelationship(Primitive.INDEX); if ((index != null) && (index.getData() instanceof Number)) { writer.write("["); writer.write(index.getData().toString()); writer.write("]"); } else if (arguments.size() > 3) { Vertex associate = arguments.get(2).getTarget(); Vertex associateRelationship = arguments.get(3).getTarget(); writer.write("["); printElement(associate, writer, indent, functions, variables, elements, network); writer.write(", "); printElement(associateRelationship, writer, indent, functions, variables, elements, network); writer.write("]"); } } else { writer.write("["); printElement(variable, writer, indent, functions, variables, elements, network); writer.write("]"); } } return; } else if (operator.is(Primitive.SET)) { Vertex source = null; if (arguments != null && !arguments.isEmpty()) { source = arguments.get(0).getTarget(); printElement(source, writer, indent, functions, variables, elements, network); } if (arguments != null && arguments.size() > 2) { Vertex variable = arguments.get(1).getTarget(); Vertex value = arguments.get(2).getTarget(); if (variable.isPrimitive() && !(source.getData() instanceof Number)) { writer.write("."); writer.write(((Primitive)variable.getData()).getIdentity()); Vertex index = expression.getRelationship(Primitive.INDEX); if ((index != null) && (index.getData() instanceof Number)) { writer.write("["); writer.write(index.getData().toString()); writer.write("]"); } } else { writer.write("["); printElement(variable, writer, indent, functions, variables, elements, network); writer.write("]"); } writer.write(" = "); printElement(value, writer, indent, functions, variables, elements, network); } return; } else if (operator.is(Primitive.ADD)) { Vertex source = null; if (arguments != null && !arguments.isEmpty()) { source = arguments.get(0).getTarget(); printElement(source, writer, indent, functions, variables, elements, network); } if (arguments != null && arguments.size() > 2) { Vertex variable = arguments.get(1).getTarget(); Vertex value = arguments.get(2).getTarget(); if (variable.isPrimitive() && !(source.getData() instanceof Number)) { writer.write("."); writer.write(((Primitive)variable.getData()).getIdentity()); Vertex index = expression.getRelationship(Primitive.INDEX); if ((index != null) && (index.getData() instanceof Number)) { writer.write("["); writer.write(index.getData().toString()); writer.write("]"); } } else { writer.write("["); printElement(variable, writer, indent, functions, variables, elements, network); writer.write("]"); } writer.write(" =+ "); printElement(value, writer, indent, functions, variables, elements, network); } return; } else if (operator.is(Primitive.REMOVE)) { Vertex source = null; if (arguments != null && !arguments.isEmpty()) { source = arguments.get(0).getTarget(); printElement(source, writer, indent, functions, variables, elements, network); } if (arguments != null && arguments.size() > 2) { Vertex variable = arguments.get(1).getTarget(); Vertex value = arguments.get(2).getTarget(); if (variable.isPrimitive() && !(source.getData() instanceof Number)) { writer.write("."); writer.write(((Primitive)variable.getData()).getIdentity()); Vertex index = expression.getRelationship(Primitive.INDEX); if ((index != null) && (index.getData() instanceof Number)) { writer.write("["); writer.write(index.getData().toString()); writer.write("]"); } } else { writer.write("["); printElement(variable, writer, indent, functions, variables, elements, network); writer.write("]"); } writer.write(" =- "); printElement(value, writer, indent, functions, variables, elements, network); } return; } else if (BINARY_OPERATORS.containsKey(operator.getData())) { if (arguments != null && !arguments.isEmpty()) { Vertex left = arguments.get(0).getTarget(); boolean bracket = left.instanceOf(Primitive.EXPRESSION); if (bracket) { writer.write("("); } printElement(left, writer, indent, functions, variables, elements, network); if (bracket) { writer.write(")"); } } writer.write(" "); writer.write(BINARY_OPERATORS.get(operator.getData())); writer.write(" "); if (arguments != null && arguments.size() > 1) { Vertex right = arguments.get(1).getTarget(); boolean bracket = right.instanceOf(Primitive.EXPRESSION); if (bracket) { writer.write("("); } printElement(arguments.get(1).getTarget(), writer, indent, functions, variables, elements, network); if (bracket) { writer.write(")"); } } return; } else if (operator.is(Primitive.NOT)) { writer.write("! "); if (arguments != null && arguments.size() > 0) { printElement(arguments.get(0).getTarget(), writer, indent, functions, variables, elements, network); } return; } else { writer.write(((Primitive)operator.getData()).getIdentity()); } // Print arguments. if (operator.is(Primitive.WHILE)) { writer.write(" "); // Print arguments. printArguments(expression, Primitive.ARGUMENT, 0, null, false, false, false, true, writer, indent, variables, functions, elements, false, network); // Print do. Collection<Relationship> dos = expression.orderedRelationships(Primitive.DO); if (dos != null) { String newIndent = indent + "\t"; writer.write(" {\r\n"); writer.write(newIndent); writer.write("\t"); writer.write("\t"); printArguments(expression, Primitive.DO, 0, null, false, true, true, false, writer, newIndent, variables, functions, elements, true, network); writer.write(newIndent); writer.write("\t"); writer.write("}"); } else { writer.write(" {}"); } } else if (operator.is(Primitive.DO) || operator.is(Primitive.THINK)) { // Print do. Collection<Relationship> dos = expression.orderedRelationships(Primitive.DO); if (dos != null) { String newIndent = indent + "\t"; writer.write(" {\r\n"); writer.write(newIndent); writer.write("\t"); writer.write("\t"); printArguments(expression, Primitive.DO, 0, null, false, true, true, false, writer, newIndent, variables, functions, elements, true, network); writer.write(newIndent); writer.write("\t"); writer.write("}"); } else { writer.write(" {}"); } } else if (operator.is(Primitive.FOR)) { writer.write(" ("); int index = 0; while (arguments != null && (arguments.size() > index)) { if (index > 0) { writer.write(", "); } printElement(arguments.get(index++).getTarget(), writer, indent, functions, variables, elements, network); writer.write(" in "); printElement(arguments.get(index++).getTarget(), writer, indent, functions, variables, elements, network); index = index + 2; } writer.write(") "); // Print do. Collection<Relationship> dos = expression.orderedRelationships(Primitive.DO); if (dos != null) { String newIndent = indent + "\t"; writer.write(" {\r\n"); writer.write(newIndent); writer.write("\t"); writer.write("\t"); printArguments(expression, Primitive.DO, 0, null, false, true, true, false, writer, newIndent, variables, functions, elements, true, network); writer.write(newIndent); writer.write("\t"); writer.write("}"); } else { writer.write(" {}"); } } else if (operator.is(Primitive.IF)) { writer.write(" "); // Print arguments. printArguments(expression, Primitive.ARGUMENT, 0, null, false, false, false, true, writer, indent, variables, functions, elements, false, network); // Print then. Collection<Relationship> thens = expression.orderedRelationships(Primitive.THEN); Collection<Relationship> elses = expression.orderedRelationships(Primitive.ELSE); Collection<Relationship> elseifs = expression.orderedRelationships(Primitive.ELSEIF); if (thens == null && elseifs == null && elses == null) { writer.write(" {}\r\n"); } else { String newIndent = indent + "\t"; writer.write(" {\r\n"); if (thens != null) { writer.write(newIndent); writer.write("\t"); writer.write("\t"); printArguments(expression, Primitive.THEN, 0, null, false, true, true, false, writer, newIndent, variables, functions, elements, true, network); if (elses == null && elseifs == null) { writer.write(newIndent); writer.write("\t"); writer.write("}"); } } // Print else ifs. if (elseifs != null) { writer.write(newIndent); writer.write("\t"); writer.write("} else "); for (Relationship elseif : elseifs) { printOperator(elseif.getTarget(), writer, indent, functions, variables, elements, network); } } // Print else. if (elses != null) { if (elseifs == null) { writer.write(newIndent); writer.write("\t"); writer.write("}"); } writer.write(" else {\r\n"); writer.write(newIndent); writer.write("\t"); writer.write("\t"); printArguments(expression, Primitive.ELSE, 0, null, false, true, true, false, writer, newIndent, variables, functions, elements, true, network); writer.write(newIndent); writer.write("\t"); writer.write("}"); } } } else { writer.write(" "); // Print arguments. printArguments(expression, Primitive.ARGUMENT, 0, null, false, (operator.is(Primitive.DO)), false, true, writer, indent, variables, functions, elements, false, network); } } /** * Parse the operator and its arguments from bytecode. */ @Override public Vertex parseOperatorByteCode(DataInputStream dataStream, Network network) throws IOException { return parseOperatorByteCode(dataStream, null, network); } /** * Parse the operator and its arguments from bytecode. */ public Vertex parseOperatorByteCode(DataInputStream dataStream, Vertex pop, Network network) throws IOException { Vertex expression = network.createTemporyVertex(); expression.addRelationship(Primitive.INSTANTIATION, Primitive.EXPRESSION); long id = dataStream.readLong(); Vertex operator = network.findById(id); if (operator == null) { return expression; } expression.setName(operator.getDataValue()); expression.addRelationship(Primitive.OPERATOR, operator); id = dataStream.readLong(); if (id == 0) { return expression; } while (id > 0) { Vertex next = network.findById(id); if (next == null) { return expression; } parseArgumentsByteCode(expression, dataStream, next, pop, network); id = dataStream.readLong(); } return expression; } /** * Print the IF condition and any variables and states that it references. */ @Override public void printCase(Vertex expression, Writer writer, String indent, Set<Vertex> elements, List<Vertex> newVariables, List<Vertex> newFunctions, List<Vertex> newStates, Network network) throws IOException { Vertex variable = expression.getRelationship(Primitive.CASE); Vertex pattern = expression.getRelationship(Primitive.PATTERN); Vertex template = expression.getRelationship(Primitive.TEMPLATE); Vertex that = expression.getRelationship(Primitive.THAT); Vertex topic = expression.getRelationship(Primitive.TOPIC); Vertex as = expression.getRelationship(Primitive.AS); List<Relationship> states = expression.orderedRelationships(Primitive.GOTO); List<Relationship> fors = expression.orderedRelationships(Primitive.FOR); if (variable == null && pattern == null) { return; } if ((variable != null) && variable.instanceOf(Primitive.VARIABLE) && (!elements.contains(variable))) { newVariables.add(variable); elements.add(variable); } writer.write(indent); if (pattern != null) { writer.write("pattern "); variable = pattern; } else { writer.write("case "); } if (variable.instanceOf(Primitive.EXPRESSION)) { writer.write("("); } else if (variable.instanceOf(Primitive.ARRAY)) { writer.write("any "); if (!variable.hasRelationship(Primitive.TYPE, Primitive.REQUIRED)) { writer.write("or none "); } } printElement(variable, writer, indent, newFunctions, newVariables, elements, network); if (variable.instanceOf(Primitive.EXPRESSION)) { writer.write(")"); } if (as != null) { writer.write(" as "); printElement(as, writer, indent, newFunctions, newVariables, elements, network); } if (topic != null) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("topic "); printElement(topic, writer, indent, newFunctions, newVariables, elements, network); } if (that != null) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("that "); printElement(that, writer, indent, newFunctions, newVariables, elements, network); } if (template != null) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("template "); if (template.instanceOf(Primitive.EXPRESSION)) { writer.write("("); } printElement(template, writer, indent, newFunctions, newVariables, elements, network); if (template.instanceOf(Primitive.EXPRESSION)) { writer.write(")"); } } if (states != null) { if ((states.size() == 1) && (states.get(0).getTarget().is(Primitive.RETURN))) { writer.write(" return"); } else { writer.write(" goto "); for (Iterator<Relationship> iterator = states.iterator(); iterator.hasNext(); ) { Vertex state = iterator.next().getTarget(); if (!elements.contains(state)) { newStates.add(state); elements.add(state); } printElement(state, writer, indent, null, newVariables, elements, network); if (iterator.hasNext()) { writer.write(", "); } } } } if (fors != null) { writer.write(" for each "); for (Iterator<Relationship> iterator = fors.iterator(); iterator.hasNext(); ) { Vertex argument = iterator.next().getTarget(); if (argument.instanceOf(Primitive.VARIABLE) && (!elements.contains(argument))) { newVariables.add(argument); elements.add(argument); } printElement(argument, writer, indent, null, newVariables, elements, network); if (iterator.hasNext()) { writer.write(" of "); } } } writer.write(";\r\n\r\n"); } /** * Print the GOTO condition and any variables and states that it references. */ public void printGoto(Vertex expression, Writer writer, String indent, Set<Vertex> elements, List<Vertex> newVariables, List<Vertex> newFunctions, List<Vertex> newStates, Network network) throws IOException { Vertex state = expression.getRelationship(Primitive.GOTO); writer.write(indent); writer.write("goto "); if (expression.hasRelationship(Primitive.FINALLY)) { writer.write("finally "); } if (!elements.contains(state)) { newStates.add(state); elements.add(state); } printElement(state, writer, indent, newFunctions, newVariables, elements, network); Collection<Relationship> arguments = expression.getRelationships(Primitive.ARGUMENT); if (arguments != null) { writer.write(" with ("); for (Iterator<Relationship> iterator = arguments.iterator(); iterator.hasNext(); ) { Relationship argument = iterator.next(); printElement(argument.getTarget(), writer, indent, newFunctions, newVariables, elements, network); if (iterator.hasNext()) { writer.write(", "); } } writer.write(")"); } writer.write(";\r\n\r\n"); } /** * Print the PUSH condition and any variables and states that it references. */ public void printPush(Vertex expression, Writer writer, String indent, Set<Vertex> elements, List<Vertex> newVariables, List<Vertex> newFunctions, List<Vertex> newStates, Network network) throws IOException { Vertex state = expression.getRelationship(Primitive.ARGUMENT); writer.write(indent); writer.write("push "); if (!elements.contains(state)) { newStates.add(state); elements.add(state); } printElement(state, writer, indent, newFunctions, newVariables, elements, network); writer.write(";\r\n\r\n"); } /** * Print the DO operation. */ public void printDo(Vertex expression, Writer writer, String indent, Set<Vertex> elements, List<Vertex> newVariables, List<Vertex> newFunctions, List<Vertex> newStates, Network network) throws IOException { printComments(expression, writer, indent, false, network); writer.write(indent); writer.write("\t"); writer.write("do"); Collection<Relationship> dos = expression.getRelationships(Primitive.DO); if (dos != null) { writer.write(" {\r\n"); writer.write(indent); writer.write("\t"); writer.write("\t"); printArguments(expression, Primitive.DO, 0, null, false, true, true, false, writer, indent, newVariables, newFunctions, elements, true, network); writer.write(indent); writer.write("\t"); writer.write("}\r\n"); } else { writer.write(" {}\r\n"); } } /** * Print the RETURN condition and any variables it references. */ public void printReturn(Vertex expression, Writer writer, String indent, Set<Vertex> elements, List<Vertex> newVariables, List<Vertex> newFunctions, List<Vertex> newStates, Network network) throws IOException { Vertex result = expression.getRelationship(Primitive.RETURN); writer.write(indent); writer.write("return"); if (result != null) { writer.write(" "); printElement(result, writer, indent, newFunctions, newVariables, elements, network); } Collection<Relationship> arguments = expression.getRelationships(Primitive.ARGUMENT); if (arguments != null) { writer.write(" with ("); for (Iterator<Relationship> iterator = arguments.iterator(); iterator.hasNext(); ) { Relationship argument = iterator.next(); printElement(argument.getTarget(), writer, indent, newFunctions, newVariables, elements, network); if (iterator.hasNext()) { writer.write(", "); } } writer.write(")"); } writer.write(";\r\n\r\n"); } }