package bjc.dicelang.ast; import java.util.Deque; import java.util.InputMismatchException; import java.util.function.Function; import java.util.function.Predicate; import bjc.dicelang.IDiceExpression; import bjc.dicelang.ast.nodes.DiceLiteralNode; import bjc.dicelang.ast.nodes.DiceLiteralType; import bjc.dicelang.ast.nodes.IDiceASTNode; import bjc.dicelang.ast.nodes.ILiteralDiceNode; import bjc.dicelang.ast.nodes.IntegerLiteralNode; import bjc.dicelang.ast.nodes.OperatorDiceNode; import bjc.dicelang.ast.nodes.VariableDiceNode; import bjc.utils.funcdata.FunctionalList; import bjc.utils.funcdata.FunctionalMap; import bjc.utils.funcdata.IFunctionalList; import bjc.utils.funcdata.IFunctionalMap; import bjc.utils.funcdata.ITree; import bjc.utils.funcdata.Tree; import bjc.utils.funcutils.StringUtils; import bjc.utils.parserutils.TreeConstructor; /** * Parse a string expression into AST form. Doesn't do anything else * * @author ben * */ public class DiceASTParser { /** * Create an AST from a list of tokens * * @param tokens * The list of tokens to convert * @return An AST built from the tokens */ public static ITree createFromString(IFunctionalList tokens) { Predicate specialPicker = (operator) -> { if (StringUtils.containsOnly(operator, "\\[")) { return true; } else if (StringUtils.containsOnly(operator, "\\]")) { return true; } return false; }; IFunctionalMap>, ITree>> operators = new FunctionalMap<>(); operators.put("[", (queuedTrees) -> { Tree openTree = new Tree<>("["); return openTree; }); operators.put("]", (queuedTrees) -> { return parseCloseArray(queuedTrees); }); ITree rawTokens = TreeConstructor.constructTree(tokens, (token) -> { return isOperatorNode(token); }, specialPicker, operators::get); ITree tokenizedTree = rawTokens.rebuildTree(DiceASTParser::convertLeafNode, DiceASTParser::convertOperatorNode); return tokenizedTree; } private static ITree parseCloseArray(Deque> queuedTrees) { IFunctionalList> children = new FunctionalList<>(); while (shouldContinuePopping(queuedTrees)) { children.add(queuedTrees.pop()); } queuedTrees.pop(); children.reverse(); ITree arrayTree = new Tree<>("[]", children); return arrayTree; } private static boolean shouldContinuePopping(Deque> queuedTrees) { String peekToken = queuedTrees.peek().getHead(); return !peekToken.equals("["); } private static boolean isOperatorNode(String token) { if (StringUtils.containsOnly(token, "\\[")) { return true; } else if (StringUtils.containsOnly(token, "\\]")) { return true; } if (token.equals("[]")) { // This is a synthetic operator, constructed by [ and ] return true; } try { OperatorDiceNode.fromString(token); return true; } catch (@SuppressWarnings("unused") IllegalArgumentException iaex) { // We don't care about details return false; } } private static IDiceASTNode convertLeafNode(String leafNode) { DiceLiteralType literalType = ILiteralDiceNode.getLiteralType(leafNode); if (literalType != null) { switch (literalType) { case DICE: return new DiceLiteralNode( IDiceExpression.toExpression(leafNode)); case INTEGER: return new IntegerLiteralNode( Integer.parseInt(leafNode)); default: throw new InputMismatchException( "Cannot convert string '" + leafNode + "' into a literal."); } } return new VariableDiceNode(leafNode); } private static IDiceASTNode convertOperatorNode(String operatorNode) { try { return OperatorDiceNode.fromString(operatorNode); } catch (IllegalArgumentException iaex) { InputMismatchException imex = new InputMismatchException( "Attempted to parse invalid operator " + operatorNode); imex.initCause(iaex); throw imex; } } }