package parser;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Type;
import models.algebra.Variable;
import models.dataConstraintModel.ChannelMember;
import models.dataConstraintModel.JsonAccessor;
import models.dataConstraintModel.JsonTerm;
import models.dataConstraintModel.ListTerm;
import models.dataConstraintModel.ResourceHierarchy;
import models.dataConstraintModel.ResourcePath;
import models.dataConstraintModel.StateTransition;
import models.dataConstraintModel.StateTransitionTerm;
import models.dataFlowModel.DataTransferModel;
import models.dataFlowModel.DataTransferChannel;
import parser.exceptions.ExpectedAssignment;
import parser.exceptions.ExpectedChannel;
import parser.exceptions.ExpectedChannelName;
import parser.exceptions.ExpectedColon;
import parser.exceptions.ExpectedDoubleQuotation;
import parser.exceptions.ExpectedEquals;
import parser.exceptions.ExpectedInOrOutOrRefOrSubKeyword;
import parser.exceptions.ExpectedLeftCurlyBracket;
import parser.exceptions.ExpectedRHSExpression;
import parser.exceptions.ExpectedRightBracket;
import parser.exceptions.ExpectedRightCurlyBracket;
import parser.exceptions.ExpectedStateTransition;
import parser.exceptions.WrongJsonExpression;
import parser.exceptions.WrongLHSExpression;
import parser.exceptions.WrongPathExpression;
import parser.exceptions.WrongRHSExpression;
public class Parser {
protected TokenStream stream;
public static final String CHANNEL = "channel";
public static final String INIT = "init";
public static final String IN = "in";
public static final String OUT = "out";
public static final String REF = "ref";
public static final String SUB_CHANNEL = "for";
public static final String NATIVE = "native";
public static final String LEFT_CURLY_BRACKET = "{";
public static final String RIGHT_CURLY_BRACKET = "}";
public static final String LEFT_CURLY_BRACKET_REGX = "\\{";
public static final String RIGHT_CURLY_BRACKET_REGX = "\\}";
public static final String LEFT_BRACKET = "(";
public static final String RIGHT_BRACKET = ")";
public static final String LEFT_BRACKET_REGX = "\\(";
public static final String RIGHT_BRACKET_REGX = "\\)";
public static final String LEFT_SQUARE_BRACKET = "[";
public static final String RIGHT_SQUARE_BRACKET = "]";
public static final String LEFT_SQUARE_BRACKET_REGX = "\\[";
public static final String RIGHT_SQUARE_BRACKET_REGX = "\\]";
public static final String ADD = "+";
public static final String MUL = "*";
public static final String SUB = "-";
public static final String DIV = "/";
public static final String MOD = "%";
public static final String MINUS = "-";
public static final String EQ = "==";
public static final String NEQ = "!=";
public static final String GT = ">";
public static final String LT = "<";
public static final String GE = ">=";
public static final String LE = "<=";
public static final String AND = "&&";
public static final String OR = "||";
public static final String NEG = "!";
public static final String ADD_REGX = "\\+";
public static final String MUL_REGX = "\\*";
public static final String SUB_REGX = "\\-";
public static final String DIV_REGX = "/";
public static final String OR_REGX = "\\|\\|";
public static final String EQUALS = "=";
public static final String ASSIGNMENT = "=";
public static final String COMMA = ",";
public static final String COLON = ":";
public static final String DOT = ".";
public static final String DOT_REGX = "\\.";
public static final String DOUBLE_QUOT = "\"";
public Parser(final TokenStream stream) {
this.stream = stream;
}
public Parser(final BufferedReader reader) {
this.stream = new TokenStream();
try {
String line;
while ((line = reader.readLine()) != null) {
stream.addLine(line);
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public DataTransferModel doParse()
throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket,
ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals,
ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
return parseDataFlowModel();
}
public DataTransferModel parseDataFlowModel()
throws ExpectedRightBracket, ExpectedChannel, ExpectedChannelName, ExpectedLeftCurlyBracket, ExpectedRightCurlyBracket,
ExpectedInOrOutOrRefOrSubKeyword, ExpectedStateTransition, ExpectedEquals,
ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, ExpectedAssignment, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
DataTransferModel model = new DataTransferModel();
DataTransferChannel channel;
while ((channel = parseChannel(model)) != null) {
if (channel.getAllInputChannelMembers().size() == 0) {
model.addInputChannel(channel);
} else {
model.addChannel(channel);
}
}
return model;
}
public DataTransferChannel parseChannel(DataTransferModel model)
throws ExpectedLeftCurlyBracket, ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedAssignment,
ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression,
ExpectedChannel, ExpectedChannelName, ExpectedInOrOutOrRefOrSubKeyword,
ExpectedStateTransition, ExpectedEquals, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
if (!stream.hasNext()) return null;
if (stream.checkNext().equals(RIGHT_CURLY_BRACKET)) return null;
boolean isNative = false;
String channelOrInitOrNativeKeyword = stream.next();
if (channelOrInitOrNativeKeyword.equals(NATIVE)) {
// A native channel
isNative = true;
channelOrInitOrNativeKeyword = stream.next();
}
if (!channelOrInitOrNativeKeyword.equals(CHANNEL) && !channelOrInitOrNativeKeyword.equals(SUB_CHANNEL)) {
if (!channelOrInitOrNativeKeyword.equals(INIT)) throw new ExpectedChannel(stream.getLine());
parseInit(model);
channelOrInitOrNativeKeyword = stream.next();
if (channelOrInitOrNativeKeyword.equals(NATIVE)) {
// A native channel
isNative = true;
channelOrInitOrNativeKeyword = stream.next();
}
}
if (!stream.hasNext()) throw new ExpectedChannelName(stream.getLine());
String channelName = stream.next();
if (channelName.equals(LEFT_CURLY_BRACKET)) throw new ExpectedChannelName(stream.getLine());
int fromLine = stream.getLine();
DataTransferChannel channel = new DataTransferChannel(channelName);
if (isNative) {
channel.setNative(true);
}
String leftBracket = stream.next();
if (leftBracket.equals(LEFT_BRACKET)) {
// has selectors
String rightBracket = null;
do {
String selector = stream.next();
Variable var = parseVariable(stream, model, selector);
channel.addSelector(var);
rightBracket = stream.next();
} while (rightBracket.equals(COMMA));
if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
leftBracket = stream.next();
}
if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine());
String inOrOutOrRefOrSub = null;
while (stream.hasNext() && !(inOrOutOrRefOrSub = stream.checkNext()).equals(RIGHT_CURLY_BRACKET)) {
ChannelMember channelMember = null;
if (inOrOutOrRefOrSub.equals(IN)) {
stream.next();
channelMember = parseChannelMember(model, inOrOutOrRefOrSub);
if (channelMember != null) {
channel.addChannelMemberAsInput(channelMember);
}
} else if (inOrOutOrRefOrSub.equals(OUT)) {
stream.next();
channelMember = parseChannelMember(model, inOrOutOrRefOrSub);
if (channelMember != null) {
channel.addChannelMemberAsOutput(channelMember);
}
} else if (inOrOutOrRefOrSub.equals(REF)) {
stream.next();
channelMember = parseChannelMember(model, inOrOutOrRefOrSub);
if (channelMember != null) {
channel.addChannelMemberAsReference(channelMember);
}
} else if (inOrOutOrRefOrSub.equals(SUB_CHANNEL)) {
DataTransferChannel subChannel = parseChannel(model);
if (subChannel != null) {
channel.addChild(subChannel);
}
} else {
throw new ExpectedInOrOutOrRefOrSubKeyword(stream.getLine());
}
}
if (stream.hasNext()) stream.next();
int toLine = stream.getLine();
channel.setSourceText(stream.getSourceText(fromLine, toLine));
return channel;
}
public void parseInit(DataTransferModel model)
throws ExpectedLeftCurlyBracket, ExpectedAssignment, ExpectedRHSExpression, WrongRHSExpression,
ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
String leftBracket = stream.next();
if (!leftBracket.equals(LEFT_CURLY_BRACKET)) throw new ExpectedLeftCurlyBracket(stream.getLine());
while (stream.hasNext() && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) {
int fromLine = stream.getLine();
ResourceHierarchy resourceHierarchy = parseResourceHierarchy(stream, model);
if (!stream.hasNext()) throw new ExpectedAssignment(stream.getLine());
String colon = stream.next();
if (!colon.equals(COLON)) throw new ExpectedAssignment(stream.getLine());
if (!stream.hasNext()) throw new ExpectedAssignment(stream.getLine());
String equals = stream.next();
if (!equals.equals(ASSIGNMENT)) throw new ExpectedAssignment(stream.getLine());
int toLine = stream.getLine();
Expression rightTerm = null;
if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine());
rightTerm = parseTerm(stream, model);
if (rightTerm == null) throw new WrongRHSExpression(stream.getLine());
resourceHierarchy.setInitialValue(rightTerm);
resourceHierarchy.setInitText(stream.getSourceText(fromLine, toLine));
}
if (stream.hasNext()) stream.next();
}
public ChannelMember parseChannelMember(DataTransferModel model, final String inOrOutOrRef)
throws ExpectedRightBracket, ExpectedRightCurlyBracket, ExpectedStateTransition, ExpectedEquals,
ExpectedRHSExpression, WrongLHSExpression, WrongRHSExpression, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
if (!stream.hasNext()) throw new ExpectedStateTransition(stream.getLine());
StateTransitionTerm leftTerm = parseStateTransitionTerm(stream, model);
if (leftTerm == null || !(leftTerm instanceof Term)) throw new WrongLHSExpression(stream.getLine());
Expression rightTerm = null;
if (!inOrOutOrRef.equals(REF)) {
if (!stream.hasNext()) throw new ExpectedEquals(stream.getLine());
String equals = stream.next();
if (!equals.equals(EQUALS)) throw new ExpectedEquals(stream.getLine());
if (!stream.hasNext()) throw new ExpectedRHSExpression(stream.getLine());
rightTerm = parseTerm(stream, model);
if (rightTerm == null) throw new WrongRHSExpression(stream.getLine());
}
ResourcePath resourcePath = (ResourcePath) leftTerm.getSymbol();
ChannelMember channelMember = new ChannelMember(resourcePath);
StateTransition stateTransition = new StateTransition();
stateTransition.setCurStateExpression(((Term) leftTerm).getChild(0));
stateTransition.setMessageExpression(((Term) leftTerm).getChild(1));
if (!inOrOutOrRef.equals(REF)) stateTransition.setNextStateExpression(rightTerm);
channelMember.setStateTransition(stateTransition);
// for type definition
if (resourcePath.getResourceStateType() == null && ((Term) leftTerm).getChild(0) instanceof Variable) {
Variable stateVar = (Variable) ((Term) leftTerm).getChild(0);
if (stateVar.getType() != null) {
resourcePath.setResourceStateType(stateVar.getType());
}
}
if (((Term) leftTerm).getChild(1) instanceof Term) {
Term messageTerm = (Term) ((Term) leftTerm).getChild(1);
if (messageTerm.getSymbol().getSignature() == null && messageTerm.getChildren().size() > 0) {
Type[] signature = new Type[messageTerm.getChildren().size() + 1];
int i = 1;
for (Expression e: messageTerm.getChildren()) {
if (e instanceof Variable && ((Variable) e).getType() != null) {
signature[i] = ((Variable) e).getType();
}
i++;
}
messageTerm.getSymbol().setSignature(signature);
}
}
return channelMember;
}
public Expression parseTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
ArrayList<Expression> expressions = new ArrayList<>();
ArrayList<Symbol> operators = new ArrayList<>();
String operator = null;
for (;;) {
String leftBracketOrMinusOrNeg = stream.next();
if (leftBracketOrMinusOrNeg.equals(LEFT_BRACKET)) {
Expression exp = parseTerm(stream, model);
String rightBracket = stream.next();
if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
expressions.add(exp);
} else if (leftBracketOrMinusOrNeg.equals(LEFT_CURLY_BRACKET)) {
Expression exp = parseJsonTerm(stream, model);
String rightBracket = stream.next();
if (!rightBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
expressions.add(exp);
} else if (leftBracketOrMinusOrNeg.equals(LEFT_SQUARE_BRACKET)) {
Expression exp = parseListTerm(stream, model);
String rightBracket = stream.next();
if (!rightBracket.equals(RIGHT_SQUARE_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
expressions.add(exp);
} else {
Symbol minusOrNeg = null;
String symbolName = null;
if (leftBracketOrMinusOrNeg.equals(MINUS)) {
minusOrNeg = DataTransferModel.minus; // not sub
symbolName = stream.next();
} else if (leftBracketOrMinusOrNeg.equals(NEG)) {
minusOrNeg = DataTransferModel.neg;
symbolName = stream.next();
} else if (leftBracketOrMinusOrNeg.equals(DOUBLE_QUOT)) {
symbolName = DOUBLE_QUOT + stream.next() + DOUBLE_QUOT;
String doubleQuot = stream.next();
if (!doubleQuot.equals(DOUBLE_QUOT)) throw new ExpectedDoubleQuotation(stream.getLine());
} else {
symbolName = leftBracketOrMinusOrNeg;
}
Expression exp = null;
if (stream.checkNext() != null && stream.checkNext().equals(LEFT_BRACKET)) {
// a function symbol
Symbol symbol = model.getSymbol(symbolName);
if (symbol == null) {
symbol = new Symbol(symbolName);
model.addSymbol(symbol);
}
Term term = new Term(symbol);
int arity = 0;
do {
stream.next(); // LEFT_BRACKET or COMMA
if (stream.checkNext().equals(RIGHT_BRACKET)) break;
arity++;
Expression subTerm = parseTerm(stream, model);
term.addChild(subTerm, true);
if (!stream.hasNext()) throw new ExpectedRightBracket(stream.getLine());
} while (stream.checkNext().equals(COMMA));
String rightBracket = stream.next();
if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
symbol.setArity(arity);
exp = term;
} else {
// constant or variable or json access
Symbol symbol = model.getSymbol(symbolName);
if (symbol != null && symbol.getArity() == 0) {
// a constant
exp = new Constant(symbol);
} else {
if (Character.isDigit(symbolName.charAt(0))) {
// maybe a numerical value
if (stream.checkNext() != null && stream.checkNext().equals(DOT)) {
// Because tokens are separated by a DOT.
stream.next();
symbolName += DOT + stream.next(); // decimal fraction
}
Double d = Double.parseDouble(symbolName);
// a numerical value
if (symbolName.contains(DOT)) {
exp = new Constant(symbolName, DataTransferModel.typeDouble);
} else {
exp = new Constant(symbolName, DataTransferModel.typeInt);
}
} else if (symbolName.startsWith(DOUBLE_QUOT) && symbolName.endsWith(DOUBLE_QUOT)) {
// a string value
exp = new Constant(symbolName.substring(1, symbolName.length() - 1), DataTransferModel.typeString);
} else {
// a variable
exp = parseVariable(stream, model, symbolName);
}
}
}
if (minusOrNeg != null) {
Term minusOrNegTerm = new Term(minusOrNeg);
minusOrNegTerm.addChild(exp);
expressions.add(minusOrNegTerm);
} else {
expressions.add(exp);
}
}
operator = stream.checkNext();
if (operator == null) {
break;
} else if (operator.equals(ADD)) {
operators.add(DataTransferModel.add);
stream.next();
} else if (operator.equals(MUL)) {
operators.add(DataTransferModel.mul);
stream.next();
} else if (operator.equals(SUB)) {
operators.add(DataTransferModel.sub); // not minus
stream.next();
} else if (operator.equals(DIV)) {
operators.add(DataTransferModel.div);
stream.next();
} else if (operator.equals(MOD)) {
operators.add(DataTransferModel.mod);
stream.next();
} else if (operator.equals(EQ)) {
operators.add(DataTransferModel.eq);
stream.next();
} else if (operator.equals(NEQ)) {
operators.add(DataTransferModel.neq);
stream.next();
} else if (operator.equals(GT)) {
operators.add(DataTransferModel.gt);
stream.next();
} else if (operator.equals(LT)) {
operators.add(DataTransferModel.lt);
stream.next();
} else if (operator.equals(GE)) {
operators.add(DataTransferModel.ge);
stream.next();
} else if (operator.equals(LE)) {
operators.add(DataTransferModel.le);
stream.next();
} else if (operator.equals(AND)) {
operators.add(DataTransferModel.and);
stream.next();
} else if (operator.equals(OR)) {
operators.add(DataTransferModel.or);
stream.next();
} else if (operator.equals(DOT)) {
// json accessor
while (operator.equals(DOT)) {
Expression exp = expressions.remove(expressions.size() - 1);
stream.next(); // DOT
if (stream.checkNext() == null) throw new WrongJsonExpression(stream.getLine());
String literalOrLeftCurlyBracket = stream.next();
Expression paramTerm = null;
if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) {
// parameter
paramTerm = parseTerm(stream, model);
String rightCurlyBracket = stream.next();
if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new WrongJsonExpression(stream.getLine());
} else {
// literal
paramTerm = new Constant(literalOrLeftCurlyBracket, DataTransferModel.typeString);
}
Type paramType = null;
if (paramTerm instanceof Variable) {
paramType = ((Variable) paramTerm).getType();
} else if (paramTerm instanceof Term) {
paramType = ((Term) paramTerm).getType();
}
Term term = null;
if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) {
term = new JsonAccessor(DataTransferModel.dotParam);
} else {
term = new JsonAccessor(DataTransferModel.dot);
}
term.addChild(exp);
term.addChild(paramTerm);
expressions.add(term);
operator = stream.checkNext();
if (operator == null) break;
if (operator.equals(COLON)) {
// when a type is specified.
stream.next();
String typeName = stream.next();
Type type = model.getType(typeName);
if (type == null) {
type = new Type(typeName, typeName);
}
term.setType(type);
break;
}
}
if (operator == null) {
break;
} else if (operator.equals(ADD)) {
operators.add(DataTransferModel.add);
stream.next();
} else if (operator.equals(MUL)) {
operators.add(DataTransferModel.mul);
stream.next();
} else if (operator.equals(SUB)) {
operators.add(DataTransferModel.sub); // not minus
stream.next();
} else if (operator.equals(DIV)) {
operators.add(DataTransferModel.div);
stream.next();
} else if (operator.equals(MOD)) {
operators.add(DataTransferModel.mod);
stream.next();
} else if (operator.equals(EQ)) {
operators.add(DataTransferModel.eq);
stream.next();
} else if (operator.equals(NEQ)) {
operators.add(DataTransferModel.neq);
stream.next();
} else if (operator.equals(GT)) {
operators.add(DataTransferModel.gt);
stream.next();
} else if (operator.equals(LT)) {
operators.add(DataTransferModel.lt);
stream.next();
} else if (operator.equals(GE)) {
operators.add(DataTransferModel.ge);
stream.next();
} else if (operator.equals(LE)) {
operators.add(DataTransferModel.le);
stream.next();
} else if (operator.equals(AND)) {
operators.add(DataTransferModel.and);
stream.next();
} else if (operator.equals(OR)) {
operators.add(DataTransferModel.or);
stream.next();
} else {
break;
}
} else {
break;
}
}
if (expressions.size() == 1) {
// no arithmetic operators
return expressions.get(0);
}
ArrayList<Expression> monomials = new ArrayList<>();
ArrayList<Symbol> addSubs = new ArrayList<>();
Expression first = expressions.get(0);
int i = 1;
Term rootTerm = null;
for (Symbol op: operators) {
Expression second = expressions.get(i);
if (op.getName().equals(MUL) || op.getName().equals(DIV) || op.getName().equals(MOD)) {
// higher priority than add and sub
Term term = new Term(op);
term.addChild(first);
term.addChild(second);
first = term;
} else if (op.getName().equals(EQ) || op.getName().equals(NEQ) || op.getName().equals(GT) || op.getName().equals(LT)
|| op.getName().equals(GE) || op.getName().equals(LE) || op.getName().equals(AND) || op.getName().equals(OR)) {
// lower priority than add and sub
if (first != null) monomials.add(first);
Expression firstMonomial = monomials.get(0);
int j = 1;
for (Symbol op2: addSubs) {
Expression secondMonomial = monomials.get(j);
Term term = new Term(op2);
term.addChild(firstMonomial);
term.addChild(secondMonomial);
firstMonomial = term;
j++;
}
if (rootTerm == null) {
rootTerm = new Term(op);
rootTerm.addChild(firstMonomial);
} else {
rootTerm.addChild(firstMonomial);
firstMonomial = rootTerm;
rootTerm = new Term(op);
rootTerm.addChild(firstMonomial);
}
monomials.clear();
addSubs.clear();
first = second;
} else {
// add or sub ==> new monomial
monomials.add(first);
addSubs.add(op);
first = second;
}
i++;
}
if (first != null) monomials.add(first);
Expression firstMonomial = monomials.get(0);
i = 1;
for (Symbol op: addSubs) {
Expression secondMonomial = monomials.get(i);
Term term = new Term(op);
term.addChild(firstMonomial);
term.addChild(secondMonomial);
firstMonomial = term;
i++;
}
if (rootTerm == null) {
return firstMonomial;
} else {
rootTerm.addChild(firstMonomial);
return rootTerm;
}
}
// private Expression parseJsonTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon {
// Term jsonTerm = new Constant(DataConstraintModel.nil);
// jsonTerm.setType(DataConstraintModel.typeJson);
// while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) {
// String key = stream.next();
// Constant keyExp = new Constant(key);
// keyExp.setType(DataConstraintModel.typeString);
// if (stream.checkNext() == null || !stream.checkNext().equals(COLON)) throw new ExpectedColon(stream.getLine());
// String colon = stream.next();
// Expression value = parseTerm(stream, model);
// Term nextTerm = new Term(DataConstraintModel.addMember);
// nextTerm.addChild(jsonTerm);
// nextTerm.addChild(keyExp);
// nextTerm.addChild(value);
// jsonTerm = nextTerm;
// if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break;
// String comma = stream.next();
// }
// return jsonTerm;
// }
private Expression parseJsonTerm(TokenStream stream, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
JsonTerm jsonTerm = new JsonTerm();
while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_CURLY_BRACKET)) {
if (stream.checkNext() == null || !stream.checkNext().equals(DOUBLE_QUOT)) throw new ExpectedDoubleQuotation(stream.getLine());
String doubleQuot = stream.next();
String key = stream.next();
if (stream.checkNext() == null || !stream.checkNext().equals(DOUBLE_QUOT)) throw new ExpectedDoubleQuotation(stream.getLine());
doubleQuot = stream.next();
if (stream.checkNext() == null || !stream.checkNext().equals(COLON)) throw new ExpectedColon(stream.getLine());
String colon = stream.next();
Expression value = parseTerm(stream, model);
jsonTerm.addMember(key, value);
if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break;
String comma = stream.next();
}
return jsonTerm;
}
private Expression parseListTerm(TokenStream stream2, DataTransferModel model) throws ExpectedRightBracket, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
ListTerm listTerm = new ListTerm();
listTerm.setType(DataTransferModel.typeList);
while (stream.checkNext() != null && !stream.checkNext().equals(RIGHT_SQUARE_BRACKET)) {
Expression element = parseTerm(stream, model);
listTerm.addChild(element);
if (stream.checkNext() == null || !stream.checkNext().equals(COMMA)) break;
String comma = stream.next();
}
return listTerm;
}
public Variable parseVariable(TokenStream stream, DataTransferModel model, String symbolName) {
Variable var;
if (stream.checkNext() != null && stream.checkNext().equals(COLON)) {
// when a type is specified.
stream.next();
String typeName = stream.next();
Type type = model.getType(typeName);
if (type == null) {
type = new Type(typeName, typeName);
}
var = new Variable(symbolName, type);
} else {
var = new Variable(symbolName);
}
return var;
}
public StateTransitionTerm parseStateTransitionTerm(TokenStream stream, DataTransferModel model)
throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
ResourcePath resourcePath = parseResourcePath(stream, model);
StateTransitionTerm term = new StateTransitionTerm(resourcePath);
int arity = 0;
do {
stream.next(); // LEFT_BRACKET or COMMA
arity++;
Expression subTerm = parseTerm(stream, model);
term.addChild(subTerm, true);
if (!stream.hasNext()) throw new ExpectedRightBracket(stream.getLine());
} while (stream.checkNext().equals(COMMA));
String rightBracket = stream.next();
if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
resourcePath.setArity(arity);
return term;
}
public ResourceHierarchy parseResourceHierarchy(TokenStream stream, DataTransferModel model)
throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression {
ResourceHierarchy hierarchy = null;
do {
String literalOrLeftCurlyBracket = stream.next();
if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) {
// No path parameter
String rightCurlyBracket = stream.next();
if (rightCurlyBracket == null || !rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightCurlyBracket(stream.getLine());
hierarchy = new ResourceHierarchy(hierarchy, 1);
} else {
// Path literal
hierarchy = new ResourceHierarchy(hierarchy, literalOrLeftCurlyBracket);
}
if (!stream.hasNext()) throw new WrongPathExpression(stream.getLine());
if (stream.checkNext().equals(LEFT_BRACKET)) break;
if (stream.checkNext().equals(COLON)) break;
} while (stream.next().equals(DOT));
hierarchy = model.getOrPutResourceHierarchy(hierarchy);
return hierarchy;
}
public ResourcePath parseResourcePath(TokenStream stream, DataTransferModel model)
throws ExpectedRightBracket, ExpectedRightCurlyBracket, WrongPathExpression, WrongJsonExpression, ExpectedColon, ExpectedDoubleQuotation {
ResourcePath path = null;
do {
String literalOrLeftCurlyBracket = stream.next();
if (literalOrLeftCurlyBracket.equals(LEFT_CURLY_BRACKET)) {
// Path parameter
Expression paramTerm = parseTerm(stream, model);
String rightCurlyBracket = stream.next();
if (rightCurlyBracket == null) throw new ExpectedRightCurlyBracket(stream.getLine());
Expression paramConstraint = null;
if (rightCurlyBracket.equals(EQUALS)) {
// Path constraint
paramConstraint = parseTerm(stream, model);
rightCurlyBracket = stream.next();
if (rightCurlyBracket == null) throw new ExpectedRightCurlyBracket(stream.getLine());
}
if (!rightCurlyBracket.equals(RIGHT_CURLY_BRACKET)) throw new ExpectedRightCurlyBracket(stream.getLine());
if (paramConstraint == null) {
path = new ResourcePath(path, paramTerm);
} else {
path = new ResourcePath(path, paramTerm, paramConstraint);
}
} else {
// Path literal
if (path == null) {
path = new ResourcePath(literalOrLeftCurlyBracket);
} else {
path = new ResourcePath(path, literalOrLeftCurlyBracket);
}
}
if (!stream.hasNext()) throw new WrongPathExpression(stream.getLine());
if (stream.checkNext().equals(LEFT_BRACKET)) break;
} while (stream.next().equals(DOT));
model.addResourcePath(path);
return path;
}
protected Boolean doesMatchToKeyword(final String token, final String specificTokenName) {
if(token == null) return false;
if(specificTokenName == null) return false;
return token.equals(specificTokenName);
}
public static class TokenStream {
private ArrayList<ArrayList<Token>> tokens = new ArrayList<>();
private ArrayList<String> lines = new ArrayList<>();
private int line = 0;
private int n = 0;
public TokenStream() {
line = 0;
n = 0;
}
public void addLine(String line) {
lines.add(line);
line = line.trim();
ArrayList<Token> tokenList = splitByDoubleQuotation(line);
tokenList = splitBy(tokenList, ADD, ADD_REGX);
tokenList = splitBy(tokenList, MUL, MUL_REGX);
tokenList = splitBy(tokenList, SUB, SUB_REGX);
tokenList = splitBy(tokenList, DIV, DIV_REGX);
tokenList = splitBy(tokenList, MOD, MOD);
tokenList = splitBy(tokenList, EQ, EQ);
tokenList = splitBy(tokenList, NEQ, NEQ);
tokenList = splitBy(tokenList, GE, GE);
tokenList = splitBy(tokenList, LE, LE);
tokenList = splitBy(tokenList, GT, GT);
tokenList = splitBy(tokenList, LT, LT);
tokenList = splitBy(tokenList, AND, AND);
tokenList = splitBy(tokenList, OR, OR_REGX);
tokenList = splitBy(tokenList, NEG, NEG);
tokenList = splitBy(tokenList, DOT, DOT_REGX);
tokenList = splitBy(tokenList, COMMA, COMMA);
tokenList = splitBy(tokenList, COLON, COLON);
tokenList = splitBy(tokenList, LEFT_BRACKET, LEFT_BRACKET_REGX);
tokenList = splitBy(tokenList, RIGHT_BRACKET, RIGHT_BRACKET_REGX);
tokenList = splitBy(tokenList, EQUALS, EQUALS);
tokenList = splitBy(tokenList, LEFT_CURLY_BRACKET, LEFT_CURLY_BRACKET_REGX);
tokenList = splitBy(tokenList, RIGHT_CURLY_BRACKET, RIGHT_CURLY_BRACKET_REGX);
tokenList = splitBy(tokenList, LEFT_SQUARE_BRACKET, LEFT_SQUARE_BRACKET_REGX);
tokenList = splitBy(tokenList, RIGHT_SQUARE_BRACKET, RIGHT_SQUARE_BRACKET_REGX);
tokens.add(tokenList);
}
private ArrayList<Token> splitBy(final List<Token> tokens, final String delimiter, final String delimiterRegx) {
ArrayList<Token> newTokens = new ArrayList<>();
for (Token token: tokens) {
if (token.isAtomic()) {
newTokens.add(token);
} else {
String[] splitTokens = token.split(delimiterRegx);
boolean fFirstToken = true;
for (String t: splitTokens) {
if (!fFirstToken) {
newTokens.add(new Token(delimiter, true));
}
if (t.length() > 0) {
newTokens.add(new Token(t));
}
fFirstToken = false;
}
while (token.endsWith(delimiter)) {
newTokens.add(new Token(delimiter, true));
token = token.substring(0, token.length() - 1);
}
}
}
return newTokens;
}
private ArrayList<Token> splitByDoubleQuotation(String line) {
ArrayList<Token> newTokens = new ArrayList<>();
String[] tokens = line.split(DOUBLE_QUOT);
boolean fFirstToken = true;
for (int i = 0; i < tokens.length; i++) {
String token = tokens[i];
if (!fFirstToken) {
newTokens.add(new Token(DOUBLE_QUOT, true));
}
if (!fFirstToken || token.length() > 0) {
if (i % 2 == 0) {
for (String t: token.split("[ \t]")) {
newTokens.add(new Token(t));
}
} else {
// string literal
newTokens.add(new Token(token, true));
}
}
fFirstToken = false;
}
if (line.endsWith(DOUBLE_QUOT)) {
newTokens.add(new Token(DOUBLE_QUOT, true));
}
return newTokens;
}
public String next() {
if (line >= tokens.size()) return null;
while (n >= tokens.get(line).size()) {
line++;
n = 0;
if (line >= tokens.size()) return null;
}
String token = tokens.get(line).get(n).getTokenStr();
n++;
return token;
}
public String checkNext() {
if (line >= tokens.size()) return null;
while (n >= tokens.get(line).size()) {
line++;
n = 0;
if (line >= tokens.size()) return null;
}
return tokens.get(line).get(n).getTokenStr();
}
public boolean hasNext() {
if (line >= tokens.size()) return false;
while (n >= tokens.get(line).size()) {
line++;
n = 0;
if (line >= tokens.size()) return false;
}
return true;
}
public int getLine() {
return line;
}
public String getSourceText(int from, int to) {
String text = "";
for (int l = from; l <= to; l++) {
text += lines.get(l) + "\n";
}
return text;
}
}
public static class Token {
String token;
boolean isAtomic = false;
public Token(String token) {
this.token = token;
}
public Token(String token, boolean isAtomic) {
this.token = token;
this.isAtomic = isAtomic;
}
public String getTokenStr() {
return token;
}
public boolean isAtomic() {
return isAtomic;
}
public String[] split(String delimiterRegx) {
return token.split(delimiterRegx);
}
public boolean endsWith(String delimiter) {
return token.endsWith(delimiter);
}
public int length() {
return token.length();
}
public Token substring(int beginIdx, int endIdx) {
return new Token(token.substring(beginIdx, endIdx));
}
}
}