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 tokens; private int ptr; /** * Constructor. */ public Parser() { this.tokens = new ArrayList(); } 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 terms = new ArrayList(); 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 terms = new ArrayList(); 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; } }