/******************************************************************************
*
* 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.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.botlibre.aiml.AIMLParser;
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.Bootstrap;
import org.botlibre.knowledge.Primitive;
import org.botlibre.knowledge.TextData;
import org.botlibre.thought.language.Language;
import org.botlibre.util.TextStream;
import org.botlibre.util.Utils;
/**
* Self scripting language compiler.
* This compiler compiles Self to state, function and expression knowledge objects.
*/
public class Self4Compiler extends SelfCompiler {
public static final String NULL = "null";
public static final String TRUE = "true";
public static final String FALSE = "false";
public static final String UNKNOWN = "unknown";
public static final String BREAK = "break";
public static final String CONTINUE = "continue";
public static final String VAR = "var";
public static final String EQUALS = "equals";
public static final String NOTEQUAL = "not-equal";
public static final String ADD = "add";
public static final String REMOVE = "remove";
public static final String ANY = "any";
public static final String NONE = "none";
public static final String NOT = "!";
public static final String LESSTHAN = "lessthan";
public static final String GREATERTHAN = "greaterthan";
public static final String LESSTHANEQUAL = "lessthanequal";
public static final String GREATERTHANEQUAL = "greaterthanequal";
public static final String SYMBOL = "symbol";
public static final String OBJECT = "object";
public static final String EVALCOPY = "evalcopy";
public static final String DATE = "date";
public static final String TIME = "time";
public static final String BINARY = "binary";
public static final String TEXT = "text";
public static final String TIMESTAMP = "timestamp";
public static final String NUMBER = "number";
public static final String INCREMENT = "increment";
public static final String DECREMENT = "decrement";
public static List<String> OPERATORS;
static {
OPERATORS = new ArrayList<String>();
OPERATORS.add(IF);
OPERATORS.add(WHILE);
OPERATORS.add(FOR);
OPERATORS.add(DO);
OPERATORS.add(NEW);
OPERATORS.add(GOTO);
OPERATORS.add(RETURN);
OPERATORS.add(RANDOM);
OPERATORS.add(DEBUG);
OPERATORS.add(SRAI);
OPERATORS.add(SRAIX);
OPERATORS.add(REDIRECT);
OPERATORS.add(REQUEST);
OPERATORS.add(NOT);
OPERATORS.add(SYMBOL);
OPERATORS.add(THINK);
OPERATORS.add(EVAL);
OPERATORS.add(EVALCOPY);
OPERATORS.add(LEARN);
}
public static Map<String, Primitive> BINARY_OPERATORS;
static {
BINARY_OPERATORS = new HashMap<String, Primitive>();
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);
}
public static List<Primitive> BINARY_PRECEDENCE;
static {
BINARY_PRECEDENCE = new ArrayList<Primitive>();
BINARY_PRECEDENCE.add(Primitive.OR);
BINARY_PRECEDENCE.add(Primitive.AND);
BINARY_PRECEDENCE.add(Primitive.EQUALS);
BINARY_PRECEDENCE.add(Primitive.NOTEQUALS);
BINARY_PRECEDENCE.add(Primitive.GREATERTHAN);
BINARY_PRECEDENCE.add(Primitive.LESSTHAN);
BINARY_PRECEDENCE.add(Primitive.GREATERTHANEQUAL);
BINARY_PRECEDENCE.add(Primitive.LESSTHANEQUAL);
BINARY_PRECEDENCE.add(Primitive.MINUS);
BINARY_PRECEDENCE.add(Primitive.PLUS);
BINARY_PRECEDENCE.add(Primitive.MULTIPLY);
BINARY_PRECEDENCE.add(Primitive.DIVIDE);
}
public static List<String> TYPES;
static {
TYPES = new ArrayList<String>();
TYPES.add(STATE);
TYPES.add(VARIABLE);
TYPES.add(VERTEX);
TYPES.add(VAR);
TYPES.add(FUNCTION);
TYPES.add(TEMPLATE);
TYPES.add(PATTERN);
TYPES.add(OBJECT);
TYPES.add(DATE);
TYPES.add(TIME);
TYPES.add(TIMESTAMP);
TYPES.add(BINARY);
TYPES.add(TEXT);
}
@Override
public int getVersion() {
return 4;
}
/**
* Parse the code into a vertex state machine defined in the network.
*/
@Override
public Vertex parseStateMachine(String code, boolean debug, Network network) {
TextStream stream = new TextStream(code);
try {
Map<String, Map<String, Vertex>> elements = buildElementsMap(network);
List<String> comments = getComments(stream);
stream.skipWhitespace();
Vertex state = null;
if (stream.peek(6).equalsIgnoreCase("state:")) {
state = new SelfCompiler().parseState(stream, elements, debug, network);
state.addRelationship(Primitive.LANGUAGE, network.createVertex(Primitive.SELF));
state.addRelationship(Primitive.LANGUAGE, network.createVertex(Primitive.SELF2));
} else {
state = parseState(stream, elements, debug, network);
state.addRelationship(Primitive.LANGUAGE, network.createVertex(Primitive.SELF));
state.addRelationship(Primitive.LANGUAGE, network.createVertex(Primitive.SELF4));
}
if (debug) {
for (String comment : comments) {
state.addRelationship(Primitive.COMMENT, network.createVertex(comment), Integer.MAX_VALUE);
}
}
TextData text = new TextData();
text.setText(code);
Vertex sourceCode = network.createVertex(text);
sourceCode.setPinned(true);
state.addRelationship(Primitive.SOURCECODE, sourceCode);
network.getBot().log(this, "Compiled new state machine", Level.INFO, state);
return state;
} catch (SelfParseException exception) {
throw exception;
} catch (Exception exception) {
network.getBot().log(this, exception);
throw new SelfParseException("Parsing error occurred", stream, exception);
}
}
/**
* Parse and evaluate the code.
*/
public Vertex evaluateExpression(String code, Vertex speaker, Vertex target, boolean debug, Network network) {
Vertex expression = parseExpressionForEvaluation(code, speaker, target, debug, network);
Map<Vertex, Vertex> variables = new HashMap<Vertex, Vertex>();
return SelfInterpreter.getInterpreter().evaluateExpression(expression, variables, network, System.currentTimeMillis(), Language.MAX_STATE_PROCESS, 0);
}
/**
* Parse the code into a temporary expression so it can be evaluated.
*/
public Vertex parseExpressionForEvaluation(String code, Vertex speaker, Vertex target, boolean debug, Network network) {
TextStream stream = new TextStream(code);
try {
Map<String, Map<String, Vertex>> elements = new HashMap<String, Map<String, Vertex>>();
elements.put(VARIABLE, new HashMap<String, Vertex>());
elements.get(VARIABLE).put("speaker", speaker);
elements.get(VARIABLE).put("target", target);
elements.put(FUNCTION, new HashMap<String, Vertex>());
getComments(stream);
// Create a temporary equation to execute.
Vertex expression = network.createTemporyVertex();
expression.addRelationship(Primitive.INSTANTIATION, Primitive.EXPRESSION);
expression.addRelationship(Primitive.OPERATOR, Primitive.DO);
stream.skipWhitespace();
Vertex element = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.DO, element, Integer.MAX_VALUE);
stream.skipWhitespace();
while (stream.peek() == ';') {
stream.skip();
stream.skipWhitespace();
if (stream.atEnd()) {
break;
}
element = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.DO, element, Integer.MAX_VALUE);
stream.skipWhitespace();
}
if (!stream.atEnd()) {
throw new SelfParseException("Unexpect element " + stream.peekWord(), stream);
}
network.getBot().log(this, "Compiled new expression for evaluation", Level.INFO, expression);
return expression;
} catch (SelfParseException exception) {
throw exception;
} catch (Exception exception) {
network.getBot().log(this, exception);
throw new SelfParseException("Parsing error occurred", stream, exception);
}
}
@Override
public List<String> getComments(TextStream stream) {
List<String> comments = new ArrayList<String>();
while (!stream.atEnd()) {
stream.skipWhitespace();
String comment = stream.peek(2);
if (comment.equals("//")) {
comments.add(stream.nextLine());
} else if (comment.equals("/*")) {
comments.add(stream.upToAll("*/", true) + "\n");
} else {
return comments;
}
}
return comments;
}
/**
* Parse the state and any referenced states or variables.
*/
@Override
public Vertex parseState(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
List<String> comments = null;
String next = stream.nextWord();
if (next == null || !next.equalsIgnoreCase("state")) {
throw new SelfParseException("Expecting state not: " + next, stream);
}
Vertex state = parseElementName(Primitive.STATE, stream, elements, debug, network);
if (!elements.containsKey("root")) {
HashMap<String, Vertex> root = new HashMap<String, Vertex>(1);
root.put("root", state);
elements.put("root", root);
}
stream.skipWhitespace();
ensureNext('{', stream);
stream.skipWhitespace();
String element = stream.peekWord();
while (!("}".equals(element))) {
if (element == null) {
throw new SelfParseException("Unexpected end of state, missing '}'", stream);
}
Vertex vertex = state;
element = element.toLowerCase();
if (element.equals(CASE)) {
vertex = parseCase(stream, elements, debug, network);
state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE);
} else if (element.equals(PATTERN)) {
vertex = parsePattern(stream, elements, debug, network);
} else if (element.equals(STATE)) {
vertex = parseState(stream, elements, debug, network);
} else if (element.equals(VAR) || element.equals(VARIABLE)) {
vertex = parseVariable(stream, elements, debug, network);
} else if (element.equals(ANSWER)) {
parseAnswer(state, stream, elements, debug, network);
} else if (element.equals(FUNCTION) || element.equals(EQUATION)) {
vertex = parseFunction(stream, elements, debug, network);
} else if (element.equals(DO)) {
vertex = network.createInstance(Primitive.DO);
Vertex expression = parseOperator(stream, elements, debug, network);
vertex.addRelationship(Primitive.DO, expression, Integer.MAX_VALUE);
state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE);
} else if (element.equals(GOTO)) {
vertex = parseGoto(stream, elements, debug, network);
state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE);
ensureNext(';', stream);
} else if (element.equals(PUSH)) {
vertex = parsePush(stream, elements, debug, network);
state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE);
ensureNext(';', stream);
} else if (element.equals(RETURN)) {
vertex = parseReturn(stream, elements, debug, network);
state.addRelationship(Primitive.DO, vertex, Integer.MAX_VALUE);
ensureNext(';', stream);
} else if (element.equals("/")) {
comments = getComments(stream);
if (comments.isEmpty()) {
throw new SelfParseException("Unknown element: " + element, stream);
}
vertex = null; // Associate the comments with the next element parsed.
} else {
throw new SelfParseException("Unknown element: " + element, stream);
}
if (debug && (comments != null) && (vertex != null)) {
for (String comment : comments) {
vertex.addRelationship(Primitive.COMMENT, network.createVertex(comment), Integer.MAX_VALUE);
}
comments = null;
}
stream.skipWhitespace();
element = stream.peekWord();
}
ensureNext('}', stream);
return state;
}
/**
* Parse the quotient.
* answer:0.5 "World" { previous "Hello"; previous ! "Hi"; }
*/
public void parseAnswer(Vertex state, TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
stream.skipWhitespace();
float correctness = 1.0f;
if (stream.peek() == ':') {
stream.skip();
stream.skipWhitespace();
if (Character.isDigit(stream.peek())) {
String correctnessText = stream.nextWord();
try {
correctness = Float.valueOf(correctnessText);
} catch (NumberFormatException exception) {
throw new SelfParseException("Invalid correctness: " + correctnessText, stream);
}
}
}
Vertex value = parseElement(stream, elements, debug, network);
Relationship relationship = state.addWeakRelationship(Primitive.QUOTIENT, value, correctness);
stream.skipWhitespace();
if (stream.peek() == '{') {
Vertex meta = network.createMeta(relationship);
stream.skip();
String next = stream.nextWord();
while (!("}".equals(next))) {
if (next == null) {
throw new SelfParseException("Unexpected end of quotient, missing '}'", stream);
}
next = next.toLowerCase();
if (!(PREVIOUS.equals(next))) {
throw new SelfParseException("Unexpected word: '" + next + "' expected 'PREVIOUS'", stream);
}
boolean not = false;
next = stream.peekWord();
if (NOT.equals(next)) {
not = true;
stream.nextWord();
}
Vertex previous = parseElement(stream, elements, debug, network);
ensureNext(';', stream);
if (not) {
meta.removeRelationship(Primitive.PREVIOUS, previous);
} else {
meta.addRelationship(Primitive.PREVIOUS, previous);
}
next = stream.nextWord();
}
}
ensureNext(';', stream);
}
/**
* Parse the reference to either a state, variable, expression, or data.
* One of,
* state:1234:name, variable:1234:name, function:1234:name,
* var variable = "value", variable.attribute = "value", function(), function(arg, arg2).attribute = variable
* if (value == "value") {}, for () {}, new (class1, class2), variable.function()
* 1234, "string", 'string', #primitive, DATE("1972,01,01"), ...
*/
@Override
public Vertex parseElement(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
return parseElement(stream, elements, null, debug, network);
}
/**
* Parse the reference to either a state, variable, expression, or data.
* One of,
* state:1234:name, variable:1234:name, function:1234:name,
* var variable = "value", variable.attribute = "value", function(), function(arg, arg2).attribute = variable
* if (value == "value") {}, for () {}, new (class1, class2), variable.function()
* 1234, "string", 'string', #primitive, DATE("1972,01,01"), ...
*/
public Vertex parseElement(TextStream stream, Map<String, Map<String, Vertex>> elements, Primitive lastBinary, boolean debug, Network network) {
List<String> comments = getComments(stream);
stream.skipWhitespace();
int brackets = 0;
while (stream.peek() == '(') {
lastBinary = null;
brackets++;
stream.skip();
stream.skipWhitespace();
}
Vertex expression = null;
String source = "";
int lineNumber = 0;
if (debug) {
source = stream.currentLine();
lineNumber = stream.currentLineNumber();
}
if (stream.peek() == '[') {
stream.skip();
// Parse array.
Vertex array = network.createInstance(Primitive.ARRAY);
stream.skipWhitespace();
if (stream.peek() == ']') {
stream.skip();
array.addRelationship(Primitive.LENGTH, network.createVertex(0));
return array;
}
boolean more = true;
int index = 0;
while (more) {
Vertex element = parseElement(stream, elements, debug, network);
array.addRelationship(Primitive.ELEMENT, element, index);
index++;
stream.skipWhitespace();
if (stream.peek() == ',') {
stream.skip();
} else {
more = false;
}
}
stream.skipWhitespace();
ensureNext(']', stream);
// Need to evaluate expressions inside the object.
expression = network.createInstance(Primitive.EXPRESSION);
Vertex operator = network.createVertex(new Primitive(EVALCOPY));
expression.addRelationship(Primitive.OPERATOR, operator);
expression.setName(EVALCOPY);
expression.addRelationship(Primitive.ARGUMENT, array);
} else if (stream.peek() == '{') {
stream.skip();
// Parse object.
Vertex object = null;
stream.skipWhitespace();
if (stream.peek() == '}') {
stream.skip();
return object;
}
boolean more = true;
while (more) {
String attribute = stream.nextWord();
if (attribute.equals("\"")) {
attribute = stream.nextWord();
ensureNext('"', stream);
}
ensureNext(':', stream);
Vertex element = parseElement(stream, elements, debug, network);
if (object == null) {
if (attribute.equals("#data")) {
object = element;
} else {
object = network.createVertex();
}
}
object.addRelationship(new Primitive(attribute), element);
stream.skipWhitespace();
if (stream.peek() == ',') {
stream.skip();
} else {
more = false;
}
}
if (object == null) {
object = network.createVertex();
}
stream.skipWhitespace();
ensureNext('}', stream);
// Need to evaluate expressions inside the object.
expression = network.createInstance(Primitive.EXPRESSION);
Vertex operator = network.createVertex(new Primitive(EVALCOPY));
expression.addRelationship(Primitive.OPERATOR, operator);
expression.setName(EVALCOPY);
expression.addRelationship(Primitive.ARGUMENT, object);
} else {
// Check if reference or data.
String token = stream.peekWord();
if (token == null) {
throw new SelfParseException("Unexpected end, element expected", stream);
}
token = token.toLowerCase();
if (token.equals(VAR)) {
token = VARIABLE;
}
if (OPERATORS.contains(token)) {
expression = parseOperator(stream, elements, debug, network);
} else if (token.equals("^")) {
stream.nextWord();
expression = parseElementName(Primitive.VARIABLE, stream, elements, debug, network);
Vertex meaning = network.createInstance(Primitive.VARIABLE);
meaning.addRelationship(Primitive.INSTANTIATION, new Primitive(expression.getName()));
expression.addRelationship(Primitive.MEANING, meaning);
} else if (TYPES.contains(token)) {
stream.nextWord();
if (token.equals(TEMPLATE)) {
stream.skipWhitespace();
ensureNext('(', stream);
expression = parseTemplate(null, stream, elements, debug, network);
stream.skipWhitespace();
ensureNext(')', stream);
} else if (token.equals(PATTERN)) {
stream.skipWhitespace();
ensureNext('(', stream);
ensureNext('"', stream);
expression = network.createPattern(stream.nextStringWithBracketsDoubleQuotes(), this);
stream.skipWhitespace();
ensureNext(')', stream);
} else if (token.equals(VARIABLE)) {
expression = parseElementName(Primitive.VARIABLE, stream, elements, debug, network);
} else if (token.equals(TIME)) {
stream.skipWhitespace();
ensureNext('(', stream);
ensureNext('"', stream);
String value = stream.nextQuotesExcludeDoubleQuote();
expression = network.createVertex(Utils.parseTime(value));
stream.skipWhitespace();
ensureNext(')', stream);
} else if (token.equals(DATE)) {
stream.skipWhitespace();
ensureNext('(', stream);
ensureNext('"', stream);
String value = stream.nextQuotesExcludeDoubleQuote();
expression = network.createVertex(Utils.parseDate(value));
stream.skipWhitespace();
ensureNext(')', stream);
} else if (token.equals(TIMESTAMP)) {
stream.skipWhitespace();
ensureNext('(', stream);
ensureNext('"', stream);
String value = stream.nextQuotesExcludeDoubleQuote();
expression = network.createVertex(Utils.parseTimestamp(value));
stream.skipWhitespace();
ensureNext(')', stream);
} else if (token.equals(BINARY)) {
stream.skipWhitespace();
ensureNext('(', stream);
ensureNext('"', stream);
String value = stream.nextQuotesExcludeDoubleQuote();
expression = network.createVertex(new BinaryData(value));
stream.skipWhitespace();
ensureNext(')', stream);
} else if (token.equals(TEXT)) {
stream.skipWhitespace();
ensureNext('(', stream);
ensureNext('"', stream);
String value = stream.nextQuotesExcludeDoubleQuote();
expression = network.createVertex(new TextData(value));
stream.skipWhitespace();
ensureNext(')', stream);
} else {
stream.skipWhitespace();
if (stream.peek() != '(') {
throw new SelfParseException("Expected '(' in " + token + " declaration", stream);
}
stream.skip();
Long id = null;
// Check for id or name.
if (Character.isDigit(stream.peek())) {
String idText = stream.nextWord();
try {
id = Long.valueOf(idText);
} catch (NumberFormatException exception) {
throw new SelfParseException("Invalid " + token + " id: " + idText, stream);
}
}
char peek = stream.peek();
String name = null;
if ((id == null) || (peek == ':')) {
if (id != null) {
stream.skip();
}
name = stream.nextWord();
if (name != null && Character.isLetter(name.charAt(0))) {
throw new SelfParseException("Invalid " + token + " declaration: " + name, stream);
}
}
Map<String, Vertex> elementsForType = elements.get(token);
if (name != null) {
if (elementsForType != null) {
expression = elementsForType.get(name);
}
}
if (expression == null) {
if (id != null) {
expression = network.findById(id);
if (expression == null) {
throw new SelfParseException("Id element reference not found: " + id, stream);
}
if ((elementsForType != null) && (name != null)) {
elementsForType.put(name, expression);
}
} else if (name != null) {
if (token.equals(STATE)) {
expression = network.createInstance(Primitive.STATE);
expression.setName(name);
} else if (token.equals(VARIABLE)) {
expression = network.createInstance(Primitive.VARIABLE);
expression.setName(name);
} else if (token.equals(FUNCTION)) {
expression = network.createInstance(Primitive.FUNCTION);
expression.setName(name);
} else {
throw new SelfParseException("Invalid element: " + token, stream);
}
if (name != null) {
elementsForType = elements.get(token);
if (elementsForType != null) {
elementsForType.put(name, expression);
}
}
} else {
throw new SelfParseException("Invalid element: " + token, stream);
}
}
stream.skipWhitespace();
ensureNext(')', stream);
}
} else {
char next = stream.peek();
try {
if (next == '#') {
stream.skip();
String data = stream.upToAny(PRIMITIVE_TOKENS);
expression = network.createVertex(new Primitive(data));
} else if (next == '"') {
stream.skip();
String data = stream.nextStringDoubleQuotes();
data = data.replace("\\\"", "\"");
expression = network.createVertex(data);
} else if (next == '\'') {
stream.skip();
String data = stream.nextStringQuotes();
data = data.replace("\\'", "'");
expression = network.createVertex(data);
} else if (Character.isDigit(next) || next == '-' || next == '+') {
int position = stream.getPosition();
String data = stream.nextWord();
if (data.indexOf(',') != -1) {
stream.setPosition(position);
data = stream.upTo(',');
}
int index = data.indexOf('.');
if ((index != -1) && (index + 1 < data.length())) {
// Check for 4.next
if (!Character.isDigit(data.charAt(index + 1))) {
stream.setPosition(position);
data = stream.upTo('.');
}
}
if (index != -1) {
expression = network.createVertex(new BigDecimal(data));
} else {
expression = network.createVertex(new BigInteger(data));
}
} else {
expression = parseElementName(null, stream, elements, debug, network);
}
} catch (SelfParseException exception) {
throw exception;
} catch (Exception exception) {
throw new SelfParseException("Invalid data: " + next, stream, exception);
}
}
}
stream.skipWhitespace();
char peek = stream.peek();
while (".=!&|)[<>+-*/".indexOf(peek) != -1) {
String operator1 = stream.peek(1);
String operator = stream.peek(2);
if (peek == ')') {
if (brackets > 0) {
brackets--;
stream.skip();
} else {
break;
}
} else if (peek == '.') {
stream.skip();
int position = stream.getPosition();
String attribute = stream.nextWord();
if (!Character.isAlphabetic(attribute.charAt(0)) && attribute.charAt(0) != '@') {
throw new SelfParseException("Invalid attribute name: " + attribute, stream);
}
if (attribute.indexOf('.') != -1) {
stream.setPosition(position);
stream.skipWhitespace();
attribute = stream.upTo('.');
}
Vertex newExpression = network.createInstance(Primitive.EXPRESSION);
stream.skipWhitespace();
Vertex associate = null;
Vertex associateRelationship = null;
peek = stream.peek();
if (peek == '(') {
newExpression.addRelationship(Primitive.OPERATOR, Primitive.CALL);
newExpression.setName(CALL);
newExpression.addRelationship(Primitive.THIS, expression, 0);
newExpression.addRelationship(Primitive.FUNCTION, network.createVertex(new Primitive(attribute)), 1);
parseArguments(newExpression, Primitive.ARGUMENT, 0, stream, elements, false, debug, network);
} else {
if (peek == '[') {
stream.skip();
Vertex index = parseElement(stream, elements, debug, network);
stream.skipWhitespace();
if (stream.peek() == ',') {
associate = index;
associateRelationship = parseElement(stream, elements, debug, network);
} else {
newExpression.addRelationship(Primitive.INDEX, index);
}
ensureNext(']', stream);
peek = stream.peek();
}
boolean isSet = false;
String peek2 = stream.peek(2);
if (peek == '=' && peek2.equals("=+")) {
isSet = true;
stream.skip(2);
newExpression.addRelationship(Primitive.OPERATOR, Primitive.ADD);
newExpression.setName(ADD);
} else if (peek == '=' && peek2.equals("=-")) {
isSet = true;
stream.skip(2);
newExpression.addRelationship(Primitive.OPERATOR, Primitive.REMOVE);
newExpression.setName(REMOVE);
} else if (peek == '=' && !peek2.equals("==")) {
isSet = true;
stream.skip();
newExpression.addRelationship(Primitive.OPERATOR, Primitive.SET);
newExpression.setName(SET);
} else {
newExpression.addRelationship(Primitive.OPERATOR, Primitive.GET);
newExpression.setName(GET);
}
newExpression.addRelationship(Primitive.ARGUMENT, expression, 0);
newExpression.addRelationship(Primitive.ARGUMENT, network.createVertex(new Primitive(attribute)), 1);
if (isSet) {
Vertex argument = parseElement(stream, elements, debug, network);
newExpression.addRelationship(Primitive.ARGUMENT, argument, 2);
}
if (associate != null) {
newExpression.addRelationship(Primitive.ARGUMENT, associate, 3);
newExpression.addRelationship(Primitive.ARGUMENT, associateRelationship, 4);
}
}
expression = newExpression;
} else if (peek == '[') {
stream.skip();
Vertex newExpression = network.createInstance(Primitive.EXPRESSION);
Vertex variable = parseElement(stream, elements, debug, network);
stream.skipWhitespace();
ensureNext(']', stream);
stream.skipWhitespace();
peek = stream.peek();
if (peek == '(') {
newExpression.addRelationship(Primitive.OPERATOR, Primitive.CALL);
newExpression.setName(CALL);
newExpression.addRelationship(Primitive.THIS, expression, 0);
newExpression.addRelationship(Primitive.FUNCTION, variable, 1);
parseArguments(newExpression, Primitive.ARGUMENT, 0, stream, elements, false, debug, network);
} else {
boolean isSet = false;
String peek2 = stream.peek(2);
if (peek == '=' && peek2.equals("=+")) {
isSet = true;
stream.skip(2);
newExpression.addRelationship(Primitive.OPERATOR, Primitive.ADD);
newExpression.setName(ADD);
} else if (peek == '=' && peek2.equals("=-")) {
isSet = true;
stream.skip(2);
newExpression.addRelationship(Primitive.OPERATOR, Primitive.REMOVE);
newExpression.setName(REMOVE);
} else if (stream.peek() == '=' && !stream.peek(2).equals("==")) {
isSet = true;
stream.skip();
newExpression.addRelationship(Primitive.OPERATOR, Primitive.SET);
newExpression.setName(SET);
} else {
newExpression.addRelationship(Primitive.OPERATOR, Primitive.GET);
newExpression.setName(GET);
}
newExpression.addRelationship(Primitive.ARGUMENT, expression, 0);
newExpression.addRelationship(Primitive.ARGUMENT, variable, 1);
if (isSet) {
Vertex argument = parseElement(stream, elements, debug, network);
newExpression.addRelationship(Primitive.ARGUMENT, argument, 2);
}
}
expression = newExpression;
} else {
Primitive operation = BINARY_OPERATORS.get(operator);
Primitive operation1 = null;
if (operation == null) {
operation1 = BINARY_OPERATORS.get(operator1);
}
if (operator.equals("//")) {
break;
} else if (operator.equals("++")) {
stream.skip(2);
Vertex newExpression = network.createInstance(Primitive.EXPRESSION);
newExpression.addRelationship(Primitive.OPERATOR, Primitive.INCREMENT);
newExpression.setName(INCREMENT);
newExpression.addRelationship(Primitive.ARGUMENT, expression, 0);
expression = newExpression;
} else if (operator.equals("--")) {
stream.skip(2);
Vertex newExpression = network.createInstance(Primitive.EXPRESSION);
newExpression.addRelationship(Primitive.OPERATOR, Primitive.DECREMENT);
newExpression.setName(DECREMENT);
newExpression.addRelationship(Primitive.ARGUMENT, expression, 0);
expression = newExpression;
} else if (operation != null || operation1 != null) {
if (lastBinary != null) {
// Check order of binary operations.
int lastIndex = BINARY_PRECEDENCE.indexOf(lastBinary);
int index = 0;
if (operation == null) {
index = BINARY_PRECEDENCE.indexOf(operation1);
} else {
index = BINARY_PRECEDENCE.indexOf(operation);
}
if (index <= lastIndex) {
break;
}
}
if (operation == null) {
stream.skip();
operator = operator1;
operation = operation1;
} else {
stream.skip(2);
}
Vertex newExpression = network.createInstance(Primitive.EXPRESSION);
newExpression.addRelationship(Primitive.OPERATOR, operation);
newExpression.setName(operator);
newExpression.addRelationship(Primitive.ARGUMENT, expression, 0);
Vertex argument = parseElement(stream, elements, operation, debug, network);
newExpression.addRelationship(Primitive.ARGUMENT, argument, 1);
expression = newExpression;
} else if (peek == '=' && expression.isVariable()) {
stream.skip();
Vertex newExpression = network.createInstance(Primitive.EXPRESSION);
newExpression.addRelationship(Primitive.OPERATOR, Primitive.ASSIGN);
newExpression.setName(ASSIGN);
newExpression.addRelationship(Primitive.ARGUMENT, expression, 0);
Vertex argument = parseElement(stream, elements, null, debug, network);
newExpression.addRelationship(Primitive.ARGUMENT, argument, 1);
expression = newExpression;
} else {
throw new SelfParseException("Invalid operator: " + operator, stream);
}
}
stream.skipWhitespace();
peek = stream.peek();
}
stream.skipWhitespace();
while (brackets > 0) {
stream.skipWhitespace();
ensureNext(')', stream);
brackets--;
}
if (debug && expression.instanceOf(Primitive.EXPRESSION)) {
for (String comment : comments) {
expression.addRelationship(Primitive.COMMENT, network.createVertex(comment));
}
expression.addRelationship(Primitive.SOURCE, network.createVertex(source));
expression.addRelationship(Primitive.LINE_NUMBER, network.createVertex(lineNumber));
}
return expression;
}
/**
* Parse the element name (state, function, variable)
*/
public Vertex parseElementName(Primitive type, TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.skipWhitespace();
int position = stream.getPosition();
String name = stream.nextWord();
if (name.indexOf('.') != -1) {
stream.setPosition(position);
name = stream.upTo('.');
}
if ("}".equals(name)) {
stream.previous();
return network.createVertex(Primitive.NULL);
}
if (name == null || !Character.isAlphabetic(name.charAt(0))) {
throw new SelfParseException("Invalid element: " + name, stream);
}
if (name.equalsIgnoreCase(NULL)) {
return network.createVertex(Primitive.NULL);
} else if (name.equalsIgnoreCase(TRUE)) {
return network.createVertex(Primitive.TRUE);
} else if (name.equalsIgnoreCase(FALSE)) {
return network.createVertex(Primitive.FALSE);
} else if (name.equalsIgnoreCase(UNKNOWN)) {
return network.createVertex(Primitive.UNKNOWN);
} else if (name.equalsIgnoreCase(BREAK)) {
return network.createVertex(Primitive.BREAK);
} else if (name.equalsIgnoreCase(CONTINUE)) {
return network.createVertex(Primitive.CONTINUE);
}
Long id = null;
if (stream.peek() == ':') {
// Check for id.
if (Character.isDigit(stream.peek())) {
String idText = stream.nextWord();
try {
id = Long.valueOf(idText);
} catch (NumberFormatException exception) {
throw new SelfParseException("Invalid id: " + idText, stream);
}
}
}
if (type == null) {
if (stream.peek() == '(') {
type = Primitive.FUNCTION;
} else {
// By default assume capitalized words are global primitives, lower case are variables.
if (Character.isUpperCase(name.charAt(0))) {
Map<String, Vertex> elementsForType = elements.get(Primitive.VARIABLE.getIdentity());
Vertex variable = elementsForType.get(name);
if (variable != null) {
return variable;
}
return network.createVertex(new Primitive(name.toLowerCase()));
}
type = Primitive.VARIABLE;
}
}
String typeName = type.getIdentity();
Vertex vertex = null;
Map<String, Vertex> elementsForType = elements.get(typeName);
if (name != null) {
if (elementsForType != null) {
vertex = elementsForType.get(name);
}
}
if (vertex == null && id != null) {
vertex = network.findById(id);
if (vertex == null) {
throw new SelfParseException("Id element reference not found: " + id, stream);
}
if ((elementsForType != null) && (name != null)) {
elementsForType.put(name, vertex);
}
return vertex;
}
if (vertex == null) {
vertex = network.createInstance(type);
vertex.setName(name);
if (name != null) {
elementsForType = elements.get(typeName);
if (elementsForType != null) {
elementsForType.put(name, vertex);
}
}
}
if (type == Primitive.FUNCTION) {
if (stream.peek() == '(') {
position = stream.getPosition();
stream.skip();
stream.skipWhitespace();
if (stream.peek() != ')') {
stream.setPosition(position);
parseArguments(vertex, Primitive.ARGUMENT, 1, stream, elements, false, debug, network);
} else {
stream.skip();
}
}
}
return vertex;
}
/**
* Parse the variable.
*/
@Override
public Vertex parseVariable(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
Vertex variable = parseElementName(Primitive.VARIABLE, stream, elements, debug, network);
stream.skipWhitespace();
ensureNext('{', stream);
stream.skipWhitespace();
while (stream.peek() != '}') {
if (stream.atEnd()) {
throw new SelfParseException("Unexpected end of variable, missing '}'", stream);
}
if (stream.peek() == ':') {
stream.skip();
boolean more = true;
while (more) {
boolean not = false;
stream.skipWhitespace();
if (stream.peek() == '!') {
not = true;
stream.next();
}
Vertex value = parseElement(stream, elements, debug, network);
if (not) {
variable.removeRelationship(Primitive.EQUALS, value);
} else {
variable.addRelationship(Primitive.EQUALS, value);
}
stream.skipWhitespace();
more = stream.peek() == ',';
if (more) {
stream.next();
}
}
} else {
String name = stream.nextWord();
if (name == null || !Character.isAlphabetic(name.charAt(0))) {
throw new SelfParseException("Invalid variable attribute: " + name, stream);
}
Vertex attribute = network.createVertex(new Primitive(name));
stream.skipWhitespace();
ensureNext(':', stream);
boolean more = true;
while (more) {
boolean not = false;
stream.skipWhitespace();
if (stream.peek() == '!') {
not = true;
stream.skip();
}
Vertex value = parseElement(stream, elements, debug, network);
if (not) {
variable.removeRelationship(attribute, value);
} else {
variable.addRelationship(attribute, value);
}
stream.skipWhitespace();
more = stream.peek() == ',';
if (more) {
stream.skip();
stream.skipWhitespace();
}
}
}
stream.skipWhitespace();
ensureNext(';', stream);
stream.skipWhitespace();
}
ensureNext('}', stream);
return variable;
}
/**
* Parse the arguments to the expression.
*/
@Override
protected List<Vertex> parseArguments(Vertex expression, Primitive type, int index, TextStream stream, Map<String, Map<String, Vertex>> elements, boolean outerBracket, boolean debug, Network network) {
List<Vertex> arguments = new ArrayList<Vertex>();
boolean bracket = false;
if (!outerBracket) {
bracket = checkNext('(', stream);
}
boolean moreArguments = true;
stream.skipWhitespace();
char peek = stream.peek();
if (peek == ')') {
moreArguments = false;
}
while (moreArguments) {
stream.skipWhitespace();
peek = stream.peek();
if (peek == ')' || peek == '}') {
break;
}
if ((peek == ',') || (peek == ';')) {
break;
}
Vertex argument = parseElement(stream, elements, debug, network);
arguments.add(argument);
expression.addRelationship(type, argument, index);
if (!bracket && !outerBracket) {
break;
}
stream.skipWhitespace();
peek = stream.peek();
if ((peek == ',') || (peek == ';')) {
stream.skip();
} else {
String previous = stream.peekPreviousWord();
if (!"}".equals(previous)) {
moreArguments = false;
}
}
index++;
}
if (bracket) {
ensureNext(')', stream);
}
return arguments;
}
/**
* Parse the function.
*/
public Vertex parseFunction(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
Vertex function = parseElementName(Primitive.FUNCTION, stream, elements, debug, network);
stream.skipWhitespace();
ensureNext('{', stream);
function.addRelationship(Primitive.OPERATOR, new Primitive(function.getName()));
stream.skipWhitespace();
char peek = stream.peek();
int index = 0;
while (peek != '}') {
stream.skipWhitespace();
Vertex element = parseElement(stream, elements, debug, network);
function.addRelationship(Primitive.DO, element, index);
String previous = stream.peekPreviousWord();
stream.skipWhitespace();
if (!"}".equals(previous)) {
ensureNext(';', ',', stream);
}
stream.skipWhitespace();
peek = stream.peek();
index++;
}
ensureNext('}', stream);
return function;
}
/**
* Parse the template.
*/
@Override
public Vertex parseTemplate(Vertex formula, TextStream stream, boolean debug, Network network) {
Map<String, Map<String, Vertex>> elements = buildElementsMap(network);
return parseTemplate(formula, stream, elements, debug, network);
}
@Override
public Map<String, Map<String, Vertex>> buildElementsMap(Network network) {
Map<String, Map<String, Vertex>> elements = new HashMap<String, Map<String, Vertex>>();
Map<String, Vertex> variables = new HashMap<String, Vertex>();
elements.put(VARIABLE, variables);
elements.put(STATE, new HashMap<String, Vertex>());
elements.put(FUNCTION, new HashMap<String, Vertex>());
elements.put(EQUATION, new HashMap<String, Vertex>());
elements.put(FORMULA, new HashMap<String, Vertex>());
Vertex input = network.createVertex(Primitive.INPUT_VARIABLE);
Bootstrap.checkInputVariable(input, network);
variables.put("input", input);
variables.put("star", network.createVertex(Primitive.WILDCARD));
variables.put("thatstar", network.createVertex(Primitive.THATWILDCARD));
variables.put("topicstar", network.createVertex(Primitive.TOPICWILDCARD));
variables.put("speaker", input.getRelationship(Primitive.SPEAKER));
variables.put("target", input.getRelationship(Primitive.TARGET));
variables.put("sentence", input.getRelationship(Primitive.INPUT));
variables.put("conversation", input.getRelationship(Primitive.CONVERSATION));
return elements;
}
/**
* Parse the template.
*/
@Override
public Vertex parseTemplate(Vertex formula, TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
String name = "Template(";
stream.skipWhitespace();
ensureNext('"', stream);
int position = stream.getPosition();
String text = stream.nextStringWithBracketsDoubleQuotes();
Map<String, Vertex> cache = elements.get(FORMULA);
if (formula == null && cache != null) {
formula = cache.get(text);
if (formula != null) {
return formula;
}
}
try {
TextStream formulaStream = new TextStream(text);
if (formula == null) {
formula = network.createInstance(Primitive.FORMULA);
}
if (cache != null) {
cache.put(text, formula);
}
String token = formulaStream.nextWord();
char peek = formulaStream.peek();
int index = 0;
Vertex space = network.createVertex(Primitive.SPACE);
formula.addRelationship(Primitive.TYPE, space);
while ((token != null) && ((!token.equals("\\") || (peek == '"')))) {
Vertex word = null;
if (token.equals("\\") && (peek == '"')) {
token = formulaStream.nextWord();
} else if (token.endsWith("\\") && (peek == '"')) {
token = token.substring(0, token.length() - 1);
}
if (token.equals("{")) {
word = parseElement(formulaStream, elements, debug, network);
formulaStream.skipWhitespace();
if (formulaStream.peek() == ';') {
Vertex expression = network.createVertex();
expression.addRelationship(Primitive.INSTANTIATION, Primitive.EXPRESSION);
expression.addRelationship(Primitive.OPERATOR, Primitive.DO);
expression.addRelationship(Primitive.DO, word, Integer.MAX_VALUE);
while (formulaStream.peek() == ';') {
formulaStream.skip();
formulaStream.skipWhitespace();
if (formulaStream.peek() == '}') {
break;
}
word = parseElement(formulaStream, elements, debug, network);
expression.addRelationship(Primitive.DO, word, Integer.MAX_VALUE);
formulaStream.skipWhitespace();
}
word = expression;
}
ensureNext('}', formulaStream);
} else {
word = network.createWord(token);
}
formula.addRelationship(Primitive.WORD, word, index);
if (formulaStream.skipWhitespace()) {
index++;
formula.addRelationship(Primitive.WORD, space, index);
}
token = formulaStream.nextWord();
peek = formulaStream.peek();
index++;
}
} catch (SelfParseException exception) {
int newPosition = stream.getPosition();
stream.setPosition(position);
int column = exception.getColumnNumber();
exception.initFromStream(stream);
exception.setColumnNumber(position + column);
stream.setPosition(newPosition);
throw exception;
}
formula.setName(name + "\"" + text + "\")");
return formula;
}
/**
* Parse the operator.
*/
@Override
public Vertex parseOperator(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
Vertex expression = network.createInstance(Primitive.EXPRESSION);
String next = stream.nextWord();
next = next.toLowerCase();
if (!OPERATORS.contains(next)) {
throw new SelfParseException("Invalid operator: '" + next + "' valid operators are: " + OPERATORS, stream);
}
String last = next.toLowerCase();
if (next.equals(NOT)) {
next = "not";
}
Vertex operator = network.createVertex(new Primitive(next));
expression.addRelationship(Primitive.OPERATOR, operator);
expression.setName(next);
if (last.equals(IF)) {
List<Vertex> arguments = parseArguments(expression, Primitive.ARGUMENT, 0, stream, elements, false, debug, network);
ensureArguments(IF, 1, arguments, stream);
stream.skipWhitespace();
ensureNext('{', stream);
parseArguments(expression, Primitive.THEN, 0, stream, elements, true, debug, network);
stream.skipWhitespace();
ensureNext('}', stream);
next = lower(stream.peekWord());
boolean elseif = true;
while (elseif) {
elseif = false;
if (ELSE.equals(next)) {
stream.nextWord();
next = lower(stream.peekWord());
if (IF.equals(next)) {
elseif = true;
Vertex elseifExpression = parseOperator(stream, elements, debug, network);
expression.addRelationship(Primitive.ELSEIF, elseifExpression);
} else {
stream.skipWhitespace();
ensureNext('{', stream);
parseArguments(expression, Primitive.ELSE, 0, stream, elements, true, debug, network);
stream.skipWhitespace();
ensureNext('}', stream);
}
}
}
} else if (last.equals(WHILE)) {
List<Vertex> arguments = parseArguments(expression, Primitive.ARGUMENT, 0, stream, elements, false, debug, network);
ensureArguments(WHILE, 1, arguments, stream);
stream.skipWhitespace();
ensureNext('{', stream);
parseArguments(expression, Primitive.DO, 0, stream, elements, true, debug, network);
stream.skipWhitespace();
ensureNext('}', stream);
} else if (last.equals(DO) || last.equals(THINK)) {
stream.skipWhitespace();
ensureNext('{', stream);
parseArguments(expression, Primitive.DO, 0, stream, elements, true, debug, network);
stream.skipWhitespace();
ensureNext('}', stream);
} else if (last.equals(FOR)) {
stream.skipWhitespace();
ensureNext('(', stream);
boolean more = true;
while (more) {
Vertex variable = parseElement(stream, elements, debug, network);
stream.skipWhitespace();
ensureNext("in", stream);
Vertex object = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, variable, Integer.MAX_VALUE);
expression.addRelationship(Primitive.ARGUMENT, object, Integer.MAX_VALUE);
stream.skipWhitespace();
if (stream.peek() == ',') {
stream.skip();
} else {
more = false;
}
}
ensureNext(')', stream);
stream.skipWhitespace();
ensureNext('{', stream);
parseArguments(expression, Primitive.DO, 0, stream, elements, true, debug, network);
stream.skipWhitespace();
ensureNext('}', stream);
} else if (last.equals(NEW)) {
// Handle new Date() vs new (#human, #thing)
stream.skipWhitespace();
if (Character.isUpperCase(stream.peek())) {
int position = stream.getPosition();
String type = stream.nextWord();
if (stream.peek() == '(') {
// TODO handle constructors.
stream.skip();
stream.skipWhitespace();
ensureNext(')', stream);
expression.addRelationship(Primitive.ARGUMENT, new Primitive(type.toLowerCase()));
} else {
stream.setPosition(position);
parseArguments(expression, Primitive.ARGUMENT, 0, stream, elements, false, debug, network);
}
} else {
parseArguments(expression, Primitive.ARGUMENT, 0, stream, elements, false, debug, network);
}
} else {
parseArguments(expression, Primitive.ARGUMENT, 0, stream, elements, false, debug, network);
}
return expression;
}
/**
* Parse the CASE condition.
*/
@Override
public Vertex parseCase(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
stream.skipWhitespace();
Vertex expression = network.createInstance(Primitive.CASE);
String next = stream.peekWord();
boolean anyOrNone = false;
if (next.equalsIgnoreCase(ANY)) {
stream.nextWord();
next = stream.peekWord();
if (next.equalsIgnoreCase(OR)) {
stream.nextWord();
stream.skipWhitespace();
ensureNext(NONE, stream);
anyOrNone = true;
}
}
Vertex variable = parseElement(stream, elements, debug, network);
if (!anyOrNone && variable.instanceOf(Primitive.ARRAY)) {
variable.addRelationship(Primitive.TYPE, Primitive.REQUIRED);
}
expression.addRelationship(Primitive.CASE, variable);
next = stream.nextWord();
if (next.equalsIgnoreCase(AS)) {
Vertex as = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.AS, as);
next = stream.nextWord();
}
if (next.equalsIgnoreCase(TOPIC)) {
Vertex topic = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.TOPIC, topic);
next = stream.nextWord();
}
if (next.equalsIgnoreCase(THAT)) {
Vertex template = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.THAT, template);
next = stream.nextWord();
}
if (next.equalsIgnoreCase(GOTO)) {
List<Vertex> thens = new ArrayList<Vertex>();
stream.skipWhitespace();
boolean parseGoto = true;
while (parseGoto) {
thens.add(parseElementName(Primitive.STATE, stream, elements, debug, network));
stream.skipWhitespace();
if (stream.peek() == ',') {
stream.skip();
} else {
parseGoto = false;
}
}
for (Vertex then : thens) {
expression.addRelationship(Primitive.GOTO, then);
}
} else if (next.equalsIgnoreCase(TEMPLATE) || next.equalsIgnoreCase(ANSWER)) {
Vertex template = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.TEMPLATE, template);
} else if (next.equalsIgnoreCase(RETURN)) {
expression.addRelationship(Primitive.GOTO, Primitive.RETURN);
} else {
stream.setPosition(stream.getPosition() - next.length());
throw new SelfParseException("expected one of 'goto, template, answer, return, that, topic', found: " + next, stream);
}
next = stream.peekWord();
if (next.equalsIgnoreCase(FOR)) {
stream.nextWord();
ensureNext(EACH, stream);
expression.addRelationship(Primitive.FOR, parseElement(stream, elements, debug, network));
ensureNext(OF, stream);
expression.addRelationship(Primitive.FOR, parseElement(stream, elements, debug, network));
}
ensureNext(';', stream);
return expression;
}
/**
* Parse the PATTERN condition.
*/
@Override
public Vertex parsePattern(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
stream.skipWhitespace();
Vertex expression = network.createInstance(Primitive.CASE);
Vertex pattern = null;
if (stream.peek() == '"') {
stream.skip();
pattern = network.createPattern(stream.nextStringWithBracketsDoubleQuotes(), this);
} else {
pattern = parseElement(stream, elements, debug, network);
}
expression.addRelationship(Primitive.PATTERN, pattern);
String next = stream.nextWord().toLowerCase();
Vertex topic = null;
if (next.equals(TOPIC)) {
topic = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.TOPIC, topic);
next = stream.nextWord().toLowerCase();
}
Vertex that = null;
if (next.equals(THAT)) {
stream.skipWhitespace();
if (stream.peek() == '"') {
stream.skip();
that = network.createPattern(stream.nextStringWithBracketsDoubleQuotes(), this);
} else {
that = parseElement(stream, elements, debug, network);
}
expression.addRelationship(Primitive.THAT, that);
next = stream.nextWord().toLowerCase();
}
if (next.equals(GOTO)) {
List<Vertex> thens = new ArrayList<Vertex>();
stream.skipWhitespace();
boolean parseGoto = true;
while (parseGoto) {
thens.add(parseElement(stream, elements, debug, network));
stream.skipWhitespace();
if (stream.peek() == ',') {
stream.skip();
} else {
parseGoto = false;
}
}
for (Vertex then : thens) {
expression.addRelationship(Primitive.GOTO, then);
}
} else if (next.equals(RETURN)) {
expression.addRelationship(Primitive.GOTO, Primitive.RETURN);
} else if (next.equals(TEMPLATE) || next.equals(ANSWER)) {
Vertex template = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.TEMPLATE, template);
} else {
stream.setPosition(stream.getPosition() - next.length());
throw new SelfParseException("expected one of GOTO, TEMPLATE, RETURN, THAT, TOPIC, found: " + next, stream);
}
ensureNext(';', stream);
Vertex sentenceState = AIMLParser.parser().createSentenceState(elements.get("root").get("root"), network);
Vertex state = AIMLParser.parser().createState(pattern, sentenceState, network);
state.addRelationship(Primitive.DO, expression);
return expression;
}
/**
* Parse the RETURN condition.
*/
@Override
public Vertex parseReturn(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
stream.skipWhitespace();
Vertex expression = network.createInstance(Primitive.RETURN);
if (stream.peek() != ';') {
boolean with = stream.peekWord().toLowerCase().equals(WITH);
if (!with) {
Vertex result = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.RETURN, result);
stream.skipWhitespace();
with = stream.peekWord().toLowerCase().equals(WITH);
}
if (with) {
stream.skipWord();
stream.skipWhitespace();
if (stream.peek() == '(') {
stream.skip();
stream.skipWhitespace();
Vertex argument = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, argument, Integer.MAX_VALUE);
stream.skipWhitespace();
while (stream.peek() == ',') {
stream.skip();
stream.skipWhitespace();
argument = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, argument, Integer.MAX_VALUE);
}
ensureNext(')', stream);
} else {
Vertex argument = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, argument, Integer.MAX_VALUE);
}
}
}
return expression;
}
/**
* Parse the GOTO condition.
*/
@Override
public Vertex parseGoto(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
Vertex expression = network.createInstance(Primitive.GOTO);
stream.skipWhitespace();
boolean gotoFinally = stream.peekWord().toLowerCase().equals(FINALLY);
if (gotoFinally) {
stream.nextWord();
expression.addRelationship(Primitive.FINALLY, Primitive.FINALLY);
}
Vertex value = parseElementName(Primitive.STATE, stream, elements, debug, network);
expression.addRelationship(Primitive.GOTO, value);
if (stream.peek() != ';') {
if (stream.peekWord().toLowerCase().equals(WITH)) {
stream.skipWord();
stream.skipWhitespace();
if (stream.peek() == '(') {
stream.skip();
stream.skipWhitespace();
Vertex argument = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, argument, Integer.MAX_VALUE);
stream.skipWhitespace();
while (stream.peek() == ',') {
stream.skip();
stream.skipWhitespace();
argument = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, argument, Integer.MAX_VALUE);
}
ensureNext(')', stream);
} else {
Vertex argument = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, argument, Integer.MAX_VALUE);
}
}
}
return expression;
}
/**
* Parse the PUSH condition.
*/
@Override
public Vertex parsePush(TextStream stream, Map<String, Map<String, Vertex>> elements, boolean debug, Network network) {
stream.nextWord();
Vertex expression = network.createInstance(Primitive.PUSH);
Vertex value = parseElement(stream, elements, debug, network);
expression.addRelationship(Primitive.ARGUMENT, value, Integer.MAX_VALUE);
return expression;
}
}