/****************************************************************************** * * 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.StringWriter; import java.io.Writer; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.logging.Level; import org.botlibre.BotException; import org.botlibre.api.knowledge.Network; import org.botlibre.api.knowledge.Relationship; import org.botlibre.api.knowledge.Vertex; import org.botlibre.knowledge.AbstractNetwork; import org.botlibre.knowledge.BinaryData; import org.botlibre.knowledge.Primitive; import org.botlibre.util.Utils; /** * Utility class for printing the Self programming model. * Self is the language that Bot programs herself in. */ public class SelfDecompiler { public static long TIMEOUT = 10000; protected static SelfDecompiler decompiler; /** Printing ids doesn't work, recompile will link back to same state. */ protected boolean printIds; public static SelfDecompiler getDecompiler() { if (decompiler == null) { decompiler = new Self4Decompiler(); } return decompiler; } public static void setDecompiler(SelfDecompiler compiler) { SelfDecompiler.decompiler = compiler; } /** * Print the Self code for the state machine. */ public String decompileStateMachine(Vertex state, Network network) { long start = System.currentTimeMillis(); StringWriter writer = new StringWriter(); printStateMachine(state, writer, network, start, TIMEOUT); writer.flush(); network.getBot().log(state, "Decompile time", Level.INFO, System.currentTimeMillis() - start); return writer.toString(); } /** * Print the Self code for the state machine. */ public void printStateMachine(Vertex state, Writer writer, Network network, long start, long timeout) { try { 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 comments */ public boolean printComments(Vertex element, Writer writer, String indent, boolean newLine, Network network) throws IOException { List<Relationship> comments = element.orderedRelationships(Primitive.COMMENT); if (comments != null && !comments.isEmpty()) { if (newLine) { writer.write("\r\n"); } for (Relationship comment : comments) { writer.write(indent); writer.write((String)comment.getTarget().getData()); } if (newLine) { writer.write(indent); } return true; } return false; } /** * Print the state and any referenced states or variables that have not yet been printed. */ 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); 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> equations = state.orderedRelationships(Primitive.DO); List<Vertex> newEquations = new ArrayList<Vertex>(); List<Vertex> newVariables = new ArrayList<Vertex>(); List<Vertex> newStates = new ArrayList<Vertex>(); if (equations != null) { for (Relationship equation : equations) { printComments(equation.getTarget(), writer, indent, true, network); if (equation.getTarget().instanceOf(Primitive.CASE)) { printCase(equation.getTarget(), writer, childIndent, elements, newVariables, newEquations, newStates, network); } else if (equation.getTarget().instanceOf(Primitive.DO)) { writer.write(childIndent); printOperator(equation.getTarget().getRelationship(Primitive.DO), writer, indent, newEquations, newVariables, elements, network); writer.write(";\r\n"); } else if (equation.getTarget().instanceOf(Primitive.GOTO)) { printGoto(equation.getTarget(), writer, childIndent, elements, network, start, timeout); } else if (equation.getTarget().instanceOf(Primitive.PUSH)) { printPush(equation.getTarget(), writer, childIndent, elements, network, start, timeout); } else if (equation.getTarget().instanceOf(Primitive.RETURN)) { printReturn(equation.getTarget(), writer, childIndent, elements, network, start, timeout); } } } for (Vertex variable : newVariables) { printVariable(variable, writer, childIndent, elements, network); } for (Vertex newEquation : newEquations) { printEquation(newEquation, writer, childIndent, elements, network); } newEquations = 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:"); writer.write(String.format("%.02f", quotient.getCorrectness())); writer.write(":"); printElement(quotient.getTarget(), writer, indent, newEquations, 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 is "); } else { writer.write("\tprevious is not "); } printElement(previous.getTarget(), writer, indent + 1, newEquations, newVariables, elements, network); writer.write(";\r\n"); } } writer.write(childIndent); writer.write("}"); } writer.write(";\r\n"); for (Vertex variable : newVariables) { printVariable(variable, writer, childIndent, elements, network); } for (Vertex newEquation : newEquations) { printEquation(newEquation, writer, childIndent, elements, network); } } } Collection<Relationship> possibleQuotients = state.orderedRelationships(Primitive.POSSIBLE_QUOTIENT); if (possibleQuotients != null) { for (Relationship quotient : possibleQuotients) { writer.write(childIndent); writer.write("//Possible Quotient:"); printElement(quotient.getTarget(), writer, indent, newEquations, newVariables, elements, network); writer.write(";\r\n"); } } for (Vertex element : newStates) { if (element.instanceOf(Primitive.STATE)) { printState(element, writer, childIndent, elements, network, start, timeout); } } writer.write(indent); writer.write("}\r\n"); } /** * Print the vertex data. */ 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, equation, or raw data. */ public void printElement(Vertex vertex, Writer writer, String indent, List<Vertex> equations, List<Vertex> variables, Set<Vertex> elements, Network network) throws IOException { if (vertex == null) { writer.write("#null"); return; } boolean printId = getPrintIds(); if (vertex.instanceOf(Primitive.STATE)) { writer.write("State:"); } else if (vertex.instanceOf(Primitive.VARIABLE)) { writer.write(":"); } else if (vertex.instanceOf(Primitive.FORMULA)) { if (vertex.hasData()) { writer.write(String.valueOf(vertex.getData())); } else { printFormula(vertex, writer, indent, equations, variables, elements, network); } return; } else if (vertex.instanceOf(Primitive.PATTERN)) { writer.write(String.valueOf(vertex.getData())); return; } else if (vertex.instanceOf(Primitive.EQUATION)) { // If unnamed, then just inline. if (vertex.getName() == null) { if (!elements.contains(vertex)) { String newIndent = indent + "\t"; if (vertex.getData() instanceof BinaryData) { Vertex detached = parseEquationByteCode(vertex, (BinaryData)vertex.getData(), network); elements.add(detached); vertex = detached; } printOperator(vertex, writer, newIndent, equations, variables, elements, network); return; } else { // No name, but was used twice, change it to a named equation and reference it. vertex.setName("dangling"); elements.add(vertex); } } writer.write("Function:"); if (!elements.contains(vertex)) { equations.add(vertex); elements.add(vertex); } } 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) { 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 { writer.write("Vertex:"); printId = true; } if ((printId || (vertex.getName() == null)) && (vertex.getId() != null)) { writer.write(vertex.getId().toString()); } if (vertex.getName() != null) { String name = Utils.compress(vertex.getName(), 100); if (!name.isEmpty()) { if (printId) { writer.write(":"); } writer.write(name); } } } public void printFormula(Vertex formula, Writer writer, String indent, List<Vertex> equations, List<Vertex> variables, Set<Vertex> elements, Network network) throws IOException { writer.write("Formula:"); 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()); writer.write(text); if (text.equals("\"")) { writer.write("\""); } } else { writer.write("{"); printElement(word, writer, indent, equations, variables, elements, network); writer.write("}"); } } writer.write("\""); } /** * Print the formula to create a unique instance of it. */ public Vertex createUniqueTemplate(Vertex formula, Network network) { try { StringWriter writer = new StringWriter(); printTemplate(formula, writer, "", new ArrayList<Vertex>(), new ArrayList<Vertex>(), new HashSet<Vertex>(), network); String source = writer.toString(); if (source.length() > AbstractNetwork.MAX_TEXT) { return formula; } // Maintain identity on formulas through printing them. Vertex existingFormula = network.createVertex(source); if (!existingFormula.instanceOf(Primitive.FORMULA)) { for (Iterator<Relationship> iterator = formula.orderedAllRelationships(); iterator.hasNext(); ) { Relationship relationship = iterator.next(); existingFormula.addRelationship(relationship.getType(), relationship.getTarget(), relationship.getIndex()); } } return existingFormula; } catch (IOException ignore) { throw new BotException(ignore); } } public void printTemplate(Vertex formula, Writer writer, String indent, List<Vertex> expressions, List<Vertex> variables, Set<Vertex> elements, Network network) throws IOException { printFormula(formula, writer, indent, expressions, variables, elements, network); } /** * Print the formula to create a unique instance of it. */ public Vertex createUniqueFormula(Vertex formula, Network network) { try { StringWriter writer = new StringWriter(); printFormula(formula, writer, "", new ArrayList<Vertex>(), new ArrayList<Vertex>(), new HashSet<Vertex>(), network); String source = writer.toString(); if (source.length() > AbstractNetwork.MAX_TEXT) { return formula; } // Maintain identity on formulas through printing them. Vertex existingFormula = network.createVertex(source); if (!existingFormula.instanceOf(Primitive.FORMULA)) { for (Iterator<Relationship> iterator = formula.orderedAllRelationships(); iterator.hasNext(); ) { Relationship relationship = iterator.next(); existingFormula.addRelationship(relationship.getType(), relationship.getTarget(), relationship.getIndex()); } } return existingFormula; } catch (IOException ignore) { throw new BotException(ignore); } } /** * Print the variable and any variables it references that have not been printed. */ public void printVariable(Vertex variable, Writer writer, String indent, Set<Vertex> elements, Network network) throws IOException { 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); 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.instanceOf(Primitive.VARIABLE) && !elements.contains(type)) { localElements.add(type); elements.add(type); } if (target.instanceOf(Primitive.VARIABLE) && !elements.contains(target)) { localElements.add(target); elements.add(target); } boolean equality = type.is(Primitive.EQUALS); if (relationship.isInverse()) { writer.write("\texclude "); } else if (equality) { writer.write("\tinclude "); } else { writer.write("\tset "); } if (equality) { printElement(target, writer, indent, null, null, elements, network); } else { printElement(type, writer, indent, null, null, elements, network); if (relationship.isInverse()) { writer.write(" from "); } else { writer.write(" to "); } printElement(target, writer, indent, null, null, elements, network); } writer.write(";\r\n"); writer.write(indent); } writer.write("}\r\n"); for (Vertex element : localElements) { printVariable(element, writer, indent, elements, network); } } /** * Print the operation arguments. */ public void printArguments(Vertex equation, Primitive type, String[] tokens, boolean reverse, boolean newLine, boolean unravel, Writer writer, String indent, List<Vertex> variables, List<Vertex> equations, Set<Vertex> elements, boolean space, Network network) throws IOException { List<Relationship> arguments = equation.orderedRelationships(type); if (arguments != null) { boolean needsBrackets = !unravel; if ((arguments.size() == 1) || (tokens != null)) { needsBrackets = false; } if (arguments.size() == 1) { newLine = false; } else if (arguments.size() > 3) { newLine = true; } if (!unravel && space) { writer.write(" "); } if (needsBrackets) { writer.write("("); } int size = arguments.size(); for (int index = 0; index < size; index++) { if (newLine && (!unravel || (index > 0))) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("\t"); } Vertex argument = null; 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 isEquation = argument.instanceOf(Primitive.EQUATION); if (!unravel && !needsBrackets && isEquation) { writer.write("("); } printElement(argument, writer, indent, equations, variables, elements, network); if (!unravel && !needsBrackets && isEquation) { writer.write(")"); } if (index < (size - 1)) { if (tokens != null) { writer.write(" "); writer.write(tokens[index]); writer.write(" "); } else { if (unravel) { writer.write(";"); } else { writer.write(","); } if (!newLine) { writer.write(" "); } } } } if (!unravel && newLine) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); } if (needsBrackets) { writer.write(")"); } } } /** * Parse the operation arguments. */ public void parseArgumentsByteCode(Vertex equation, DataInputStream dataStream, Vertex type, Network network) throws IOException { long id = dataStream.readLong(); while (id > 0) { Vertex element = network.findById(id); if (element == null) { id = dataStream.readLong(); continue; } if (element.is(Primitive.EQUATION)) { element = parseOperatorByteCode(dataStream, network); } equation.addRelationship(type, element, Integer.MAX_VALUE); id = dataStream.readLong(); } } /** * Check if the function is bytecode and decompile. */ public Vertex decompileFunction(Vertex equation, Network network) { return decompileEquation(equation, network); } /** * Check if the expression is bytecode and decompile. */ public Vertex decompileExpression(Vertex equation, Network network) { return decompileEquation(equation, network); } /** * Check if the equation is bytecode and decompile. */ public Vertex decompileEquation(Vertex equation, Network network) { if (equation.getData() instanceof BinaryData) { try { return parseEquationByteCode(equation, (BinaryData)equation.getData(), network); } catch (IOException exception) { throw new SelfExecutionException(equation, exception); } } return equation; } /** * Check if the state is bytecode and decompile. */ 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 equation and any equations it references that have not been printed. */ public void printEquation(Vertex equation, Writer writer, String indent, Set<Vertex> elements, Network network) throws IOException { if (equation.getData() instanceof BinaryData) { Vertex detached = parseEquationByteCode(equation, (BinaryData)equation.getData(), network); elements.add(detached); printEquation(detached, writer, indent, elements, network); return; } printComments(equation, writer, indent, false, network); List<Vertex> equations = new ArrayList<Vertex>(); List<Vertex> variables = new ArrayList<Vertex>(); writer.write(indent); printElement(equation, writer, indent, equations, variables, elements, network); writer.write(" {\r\n"); writer.write(indent); Vertex operator = equation.getRelationship(Primitive.OPERATOR); if (operator == null) { writer.write("}\r\n"); return; } if (operator.is(Primitive.DO)) { // Unravel DO. writer.write("\t"); printArguments(equation, Primitive.ARGUMENT, null, false, true, true, writer, indent.substring(0, indent.length() - 1), variables, equations, elements, true, network); writer.write(";\r\n"); } else { writer.write("\t"); printOperator(equation, writer, indent, equations, variables, elements, network); writer.write(";\r\n"); } writer.write(indent); writer.write("}\r\n"); for (Vertex element : variables) { printVariable(element, writer, indent, elements, network); } for (Vertex element : equations) { printEquation(element, writer, indent, elements, network); } } /** * Parse the expression from bytecode. */ public Vertex parseExpressionByteCode(Vertex equation, BinaryData data, Network network) throws IOException { return parseEquationByteCode(equation, data, network); } /** * Parse the function from bytecode. */ public Vertex parseFunctionByteCode(Vertex equation, BinaryData data, Network network) throws IOException { return parseEquationByteCode(equation, data, network); } /** * Parse the equation from bytecode. */ public Vertex parseEquationByteCode(Vertex equation, BinaryData data, Network network) throws IOException { if (data.getCache() != null) { return (Vertex)data.getCache(); } BinaryData bytes = data; if (!equation.isTemporary()) { bytes = (BinaryData)network.findData(data); if (bytes == null) { bytes = data; } } ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes.getBytes()); DataInputStream dataStream = new DataInputStream(byteStream); Vertex cache = parseOperatorByteCode(dataStream, network); cache.setName(equation.getName()); data.setCache(cache); bytes.setCache(cache); return cache; } /** * Parse the state from bytecode. */ 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); Vertex cache = parseStateByteCode(dataStream, network); // Add any dynamically added cases. Collection<Relationship> cases = state.getRelationships(Primitive.DO); if (cases != null) { for (Relationship equation : cases) { cache.addRelationship(equation, true); } } cache.setName(state.getName()); data.setCache(cache); bytes.setCache(cache); return cache; } /** * Parse the state and its cases from bytecode. */ public Vertex parseStateByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex state = network.createTemporyVertex(); state.addRelationship(Primitive.INSTANTIATION, Primitive.STATE); 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(); } return state; } /** * Parse the CASE bytecode. */ 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; } Vertex variable = network.findById(id); if (variable == null) { return vertex; } if (variable.is(Primitive.PATTERN)) { id = dataStream.readLong(); if (id == 0) { return vertex; } variable = network.findById(id); if (variable == null) { return vertex; } vertex.addRelationship(Primitive.PATTERN, variable); } else { vertex.addRelationship(Primitive.CASE, variable); } id = dataStream.readLong(); 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; } Vertex element = network.findById(id); if (element == null) { id = dataStream.readLong(); continue; } if (element.is(Primitive.EQUATION)) { element = parseOperatorByteCode(dataStream, network); } vertex.addRelationship(type, element); id = dataStream.readLong(); } return vertex; } /** * Parse the PUSH bytecode. */ public Vertex parsePushByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex equation = network.createTemporyVertex(); equation.addRelationship(Primitive.INSTANTIATION, Primitive.PUSH); long id = dataStream.readLong(); if (id == 0) { return equation; } Vertex element = network.findById(id); if (element != null) { equation.addRelationship(Primitive.ARGUMENT, element, Integer.MAX_VALUE); } return equation; } /** * Parse the DO bytecode. */ public Vertex parseDoByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex equation = network.createTemporyVertex(); equation.addRelationship(Primitive.INSTANTIATION, Primitive.DO); Vertex operation = parseOperatorByteCode(dataStream, network); equation.addRelationship(Primitive.DO, operation, Integer.MAX_VALUE); return equation; } /** * Parse the RETURN bytecode. */ public Vertex parseReturnByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex equation = network.createTemporyVertex(); equation.addRelationship(Primitive.INSTANTIATION, Primitive.RETURN); long id = dataStream.readLong(); if (id == 0) { return equation; } Vertex element = network.findById(id); if (element != null) { if (element.is(Primitive.ARGUMENT)) { equation.addRelationship(Primitive.RETURN, element); id = dataStream.readLong(); while (id > 0) { element = network.findById(id); if (element != null) { equation.addRelationship(Primitive.ARGUMENT, element, Integer.MAX_VALUE); } id = dataStream.readLong(); } } else { equation.addRelationship(Primitive.RETURN, element); } } return equation; } /** * Parse the GOTO bytecode. */ public Vertex parseGotoByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex equation = network.createTemporyVertex(); equation.addRelationship(Primitive.INSTANTIATION, Primitive.GOTO); long id = dataStream.readLong(); if (id == 0) { return equation; } Vertex element = network.findById(id); if (element == null) { return equation; } if (element.is(Primitive.FINALLY)) { equation.addRelationship(Primitive.FINALLY, Primitive.FINALLY); id = dataStream.readLong(); if (id == 0) { return equation; } element = network.findById(id); if (element == null) { return equation; } } equation.addRelationship(Primitive.GOTO, element); id = dataStream.readLong(); if (id == 0) { return equation; } element = network.findById(id); if (element == null) { return equation; } if (element.is(Primitive.ARGUMENT)) { id = dataStream.readLong(); while (id > 0) { element = network.findById(id); if (element != null) { equation.addRelationship(Primitive.ARGUMENT, element, Integer.MAX_VALUE); } id = dataStream.readLong(); } id = dataStream.readLong(); } return equation; } /** * Parse the GOTO bytecode. */ public void parseQuotientByteCode(Vertex state, DataInputStream dataStream, Network network) throws IOException { float correctness = dataStream.readFloat(); long id = dataStream.readLong(); if (id == 0) { return; } Vertex element = network.findById(id); if (element == null) { return; } Relationship relationship = state.addWeakRelationship(Primitive.QUOTIENT, element, correctness); id = dataStream.readLong(); if (id == 0) { return; } element = network.findById(id); if (element == null) { return; } if (element.is(Primitive.PREVIOUS)) { id = dataStream.readLong(); Vertex meta = network.createTemporyVertex(); relationship.setMeta(meta); while (id > 0) { element = network.findById(id); if (element != null) { if (element.is(Primitive.NOT)) { id = dataStream.readLong(); if (id == 0) { return; } element = network.findById(id); if (element == null) { continue; } meta.removeRelationship(Primitive.PREVIOUS, element); } else { meta.addRelationship(Primitive.PREVIOUS, element); } } id = dataStream.readLong(); } } } /** * Print the equation and any equations it references that have not been printed. */ public void printOperator(Vertex equation, Writer writer, String indent, List<Vertex> equations, List<Vertex> variables, Set<Vertex> elements, Network network) throws IOException { printComments(equation, writer, indent + "\t", true, network); Vertex operator = equation.getRelationship(Primitive.OPERATOR); if (operator == null) { return; } if (operator.is(Primitive.RELATION)) { writer.write("is"); } else if (operator.is(Primitive.WEAKASSOCIATE)) { writer.write("weak associate"); } else { writer.write(((Primitive)operator.getData()).getIdentity()); } if (equation.hasRelationship(Primitive.NOT, Primitive.NOT)) { writer.write(" not"); } // Print arguments. if (operator.is(Primitive.ASSIGN)) { String[] tokens = {"to"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.DEFINE)) { String[] tokens = {"as"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.GET)) { String[] tokens = {"from", "associated to", "by"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); // Print AT. Collection<Relationship> index = equation.getRelationships(Primitive.INDEX); if (index != null) { writer.write(" at"); printArguments(equation, Primitive.INDEX, null, false, false, false, writer, indent, variables, equations, elements, true, network); } Collection<Relationship> lastindex = equation.getRelationships(Primitive.LASTINDEX); if (lastindex != null) { writer.write(" at last"); printArguments(equation, Primitive.LASTINDEX, null, false, false, false, writer, indent, variables, equations, elements, true, network); } } else if (operator.is(Primitive.INPUT)) { String[] tokens = {"for"}; if (equation.getRelationships(Primitive.ARGUMENT).size() == 3) { tokens = new String[]{"part", "for"}; } printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.ALL)) { String[] tokens = {"from", "associated to", "by"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.COUNT)) { String[] tokens = {"of"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.APPEND)) { String[] tokens = {"to", "of", "with meta", "as"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.ASSOCIATE) || operator.is(Primitive.DISSOCIATE) || operator.is(Primitive.WEAKASSOCIATE)) { String[] tokens = {"to", "by", "with meta", "as"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.SET)) { String[] tokens = {"to", "on"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.RELATION)) { String[] tokens = {"related to", "by"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.RELATED)) { writer.write(" to "); String[] tokens = {"by"}; printArguments(equation, Primitive.ARGUMENT, tokens, false, false, false, writer, indent, variables, equations, elements, true, network); } else if (operator.is(Primitive.WHILE)) { // Print arguments. printArguments(equation, Primitive.ARGUMENT, null, false, false, false, writer, indent, variables, equations, elements, true, network); // Print do. Collection<Relationship> dos = equation.orderedRelationships(Primitive.DO); if (dos != null) { String newIndent = indent + "\t"; writer.write("\r\n"); writer.write(newIndent); writer.write("\tdo"); printArguments(equation, Primitive.DO, null, false, true, false, writer, newIndent, variables, equations, elements, true, network); } } else if (operator.is(Primitive.FOR)) { writer.write(" each "); List<Relationship> arguments = equation.orderedRelationships(Primitive.ARGUMENT); printElement(arguments.get(0).getTarget(), writer, indent, equations, variables, elements, network); writer.write(" of "); printElement(arguments.get(1).getTarget(), writer, indent, equations, variables, elements, network); writer.write(" as "); printElement(arguments.get(2).getTarget(), writer, indent, equations, variables, elements, network); int index = 3; if (arguments.size() > index) { writer.write(" and each "); printElement(arguments.get(index++).getTarget(), writer, indent, equations, variables, elements, network); writer.write(" of "); printElement(arguments.get(index++).getTarget(), writer, indent, equations, variables, elements, network); writer.write(" as "); printElement(arguments.get(index++).getTarget(), writer, indent, equations, variables, elements, network); } // Print do. Collection<Relationship> dos = equation.orderedRelationships(Primitive.DO); if (dos != null) { String newIndent = indent + "\t"; writer.write("\r\n"); writer.write(newIndent); writer.write("\tdo"); printArguments(equation, Primitive.DO, null, false, true, false, writer, newIndent, variables, equations, elements, true, network); } } else if (operator.is(Primitive.CALL)) { List<Relationship> arguments = equation.orderedRelationships(Primitive.ARGUMENT); writer.write(" "); printElement(arguments.get(0).getTarget(), writer, indent, equations, variables, elements, network); writer.write(" on "); printElement(arguments.get(1).getTarget(), writer, indent, equations, variables, elements, network); if (arguments.size() > 2) { writer.write(" with ("); for (int index = 2; index < arguments.size(); index++) { printElement(arguments.get(index).getTarget(), writer, indent, equations, variables, elements, network); if (index == (arguments.size() - 1)) { writer.write(")"); } else { writer.write(", "); } } } } else if (operator.is(Primitive.LEARN)) { List<Relationship> arguments = equation.orderedRelationships(Primitive.ARGUMENT); writer.write(" "); printElement(arguments.get(0).getTarget(), writer, indent, equations, variables, elements, network); // Print that. Collection<Relationship> that = equation.orderedRelationships(Primitive.THAT); if (that != null) { String newIndent = indent + "\t"; writer.write("\r\n"); writer.write(newIndent); writer.write("\tthat"); printArguments(equation, Primitive.THAT, null, false, false, false, writer, newIndent, variables, equations, elements, true, network); } // Print topic. Collection<Relationship> topic = equation.orderedRelationships(Primitive.TOPIC); if (topic != null) { String newIndent = indent + "\t"; writer.write("\r\n"); writer.write(newIndent); writer.write("\ttopic"); printArguments(equation, Primitive.TOPIC, null, false, false, false, writer, newIndent, variables, equations, elements, true, network); } String newIndent = indent + "\t"; writer.write("\r\n"); writer.write(newIndent); writer.write("\ttemplate "); printElement(arguments.get(1).getTarget(), writer, newIndent, equations, variables, elements, network); } else if (operator.is(Primitive.IF)) { // Print arguments. printArguments(equation, Primitive.ARGUMENT, null, false, (operator.is(Primitive.DO)), false, writer, indent, variables, equations, elements, true, network); // Print ands/ors brackets. Collection<Relationship> conditions = equation.orderedRelationships(Primitive.CONDITION); if (conditions != null) { for (Relationship condition : conditions) { writer.write(" "); printOperator(condition.getTarget(), writer, indent, equations, variables, elements, network); } } // Print then. Collection<Relationship> thens = equation.orderedRelationships(Primitive.THEN); if (thens != null) { String newIndent = indent + "\t"; writer.write("\r\n"); writer.write(newIndent); writer.write("\tthen"); printArguments(equation, Primitive.THEN, null, false, false, false, writer, newIndent, variables, equations, elements, true, network); } // Print else. Collection<Relationship> elses = equation.orderedRelationships(Primitive.ELSE); if (elses != null) { String newIndent = indent + "\t"; writer.write("\r\n"); writer.write(newIndent); writer.write("\telse"); printArguments(equation, Primitive.ELSE, null, false, false, false, writer, newIndent, variables, equations, elements, true, network); } } else if (operator.is(Primitive.REQUEST) || operator.is(Primitive.SRAIX)) { // Print arguments. printArguments(equation, Primitive.ARGUMENT, null, false, (operator.is(Primitive.DO)), false, writer, indent, variables, equations, elements, true, network); // Print parameters. Collection<Relationship> param = equation.orderedRelationships(Primitive.BOT); if (param != null) { writer.write(" bot"); printArguments(equation, Primitive.BOT, null, false, false, false, writer, indent, variables, equations, elements, true, network); } param = equation.orderedRelationships(Primitive.BOTID); if (param != null) { writer.write(" botid"); printArguments(equation, Primitive.BOTID, null, false, false, false, writer, indent, variables, equations, elements, true, network); } param = equation.orderedRelationships(Primitive.SERVICE); if (param != null) { writer.write(" service"); printArguments(equation, Primitive.SERVICE, null, false, false, false, writer, indent, variables, equations, elements, true, network); } param = equation.orderedRelationships(Primitive.SERVER); if (param != null) { writer.write(" server"); printArguments(equation, Primitive.SERVER, null, false, false, false, writer, indent, variables, equations, elements, true, network); } param = equation.orderedRelationships(Primitive.APIKEY); if (param != null) { writer.write(" apikey"); printArguments(equation, Primitive.APIKEY, null, false, false, false, writer, indent, variables, equations, elements, true, network); } param = equation.orderedRelationships(Primitive.LIMIT); if (param != null) { writer.write(" limit"); printArguments(equation, Primitive.LIMIT, null, false, false, false, writer, indent, variables, equations, elements, true, network); } param = equation.orderedRelationships(Primitive.HINT); if (param != null) { writer.write(" hint"); printArguments(equation, Primitive.HINT, null, false, false, false, writer, indent, variables, equations, elements, true, network); } param = equation.orderedRelationships(Primitive.DEFAULT); if (param != null) { writer.write(" default"); printArguments(equation, Primitive.DEFAULT, null, false, false, false, writer, indent, variables, equations, elements, true, network); } } else { Collection<Relationship> conditions = equation.orderedRelationships(Primitive.CONDITION); if (conditions != null) { writer.write(" ("); } // Print arguments. printArguments(equation, Primitive.ARGUMENT, null, false, (operator.is(Primitive.DO)), false, writer, indent, variables, equations, elements, conditions == null, network); // Print ands/ors brackets. if (conditions != null) { for (Relationship condition : conditions) { writer.write(" "); printOperator(condition.getTarget(), writer, indent, equations, variables, elements, network); } writer.write(")"); } if (operator.is(Primitive.FORMAT)) { // Print AS. Collection<Relationship> as = equation.orderedRelationships(Primitive.AS); if (as != null) { writer.write(" as "); printArguments(equation, Primitive.AS, null, false, false, false, writer, indent, variables, equations, elements, true, network); } } } } /** * Parse the operator and its arguments from bytecode. */ public Vertex parseOperatorByteCode(DataInputStream dataStream, Network network) throws IOException { Vertex equation = network.createTemporyVertex(); equation.addRelationship(Primitive.INSTANTIATION, Primitive.EQUATION); long id = dataStream.readLong(); Vertex operator = network.findById(id); if (operator == null) { return equation; } equation.addRelationship(Primitive.OPERATOR, operator); id = dataStream.readLong(); if (id == 0) { return equation; } Vertex next = network.findById(id); if (next == null) { return equation; } if (next.is(Primitive.NOT)) { equation.addRelationship(Primitive.NOT, Primitive.NOT); id = dataStream.readLong(); if (id == 0) { return equation; } } if (operator.is(Primitive.IF)) { List<Vertex> stack = new ArrayList<Vertex>(); stack.add(equation); Vertex top = equation; // Handle AND / OR / NOT next = network.findById(id); if (next == null) { return equation; } if (next.is(Primitive.ARGUMENT)) { parseArgumentsByteCode(equation, dataStream, next, network); id = dataStream.readLong(); if (id == 0) { return equation; } next = network.findById(id); if (next == null) { return equation; } } while (next.is(Primitive.AND) || next.is(Primitive.OR)) { Vertex operation = next; Vertex condition = network.createTemporyVertex(); condition.addRelationship(Primitive.INSTANTIATION, Primitive.EQUATION); id = dataStream.readLong(); if (id == 0) { return equation; } next = network.findById(id); if (next == null) { return equation; } if (next.is(Primitive.NOT)) { condition.addRelationship(Primitive.NOT, Primitive.NOT); id = dataStream.readLong(); if (id == 0) { return equation; } next = network.findById(id); if (next == null) { return equation; } } boolean bracket = false; while (next.is(Primitive.LEFTBRACKET)) { bracket = true; stack.add(condition); id = dataStream.readLong(); if (id == 0) { return equation; } next = network.findById(id); if (next == null) { return equation; } } condition.addRelationship(Primitive.OPERATOR, operation); top.addRelationship(Primitive.CONDITION, condition); parseArgumentsByteCode(condition, dataStream, next, network); id = dataStream.readLong(); if (id == 0) { return equation; } next = network.findById(id); if (next == null) { return equation; } if (bracket) { stack.remove(stack.size() - 1); top = stack.get(stack.size() - 1); while (next.is(Primitive.RIGHTBRACKET)) { stack.remove(stack.size() - 1); top = stack.get(stack.size() - 1); id = dataStream.readLong(); if (id == 0) { return equation; } next = network.findById(id); if (next == null) { return equation; } } } } } while (id > 0) { next = network.findById(id); if (next == null) { return equation; } parseArgumentsByteCode(equation, dataStream, next, network); id = dataStream.readLong(); } return equation; } /** * Print the IF condition and any variables and states that it references. */ public void printCase(Vertex equation, Writer writer, String indent, Set<Vertex> elements, List<Vertex> newVariables, List<Vertex> newEquations, List<Vertex> newStates, Network network) throws IOException { Vertex variable = equation.getRelationship(Primitive.CASE); Vertex pattern = equation.getRelationship(Primitive.PATTERN); Vertex template = equation.getRelationship(Primitive.TEMPLATE); Vertex that = equation.getRelationship(Primitive.THAT); Vertex topic = equation.getRelationship(Primitive.TOPIC); Vertex as = equation.getRelationship(Primitive.AS); List<Relationship> states = equation.orderedRelationships(Primitive.GOTO); List<Relationship> fors = equation.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.EQUATION)) { writer.write("("); } printElement(variable, writer, indent, newEquations, newVariables, elements, network); if (variable.instanceOf(Primitive.EQUATION)) { writer.write(")"); } if (as != null) { writer.write(" as "); printElement(as, writer, indent, newEquations, newVariables, elements, network); } if (topic != null) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("topic "); printElement(topic, writer, indent, newEquations, newVariables, elements, network); } if (that != null) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("that "); printElement(that, writer, indent, newEquations, newVariables, elements, network); } if (template != null) { writer.write("\r\n"); writer.write(indent); writer.write("\t"); writer.write("template "); if (template.instanceOf(Primitive.EQUATION)) { writer.write("("); } printElement(template, writer, indent, newEquations, newVariables, elements, network); if (template.instanceOf(Primitive.EQUATION)) { 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 equation, Writer writer, String indent, Set<Vertex> elements, Network network, long start, long timeout) throws IOException { Vertex state = equation.getRelationship(Primitive.GOTO); List<Vertex> localElements = new ArrayList<Vertex>(); writer.write(indent); writer.write("goto "); if (equation.hasRelationship(Primitive.FINALLY)) { writer.write("finally "); } if (!elements.contains(state)) { localElements.add(state); elements.add(state); } printElement(state, writer, indent, null, null, elements, network); Collection<Relationship> arguments = equation.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, null, null, elements, network); if (iterator.hasNext()) { writer.write(", "); } } writer.write(")"); } writer.write(";\r\n\r\n"); for (Vertex element : localElements) { if (element.instanceOf(Primitive.VARIABLE)) { printVariable(element, writer, indent, elements, network); } else if (element.instanceOf(Primitive.STATE)) { printState(element, writer, indent, elements, network, start, timeout); } } } /** * Print the PUSH condition and any variables and states that it references. */ public void printPush(Vertex equation, Writer writer, String indent, Set<Vertex> elements, Network network, long start, long timeout) throws IOException { Vertex state = equation.getRelationship(Primitive.ARGUMENT); List<Vertex> localElements = new ArrayList<Vertex>(); writer.write(indent); writer.write("push "); if (!elements.contains(state)) { localElements.add(state); elements.add(state); } printElement(state, writer, indent, null, null, elements, network); writer.write(";\r\n\r\n"); for (Vertex element : localElements) { if (element.instanceOf(Primitive.VARIABLE)) { printVariable(element, writer, indent, elements, network); } else if (element.instanceOf(Primitive.STATE)) { printState(element, writer, indent, elements, network, start, timeout); } } } /** * Print the RETURN condition and any variables it references. */ public void printReturn(Vertex equation, Writer writer, String indent, Set<Vertex> elements, Network network, long start, long timeout) throws IOException { Vertex result = equation.getRelationship(Primitive.RETURN); writer.write(indent); writer.write("return"); if (result != null) { writer.write(" "); printElement(result, writer, indent, null, null, elements, network); } Collection<Relationship> arguments = equation.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, null, null, elements, network); if (iterator.hasNext()) { writer.write(", "); } } writer.write(")"); } writer.write(";\r\n\r\n"); } public boolean getPrintIds() { return printIds; } public void setPrintIds(boolean printIds) { this.printIds = printIds; } @Override public String toString() { return getClass().getSimpleName(); } }