package parser;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import constants.Symbols;
import constants.Types;
import models.algebra.Constant;
import models.algebra.Expression;
import models.algebra.Symbol;
import models.algebra.Term;
import models.algebra.Variable;
import parser.exceptions.ExpectedDoubleQuotation;
import parser.exceptions.ExpectedRightBracket;
public class Parser {
protected TokenStream stream;
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 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 Expression parseTerm(TokenStream stream) throws ExpectedRightBracket, 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);
String rightBracket = stream.next();
if (!rightBracket.equals(RIGHT_BRACKET)) throw new ExpectedRightBracket(stream.getLine());
expressions.add(exp);
} else {
Symbol minusOrNeg = null;
String symbolName = null;
if (leftBracketOrMinusOrNeg.equals(MINUS)) {
minusOrNeg = Symbols.minus; // not sub
symbolName = stream.next();
} else if (leftBracketOrMinusOrNeg.equals(NEG)) {
minusOrNeg = Symbols.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 (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, Types.typeDouble);
} else {
exp = new Constant(symbolName, Types.typeInt);
}
} else if (symbolName.startsWith(DOUBLE_QUOT) && symbolName.endsWith(DOUBLE_QUOT)) {
// a string value
exp = new Constant(symbolName.substring(1, symbolName.length() - 1), Types.typeString);
} else {
// a variable
exp = parseVariable(stream, 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(Symbols.add);
stream.next();
} else if (operator.equals(MUL)) {
operators.add(Symbols.mul);
stream.next();
} else if (operator.equals(SUB)) {
operators.add(Symbols.sub); // not minus
stream.next();
} else if (operator.equals(DIV)) {
operators.add(Symbols.div);
stream.next();
} else if (operator.equals(MOD)) {
operators.add(Symbols.mod);
stream.next();
} else if (operator.equals(EQ)) {
operators.add(Symbols.eq);
stream.next();
} else if (operator.equals(NEQ)) {
operators.add(Symbols.neq);
stream.next();
} else if (operator.equals(GT)) {
operators.add(Symbols.gt);
stream.next();
} else if (operator.equals(LT)) {
operators.add(Symbols.lt);
stream.next();
} else if (operator.equals(GE)) {
operators.add(Symbols.ge);
stream.next();
} else if (operator.equals(LE)) {
operators.add(Symbols.le);
stream.next();
// } else if (operator.equals(AND)) {
// operators.add(Symbols.and);
// stream.next();
// } else if (operator.equals(OR)) {
// operators.add(Symbols.or);
// stream.next();
} 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;
}
}
public Variable parseVariable(TokenStream stream, String symbolName) {
return new Variable(symbolName);
}
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);
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));
}
}
}