private Token ProcessDieDef( StringScanner steve, Stack<string> opStack, List<Token> output, bool lastTokenWasNumber) { steve.TrySkip(); // move past the d\ if (!steve.HasNext()) throw new InvalidExpressionException("no die type given"); if (Char.IsDigit (steve.Peek ())) { // check that the syntax is valid before just trying to read it Token dieCount = new NumToken(1); if (lastTokenWasNumber) { // the last number was the die count, because it was followed by a 'd' dieCount = output.Last(); output.RemoveAt (output.Count - 1); } BigInteger dieType = steve.ReadLong(); // this is safe because we checked that the next char is a digit // we now know that die type and the die count, now we need to see if there are extra instructions for the roll long keepCount = 1; KeepStrategy keepstrat = KeepStrategy.ALL; if (steve.HasNext () && char.IsLetter (steve.Peek ()) && Char.ToLower (steve.Peek ()) != 'd') { char extension = Char.ToLower (steve.Read ()); if (extension == 'h') { keepstrat = KeepStrategy.HIGHEST; BigInteger temp = null; if (steve.TryReadLong (ref temp)) { if (temp > MAX_DIECOUNT) { throw new InvalidExpressionException (temp.ToString () + " is too many dice"); } else { keepCount = temp.LongValue (); } } } else if (extension == 'l') { keepstrat = KeepStrategy.LOWEST; BigInteger temp = null; if (steve.TryReadLong (ref temp)) { if (temp > MAX_DIECOUNT) { throw new InvalidExpressionException (temp.ToString () + " is too many dice"); } else { keepCount = temp.LongValue (); } } } else { throw new InvalidExpressionException("invalid die extension " + extension); } } var countList = new List<Token> (); countList.Add (dieCount); return new DiceToken (countList, dieType, keepCount, keepstrat, this); } else { throw new InvalidExpressionException("no die type given"); } }
// converts an infix dice formula to a postfix list of tokens. Can return throw InvalidExpressionException if the expression is invalid. private List<Token> InfixToPostfix(string input, int index, string formulaName) { if (formulaName != null) { // we are evaluating a formula, and will push it to the formulaNest so that we can prevent self-referential formulas Assert(!formulaNest.Contains (formulaName), "[" + formulaName + "] is self-referential"); formulaNest.Push(formulaName); } StringScanner steve = new StringScanner (input, index); Stack<string> operatorStack = new Stack<string> (); List<Token> output = new List<Token>(); steve.skipWhitespace (); bool lastTokenWasNumber = false; while (steve.HasNext()) { bool tokenIsNumber = false; if (Char.ToLower (steve.Peek ()) == 'd') { output.Add (ProcessDieDef (steve, operatorStack, output, lastTokenWasNumber)); tokenIsNumber = true; } else if (IsCharOperator (steve.Peek ())) { ProcessOperator (steve, operatorStack, output, lastTokenWasNumber, ref tokenIsNumber); } else if (Char.IsDigit (steve.Peek ())) { output.Add (new NumToken(steve.ReadLong ())); tokenIsNumber = true; } else if (steve.Peek () == '(') { ProcessParentheses(steve, output, lastTokenWasNumber); tokenIsNumber = true; } else if (steve.Peek() == '[') { ProcessFormula(steve, output, lastTokenWasNumber); tokenIsNumber = true; } else if (steve.Peek () == ')') { // processParentheses reads all the valid close-parens, so if we find one here it must be mismatched throw new InvalidExpressionException ("mismatched parentheses"); } else { throw new InvalidExpressionException("invalid symbol: " + steve.Peek()); } steve.skipWhitespace (); lastTokenWasNumber = tokenIsNumber; } while (operatorStack.Count > 0) { Assert (operatorStack.Peek () == "(", "mismatched parentheses"); output.Add(OpToken.Get(operatorStack.Pop ())); } if (formulaName != null) { formulaNest.Pop (); } return output; }