summaryrefslogtreecommitdiff
path: root/BJC-Utils2/src/main/java/bjc/utils/dice/DiceExpressionParser.java
blob: 71801f82ddeb272a5540236e046423a5e53d5186 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package bjc.utils.dice;

import java.util.Stack;

import bjc.utils.funcdata.FunctionalList;
import bjc.utils.funcdata.FunctionalStringTokenizer;
import bjc.utils.parserutils.ShuntingYard;

/**
 * Parse a dice expression from a string
 * 
 * @author ben
 *
 */
public class DiceExpressionParser {
	/**
	 * Parse a dice expression from a string
	 * 
	 * @param exp
	 *            The string to parse an expression from
	 * @return The parsed dice expression
	 */
	public IDiceExpression parse(String exp) {
		/*
		 * Create a tokenizer over the strings
		 */
		FunctionalStringTokenizer fst = new FunctionalStringTokenizer(exp);

		/*
		 * Create a shunter to rewrite the expression
		 */
		ShuntingYard<String> yard = new ShuntingYard<>();

		/*
		 * Add our custom operators to the yard
		 */
		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

		/*
		 * Shunt the expression to postfix form
		 */
		FunctionalList<String> ls = yard.postfix(fst.toList(s -> s),
				s -> s);

		/*
		 * Create a stack for building an expression from parts
		 */
		Stack<IDiceExpression> dexps = new Stack<>();

		/*
		 * Create the expression from parts
		 */
		ls.forEach((tok) -> {
			/*
			 * Handle compound dice
			 */
			if (tok.contains("c") && !tok.equalsIgnoreCase("c")) {
				String[] strangs = tok.split("c");

				dexps.push(new CompoundDice(LazyDice.fromString(strangs[0]),
						LazyDice.fromString(strangs[1])));
			} else if (tok.contains("d") && !tok.equalsIgnoreCase("d")) {
				/*
				 * Handle dice groups
				 */
				dexps.push(LazyDice.fromString(tok));
			} else {
				try {
					/*
					 * Handle scalar numbers
					 */
					dexps.push(new ScalarDie(Integer.parseInt(tok)));
				} catch (NumberFormatException nfex) {

					/*
					 * Apply an operation to two dice
					 */
					IDiceExpression l = dexps.pop();
					IDiceExpression r = dexps.pop();

					switch (tok) {
						case "+":
							dexps.push(new CompoundDiceExpression(l, r,
									DiceExpressionType.ADD));
							break;
						case "-":
							dexps.push(new CompoundDiceExpression(l, r,
									DiceExpressionType.SUBTRACT));
							break;
						case "*":
							dexps.push(new CompoundDiceExpression(l, r,
									DiceExpressionType.MULTIPLY));
							break;
						case "/":
							dexps.push(new CompoundDiceExpression(l, r,
									DiceExpressionType.DIVIDE));
							break;
						case "c":
							dexps.push(new CompoundDice(l, r));
							break;
						case "d":
							dexps.push(new LazyDice(l, r));
							break;
						default:
							/*
							 * Tell the user the operator is invalid
							 */
							throw new IllegalStateException(
									"Detected invalid operator " + tok);
					}
				}
			}
		});

		/*
		 * Return the built expression
		 */
		return dexps.pop();
	}
}