From e1480c4e706d4902f9865f6119e71e30b4173153 Mon Sep 17 00:00:00 2001 From: student Date: Mon, 12 Feb 2018 17:17:37 -0500 Subject: Refactor EvaluatorResult --- base/src/bjc/dicelang/DiceLangEngine.java | 20 +- base/src/bjc/dicelang/Evaluator.java | 593 -------------------- base/src/bjc/dicelang/EvaluatorResult.java | 223 -------- base/src/bjc/dicelang/Node.java | 38 +- .../src/bjc/dicelang/eval/DiceEvaluatorResult.java | 32 ++ base/src/bjc/dicelang/eval/Evaluator.java | 602 +++++++++++++++++++++ base/src/bjc/dicelang/eval/EvaluatorResult.java | 82 +++ .../bjc/dicelang/eval/FailureEvaluatorResult.java | 55 ++ .../bjc/dicelang/eval/FloatEvaluatorResult.java | 43 ++ .../bjc/dicelang/eval/StringEvaluatorResult.java | 44 ++ 10 files changed, 896 insertions(+), 836 deletions(-) delete mode 100644 base/src/bjc/dicelang/Evaluator.java delete mode 100644 base/src/bjc/dicelang/EvaluatorResult.java create mode 100644 base/src/bjc/dicelang/eval/DiceEvaluatorResult.java create mode 100644 base/src/bjc/dicelang/eval/Evaluator.java create mode 100644 base/src/bjc/dicelang/eval/EvaluatorResult.java create mode 100644 base/src/bjc/dicelang/eval/FailureEvaluatorResult.java create mode 100644 base/src/bjc/dicelang/eval/FloatEvaluatorResult.java create mode 100644 base/src/bjc/dicelang/eval/StringEvaluatorResult.java (limited to 'base') diff --git a/base/src/bjc/dicelang/DiceLangEngine.java b/base/src/bjc/dicelang/DiceLangEngine.java index ebdb07f..e7ba4e8 100644 --- a/base/src/bjc/dicelang/DiceLangEngine.java +++ b/base/src/bjc/dicelang/DiceLangEngine.java @@ -8,6 +8,10 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import bjc.dicelang.eval.DiceEvaluatorResult; +import bjc.dicelang.eval.Evaluator; +import bjc.dicelang.eval.EvaluatorResult; +import bjc.dicelang.eval.FailureEvaluatorResult; import bjc.dicelang.scl.StreamEngine; import bjc.utils.data.ITree; import bjc.utils.funcdata.FunctionalList; @@ -538,11 +542,15 @@ public class DiceLangEngine { System.out.printf(" (result is %s", res); if (res.type == EvaluatorResult.Type.DICE) { - System.out.printf(" (sample roll %s)", res.diceVal.value()); + String value = ((DiceEvaluatorResult) res).diceVal.value(); + + System.out.printf(" (sample roll %s)", value); } - if (res.origVal != null) { - System.out.printf(" (original tree is %s)", res.origVal); + if (res.type == EvaluatorResult.Type.FAILURE) { + ITree otree = ((FailureEvaluatorResult) res).origVal; + + System.out.printf(" (original tree is %s)", otree); } System.out.printf(")"); @@ -560,7 +568,9 @@ public class DiceLangEngine { System.out.printf("\t\tEvaluates to %s", res); if (res.type == EvaluatorResult.Type.DICE) { - System.out.println("\t\t (sample roll " + res.diceVal.value() + ")"); + String value = ((DiceEvaluatorResult) res).diceVal.value(); + + System.out.println("\t\t (sample roll " + value + ")"); } } } @@ -653,7 +663,7 @@ public class DiceLangEngine { } /* Get a string literal from the string literal table. */ - String getStringLiteral(final int key) { + public String getStringLiteral(final int key) { return stringLits.get(key); } diff --git a/base/src/bjc/dicelang/Evaluator.java b/base/src/bjc/dicelang/Evaluator.java deleted file mode 100644 index aa2d2a0..0000000 --- a/base/src/bjc/dicelang/Evaluator.java +++ /dev/null @@ -1,593 +0,0 @@ -package bjc.dicelang; - -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.function.Consumer; - -import bjc.dicelang.dice.CompoundDie; -import bjc.dicelang.dice.Die; -import bjc.dicelang.dice.MathDie; -import bjc.dicelang.dice.ScalarDiceExpression; -import bjc.dicelang.dice.ScalarDie; -import bjc.dicelang.dice.SimpleDie; -import bjc.dicelang.dice.SimpleDieList; - -import bjc.utils.data.ITree; -import bjc.utils.data.SingleIterator; -import bjc.utils.data.TopDownTransformIterator; -import bjc.utils.data.TopDownTransformResult; -import bjc.utils.data.Tree; - -import static bjc.dicelang.Errors.ErrorKey.*; -import static bjc.dicelang.EvaluatorResult.Type.DICE; -import static bjc.dicelang.EvaluatorResult.Type.FAILURE; -import static bjc.dicelang.EvaluatorResult.Type.FLOAT; -import static bjc.dicelang.EvaluatorResult.Type.INT; -import static bjc.dicelang.EvaluatorResult.Type.STRING; - -/* - * @TODO 10/09/17 Ben Culkin :EvaluatorSplit - * - * Type/sanity checking should be moved into a seperate stage, not part of - * evaluation. - */ -/** - * Evaluate DiceLang ASTs - * - * @author EVE - * - */ -public class Evaluator { - /* The steps of type coercion. */ - private static enum CoerceSteps { - INTEGER, DOUBLE; - } - - /* The context during iteration. */ - private static class Context { - public Consumer>> thunk; - - public boolean isDebug; - - public Context() { - /* Empty block. */ - } - } - - /* The engine we are connected to. */ - private final DiceLangEngine eng; - - /** - * Create a new evaluator. - * - * @param en - * The engine. - */ - public Evaluator(final DiceLangEngine en) { - eng = en; - } - - /** - * Evaluate a AST. - * - * @param comm - * The AST to evaluate. - * - * @return The result of the tree. - */ - public EvaluatorResult evaluate(final ITree comm) { - final Context ctx = new Context(); - - ctx.isDebug = false; - ctx.thunk = itr -> { - /* - * Deliberately finish the iterator, but ignore results. It's only for stepwise - * evaluation, but we don't know if stepping the iterator has side effects. - */ - while (itr.hasNext()) { - itr.next(); - } - }; - - /* The result. */ - final ITree res = comm.topDownTransform(this::pickEvaluationType, node -> this.evaluateNode(node, ctx)); - - return res.getHead().resultVal; - } - - /* - * @NOTE - * - * This is broken until stepwise top-down transforms are fixed. - */ - public Iterator> stepDebug(final ITree comm) { - final Context ctx = new Context(); - - ctx.isDebug = true; - - return new TopDownTransformIterator<>(this::pickEvaluationType, (node, thnk) -> { - ctx.thunk = thnk; - - return this.evaluateNode(node, ctx); - }, comm); - } - - /* Pick the way to evaluate a node. */ - private TopDownTransformResult pickEvaluationType(final Node nd) { - switch (nd.type) { - case UNARYOP: - switch (nd.operatorType) { - case COERCE: - /* Coerce does special things to the tree. */ - return TopDownTransformResult.RTRANSFORM; - default: - return TopDownTransformResult.PUSHDOWN; - } - - default: - return TopDownTransformResult.PUSHDOWN; - } - } - - /* Evaluate a node. */ - private ITree evaluateNode(final ITree ast, final Context ctx) { - switch (ast.getHead().type) { - case UNARYOP: - return evaluateUnaryOp(ast, ctx); - case BINOP: - return evaluateBinaryOp(ast, ctx); - case TOKREF: - return evaluateTokenRef(ast.getHead().tokenVal, ctx); - case ROOT: - return ast.getChild(ast.getChildrenCount() - 1); - case RESULT: - return ast; - default: - Errors.inst.printError(EK_EVAL_INVNODE, ast.getHead().type.toString()); - return new Tree<>(Node.FAIL(ast)); - } - } - - /* Evaluate a unary operator. */ - private ITree evaluateUnaryOp(final ITree ast, final Context ctx) { - /* Unary operators only take one operand. */ - if (ast.getChildrenCount() != 1) { - Errors.inst.printError(EK_EVAL_UNUNARY, Integer.toString(ast.getChildrenCount())); - return new Tree<>(Node.FAIL(ast)); - } - - switch (ast.getHead().operatorType) { - /* - * @TODO 10/09/17 Ben Culkin :CoerceRefactor - * - * :EvaluatorSplit - * - * Coercing should be moved to its own class, or at the very least its own - * method. When the evaluator splits, this node type'll be handled exclusively - * by the type-checker. - * - * Coerce also needs to be able to coerce things to dice and ratios (whenever - * they get added). - */ - case COERCE: - final ITree toCoerce = ast.getChild(0); - final ITree retVal = new Tree<>(toCoerce.getHead()); - final Deque> children = new LinkedList<>(); - - /* The current type we are coercing to. */ - CoerceSteps curLevel = CoerceSteps.INTEGER; - - for (int i = 0; i < toCoerce.getChildrenCount(); i++) { - final ITree child = toCoerce.getChild(i); - ITree nChild = null; - - /* Tell our thunk we processed a node. */ - if (ctx.isDebug) { - /* Evaluate each step of the child. */ - final Iterator> nd = stepDebug(child); - - for (; nd.hasNext(); nChild = nd.next()) { - ctx.thunk.accept(new SingleIterator<>(child)); - } - } else { - /* Evaluate the child. */ - nChild = new Tree<>(new Node(Node.Type.RESULT, evaluate(child))); - - ctx.thunk.accept(new SingleIterator<>(nChild)); - } - - if (nChild == null) { - Errors.inst.printError(EK_EVAL_INVNODE); - return new Tree<>(Node.FAIL(ast)); - } - - final Node childNode = nChild.getHead(); - final EvaluatorResult res = childNode.resultVal; - - /* Move up to coercing to a float. */ - if (res.type == FLOAT) { - curLevel = CoerceSteps.DOUBLE; - } - - children.add(nChild); - } - - for (final ITree child : children) { - final Node nd = child.getHead(); - final EvaluatorResult res = nd.resultVal; - - switch (res.type) { - case INT: - /* Coerce ints to doubles if we need to. */ - if (curLevel == CoerceSteps.DOUBLE) { - nd.resultVal = new EvaluatorResult(FLOAT, (double) res.intVal); - } - default: - /* Do nothing */ - break; - } - - retVal.addChild(child); - } - - return retVal; - case DICESCALAR: - final EvaluatorResult opr = ast.getChild(0).getHead().resultVal; - - if (opr.type != INT) { - Errors.inst.printError(EK_EVAL_INVDCREATE, opr.type.toString()); - } - - final EvaluatorResult sres = new EvaluatorResult(DICE, new ScalarDie(opr.intVal)); - return new Tree<>(new Node(Node.Type.RESULT, sres)); - case DICEFUDGE: - final EvaluatorResult oprn = ast.getChild(0).getHead().resultVal; - - if (oprn.type != INT) { - Errors.inst.printError(EK_EVAL_INVDCREATE, oprn.type.toString()); - } - - final EvaluatorResult fres = new EvaluatorResult(DICE, new ScalarDie(oprn.intVal)); - return new Tree<>(new Node(Node.Type.RESULT, fres)); - default: - Errors.inst.printError(EK_EVAL_INVUNARY, ast.getHead().operatorType.toString()); - return new Tree<>(Node.FAIL(ast)); - } - } - - /* Evaluate a binary operator. */ - private static ITree evaluateBinaryOp(final ITree ast, final Context ctx) { - final Token.Type binOp = ast.getHead().operatorType; - - /* Binary operators always have two children. */ - if (ast.getChildrenCount() != 2) { - Errors.inst.printError(EK_EVAL_INVBIN, Integer.toString(ast.getChildrenCount()), ast.toString()); - - return new Tree<>(Node.FAIL(ast)); - } - - final ITree left = ast.getChild(0); - final ITree right = ast.getChild(1); - - final EvaluatorResult leftRes = left.getHead().resultVal; - final EvaluatorResult rightRes = right.getHead().resultVal; - - switch (binOp) { - case ADD: - case SUBTRACT: - case MULTIPLY: - case DIVIDE: - case IDIVIDE: - return evaluateMathBinary(binOp, leftRes, rightRes, ctx); - case DICEGROUP: - case DICECONCAT: - case DICELIST: - return evaluateDiceBinary(binOp, leftRes, rightRes, ctx); - case STRCAT: - case STRREP: - return evaluateStringBinary(binOp, leftRes, rightRes, ctx); - default: - Errors.inst.printError(EK_EVAL_UNBIN, binOp.toString()); - return new Tree<>(Node.FAIL(ast)); - } - } - - /* Evaluate a binary operator on strings. */ - private static ITree evaluateStringBinary(final Token.Type op, final EvaluatorResult left, - final EvaluatorResult right, final Context ctx) { - if (left.type != STRING) { - Errors.inst.printError(EK_EVAL_INVSTRING, left.type.toString()); - return new Tree<>(Node.FAIL(left)); - } - - final String strang = left.stringVal; - - switch (op) { - case STRCAT: - if (right.type != STRING) { - Errors.inst.printError(EK_EVAL_UNSTRING, right.type.toString()); - return new Tree<>(Node.FAIL(right)); - } - - final String strung = right.stringVal; - final EvaluatorResult cres = new EvaluatorResult(STRING, strang + strung); - - return new Tree<>(new Node(Node.Type.RESULT, cres)); - case STRREP: - if (right.type != INT) { - Errors.inst.printError(EK_EVAL_INVSTRING, right.type.toString()); - return new Tree<>(Node.FAIL(right)); - } - - String res = strang; - final long count = right.intVal; - - for (long i = 1; i < count; i++) { - res += strang; - } - - return new Tree<>(new Node(Node.Type.RESULT, new EvaluatorResult(STRING, res))); - default: - Errors.inst.printError(EK_EVAL_UNSTRING, op.toString()); - return new Tree<>(Node.FAIL()); - } - } - - /* Evaluate dice binary operators. */ - private static ITree evaluateDiceBinary(final Token.Type op, final EvaluatorResult left, - final EvaluatorResult right, final Context ctx) { - EvaluatorResult res = null; - - switch (op) { - /* - * @TODO 10/09/17 Ben Culkin :DiceSimplify - * - * Figure out some way to simplify this sort of thing. - */ - case DICEGROUP: - if (left.type == DICE && !left.diceVal.isList()) { - Die lhs = ((ScalarDiceExpression) left.diceVal).scalar; - - if (right.type == DICE && !right.diceVal.isList()) { - Die rhs = ((ScalarDiceExpression) right.diceVal).scalar; - - Die simple = new SimpleDie(lhs, rhs); - - res = new EvaluatorResult(DICE, simple); - } else if (right.type == INT) { - res = new EvaluatorResult(DICE, new SimpleDie(lhs, right.intVal)); - } else { - Errors.inst.printError(EK_EVAL_INVDGROUP, right.type.toString()); - return new Tree<>(Node.FAIL(right)); - } - } else if (left.type == INT) { - if (right.type == DICE && !right.diceVal.isList()) { - Die rhs = ((ScalarDiceExpression) right.diceVal).scalar; - - res = new EvaluatorResult(DICE, new SimpleDie(left.intVal, rhs)); - } else if (right.type == INT) { - res = new EvaluatorResult(DICE, new SimpleDie(left.intVal, right.intVal)); - } else { - Errors.inst.printError(EK_EVAL_INVDGROUP, right.type.toString()); - return new Tree<>(Node.FAIL(right)); - } - } else { - Errors.inst.printError(EK_EVAL_INVDGROUP, left.type.toString()); - return new Tree<>(Node.FAIL(left)); - } - - case DICECONCAT: - if (left.type != DICE || left.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, left.type.toString()); - return new Tree<>(Node.FAIL(left)); - } else if (right.type != DICE || right.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, right.type.toString()); - return new Tree<>(Node.FAIL(right)); - } else { - Die lhs = ((ScalarDiceExpression) left.diceVal).scalar; - Die rhs = ((ScalarDiceExpression) right.diceVal).scalar; - - res = new EvaluatorResult(DICE, new CompoundDie(lhs, rhs)); - } - - break; - - case DICELIST: - if (left.type != DICE || left.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, left.type.toString()); - return new Tree<>(Node.FAIL(left)); - } else if (right.type != DICE || right.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, right.type.toString()); - return new Tree<>(Node.FAIL(right)); - } else { - Die lhs = ((ScalarDiceExpression) left.diceVal).scalar; - Die rhs = ((ScalarDiceExpression) right.diceVal).scalar; - - res = new EvaluatorResult(DICE, new SimpleDieList(lhs, rhs)); - } - - break; - - default: - Errors.inst.printError(EK_EVAL_UNDICE, op.toString()); - return new Tree<>(Node.FAIL()); - } - - return new Tree<>(new Node(Node.Type.RESULT, res)); - } - - /* Evaluate a binary math operator. */ - private static ITree evaluateMathBinary(final Token.Type op, final EvaluatorResult left, - final EvaluatorResult right, final Context ctx) { - if (left.type == STRING || right.type == STRING) { - Errors.inst.printError(EK_EVAL_STRINGMATH); - return new Tree<>(Node.FAIL()); - } else if (left.type == FAILURE || right.type == FAILURE) { - return new Tree<>(Node.FAIL()); - } else if (left.type == INT && right.type != INT) { - Errors.inst.printError(EK_EVAL_MISMATH); - return new Tree<>(Node.FAIL(right)); - } else if (left.type == FLOAT && right.type != FLOAT) { - Errors.inst.printError(EK_EVAL_MISMATH); - return new Tree<>(Node.FAIL(right)); - } else if (left.type == DICE && right.type != DICE) { - Errors.inst.printError(EK_EVAL_MISMATH); - return new Tree<>(Node.FAIL(right)); - } else if (right.type == INT && left.type != INT) { - Errors.inst.printError(EK_EVAL_MISMATH); - return new Tree<>(Node.FAIL(left)); - } else if (right.type == FLOAT && left.type != FLOAT) { - Errors.inst.printError(EK_EVAL_MISMATH); - return new Tree<>(Node.FAIL(left)); - } else if (right.type == DICE && left.type != DICE) { - Errors.inst.printError(EK_EVAL_MISMATH); - return new Tree<>(Node.FAIL(left)); - } - - EvaluatorResult res = null; - - switch (op) { - case ADD: - if (left.type == INT) { - res = new EvaluatorResult(INT, left.intVal + right.intVal); - } else if (left.type == DICE) { - if (left.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); - return new Tree<>(Node.FAIL(left)); - } else if (right.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); - return new Tree<>(Node.FAIL(right)); - } - - Die lhs = ((ScalarDiceExpression) left.diceVal).scalar; - Die rhs = ((ScalarDiceExpression) right.diceVal).scalar; - - res = new EvaluatorResult(DICE, new MathDie(MathDie.MathOp.ADD, lhs, rhs)); - } else { - res = new EvaluatorResult(FLOAT, left.floatVal + right.floatVal); - } - - break; - - case SUBTRACT: - if (left.type == INT) { - res = new EvaluatorResult(INT, left.intVal - right.intVal); - } else if (left.type == DICE) { - if (left.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); - return new Tree<>(Node.FAIL(left)); - } else if (right.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); - return new Tree<>(Node.FAIL(right)); - } - - Die lhs = ((ScalarDiceExpression) left.diceVal).scalar; - Die rhs = ((ScalarDiceExpression) right.diceVal).scalar; - - res = new EvaluatorResult(DICE, new MathDie(MathDie.MathOp.SUBTRACT, lhs, rhs)); - } else { - res = new EvaluatorResult(FLOAT, left.floatVal - right.floatVal); - } - - break; - - case MULTIPLY: - if (left.type == INT) { - res = new EvaluatorResult(INT, left.intVal * right.intVal); - } else if (left.type == DICE) { - if (left.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); - return new Tree<>(Node.FAIL(left)); - } else if (right.diceVal.isList()) { - Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); - return new Tree<>(Node.FAIL(right)); - } - - Die lhs = ((ScalarDiceExpression) left.diceVal).scalar; - Die rhs = ((ScalarDiceExpression) right.diceVal).scalar; - - res = new EvaluatorResult(DICE, new MathDie(MathDie.MathOp.MULTIPLY, lhs, rhs)); - } else { - res = new EvaluatorResult(FLOAT, left.floatVal * right.floatVal); - } - - break; - - case DIVIDE: - if (left.type == INT) { - if (right.intVal == 0) { - Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(FAILURE, right); - } else { - res = new EvaluatorResult(FLOAT, left.intVal / right.intVal); - } - } else if (left.type == FLOAT) { - if (right.floatVal == 0) { - Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(FAILURE, right); - } else { - res = new EvaluatorResult(FLOAT, left.floatVal / right.floatVal); - } - } else { - Errors.inst.printError(EK_EVAL_DIVDICE); - return new Tree<>(Node.FAIL()); - } - - break; - - case IDIVIDE: - if (left.type == INT) { - if (right.intVal == 0) { - Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(FAILURE, right); - } else { - res = new EvaluatorResult(INT, (int) (left.intVal / right.intVal)); - } - } else if (left.type == FLOAT) { - if (right.floatVal == 0) { - Errors.inst.printError(EK_EVAL_DIVZERO); - res = new EvaluatorResult(FAILURE, right); - } else { - res = new EvaluatorResult(INT, (int) (left.floatVal / right.floatVal)); - } - } else { - Errors.inst.printError(EK_EVAL_DIVDICE); - return new Tree<>(Node.FAIL()); - } - - break; - - default: - Errors.inst.printError(EK_EVAL_UNMATH, op.toString()); - return new Tree<>(Node.FAIL()); - } - - return new Tree<>(new Node(Node.Type.RESULT, res)); - } - - /* Evaluate a token reference. */ - private ITree evaluateTokenRef(final Token tk, final Context ctx) { - EvaluatorResult res = null; - - switch (tk.type) { - case INT_LIT: - res = new EvaluatorResult(INT, tk.intValue); - break; - case FLOAT_LIT: - res = new EvaluatorResult(FLOAT, ((FloatToken)tk).floatValue); - break; - case DICE_LIT: - res = new EvaluatorResult(DICE, ((DiceToken) tk).diceValue); - break; - case STRING_LIT: - res = new EvaluatorResult(STRING, eng.getStringLiteral((int) tk.intValue)); - break; - default: - Errors.inst.printError(EK_EVAL_UNTOK, tk.type.toString()); - res = new EvaluatorResult(FAILURE); - } - - return new Tree<>(new Node(Node.Type.RESULT, res)); - } -} diff --git a/base/src/bjc/dicelang/EvaluatorResult.java b/base/src/bjc/dicelang/EvaluatorResult.java deleted file mode 100644 index 0d0706a..0000000 --- a/base/src/bjc/dicelang/EvaluatorResult.java +++ /dev/null @@ -1,223 +0,0 @@ -package bjc.dicelang; - -import bjc.dicelang.dice.Die; -import bjc.dicelang.dice.DiceExpression; -import bjc.dicelang.dice.DieList; -import bjc.dicelang.dice.ListDiceExpression; -import bjc.dicelang.dice.ScalarDiceExpression; -import bjc.utils.data.ITree; -import bjc.utils.data.Tree; - -/* - * @TODO 10/09/17 Ben Culkin :EvalResultReorg - * - * Again, split it into separate classes based off of the type. - */ -/** - * The result from the evaluator. - * - * @author EVE - * - */ -public class EvaluatorResult { - /** - * The type of the result. - * - * @author EVE - * - */ - public static enum Type { - /** - * The type of a failure. - */ - FAILURE, - /** - * The type of an integer. - */ - INT, - /** - * The type of a float. - */ - FLOAT, - /** - * The type of a dice. - */ - DICE, - /** - * The type of a string. - */ - STRING - } - - /** - * The type of the result. - */ - public final EvaluatorResult.Type type; - - // These may or may not have values based - // off of the result type - /** - * The integer value of the result. - */ - public long intVal; - /** - * The float value of the result. - */ - public double floatVal; - /** - * The dice value of the result. - */ - public DiceExpression diceVal; - /** - * The string value of the result. - */ - public String stringVal; - - /** - * Original node data - */ - public ITree origVal; - - /** - * Create a new result. - * - * @param typ - * The type of the result. - */ - public EvaluatorResult(final EvaluatorResult.Type typ) { - type = typ; - } - - /** - * Create a new result. - * - * @param typ - * The type of the result. - * - * @param orig - * The original value of the result. - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final ITree orig) { - this(typ); - - origVal = orig; - } - - /** - * Create a new result. - * - * @param typ - * The type of the result. - * - * @param orig - * The original value of the result. - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final Node orig) { - this(typ, new Tree<>(orig)); - } - - /** - * Create a new result. - * - * @param typ - * @param orig - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final EvaluatorResult orig) { - this(typ, new Node(Node.Type.RESULT, orig)); - } - - /** - * Create a new result. - * - * @param typ - * @param iVal - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final long iVal) { - this(typ); - - intVal = iVal; - } - - /** - * Create a new result. - * - * @param typ - * @param dVal - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final double dVal) { - this(typ); - - floatVal = dVal; - } - - /** - * Create a new result. - * - * @param typ - * @param dVal - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final DiceExpression dVal) { - this(typ); - - diceVal = dVal; - } - - /** - * Create a new result. - * - * @param typ - * @param dVal - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final Die dVal) { - this(typ); - - diceVal = new ScalarDiceExpression(dVal); - } - - /** - * Create a new result. - * - * @param typ - * @param dVal - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final DieList dVal) { - this(typ); - - diceVal = new ListDiceExpression(dVal); - } - - /** - * Create a new result. - * - * @param typ - * @param strang - */ - public EvaluatorResult(final EvaluatorResult.Type typ, final String strang) { - this(typ); - - stringVal = strang; - } - - @Override - public String toString() { - switch (type) { - case INT: - return type.toString() + "(" + intVal + ")"; - - case FLOAT: - return type.toString() + "(" + floatVal + ")"; - - case DICE: - return type.toString() + "(" + diceVal + ")"; - - case STRING: - return type.toString() + "(" + stringVal + ")"; - - case FAILURE: - return type.toString(); - - default: - return "Unknown result type " + type.toString(); - } - } -} diff --git a/base/src/bjc/dicelang/Node.java b/base/src/bjc/dicelang/Node.java index ff0c83d..7e97f9c 100644 --- a/base/src/bjc/dicelang/Node.java +++ b/base/src/bjc/dicelang/Node.java @@ -1,12 +1,14 @@ package bjc.dicelang; -import static bjc.dicelang.EvaluatorResult.Type.FAILURE; - +import bjc.dicelang.eval.EvaluatorResult; +import bjc.dicelang.eval.FailureEvaluatorResult; import bjc.utils.data.ITree; /* - * @TODO 10/09/17 Ben Culkin :NodeReorg Same thing, different class. Split into - * subclasses based off of the type values. + * @TODO 10/09/17 Ben Culkin :NodeReorg + * + * Same thing, different class. Split into subclasses based off of the type + * values. */ /** * Represents a node in the AST. @@ -106,24 +108,30 @@ public class Node { return super.hashCode(); } - static Node FAIL(final EvaluatorResult res) { - EvaluatorResult eres = new EvaluatorResult(FAILURE, new Node(Type.RESULT, res)); + public static Node FAIL(final EvaluatorResult res) { + Node nd = new Node(Type.RESULT, res); + + EvaluatorResult eres = new FailureEvaluatorResult(nd); + return new Node(Type.RESULT, eres); } - static Node FAIL(final Node orig) { - return new Node(Type.RESULT, new EvaluatorResult(FAILURE, orig)); + public static Node FAIL(final Node orig) { + FailureEvaluatorResult res = new FailureEvaluatorResult(orig); + + return new Node(Type.RESULT, res); } - static Node FAIL(final ITree orig) { - return new Node(Type.RESULT, new EvaluatorResult(FAILURE, orig)); + public static Node FAIL(final ITree orig) { + FailureEvaluatorResult res = new FailureEvaluatorResult(orig); + + return new Node(Type.RESULT, res); } - /* - * @TODO 10/09/17 Ben Culkin :NodeFAIL These methods should be moved to Node. - */ /* Create a failing node. */ - static Node FAIL() { - return new Node(Type.RESULT, new EvaluatorResult(FAILURE)); + public static Node FAIL() { + FailureEvaluatorResult res = new FailureEvaluatorResult(); + + return new Node(Type.RESULT, res); } } diff --git a/base/src/bjc/dicelang/eval/DiceEvaluatorResult.java b/base/src/bjc/dicelang/eval/DiceEvaluatorResult.java new file mode 100644 index 0000000..8e50333 --- /dev/null +++ b/base/src/bjc/dicelang/eval/DiceEvaluatorResult.java @@ -0,0 +1,32 @@ +package bjc.dicelang.eval; + +import bjc.dicelang.dice.DiceExpression; +import bjc.dicelang.dice.Die; +import bjc.dicelang.dice.DieList; +import bjc.dicelang.dice.ListDiceExpression; +import bjc.dicelang.dice.ScalarDiceExpression; + +public class DiceEvaluatorResult extends EvaluatorResult { + /** + * The dice value of the result. + */ + public DiceExpression diceVal; + + public DiceEvaluatorResult(DiceExpression expr) { + super(Type.DICE); + + diceVal = expr; + } + + public DiceEvaluatorResult(Die die) { + this(new ScalarDiceExpression(die)); + } + + public DiceEvaluatorResult(DieList list) { + this(new ListDiceExpression(list)); + } + + public boolean isList() { + return diceVal.isList(); + } +} diff --git a/base/src/bjc/dicelang/eval/Evaluator.java b/base/src/bjc/dicelang/eval/Evaluator.java new file mode 100644 index 0000000..4cc2a1e --- /dev/null +++ b/base/src/bjc/dicelang/eval/Evaluator.java @@ -0,0 +1,602 @@ +package bjc.dicelang.eval; + +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.function.Consumer; + +import bjc.dicelang.DiceLangEngine; +import bjc.dicelang.DiceToken; +import bjc.dicelang.Errors; +import bjc.dicelang.FloatToken; +import bjc.dicelang.Node; +import bjc.dicelang.Token; +import bjc.dicelang.dice.CompoundDie; +import bjc.dicelang.dice.Die; +import bjc.dicelang.dice.MathDie; +import bjc.dicelang.dice.ScalarDiceExpression; +import bjc.dicelang.dice.ScalarDie; +import bjc.dicelang.dice.SimpleDie; +import bjc.dicelang.dice.SimpleDieList; + +import bjc.utils.data.ITree; +import bjc.utils.data.SingleIterator; +import bjc.utils.data.TopDownTransformIterator; +import bjc.utils.data.TopDownTransformResult; +import bjc.utils.data.Tree; + +import static bjc.dicelang.Errors.ErrorKey.*; +import static bjc.dicelang.eval.EvaluatorResult.Type.*; + +/* + * @TODO 10/09/17 Ben Culkin :EvaluatorSplit + * + * Type/sanity checking should be moved into a seperate stage, not part of + * evaluation. + */ +/** + * Evaluate DiceLang ASTs + * + * @author EVE + * + */ +public class Evaluator { + /* The steps of type coercion. */ + private static enum CoerceSteps { + INTEGER, DOUBLE; + } + + /* The context during iteration. */ + private static class Context { + public Consumer>> thunk; + + public boolean isDebug; + + public Context() { + /* Empty block. */ + } + } + + /* The engine we are connected to. */ + private final DiceLangEngine eng; + + /** + * Create a new evaluator. + * + * @param en + * The engine. + */ + public Evaluator(final DiceLangEngine en) { + eng = en; + } + + /** + * Evaluate a AST. + * + * @param comm + * The AST to evaluate. + * + * @return The result of the tree. + */ + public EvaluatorResult evaluate(final ITree comm) { + final Context ctx = new Context(); + + ctx.isDebug = false; + ctx.thunk = itr -> { + /* + * Deliberately finish the iterator, but ignore results. It's only for stepwise + * evaluation, but we don't know if stepping the iterator has side effects. + */ + while (itr.hasNext()) { + itr.next(); + } + }; + + /* The result. */ + final ITree res = comm.topDownTransform(this::pickEvaluationType, node -> this.evaluateNode(node, ctx)); + + return res.getHead().resultVal; + } + + /* + * @NOTE + * + * This is broken until stepwise top-down transforms are fixed. + */ + public Iterator> stepDebug(final ITree comm) { + final Context ctx = new Context(); + + ctx.isDebug = true; + + return new TopDownTransformIterator<>(this::pickEvaluationType, (node, thnk) -> { + ctx.thunk = thnk; + + return this.evaluateNode(node, ctx); + }, comm); + } + + /* Pick the way to evaluate a node. */ + private TopDownTransformResult pickEvaluationType(final Node nd) { + switch (nd.type) { + case UNARYOP: + switch (nd.operatorType) { + case COERCE: + /* Coerce does special things to the tree. */ + return TopDownTransformResult.RTRANSFORM; + default: + return TopDownTransformResult.PUSHDOWN; + } + + default: + return TopDownTransformResult.PUSHDOWN; + } + } + + /* Evaluate a node. */ + private ITree evaluateNode(final ITree ast, final Context ctx) { + switch (ast.getHead().type) { + case UNARYOP: + return evaluateUnaryOp(ast, ctx); + case BINOP: + return evaluateBinaryOp(ast, ctx); + case TOKREF: + return evaluateTokenRef(ast.getHead().tokenVal, ctx); + case ROOT: + return ast.getChild(ast.getChildrenCount() - 1); + case RESULT: + return ast; + default: + Errors.inst.printError(EK_EVAL_INVNODE, ast.getHead().type.toString()); + return new Tree<>(Node.FAIL(ast)); + } + } + + /* Evaluate a unary operator. */ + private ITree evaluateUnaryOp(final ITree ast, final Context ctx) { + /* Unary operators only take one operand. */ + if (ast.getChildrenCount() != 1) { + Errors.inst.printError(EK_EVAL_UNUNARY, Integer.toString(ast.getChildrenCount())); + return new Tree<>(Node.FAIL(ast)); + } + + switch (ast.getHead().operatorType) { + /* + * @TODO 10/09/17 Ben Culkin :CoerceRefactor + * + * :EvaluatorSplit + * + * Coercing should be moved to its own class, or at the very least its own + * method. When the evaluator splits, this node type'll be handled exclusively + * by the type-checker. + * + * Coerce also needs to be able to coerce things to dice and ratios (whenever + * they get added). + */ + case COERCE: + final ITree toCoerce = ast.getChild(0); + final ITree retVal = new Tree<>(toCoerce.getHead()); + final Deque> children = new LinkedList<>(); + + /* The current type we are coercing to. */ + CoerceSteps curLevel = CoerceSteps.INTEGER; + + for (int i = 0; i < toCoerce.getChildrenCount(); i++) { + final ITree child = toCoerce.getChild(i); + ITree nChild = null; + + /* Tell our thunk we processed a node. */ + if (ctx.isDebug) { + /* Evaluate each step of the child. */ + final Iterator> nd = stepDebug(child); + + for (; nd.hasNext(); nChild = nd.next()) { + ctx.thunk.accept(new SingleIterator<>(child)); + } + } else { + /* Evaluate the child. */ + nChild = new Tree<>(new Node(Node.Type.RESULT, evaluate(child))); + + ctx.thunk.accept(new SingleIterator<>(nChild)); + } + + if (nChild == null) { + Errors.inst.printError(EK_EVAL_INVNODE); + return new Tree<>(Node.FAIL(ast)); + } + + final Node childNode = nChild.getHead(); + final EvaluatorResult res = childNode.resultVal; + + /* Move up to coercing to a float. */ + if (res.type == FLOAT) { + curLevel = CoerceSteps.DOUBLE; + } + + children.add(nChild); + } + + for (final ITree child : children) { + final Node nd = child.getHead(); + final EvaluatorResult res = nd.resultVal; + + switch (res.type) { + case INT: + /* Coerce ints to doubles if we need to. */ + if (curLevel == CoerceSteps.DOUBLE) { + nd.resultVal = new FloatEvaluatorResult((double) res.intVal); + } + default: + /* Do nothing */ + break; + } + + retVal.addChild(child); + } + + return retVal; + case DICESCALAR: + final EvaluatorResult opr = ast.getChild(0).getHead().resultVal; + + if (opr.type != INT) { + Errors.inst.printError(EK_EVAL_INVDCREATE, opr.type.toString()); + } + + final EvaluatorResult sres = new DiceEvaluatorResult(new ScalarDie(opr.intVal)); + return new Tree<>(new Node(Node.Type.RESULT, sres)); + case DICEFUDGE: + final EvaluatorResult oprn = ast.getChild(0).getHead().resultVal; + + if (oprn.type != INT) { + Errors.inst.printError(EK_EVAL_INVDCREATE, oprn.type.toString()); + } + + final EvaluatorResult fres = new DiceEvaluatorResult(new ScalarDie(oprn.intVal)); + return new Tree<>(new Node(Node.Type.RESULT, fres)); + default: + Errors.inst.printError(EK_EVAL_INVUNARY, ast.getHead().operatorType.toString()); + return new Tree<>(Node.FAIL(ast)); + } + } + + /* Evaluate a binary operator. */ + private static ITree evaluateBinaryOp(final ITree ast, final Context ctx) { + final Token.Type binOp = ast.getHead().operatorType; + + /* Binary operators always have two children. */ + if (ast.getChildrenCount() != 2) { + Errors.inst.printError(EK_EVAL_INVBIN, Integer.toString(ast.getChildrenCount()), ast.toString()); + + return new Tree<>(Node.FAIL(ast)); + } + + final ITree left = ast.getChild(0); + final ITree right = ast.getChild(1); + + final EvaluatorResult leftRes = left.getHead().resultVal; + final EvaluatorResult rightRes = right.getHead().resultVal; + + switch (binOp) { + case ADD: + case SUBTRACT: + case MULTIPLY: + case DIVIDE: + case IDIVIDE: + return evaluateMathBinary(binOp, leftRes, rightRes, ctx); + case DICEGROUP: + case DICECONCAT: + case DICELIST: + return evaluateDiceBinary(binOp, leftRes, rightRes, ctx); + case STRCAT: + case STRREP: + return evaluateStringBinary(binOp, leftRes, rightRes, ctx); + default: + Errors.inst.printError(EK_EVAL_UNBIN, binOp.toString()); + return new Tree<>(Node.FAIL(ast)); + } + } + + /* Evaluate a binary operator on strings. */ + private static ITree evaluateStringBinary(final Token.Type op, final EvaluatorResult left, + final EvaluatorResult right, final Context ctx) { + if (left.type != STRING) { + Errors.inst.printError(EK_EVAL_INVSTRING, left.type.toString()); + return new Tree<>(Node.FAIL(left)); + } + + final String strang = ((StringEvaluatorResult) left).stringVal; + + switch (op) { + case STRCAT: + if (right.type != STRING) { + Errors.inst.printError(EK_EVAL_UNSTRING, right.type.toString()); + return new Tree<>(Node.FAIL(right)); + } + + final String strung = ((StringEvaluatorResult) right).stringVal; + final EvaluatorResult cres = new StringEvaluatorResult(strang + strung); + + return new Tree<>(new Node(Node.Type.RESULT, cres)); + case STRREP: + if (right.type != INT) { + Errors.inst.printError(EK_EVAL_INVSTRING, right.type.toString()); + return new Tree<>(Node.FAIL(right)); + } + + String res = strang; + final long count = right.intVal; + + for (long i = 1; i < count; i++) { + res += strang; + } + + return new Tree<>(new Node(Node.Type.RESULT, new StringEvaluatorResult(res))); + default: + Errors.inst.printError(EK_EVAL_UNSTRING, op.toString()); + return new Tree<>(Node.FAIL()); + } + } + + /* Evaluate dice binary operators. */ + private static ITree evaluateDiceBinary(final Token.Type op, final EvaluatorResult left, + final EvaluatorResult right, final Context ctx) { + EvaluatorResult res = null; + + switch (op) { + /* + * @TODO 10/09/17 Ben Culkin :DiceSimplify + * + * Figure out some way to simplify this sort of thing. + * + * ADDENDA: Replace the .diceVal.isList() with .isList() + */ + case DICEGROUP: + if (left.type == DICE && !((DiceEvaluatorResult) left).diceVal.isList()) { + Die lhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) left).diceVal).scalar; + + if (right.type == DICE && !((DiceEvaluatorResult) right).diceVal.isList()) { + Die rhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) right).diceVal).scalar; + + Die simple = new SimpleDie(lhs, rhs); + + res = new DiceEvaluatorResult(simple); + } else if (right.type == INT) { + res = new DiceEvaluatorResult(new SimpleDie(lhs, right.intVal)); + } else { + Errors.inst.printError(EK_EVAL_INVDGROUP, right.type.toString()); + return new Tree<>(Node.FAIL(right)); + } + } else if (left.type == INT) { + if (right.type == DICE && !((DiceEvaluatorResult) right).diceVal.isList()) { + Die rhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) right).diceVal).scalar; + + res = new DiceEvaluatorResult(new SimpleDie(left.intVal, rhs)); + } else if (right.type == INT) { + res = new DiceEvaluatorResult(new SimpleDie(left.intVal, right.intVal)); + } else { + Errors.inst.printError(EK_EVAL_INVDGROUP, right.type.toString()); + return new Tree<>(Node.FAIL(right)); + } + } else { + Errors.inst.printError(EK_EVAL_INVDGROUP, left.type.toString()); + return new Tree<>(Node.FAIL(left)); + } + + case DICECONCAT: + if (left.type != DICE || ((DiceEvaluatorResult) left).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, left.type.toString()); + return new Tree<>(Node.FAIL(left)); + } else if (right.type != DICE || ((DiceEvaluatorResult) right).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, right.type.toString()); + return new Tree<>(Node.FAIL(right)); + } else { + Die lhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) left).diceVal).scalar; + Die rhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) right).diceVal).scalar; + + res = new DiceEvaluatorResult(new CompoundDie(lhs, rhs)); + } + + break; + + case DICELIST: + if (left.type != DICE || ((DiceEvaluatorResult) left).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, left.type.toString()); + return new Tree<>(Node.FAIL(left)); + } else if (right.type != DICE || ((DiceEvaluatorResult) right).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, right.type.toString()); + return new Tree<>(Node.FAIL(right)); + } else { + Die lhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) left).diceVal).scalar; + Die rhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) right).diceVal).scalar; + + res = new DiceEvaluatorResult(new SimpleDieList(lhs, rhs)); + } + + break; + + default: + Errors.inst.printError(EK_EVAL_UNDICE, op.toString()); + return new Tree<>(Node.FAIL()); + } + + return new Tree<>(new Node(Node.Type.RESULT, res)); + } + + /* Evaluate a binary math operator. */ + private static ITree evaluateMathBinary(final Token.Type op, final EvaluatorResult left, + final EvaluatorResult right, final Context ctx) { + if (left.type == STRING || right.type == STRING) { + Errors.inst.printError(EK_EVAL_STRINGMATH); + return new Tree<>(Node.FAIL()); + } else if (left.type == FAILURE || right.type == FAILURE) { + return new Tree<>(Node.FAIL()); + } else if (left.type == INT && right.type != INT) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(Node.FAIL(right)); + } else if (left.type == FLOAT && right.type != FLOAT) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(Node.FAIL(right)); + } else if (left.type == DICE && right.type != DICE) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(Node.FAIL(right)); + } else if (right.type == INT && left.type != INT) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(Node.FAIL(left)); + } else if (right.type == FLOAT && left.type != FLOAT) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(Node.FAIL(left)); + } else if (right.type == DICE && left.type != DICE) { + Errors.inst.printError(EK_EVAL_MISMATH); + return new Tree<>(Node.FAIL(left)); + } + + EvaluatorResult res = null; + + switch (op) { + case ADD: + if (left.type == INT) { + res = new EvaluatorResult(INT, left.intVal + right.intVal); + } else if (left.type == DICE) { + if (((DiceEvaluatorResult) left).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); + return new Tree<>(Node.FAIL(left)); + } else if (((DiceEvaluatorResult) right).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); + return new Tree<>(Node.FAIL(right)); + } + + Die lhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) left).diceVal).scalar; + Die rhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) right).diceVal).scalar; + + res = new DiceEvaluatorResult(new MathDie(MathDie.MathOp.ADD, lhs, rhs)); + } else { + res = new FloatEvaluatorResult( + ((FloatEvaluatorResult) left).floatVal + ((FloatEvaluatorResult) right).floatVal); + } + + break; + + case SUBTRACT: + if (left.type == INT) { + res = new EvaluatorResult(INT, left.intVal - right.intVal); + } else if (left.type == DICE) { + if (((DiceEvaluatorResult) left).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); + return new Tree<>(Node.FAIL(left)); + } else if (((DiceEvaluatorResult) right).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); + return new Tree<>(Node.FAIL(right)); + } + + Die lhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) left).diceVal).scalar; + Die rhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) right).diceVal).scalar; + + res = new DiceEvaluatorResult(new MathDie(MathDie.MathOp.SUBTRACT, lhs, rhs)); + } else { + res = new FloatEvaluatorResult( + ((FloatEvaluatorResult) left).floatVal - ((FloatEvaluatorResult) right).floatVal); + } + + break; + + case MULTIPLY: + if (left.type == INT) { + res = new EvaluatorResult(INT, left.intVal * right.intVal); + } else if (left.type == DICE) { + if (((DiceEvaluatorResult) left).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, left.toString()); + return new Tree<>(Node.FAIL(left)); + } else if (((DiceEvaluatorResult) right).diceVal.isList()) { + Errors.inst.printError(EK_EVAL_INVDICE, right.toString()); + return new Tree<>(Node.FAIL(right)); + } + + Die lhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) left).diceVal).scalar; + Die rhs = ((ScalarDiceExpression) ((DiceEvaluatorResult) right).diceVal).scalar; + + res = new DiceEvaluatorResult(new MathDie(MathDie.MathOp.MULTIPLY, lhs, rhs)); + } else { + res = new FloatEvaluatorResult( + ((FloatEvaluatorResult) left).floatVal * ((FloatEvaluatorResult) right).floatVal); + } + + break; + + case DIVIDE: + if (left.type == INT) { + if (right.intVal == 0) { + Errors.inst.printError(EK_EVAL_DIVZERO); + res = new FailureEvaluatorResult(right); + } else { + res = new EvaluatorResult(FLOAT, left.intVal / right.intVal); + } + } else if (left.type == FLOAT) { + if (((FloatEvaluatorResult) right).floatVal == 0) { + Errors.inst.printError(EK_EVAL_DIVZERO); + res = new FailureEvaluatorResult(right); + } else { + res = new FloatEvaluatorResult( + ((FloatEvaluatorResult) left).floatVal / ((FloatEvaluatorResult) right).floatVal); + } + } else { + Errors.inst.printError(EK_EVAL_DIVDICE); + return new Tree<>(Node.FAIL()); + } + + break; + + case IDIVIDE: + if (left.type == INT) { + if (right.intVal == 0) { + Errors.inst.printError(EK_EVAL_DIVZERO); + res = new FailureEvaluatorResult(right); + } else { + res = new EvaluatorResult(INT, (int) (left.intVal / right.intVal)); + } + } else if (left.type == FLOAT) { + if (((FloatEvaluatorResult) right).floatVal == 0) { + Errors.inst.printError(EK_EVAL_DIVZERO); + res = new FailureEvaluatorResult(right); + } else { + res = new EvaluatorResult(INT, + (int) (((FloatEvaluatorResult) left).floatVal / ((FloatEvaluatorResult) right).floatVal)); + } + } else { + Errors.inst.printError(EK_EVAL_DIVDICE); + return new Tree<>(Node.FAIL()); + } + + break; + + default: + Errors.inst.printError(EK_EVAL_UNMATH, op.toString()); + return new Tree<>(Node.FAIL()); + } + + return new Tree<>(new Node(Node.Type.RESULT, res)); + } + + /* Evaluate a token reference. */ + private ITree evaluateTokenRef(final Token tk, final Context ctx) { + EvaluatorResult res = null; + + switch (tk.type) { + case INT_LIT: + res = new EvaluatorResult(INT, tk.intValue); + break; + case FLOAT_LIT: + res = new FloatEvaluatorResult(((FloatToken) tk).floatValue); + break; + case DICE_LIT: + res = new DiceEvaluatorResult(((DiceToken) tk).diceValue); + break; + case STRING_LIT: + res = new StringEvaluatorResult(eng.getStringLiteral((int) tk.intValue)); + break; + default: + Errors.inst.printError(EK_EVAL_UNTOK, tk.type.toString()); + res = new EvaluatorResult(FAILURE); + } + + return new Tree<>(new Node(Node.Type.RESULT, res)); + } +} diff --git a/base/src/bjc/dicelang/eval/EvaluatorResult.java b/base/src/bjc/dicelang/eval/EvaluatorResult.java new file mode 100644 index 0000000..3ac0202 --- /dev/null +++ b/base/src/bjc/dicelang/eval/EvaluatorResult.java @@ -0,0 +1,82 @@ +package bjc.dicelang.eval; + +/* + * @TODO 10/09/17 Ben Culkin :EvalResultReorg + * + * Again, split it into separate classes based off of the type. + */ +/** + * The result from the evaluator. + * + * @author EVE + * + */ +public class EvaluatorResult { + /** + * The type of the result. + * + * @author EVE + * + */ + public static enum Type { + /** + * The type of a failure. + */ + FAILURE, + /** + * The type of an integer. + */ + INT, + /** + * The type of a float. + */ + FLOAT, + /** + * The type of a dice. + */ + DICE, + /** + * The type of a string. + */ + STRING + } + + /** + * The type of the result. + */ + public final EvaluatorResult.Type type; + + // These may or may not have values based + // off of the result type + /** + * The integer value of the result. + */ + public long intVal; + + /** + * Create a new result. + * + * @param typ + * The type of the result. + */ + protected EvaluatorResult(final EvaluatorResult.Type typ) { + type = typ; + } + + /** + * Create a new result. + * + * @param typ + * @param iVal + */ + public EvaluatorResult(final EvaluatorResult.Type typ, final long iVal) { + this(typ); + + intVal = iVal; + } + + @Override + public String toString() { + return type.toString(); + } +} diff --git a/base/src/bjc/dicelang/eval/FailureEvaluatorResult.java b/base/src/bjc/dicelang/eval/FailureEvaluatorResult.java new file mode 100644 index 0000000..3b641db --- /dev/null +++ b/base/src/bjc/dicelang/eval/FailureEvaluatorResult.java @@ -0,0 +1,55 @@ +package bjc.dicelang.eval; + +import bjc.dicelang.Node; +import bjc.utils.data.ITree; +import bjc.utils.data.Tree; + +public class FailureEvaluatorResult extends EvaluatorResult { + /** + * Original node data + */ + public ITree origVal; + + public FailureEvaluatorResult() { + super(Type.FAILURE); + } + + public FailureEvaluatorResult(final ITree orig) { + super(Type.FAILURE); + + origVal = orig; + } + + public FailureEvaluatorResult(final Node orig) { + this(new Tree<>(orig)); + } + + public FailureEvaluatorResult(EvaluatorResult right) { + this(new Node(Node.Type.RESULT, right)); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((origVal == null) ? 0 : origVal.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + FailureEvaluatorResult other = (FailureEvaluatorResult) obj; + if (origVal == null) { + if (other.origVal != null) + return false; + } else if (!origVal.equals(other.origVal)) + return false; + return true; + } +} diff --git a/base/src/bjc/dicelang/eval/FloatEvaluatorResult.java b/base/src/bjc/dicelang/eval/FloatEvaluatorResult.java new file mode 100644 index 0000000..7fbbcdc --- /dev/null +++ b/base/src/bjc/dicelang/eval/FloatEvaluatorResult.java @@ -0,0 +1,43 @@ +package bjc.dicelang.eval; + +public class FloatEvaluatorResult extends EvaluatorResult { + /** + * The float value of the result. + */ + public double floatVal; + + public FloatEvaluatorResult(double val) { + super(Type.FLOAT); + + floatVal = val; + } + + @Override + public String toString() { + return super.toString() + "(" + floatVal + ")"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(floatVal); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + FloatEvaluatorResult other = (FloatEvaluatorResult) obj; + if (Double.doubleToLongBits(floatVal) != Double.doubleToLongBits(other.floatVal)) + return false; + return true; + } +} diff --git a/base/src/bjc/dicelang/eval/StringEvaluatorResult.java b/base/src/bjc/dicelang/eval/StringEvaluatorResult.java new file mode 100644 index 0000000..870fd01 --- /dev/null +++ b/base/src/bjc/dicelang/eval/StringEvaluatorResult.java @@ -0,0 +1,44 @@ +package bjc.dicelang.eval; + +public class StringEvaluatorResult extends EvaluatorResult { + /** + * The string value of the result. + */ + public String stringVal; + + public StringEvaluatorResult(String strang) { + super(Type.STRING); + + stringVal = strang; + } + + @Override + public String toString() { + return super.toString() + "(" + stringVal + ")"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((stringVal == null) ? 0 : stringVal.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + StringEvaluatorResult other = (StringEvaluatorResult) obj; + if (stringVal == null) { + if (other.stringVal != null) + return false; + } else if (!stringVal.equals(other.stringVal)) + return false; + return true; + } +} -- cgit v1.2.3