summaryrefslogtreecommitdiff
path: root/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java')
-rw-r--r--dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java108
1 files changed, 108 insertions, 0 deletions
diff --git a/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java
new file mode 100644
index 0000000..5f16554
--- /dev/null
+++ b/dice-lang/src/main/java/bjc/dicelang/ast/DiceASTParser.java
@@ -0,0 +1,108 @@
+package bjc.dicelang.ast;
+
+import org.apache.commons.lang3.StringUtils;
+
+import bjc.utils.funcdata.FunctionalList;
+import bjc.utils.funcdata.FunctionalStringTokenizer;
+import bjc.utils.parserutils.AST;
+import bjc.utils.parserutils.ShuntingYard;
+import bjc.utils.parserutils.TreeConstructor;
+
+/**
+ * Create an AST from a string expression
+ *
+ * @author ben
+ *
+ */
+public class DiceASTParser {
+ /**
+ * The yard to use for shunting expressions
+ */
+ private static ShuntingYard<String> yard;
+
+ static {
+ yard = new ShuntingYard<>();
+
+ yard.addOp("d", 5); // dice operator: use for creating variable
+ // size dice groups
+ yard.addOp("c", 6); // compound operator: use for creating compound
+ // dice from expressions
+ yard.addOp(":=", 0); // binding operator: Bind a name to a variable
+ // expression
+ }
+
+ /**
+ * Build an AST from a string expression
+ *
+ * @param exp
+ * The string to build from
+ * @return An AST built from the passed in string
+ */
+ public AST<IDiceASTNode> buildAST(String exp) {
+ FunctionalList<String> tokens = FunctionalStringTokenizer
+ .fromString(exp).toList((s) -> s);
+
+ FunctionalList<String> shunted = yard.postfix(tokens, (s) -> s);
+
+ AST<String> rawAST = TreeConstructor.constructTree(shunted,
+ this::isOperator, (op) -> false, null);
+
+ AST<IDiceASTNode> bakedAST = rawAST.transmuteAST((tok) -> {
+ if (isOperator(tok)) {
+ return OperatorDiceNode.fromString(tok);
+ } else if (isLiteral(tok)) {
+ return new LiteralDiceNode(tok);
+ } else {
+ return new VariableDiceNode(tok);
+ }
+ });
+
+ return bakedAST;
+ }
+
+ /**
+ * Check if a token represents a literal
+ *
+ * @param tok
+ * The token to check
+ * @return Whether or not the token represents a literal
+ */
+ private static boolean isLiteral(String tok) {
+ if (StringUtils.countMatches(tok, 'c') == 1
+ && !tok.equalsIgnoreCase("c")) {
+ return true;
+ } else if (StringUtils.countMatches(tok, 'd') == 1
+ && !tok.equalsIgnoreCase("d")) {
+ return true;
+ } else {
+ try {
+ Integer.parseInt(tok);
+ return true;
+ } catch (NumberFormatException nfx) {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Check if a token represents an operator
+ *
+ * @param tok
+ * The token to check if it represents an operator
+ * @return Whether or not the token represents an operator
+ */
+ private boolean isOperator(String tok) {
+ switch (tok) {
+ case ":=":
+ case "+":
+ case "-":
+ case "*":
+ case "/":
+ case "c":
+ case "d":
+ return true;
+ default:
+ return false;
+ }
+ }
+}