FormulaToken::Operator FormulaToken::asOperator() const { if( isOperator() ) return matchOperator( m_text ); else return OperatorInvalid; }
TokenList KoEnhancedPathFormula::scan( const QString &formula ) const { // parsing state enum { Start, Finish, Bad, InNumber, InDecimal, InExpIndicator, InExponent, InString, InIdentifier } state; TokenList tokens; int i = 0; state = Start; int tokenStart = 0; QString tokenText; QString expr = formula + QChar(); // main loop while( (state != Bad) && (state != Finish) && (i < expr.length() ) ) { QChar ch = expr[i]; switch( state ) { case Start: tokenStart = i; // skip any whitespaces if( ch.isSpace() ) { i++; } // check for number else if( ch.isDigit() ) { state = InNumber; } // beginning with alphanumeric ? // could be identifier, function, function reference, modifier reference else if( isIdentifier( ch ) ) { state = InIdentifier; } // decimal dot ? else if ( ch == '.' ) { tokenText.append( expr[i++] ); state = InDecimal; } // terminator character else if ( ch == QChar::Null ) { state = Finish; } // look for operator match else { QString opString( ch ); int op = matchOperator( opString ); // any matched operator ? if( op != FormulaToken::OperatorInvalid ) { i++; tokens.append( FormulaToken( FormulaToken::TypeOperator, opString, tokenStart ) ); } else state = Bad; } break; case InIdentifier: // consume as long as alpha, dollar sign, question mark, or digit if( isIdentifier( ch ) || ch.isDigit() ) { tokenText.append( expr[i++] ); } // a '(' ? then this must be a function identifier else if( ch == '(' ) { tokens.append( FormulaToken( FormulaToken::TypeFunction, tokenText, tokenStart) ); tokenStart = i; tokenText = ""; state = Start; } // we're done with identifier else { tokens.append( FormulaToken( FormulaToken::TypeReference, tokenText, tokenStart) ); tokenStart = i; tokenText = ""; state = Start; } break; case InNumber: // consume as long as it's digit if( ch.isDigit() ) { tokenText.append( expr[i++] ); } // decimal dot ? else if( ch == '.' ) { tokenText.append( '.' ); i++; state = InDecimal; } // exponent ? else if( ch.toUpper() == 'E' ) { tokenText.append( 'E' ); i++; state = InExpIndicator; } // we're done with integer number else { tokens.append( FormulaToken( FormulaToken::TypeNumber, tokenText, tokenStart ) ); tokenText = ""; state = Start; }; break; case InDecimal: // consume as long as it's digit if( ch.isDigit() ) { tokenText.append( expr[i++] ); } // exponent ? else if( ch.toUpper() == 'E' ) { tokenText.append( 'E' ); i++; state = InExpIndicator; } // we're done with floating-point number else { tokens.append( FormulaToken( FormulaToken::TypeNumber, tokenText, tokenStart ) ); tokenText = ""; state = Start; }; break; case InExpIndicator: // possible + or - right after E, e.g 1.23E+12 or 4.67E-8 if( ( ch == '+' ) || ( ch == '-' ) ) { tokenText.append( expr[i++] ); } // consume as long as it's digit else if( ch.isDigit() ) { state = InExponent; } // invalid thing here else state = Bad; break; case InExponent: // consume as long as it's digit if( ch.isDigit() ) { tokenText.append( expr[i++] ); } // we're done with floating-point number else { tokens.append( FormulaToken( FormulaToken::TypeNumber, tokenText, tokenStart ) ); tokenText = ""; state = Start; }; break; case Bad: default: break; } } return tokens; }
bool Calculator::execute(const char *formula, const char *formula_end, double &result) { double value; #if CAVAN_MATH_DEBUG println("formula = %s", text_header(formula, formula_end - formula)); #endif mStackOperand.clear(); mStackOperator.clear(); mLastFieldType = FIELD_TYPE_NONE; while (formula < formula_end) { Operator *op = matchOperator(formula); if (op) { Operator *top; if (mStackOperator.top(top)) { if (top->getPriority() <= op->getPriority()) { if (!top->execute(mStackOperand)) { setErrMsg(top->getErrMsg()); return false; } mStackOperator.pop(top); } } if (op->getOmitMul() && mLastFieldType == FIELD_TYPE_VALUE && mStackOperand.hasData()) { Operator *mul = matchOperator("*"); if (mul) { mStackOperator.push(mul); } } switch (op->getType()) { case OPERATOR_TYPE2: case OPERATOR_TYPE1_RIGHT: if (!mStackOperator.push(op)) { setErrMsg("Operator stack overfrow"); return false; } formula += op->getLength(); break; case OPERATOR_TYPE1_LEFT: case OPERATOR_TYPE_CONSTANT: if (!op->execute(mStackOperand)) { setErrMsg(op->getErrMsg()); return false; } formula += op->getLength(); break; case OPERATOR_TYPE_LIST: { Stack<double> stack(200); if (!parseDataList(formula + op->getLength(), formula_end, &formula, stack)) { return false; } if (!op->execute(stack, mStackOperand)) { setErrMsg(op->getErrMsg()); return false; } break; } default: setErrMsg("Invalid operator"); return false; } mLastFieldType = FIELD_TYPE_OPERATOR; } else { switch (*formula) { case ' ': case '\r': case '\n': case '\t': case '\f': formula++; break; case '0' ... '9': value = text2double_unsigned(formula, formula_end, &formula, 10); if (mLastFieldType == FIELD_TYPE_OPERATOR && mStackOperator.count() == 1 && mStackOperand.isEmpty()) { Operator *op; if (mStackOperator.top(op) && strcmp(op->getSymbol(), "-") == 0) { value = -value; mStackOperator.pop(op); } } if (!mStackOperand.push(value)) { setErrMsg("Operand stack overfrow"); return false; } mLastFieldType = FIELD_TYPE_VALUE; break; case '(': case '[': case '{': { const char *p = get_bracket_pair(formula, formula_end); if (p == NULL) { setErrMsg("No matching brackets"); return false; } Calculator calculator; if (!calculator.execute(formula + 1, p, value)) { setErrMsg(calculator.getErrMsg()); return false; } if (!mStackOperand.push(value)) { setErrMsg("Operand stack overfrow"); return false; } formula = p + 1; mLastFieldType = FIELD_TYPE_BRACKET; break; } default: setErrMsg("Invalid symbol"); return false; } } } while (1) { Operator *op; if (!mStackOperator.pop(op)) { break; } if (!op->execute(mStackOperand)) { setErrMsg(op->getErrMsg()); return false; } } if (!mStackOperand.pop(result)) { setErrMsg("Missing operand"); return false; } if (mStackOperand.hasData()) { setErrMsg("Too much operand"); return false; } return true; }