public static String parseRollInfo(RollInfo rollInfo, String rollString) { // To really do this right, we change the token string // as we go along so that we maintain parser state by // means of the tokens rather than something more // explicit. In truth, this is an ideal application // of flex and friends for a "mini-language" whose // statements evaluate to dice rolls. Too much LISP // on the brain. --bko final StringTokenizer st = new StringTokenizer(rollString, " ", true); try { String tok = st.nextToken("d"); if ("d".equals(tok)) { rollInfo.times = 1; } else { rollInfo.times = Integer.parseInt(tok); if (st.hasMoreTokens()) { tok = st.nextToken("d"); // discard the 'd' if (!"d".equals(tok)) { return "Bad roll parsing in '" + rollString + "': missing 'd'"; } } else { rollInfo.sides = 1; return ""; } } String parseChars = "/\\|mM+-tT"; rollInfo.sides = Integer.parseInt(st.nextToken(parseChars)); if (rollInfo.sides < 1) { return "Bad roll parsing in '" + rollString + "': sides < 1: " + rollInfo.sides; } while (st.hasMoreTokens()) { tok = st.nextToken(parseChars); switch (tok.charAt(0)) { case '/': parseChars = "mM+-tT"; final int keepTop = Integer.parseInt(st.nextToken(parseChars)); if (keepTop > rollInfo.times) { return "Bad keepTop in '" + rollString + "': times: " + rollInfo.times + "; keepTop: " + keepTop; } rollInfo.keepList = new boolean[rollInfo.times]; // Rely on fact boolean is false by default. --bko for (int i = rollInfo.times - keepTop; i < rollInfo.times; ++i) { rollInfo.keepList[i] = true; } break; case '\\': parseChars = "mM+-tT"; final int keepBottom = Integer.parseInt(st.nextToken(parseChars)); if (keepBottom > rollInfo.times) { return "Bad keepBottom in '" + rollString + "': times: " + rollInfo.times + "; keepBottom: " + keepBottom; } rollInfo.keepList = new boolean[rollInfo.times]; // Rely on fact boolean is false by default. --bko for (int i = 0; i < keepBottom; ++i) { rollInfo.keepList[i] = true; } break; case '|': parseChars = "mM+-tT"; tok = st.nextToken(parseChars); rollInfo.keepList = new boolean[rollInfo.times]; final StringTokenizer keepSt = new StringTokenizer(tok, ","); while (keepSt.hasMoreTokens()) { rollInfo.keepList[Integer.parseInt(keepSt.nextToken(",")) - 1] = true; } break; case 'm': parseChars = "M+-tT"; rollInfo.rerollBelow = Integer.parseInt(st.nextToken(parseChars)); break; case 'M': parseChars = "m+-tT"; rollInfo.rerollAbove = Integer.parseInt(st.nextToken(parseChars)); break; case '+': parseChars = "tT"; rollInfo.modifier = Integer.parseInt(st.nextToken(" ")); break; case '-': parseChars = "tT"; rollInfo.modifier = -Integer.parseInt(st.nextToken(" ")); break; case 't': parseChars = "T"; rollInfo.totalFloor = Integer.parseInt(st.nextToken(" ")); break; case 'T': parseChars = "t"; rollInfo.totalCeiling = Integer.parseInt(st.nextToken(" ")); break; default: Logging.errorPrint( "Bizarre dice parser error in '" + rollString + "': not a valid delimiter"); return "Bad roll parsing in '" + rollString + "': invalid delimiter '" + tok.charAt(0) + "'."; } } } catch (NumberFormatException ex) { if (Logging.isDebugMode()) { Logging.debugPrint("Bad roll string in '" + rollString + "': " + ex, ex); } return "Bad roll string in '" + rollString + "': " + ex; } return ""; }