diff options
Diffstat (limited to 'dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java')
| -rw-r--r-- | dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java b/dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java new file mode 100644 index 0000000..89adb65 --- /dev/null +++ b/dice-lang/src/bjc/utils/dice/ast/DiceASTExpression.java @@ -0,0 +1,195 @@ +package bjc.utils.dice.ast; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.BinaryOperator; + +import org.apache.commons.lang3.StringUtils; + +import bjc.utils.data.Pair; +import bjc.utils.dice.ComplexDice; +import bjc.utils.dice.CompoundDice; +import bjc.utils.dice.IDiceExpression; +import bjc.utils.parserutils.AST; + +/** + * An implementation of {@link IDiceExpression} backed by an AST of + * {@link IDiceASTNode}s + * + * @author ben + * + */ +public class DiceASTExpression implements IDiceExpression { + + /** + * Build the map of operations to use when collapsing the AST + * + * @param env + * The enviroment to evaluate bindings and such against + * @return The operations to use when collapsing the AST + */ + private static + Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> + buildOperations(Map<String, DiceASTExpression> env) { + Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> opCollapsers = + new HashMap<>(); + + opCollapsers.put(OperatorDiceNode.ADD, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval + rval, + new AST<>(OperatorDiceNode.ADD, last, rast)); + })); + + }); + opCollapsers.put(OperatorDiceNode.SUBTRACT, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval - rval, + new AST<>(OperatorDiceNode.SUBTRACT, last, rast)); + })); + + }); + opCollapsers.put(OperatorDiceNode.MULTIPLY, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval * rval, + new AST<>(OperatorDiceNode.MULTIPLY, last, rast)); + })); + + }); + opCollapsers.put(OperatorDiceNode.DIVIDE, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + return new Pair<>(lval / rval, + new AST<>(OperatorDiceNode.DIVIDE, last, rast)); + })); + }); + + opCollapsers.put(OperatorDiceNode.ASSIGN, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + String nam = last.collapse((nod) -> { + return ((VariableDiceNode) nod).getVariable(); + } , (v) -> (lv, rv) -> null, (r) -> r); + + env.put(nam, new DiceASTExpression(rast, env)); + + return new Pair<>(rval, + new AST<>(OperatorDiceNode.ASSIGN, last, rast)); + })); + }); + + opCollapsers.put(OperatorDiceNode.COMPOUND, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + int ival = Integer.parseInt( + Integer.toString(lval) + Integer.toString(rval)); + + return new Pair<>(ival, + new AST<>(OperatorDiceNode.COMPOUND, last, rast)); + })); + }); + opCollapsers.put(OperatorDiceNode.GROUP, (left, right) -> { + return left.merge((lval, last) -> right.merge((rval, rast) -> { + + return new Pair<>(new ComplexDice(lval, rval).roll(), + new AST<>(OperatorDiceNode.GROUP, last, rast)); + })); + }); + + return opCollapsers; + } + + /** + * The AST this expression will evaluate + */ + private AST<IDiceASTNode> ast; + + /** + * The enviroment to evaluate bindings and such against + */ + private Map<String, DiceASTExpression> env; + + /** + * Create a new dice expression backed by an AST + * + * @param ast + * The AST backing this expression + * @param env + * The enviroment to evaluate bindings against + */ + public DiceASTExpression(AST<IDiceASTNode> ast, + Map<String, DiceASTExpression> env) { + this.ast = ast; + this.env = env; + } + + /** + * Expand a leaf AST token into a pair for evaluation + * + * @param tokn + * The token to evaluate + * @return A pair consisting of the token's value and the AST it + * represents + */ + private Pair<Integer, AST<IDiceASTNode>> evalLeaf(IDiceASTNode tokn) { + if (tokn instanceof VariableDiceNode) { + String varName = ((VariableDiceNode) tokn).getVariable(); + + if (env.containsKey(varName)) { + return new Pair<>(env.get(varName).roll(), + new AST<>(tokn)); + } else { + // Handle special case for defining variables + return new Pair<>(0, new AST<>(tokn)); + } + } else { + LiteralDiceNode lnod = (LiteralDiceNode) tokn; + String dat = lnod.getData(); + + if (StringUtils.countMatches(dat, 'c') == 1 + && !dat.equalsIgnoreCase("c")) { + String[] strangs = dat.split("c"); + return new Pair<>(new CompoundDice(strangs).roll(), + new AST<>(tokn)); + } else if (StringUtils.countMatches(dat, 'd') == 1 + && !dat.equalsIgnoreCase("d")) { + /* + * Handle dice groups + */ + return new Pair<>(ComplexDice.fromString(dat).roll(), + new AST<>(tokn)); + } else { + return new Pair<>(Integer.parseInt(dat), new AST<>(tokn)); + } + } + } + + /** + * Get the AST bound to this expression + * + * @return the ast + */ + public AST<IDiceASTNode> getAst() { + return ast; + } + + /* + * (non-Javadoc) + * + * @see bjc.utils.dice.IDiceExpression#roll() + */ + @Override + public int roll() { + Map<IDiceASTNode, BinaryOperator<Pair<Integer, AST<IDiceASTNode>>>> operations = + buildOperations(env); + + return ast.collapse(this::evalLeaf, operations::get, + (r) -> r.merge((left, right) -> left)); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return ast.toString(); + } +} |
