package com.smotricz.morgana6.client.parser;
import java.util.ArrayList;
import java.util.List;
import com.smotricz.morgana6.client.nodes.AndNode;
import com.smotricz.morgana6.client.nodes.ConstNode;
import com.smotricz.morgana6.client.nodes.Node;
import com.smotricz.morgana6.client.nodes.OrNode;
import com.smotricz.morgana6.client.nodes.ParenNode;
import com.smotricz.morgana6.client.nodes.SignedNode;
import com.smotricz.morgana6.client.nodes.VarNode;
import static com.smotricz.morgana6.client.parser.Token.*;
public class Parser {
private List<Token> tokens;
private int ptr;
/**
* Constructor.
*/
public Parser() {
this.tokens = new ArrayList<Token>();
}
public Node parse(String input, StringBuilder cleaned) throws ParseException {
String clean = Scanner.scan(input, this.tokens);
if (cleaned != null) {
cleaned.setLength(0);
cleaned.append(clean);
}
setPtr(0);
Node exp = orExp();
if (exp == null) {
throw new ParseException("Syntax error", 0);
}
if (la(0) != EOD) {
throw new ParseException("Syntax error", this.ptr);
}
return exp;
}
/**
* Look Ahead.
* @param offset
* @return the token type of the input token at {@link #ptr} + offset.
*/
private TT la(int offset) {
return tokens.get(ptr + offset).type;
}
private Token nextToken() {
return tokens.get(ptr);
}
private void setPtr(int position) {
this.ptr = position;
}
private int getPtr() {
return this.ptr;
}
private void consume() {
this.ptr++;
}
// and-exp (OR and-exp)*
private Node orExp() {
Node a1 = andExp();
if (a1 == null) return null;
if (la(0) != OR) return a1;
consume();
Node a2 = andExp();
if (a2 == null) return a1;
ArrayList<Node> terms = new ArrayList<Node>();
terms.add(a1);
terms.add(a2);
while (la(0) == OR) {
consume();
Node aN = andExp();
if (aN == null) break;
terms.add(aN);
}
return new OrNode(terms);
}
// signed-exp ([AND] signed-exp)*
private Node andExp() {
Node s1 = signed();
if (s1 == null) return null;
if (la(0) == AND) {
consume();
}
Node s2 = signed();
if (s2 == null) return s1;
ArrayList<Node> terms = new ArrayList<Node>();
terms.add(s1);
terms.add(s2);
while (true) {
if (la(0) == AND) {
consume();
}
Node sN = signed();
if (sN == null) break;
terms.add(sN);
}
return new AndNode(terms);
}
// NOT scalar
// | scalar [PRIME]
private Node signed() {
boolean negative = (la(0) == NOT);
if (negative) {
consume();
}
Node scx = scalar();
if (scx == null) return null;
if (negative) return ((SignedNode) scx).negate();
if (la(0) == PRIME) {
consume();
return ((SignedNode) scx).negate();
} else {
return scx;
}
}
// VAR
// | CONSTANT
// | LP or-exp RP
private Node scalar() {
int savPtr = getPtr();
Node scx;
if (la(0) == VAR) {
scx = VarNode.forName(String.valueOf(nextToken().text));
consume();
return scx;
}
if (la(0) == CONST) {
scx = ConstNode.forBoolean(nextToken().text == '1');
consume();
return scx;
}
if (la(0) == LP) {
consume();
scx = orExp();
if (scx == null || la(0) != RP) {
setPtr(savPtr);
return null;
}
consume();
return ParenNode.forNested(scx);
}
return null;
}
}