diff options
4 files changed, 88 insertions, 20 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/NeuSearchCalculator.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/NeuSearchCalculator.java index f7306f2a..71dedb12 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/NeuSearchCalculator.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/NeuSearchCalculator.java @@ -20,10 +20,14 @@ package io.github.moulberry.notenoughupdates.miscgui; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.auction.APIManager; +import io.github.moulberry.notenoughupdates.util.ApiUtil; import io.github.moulberry.notenoughupdates.util.Calculator; +import javax.swing.text.html.Option; import java.math.BigDecimal; import java.text.DecimalFormat; +import java.util.Optional; public class NeuSearchCalculator { @@ -43,7 +47,7 @@ public class NeuSearchCalculator { if (!lastInput.equals(input)) { lastInput = input; try { - BigDecimal calculate = Calculator.calculate(input); + BigDecimal calculate = Calculator.calculate(input, PROVIDE_LOWEST_BIN); lastResult = new DecimalFormat("#,##0.##").format(calculate); } catch (Calculator.CalculatorException ignored) { lastResult = null; @@ -52,4 +56,12 @@ public class NeuSearchCalculator { return lastResult; } + + public static Calculator.VariableProvider PROVIDE_LOWEST_BIN = name -> { + double bazaarOrBin = NotEnoughUpdates.INSTANCE.manager.auctionManager.getBazaarOrBin(name, false); + if (bazaarOrBin < 0) + return Optional.empty(); + return Optional.of(BigDecimal.valueOf(bazaarOrBin)); + }; + } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Calculator.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Calculator.java index 4d57d92f..a58fc8a6 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/Calculator.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Calculator.java @@ -27,16 +27,26 @@ import java.util.Deque; import java.util.List; import java.util.Locale; import java.util.NoSuchElementException; +import java.util.Optional; public class Calculator { + public interface VariableProvider { + Optional<BigDecimal> provideVariable(String name) throws CalculatorException; + } + + public static BigDecimal calculate(String source, VariableProvider variables) throws CalculatorException { + return evaluate(variables, shuntingYard(lex(source))); + } + public static BigDecimal calculate(String source) throws CalculatorException { - source = source.toLowerCase(Locale.ROOT); - return evaluate(shuntingYard(lex(source))); + return calculate(source, (ignored) -> Optional.empty()); } + ///<editor-fold desc="Lexing Time"> public enum TokenType { - NUMBER, BINOP, LPAREN, RPAREN, POSTOP, PREOP + NUMBER, BINOP, LPAREN, RPAREN, POSTOP, PREOP, VARIABLE } + public static class Token { public TokenType type; String operatorValue; @@ -49,6 +59,7 @@ public class Calculator { static String binops = "+-*/^x"; static String postops = "mkbts%"; static String digits = "0123456789"; + static String nameCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_"; static void readDigitsInto(Token token, String source, boolean decimals) { int startIndex = token.tokenStart + token.tokenLength; @@ -103,7 +114,7 @@ public class Calculator { } else if (binops.indexOf(c) != -1) { token.tokenLength = 1; token.type = TokenType.BINOP; - token.operatorValue = c + ""; + token.operatorValue = String.valueOf(c); if (c == '*' && i + 1 < source.length() && source.charAt(i + 1) == '*') { token.tokenLength++; token.operatorValue = "^"; @@ -111,7 +122,7 @@ public class Calculator { } else if (postops.indexOf(c) != -1) { token.tokenLength = 1; token.type = TokenType.POSTOP; - token.operatorValue = c + ""; + token.operatorValue = String.valueOf(c).toLowerCase(Locale.ROOT); } else if (c == ')') { token.tokenLength = 1; token.type = TokenType.RPAREN; @@ -127,6 +138,30 @@ public class Calculator { if (token.tokenLength == 1) { throw new CalculatorException("Invalid number literal", i, 1); } + } else if ('$' == c) { + token.tokenLength = 1; + token.type = TokenType.VARIABLE; + token.operatorValue = ""; + boolean inParenthesis = false; + if (i + 1 < source.length() && source.charAt(i + 1) == '{') { + token.tokenLength++; + inParenthesis = true; + } + for (int j = token.tokenStart + token.tokenLength; j < source.length(); j++) { + char d = source.charAt(j); + if (inParenthesis) { + if (d == '}') { + token.tokenLength++; + inParenthesis = false; + break; + } + } else if (nameCharacters.indexOf(d) == -1) break; + token.operatorValue += d; + token.tokenLength++; + } + if (token.operatorValue.length() == 0 || inParenthesis) { + throw new CalculatorException("Unterminated variable literal", token.tokenStart, token.tokenLength); + } } else if (digits.indexOf(c) != -1) { token.type = TokenType.NUMBER; readDigitsInto(token, source, false); @@ -140,7 +175,7 @@ public class Calculator { } else { throw new CalculatorException("Unknown thing " + c, i, 1); } - justParsedNumber = token.type == TokenType.NUMBER; + justParsedNumber = token.type == TokenType.NUMBER || token.type == TokenType.VARIABLE; tokens.add(token); i += token.tokenLength; } @@ -174,6 +209,7 @@ public class Calculator { for (Token currentlyShunting : toShunt) { switch (currentlyShunting.type) { case NUMBER: + case VARIABLE: out.add(currentlyShunting); break; case BINOP: @@ -230,11 +266,19 @@ public class Calculator { /// </editor-fold> ///<editor-fold desc="Evaluating Time"> - public static BigDecimal evaluate(List<Token> rpnTokens) throws CalculatorException { + public static BigDecimal evaluate(VariableProvider provider, List<Token> rpnTokens) throws CalculatorException { Deque<BigDecimal> values = new ArrayDeque<>(); try { for (Token command : rpnTokens) { switch (command.type) { + case VARIABLE: + values.push(provider.provideVariable(command.operatorValue) + .orElseThrow(() -> new CalculatorException( + "Unknown variable " + command.operatorValue, + command.tokenStart, + command.tokenLength + ))); + break; case PREOP: values.push(values.pop().negate()); break; diff --git a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt index caa57909..64ee444b 100644 --- a/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt +++ b/src/main/kotlin/io/github/moulberry/notenoughupdates/commands/misc/MiscCommands.kt @@ -28,6 +28,7 @@ import io.github.moulberry.notenoughupdates.events.RegisterBrigadierCommandEvent import io.github.moulberry.notenoughupdates.miscgui.CalendarOverlay import io.github.moulberry.notenoughupdates.miscgui.DynamicLightItemsEditor import io.github.moulberry.notenoughupdates.miscgui.GuiItemCustomize +import io.github.moulberry.notenoughupdates.miscgui.NeuSearchCalculator import io.github.moulberry.notenoughupdates.util.Calculator import io.github.moulberry.notenoughupdates.util.Calculator.CalculatorException import io.github.moulberry.notenoughupdates.util.MinecraftExecutor @@ -51,7 +52,7 @@ class MiscCommands { thenArgumentExecute("calculation", RestArgumentType) { calculation -> val calculation = this[calculation] try { - val calculate = Calculator.calculate(calculation) + val calculate = Calculator.calculate(calculation, NeuSearchCalculator.PROVIDE_LOWEST_BIN) val formatter = DecimalFormat("#,##0.##") val formatted = formatter.format(calculate) reply("$WHITE$calculation $YELLOW= $GREEN$formatted") diff --git a/src/test/java/io/github/moulberry/notenoughupdates/util/CalculatorTest.java b/src/test/java/io/github/moulberry/notenoughupdates/util/CalculatorTest.java index 66ba5442..eff4a8de 100644 --- a/src/test/java/io/github/moulberry/notenoughupdates/util/CalculatorTest.java +++ b/src/test/java/io/github/moulberry/notenoughupdates/util/CalculatorTest.java @@ -21,21 +21,32 @@ package io.github.moulberry.notenoughupdates.util; import java.math.BigDecimal; import java.util.List; +import java.util.Optional; +import java.util.Scanner; public class CalculatorTest { public static void main(String[] args) throws Calculator.CalculatorException { - List<Calculator.Token> lex = Calculator.lex("10k + 3 * 4m"); - List<Calculator.Token> shunted = Calculator.shuntingYard(lex); - for (Calculator.Token rawToken : shunted) { - System.out.printf( - "%s(%s)", - rawToken.type, - rawToken.operatorValue == null ? rawToken.numericValue + " * 10 ^ " + rawToken.exponent : rawToken.operatorValue - ); + Scanner s = new Scanner(System.in); + while (true) { + try { + List<Calculator.Token> lex = Calculator.lex(s.nextLine()); + List<Calculator.Token> shunted = Calculator.shuntingYard(lex); + for (Calculator.Token rawToken : shunted) { + System.out.printf( + "%s(%s)", + rawToken.type, + rawToken.operatorValue == null + ? rawToken.numericValue + " * 10 ^ " + rawToken.exponent + : rawToken.operatorValue + ); + } + System.out.println(); + BigDecimal evaluate = Calculator.evaluate(name -> Optional.of(BigDecimal.valueOf(16)), shunted); + System.out.println("Eval: " + evaluate); + } catch (Calculator.CalculatorException e) { + e.printStackTrace(); + } } - System.out.println(); - BigDecimal evaluate = Calculator.evaluate(shunted); - System.out.println("Eval: " + evaluate); } } |