From 1cf218ba93396c7be7f4b3ee25d8008a41777273 Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Sat, 11 Feb 2017 08:44:28 -0500 Subject: Parse double and some dice --- dice-lang/src/bjc/dicelang/v2/DiceBox.java | 102 ++++++++++++++++++++ dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java | 112 ++++++++++++++++++++-- dice-lang/src/bjc/dicelang/v2/DoubleMatcher.java | 58 +++++++++++ dice-lang/src/bjc/dicelang/v2/Shunter.java | 59 ++++++++++++ dice-lang/src/bjc/dicelang/v2/Token.java | 74 ++++++++++++++ 5 files changed, 398 insertions(+), 7 deletions(-) create mode 100644 dice-lang/src/bjc/dicelang/v2/DiceBox.java create mode 100644 dice-lang/src/bjc/dicelang/v2/DoubleMatcher.java create mode 100644 dice-lang/src/bjc/dicelang/v2/Shunter.java create mode 100644 dice-lang/src/bjc/dicelang/v2/Token.java diff --git a/dice-lang/src/bjc/dicelang/v2/DiceBox.java b/dice-lang/src/bjc/dicelang/v2/DiceBox.java new file mode 100644 index 0000000..86bce4d --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/DiceBox.java @@ -0,0 +1,102 @@ +package bjc.dicelang.v2; + +import java.util.Random; + +public class DiceBox { + private static final Random rng = new Random(); + + public interface Die { + boolean canOptimize(); + int optimize(); + + int roll(); + } + + private static class ScalarDie implements Die { + private int val; + + public ScalarDie(int vl) { + val = vl; + } + + public boolean canOptimize() { + return true; + } + + public int optimize() { + return val; + } + + public int roll() { + return val; + } + + public String toString() { + return Integer.toString(val); + } + } + + private static class SimpleDie implements Die { + private int numDice; + private int diceSize; + + public SimpleDie(int nDice, int size) { + numDice = nDice; + diceSize = size; + } + + public boolean canOptimize() { + if(diceSize == 1) return true; + else return false; + } + + public int optimize() { + return numDice; + } + + public int roll() { + int total = 0; + + for(int i = 0; i < numDice; i++) { + total += rng.nextInt(i) + 1; + } + + return total; + } + + public String toString() { + return numDice + "d" + diceSize; + } + } + + public static Die parseExpression(String exp) { + if(!isValidExpression(exp)) return null; + + if(exp.matches(scalarDiePattern)) { + return new ScalarDie(Integer.parseInt(exp)); + } else if(exp.matches(simpleDiePattern)) { + String[] dieParts = exp.split("d"); + + if(dieParts[0].equals("")) { + return new SimpleDie(1, Integer.parseInt(dieParts[1])); + } else { + return new SimpleDie(Integer.parseInt(dieParts[0]), Integer.parseInt(dieParts[1])); + } + } + + return null; + } + + private static final String scalarDiePattern = "[\\+\\-]?\\d+"; + private static final String simpleDiePattern = "(?:\\d+)?d\\d+"; + + public static boolean isValidExpression(String exp) { + if(exp.matches(scalarDiePattern)) { + return true; + } else if(exp.matches(simpleDiePattern)) { + return true; + } else { + return false; + } + } +} diff --git a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java index 51006d7..d8a43c5 100644 --- a/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java +++ b/dice-lang/src/bjc/dicelang/v2/DiceLangEngine.java @@ -12,6 +12,9 @@ import bjc.utils.funcutils.ListUtils; import java.util.Arrays; import java.util.Deque; import java.util.LinkedList; +import java.util.regex.Pattern; + +import static bjc.dicelang.v2.Token.Type.*; public class DiceLangEngine { // Input rules for processing tokens @@ -24,12 +27,17 @@ public class DiceLangEngine { // Debug indicator private boolean debugMode; + private final int MATH_PREC = 20; + private final int DICE_PREC = 10; + private final int EXPR_PREC = 0; + public DiceLangEngine() { opExpansionTokens = new LinkedList<>(); opExpansionTokens.add(new Pair<>("+", "\\+")); opExpansionTokens.add(new Pair<>("-", "-")); opExpansionTokens.add(new Pair<>("*", "\\*")); + opExpansionTokens.add(new Pair<>("//", "//")); opExpansionTokens.add(new Pair<>("/", "/")); opExpansionTokens.add(new Pair<>(":=", ":=")); opExpansionTokens.add(new Pair<>("=>", "=>")); @@ -37,12 +45,12 @@ public class DiceLangEngine { deaffixationTokens = new LinkedList<>(); deaffixationTokens.add(new Pair<>("(", "\\(")); - deaffixationTokens.add(new Pair<>(")", "\\(")); + deaffixationTokens.add(new Pair<>(")", "\\)")); deaffixationTokens.add(new Pair<>("[", "\\[")); deaffixationTokens.add(new Pair<>("]", "\\]")); nextLiteral = 1; - + // @TODO make configurable debugMode = true; } @@ -77,9 +85,99 @@ public class DiceLangEngine { }); } + IList semiExpandedTokens = + ListUtils.deAffixTokens( + destringed, deaffixationTokens); + + IList fullyExpandedTokens = + ListUtils.splitTokens( + semiExpandedTokens, opExpansionTokens); + + if(debugMode) + System.out.printf("\tCommand after token" + + " expansion: " + + fullyExpandedTokens.toString() + + "\n"); + + IList lexedTokens = new FunctionalList<>(); + + for(String token : fullyExpandedTokens.toIterable()) { + Token tk = lexToken(token); + + if(tk == null) continue; + else if(tk == Token.NIL_TOKEN) return false; + else lexedTokens.add(tk); + } + + if(debugMode) + System.out.printf("\tCommand after tokenization: %s\n", lexedTokens.toString()); + return true; } + private Token lexToken(String token) { + if(token.equals("")) return null; + + Token tk = Token.NIL_TOKEN; + + switch(token) { + case "+": + tk = new Token(ADD); + break; + case "-": + tk = new Token(SUBTRACT); + break; + case "*": + tk = new Token(MULTIPLY); + break; + case "/": + tk = new Token(DIVIDE); + break; + case "//": + tk = new Token(IDIVIDE); + break; + case "(": + tk = new Token(OPAREN); + break; + case ")": + tk = new Token(CPAREN); + break; + case "[": + tk = new Token(OBRACKET); + break; + case "]": + tk = new Token(CBRACKET); + break; + default: + + tk = tokenizeLiteral(token); + } + + return tk; + } + + private Pattern intMatcher = Pattern.compile( + "[\\-\\+]?\\d+"); + + private Token tokenizeLiteral(String token) { + Token tk = Token.NIL_TOKEN; + + if(DoubleMatcher.floatingLiteral.matcher(token).matches()) { + tk = new Token(FLOAT_LIT, Double.parseDouble(token)); + } else if(intMatcher.matcher(token).matches()) { + tk = new Token(INT_LIT, Integer.parseInt(token)); + } else if(DiceBox.isValidExpression(token)) { + tk = new Token(DICE_LIT, DiceBox.parseExpression(token)); + } else { + System.out.printf("\tERROR: Unrecognized token:" + + "%s\n", token); + + return tk; + } + + return tk; + } + private boolean destringTokens(IList tokens, IMap stringLiterals, IList destringed) { @@ -96,7 +194,7 @@ public class DiceLangEngine { String litName = literalName + nextLiteral++; stringLiterals.put(litName, - token.substring(1, token.length() - 1)); + token.substring(1, token.length() - 1)); destringed.add(litName); continue; @@ -105,7 +203,7 @@ public class DiceLangEngine { if(stringMode) { // @TODO make this not an error System.out.printf("\tPARSER ERROR: Initial" - +" quotes can only start strings\n"); + +" quotes can only start strings\n"); } else { currentLiteral.append(token.substring(1) + " "); @@ -115,11 +213,11 @@ public class DiceLangEngine { if(!stringMode) { // @TODO make this not an error System.out.printf("\tPARSER ERROR: Terminal" - +" quotes can only end strings\n"); + +" quotes can only end strings\n"); return false; } else { currentLiteral.append( - token.substring(0, token.length() - 1)); + token.substring(0, token.length() - 1)); String litName = literalName + nextLiteral++; @@ -137,7 +235,7 @@ public class DiceLangEngine { currentLiteral.append(token + " "); } else { System.out.printf("\tERROR: Escaped quote " - + " outside of string literal\n"); + + " outside of string literal\n"); return false; } } else { diff --git a/dice-lang/src/bjc/dicelang/v2/DoubleMatcher.java b/dice-lang/src/bjc/dicelang/v2/DoubleMatcher.java new file mode 100644 index 0000000..3c6f6c5 --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/DoubleMatcher.java @@ -0,0 +1,58 @@ +package bjc.dicelang.v2; + +import java.util.regex.Pattern; + +/** + * Checks if a string would pass Double.parseDouble. + * + * Uses a regex from the javadoc for Double.valueOf() + */ +public class DoubleMatcher { + private static final String Digits = + "(\\p{Digit}+)"; + private static final String HexDigits = + "(\\p{XDigit}+)"; + + // an exponent is 'e' or 'E' followed by an optionally + // signed decimal integer. + private static final String Exp = + "[eE][+-]?" + Digits; + + private static final String fpRegex = + ("[\\x00-\\x20]*" + + // Optional leading "whitespace" + "[+-]?(" + // Optional sign character + "NaN|" + // "NaN" string + "Infinity|" + // "Infinity" string + + // A decimal floating-point string representing a finite positive + // number without a leading sign has at most five basic pieces: + // Digits . Digits ExponentPart FloatTypeSuffix + // + // Since this method allows integer-only strings as input + // in addition to strings of floating-point literals, the + // two sub-patterns below are simplifications of the grammar + // productions from section 3.10.2 of + // The Java™ Language Specification. + + // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt + "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ + + // . Digits ExponentPart_opt FloatTypeSuffix_opt + "(\\.("+Digits+")("+Exp+")?)|"+ + + // Hexadecimal strings + "((" + + // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "(\\.)?)|" + + + // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt + "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + + + ")[pP][+-]?" + Digits + "))" + + "[fFdD]?))" + + "[\\x00-\\x20]*");// Optional trailing "whitespace" + + public static final Pattern floatingLiteral = Pattern.compile(fpRegex); + +} diff --git a/dice-lang/src/bjc/dicelang/v2/Shunter.java b/dice-lang/src/bjc/dicelang/v2/Shunter.java new file mode 100644 index 0000000..bca08eb --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/Shunter.java @@ -0,0 +1,59 @@ +package bjc.dicelang.v2; + +import bjc.utils.funcdata.FunctionalList; +import bjc.utils.funcdata.FunctionalMap; +import bjc.utils.funcdata.IList; +import bjc.utils.funcdata.IMap; + +import java.util.HashSet; +import java.util.Set; + +import static bjc.dicelang.v2.Token.Type.*; + +public class Shunter { + // The binary operators and their + // priorities + private IMap ops; + + // Unary operators that can only be + // applied to non-operator tokens + private Set unaryAdjectives; + + // Unary operators that con only be + // applied to operator tokens + private Set unaryAdverbs; + + private final int MATH_PREC = 20; + private final int DICE_PREC = 10; + private final int EXPR_PREC = 0; + + public Shunter() { + ops = new FunctionalMap<>(); + + unaryAdjectives = new HashSet<>(); + unaryAdverbs = new HashSet<>(); + + ops.put(ADD, 0 + MATH_PREC); + ops.put(SUBTRACT, 0 + MATH_PREC); + + ops.put(MULTIPLY, 1 + MATH_PREC); + ops.put(IDIVIDE, 1 + MATH_PREC); + ops.put(DIVIDE, 1 + MATH_PREC); + + ops.put(DICEGROUP, 0 + DICE_PREC); + ops.put(DICECONCAT, 1 + DICE_PREC); + + ops.put(LET, 0 + EXPR_PREC); + ops.put(BIND, 1 + EXPR_PREC); + } + + public IList shuntTokens(IList tks) { + IList returned = new FunctionalList<>(); + + for(Token tk : tks.toIterable()) { + + } + + return returned; + } +} diff --git a/dice-lang/src/bjc/dicelang/v2/Token.java b/dice-lang/src/bjc/dicelang/v2/Token.java new file mode 100644 index 0000000..471832e --- /dev/null +++ b/dice-lang/src/bjc/dicelang/v2/Token.java @@ -0,0 +1,74 @@ +package bjc.dicelang.v2; + +import bjc.dicelang.IDiceExpression; + +/** + * Lexer token + */ +public class Token { + public final static Token NIL_TOKEN = new Token(Type.NIL); + + /** + * Possible token types + */ + public static enum Type { + ADD, SUBTRACT, + MULTIPLY, + DIVIDE, IDIVIDE, + INT_LIT, FLOAT_LIT, STRING_LIT, + VREF, + DICE_LIT, DICEGROUP, DICECONCAT, + LET, BIND, + OPAREN, CPAREN, + OBRACKET, CBRACKET, + NIL, + } + + public final Type type; + + // At most one of these is valid + // based on the token type + public int intValue; + public double floatValue; + public DiceBox.Die diceValue; + + public Token(Type typ) { + type = typ; + } + + public Token(Type typ, int val) { + this(typ); + + intValue = val; + } + + public Token(Type typ, double val) { + this(typ); + + floatValue = val; + } + + public Token(Type typ, DiceBox.Die val) { + this(typ); + + diceValue = val; + } + + public String toString() { + switch(type) { + case INT_LIT: + case STRING_LIT: + case VREF: + return type.toString() + "(" + + intValue + ")"; + case FLOAT_LIT: + return type.toString() + "(" + + floatValue + ")"; + case DICE_LIT: + return type.toString() + "(" + + diceValue + ")"; + default: + return type.toString(); + } + } +} -- cgit v1.2.3