diff options
Diffstat (limited to 'base/src/bjc/dicelang/Parser.java')
| -rw-r--r-- | base/src/bjc/dicelang/Parser.java | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/base/src/bjc/dicelang/Parser.java b/base/src/bjc/dicelang/Parser.java new file mode 100644 index 0000000..0861e96 --- /dev/null +++ b/base/src/bjc/dicelang/Parser.java @@ -0,0 +1,221 @@ +package bjc.dicelang; + +import static bjc.dicelang.Errors.ErrorKey.EK_PARSE_BINARY; +import static bjc.dicelang.Errors.ErrorKey.EK_PARSE_INVTOKEN; +import static bjc.dicelang.Errors.ErrorKey.EK_PARSE_NOCLOSE; +import static bjc.dicelang.Errors.ErrorKey.EK_PARSE_UNCLOSE; +import static bjc.dicelang.Errors.ErrorKey.EK_PARSE_UNOPERAND; +import static bjc.dicelang.Node.Type.BINOP; +import static bjc.dicelang.Node.Type.GROUP; +import static bjc.dicelang.Node.Type.OGROUP; +import static bjc.dicelang.Node.Type.TOKREF; +import static bjc.dicelang.Node.Type.UNARYOP; +import static bjc.dicelang.Token.Type.CBRACE; +import static bjc.dicelang.Token.Type.CBRACKET; + +import java.util.Deque; +import java.util.LinkedList; + +import bjc.utils.data.ITree; +import bjc.utils.data.Tree; +import bjc.utils.funcdata.IList; + +/** + * Parse a series of tree into tokens. + * + * @author EVE + * + */ +public class Parser { + /** Create a new parser. */ + public Parser() { + + } + + /** + * Parse a series of tokens to a forest of ASTs. + * + * @param tokens + * The list of tokens to parse. + * + * @param results + * The place to set results. + * + * @return Whether or not the parse was successful. + */ + public static boolean parseTokens(final IList<Token> tokens, + final IList<ITree<Node>> results) { + final Deque<ITree<Node>> working = new LinkedList<>(); + + for (final Token tk : tokens) { + switch (tk.type) { + case OBRACKET: + case OBRACE: + /* Parse opening delims. */ + working.push(new Tree<>(new Node(OGROUP, tk))); + + break; + case CBRACKET: + case CBRACE: + /* Parse closing delims. */ + final boolean sc = parseClosingGrouper(working, tk); + + if (!sc) { + return false; + } + + break; + case MULTIPLY: + case DIVIDE: + case IDIVIDE: + case DICEGROUP: + case DICECONCAT: + case DICELIST: + case STRCAT: + case STRREP: + case LET: + case BIND: + /* Parse binary operator. */ + if (working.size() < 2) { + Errors.inst.printError(EK_PARSE_BINARY); + return false; + } + + handleBinaryNode(working, tk); + break; + case ADD: + case SUBTRACT: + /* Handle binary/unary operators. */ + if (working.size() == 0) { + Errors.inst.printError(EK_PARSE_UNOPERAND, tk.toString()); + return false; + } else if (working.size() == 1) { + final ITree<Node> operand = working.pop(); + final ITree<Node> opNode = new Tree<>(new Node(UNARYOP, tk.type)); + + opNode.addChild(operand); + + working.push(opNode); + } else { + handleBinaryNode(working, tk); + } + + break; + case COERCE: + case DICESCALAR: + case DICEFUDGE: + /* Handle unary operators. */ + if (working.size() == 0) { + Errors.inst.printError(EK_PARSE_UNOPERAND, tk.toString()); + } else { + final ITree<Node> operand = working.pop(); + final ITree<Node> opNode = new Tree<>(new Node(UNARYOP, tk.type)); + + opNode.addChild(operand); + + working.push(opNode); + } + + break; + case INT_LIT: + case FLOAT_LIT: + case STRING_LIT: + case VREF: + case DICE_LIT: + /* Handle literals. */ + working.push(new Tree<>(new Node(TOKREF, tk))); + break; + default: + Errors.inst.printError(EK_PARSE_INVTOKEN, tk.type.toString()); + return false; + } + } + + /* + * Collect the remaining nodes as the roots of the trees in the + * AST forest. + */ + for (final ITree<Node> ast : working) { + results.add(ast); + } + + return true; + } + + /* Handle a binary operator. */ + private static void handleBinaryNode(final Deque<ITree<Node>> working, final Token tk) { + final ITree<Node> right = working.pop(); + final ITree<Node> left = working.pop(); + + final ITree<Node> opNode = new Tree<>(new Node(BINOP, tk.type)); + + opNode.addChild(left); + opNode.addChild(right); + + working.push(opNode); + } + + /* Parse a closing delimiter. */ + private static boolean parseClosingGrouper(final Deque<ITree<Node>> working, + final Token tk) { + if (working.size() == 0) { + Errors.inst.printError(EK_PARSE_NOCLOSE); + return false; + } + + ITree<Node> groupNode = null; + + switch (tk.type) { + case CBRACE: + groupNode = new Tree<>(new Node(GROUP, Node.GroupType.CODE)); + break; + case CBRACKET: + groupNode = new Tree<>(new Node(GROUP, Node.GroupType.ARRAY)); + break; + default: + Errors.inst.printError(EK_PARSE_UNCLOSE, tk.type.toString()); + return false; + } + + Token matching = null; + + if (tk.type == CBRACKET) { + matching = new Token(Token.Type.OBRACKET, tk.intValue); + } else if (tk.type == CBRACE) { + matching = new Token(Token.Type.OBRACE, tk.intValue); + } + + final ITree<Node> matchNode = new Tree<>(new Node(OGROUP, matching)); + + if (!working.contains(matchNode)) { + Errors.inst.printError(EK_PARSE_UNCLOSE, tk.toString(), matchNode.toString()); + + System.out.println("\tCurrent forest is: "); + + int treeNo = 1; + + for (final ITree<Node> ast : working) { + System.out.println("Tree " + treeNo++ + ": " + ast.toString()); + } + + return false; + } + + final Deque<ITree<Node>> childs = new LinkedList<>(); + + while (!working.peek().equals(matchNode)) { + childs.push(working.pop()); + } + + /* Discard opener */ + working.pop(); + + for (final ITree<Node> child : childs) { + groupNode.addChild(child); + } + + working.push(groupNode); + + return true; + } +} |
