private void ProcessOperator(StringScanner steve, Stack<string> opStack, List<Token> output, bool lastTokenWasNumber, ref bool tokenIsNumber) { string op = steve.Read ().ToString(); if (lastTokenWasNumber) { PushOperatorToStack (op, opStack, output); } else if (op.Equals ("-")) { // The last token wasn't a number, but we encountered an operator, so this must be a negative sign steve.skipWhitespace (); if (!steve.HasNext ()) { throw new InvalidExpressionException("misplaced operator: " + op); } BigInteger num = 1; // if there's an expression after the minus sign, just process it and negate it if (steve.TryReadLong (ref num) || Char.ToLower (steve.Peek ()) == 'd' || steve.Peek() == '(' || steve.Peek() == '[') { output.Add (new NumToken(-num)); // if the next token is a diedef, paren, or formula, iterativelyAdd will detect the -1 and use it to negate the expression tokenIsNumber = true; } else { // there is no number after the minus sign, so it can't be negating a number, and it can't be doing subtraction throw new InvalidExpressionException ("misplaced operator: " + op); } } else { throw new InvalidExpressionException ("misplaced operator" + op); } }
// 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; }