Esempio n. 1
0
        int Parser::evaluateRelational(Expr* lhs, Expr* rhs)
        {
            if (lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalString)
                return (((LiteralString*)lhs)->value)->compareTo(((LiteralString*)rhs)->value) < 0 ? 1 : 0;

            double l = evaluateToNumber(lhs);
            double r = evaluateToNumber(rhs);
            if (MathUtils::isNaN(l) || MathUtils::isNaN(r))
                return -1;
            return l < r ? 1 : 0;
        }
Esempio n. 2
0
 uint32_t Parser::evaluateToUInt32(Expr* e)
 {
     if (e->tag() == TAG_literalUInt)
         return ((LiteralUInt*)e)->value;
     double d = evaluateToNumber(e);
     if (d == 0 || MathUtils::isNaN(d) || MathUtils::isInfinite(d))
         return 0;
     d = (d < 0 ? -1 : 1) * floor(fabs(d));
     d = fmod(d, 4294967296.0);
     return uint32_t(d);
 }
/*
 * Evaluates this Expr based on the given context node and processor state
 * @param context the context node for evaluation of this Expr
 * @param ps the ContextState containing the stack information needed
 * for evaluation
 * @return the result of the evaluation
 */
nsresult
txCoreFunctionCall::evaluate(txIEvalContext* aContext, txAExprResult** aResult)
{
    *aResult = nsnull;

    if (!requireParams(descriptTable[mType].mMinParams,
                       descriptTable[mType].mMaxParams,
                       aContext)) {
        return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;
    }

    nsresult rv = NS_OK;
    switch (mType) {
        case COUNT:
        {
            nsRefPtr<txNodeSet> nodes;
            rv = evaluateToNodeSet(mParams[0], aContext,
                                   getter_AddRefs(nodes));
            NS_ENSURE_SUCCESS(rv, rv);

            return aContext->recycler()->getNumberResult(nodes->size(),
                                                         aResult);
        }
        case ID:
        {
            nsRefPtr<txAExprResult> exprResult;
            rv = mParams[0]->evaluate(aContext, getter_AddRefs(exprResult));
            NS_ENSURE_SUCCESS(rv, rv);

            nsRefPtr<txNodeSet> resultSet;
            rv = aContext->recycler()->getNodeSet(getter_AddRefs(resultSet));
            NS_ENSURE_SUCCESS(rv, rv);

            txXPathTreeWalker walker(aContext->getContextNode());
            
            if (exprResult->getResultType() == txAExprResult::NODESET) {
                txNodeSet* nodes = static_cast<txNodeSet*>
                                              (static_cast<txAExprResult*>
                                                          (exprResult));
                PRInt32 i;
                for (i = 0; i < nodes->size(); ++i) {
                    nsAutoString idList;
                    txXPathNodeUtils::appendNodeValue(nodes->get(i), idList);
                    nsWhitespaceTokenizer tokenizer(idList);
                    while (tokenizer.hasMoreTokens()) {
                        if (walker.moveToElementById(tokenizer.nextToken())) {
                            resultSet->add(walker.getCurrentPosition());
                        }
                    }
                }
            }
            else {
                nsAutoString idList;
                exprResult->stringValue(idList);
                nsWhitespaceTokenizer tokenizer(idList);
                while (tokenizer.hasMoreTokens()) {
                    if (walker.moveToElementById(tokenizer.nextToken())) {
                        resultSet->add(walker.getCurrentPosition());
                    }
                }
            }

            *aResult = resultSet;
            NS_ADDREF(*aResult);

            return NS_OK;
        }
        case LAST:
        {
            return aContext->recycler()->getNumberResult(aContext->size(),
                                                         aResult);
        }
        case LOCAL_NAME:
        case NAME:
        case NAMESPACE_URI:
        {
            // Check for optional arg
            nsRefPtr<txNodeSet> nodes;
            if (!mParams.IsEmpty()) {
                rv = evaluateToNodeSet(mParams[0], aContext,
                                       getter_AddRefs(nodes));
                NS_ENSURE_SUCCESS(rv, rv);

                if (nodes->isEmpty()) {
                    aContext->recycler()->getEmptyStringResult(aResult);

                    return NS_OK;
                }
            }

            const txXPathNode& node = nodes ? nodes->get(0) :
                                              aContext->getContextNode();
            switch (mType) {
                case LOCAL_NAME:
                {
                    StringResult* strRes = nsnull;
                    rv = aContext->recycler()->getStringResult(&strRes);
                    NS_ENSURE_SUCCESS(rv, rv);

                    *aResult = strRes;
                    txXPathNodeUtils::getLocalName(node, strRes->mValue);

                    return NS_OK;
                }
                case NAMESPACE_URI:
                {
                    StringResult* strRes = nsnull;
                    rv = aContext->recycler()->getStringResult(&strRes);
                    NS_ENSURE_SUCCESS(rv, rv);

                    *aResult = strRes;
                    txXPathNodeUtils::getNamespaceURI(node, strRes->mValue);

                    return NS_OK;
                }
                case NAME:
                {
                    // XXX Namespace: namespaces have a name
                    if (txXPathNodeUtils::isAttribute(node) ||
                        txXPathNodeUtils::isElement(node) ||
                        txXPathNodeUtils::isProcessingInstruction(node)) {
                        StringResult* strRes = nsnull;
                        rv = aContext->recycler()->getStringResult(&strRes);
                        NS_ENSURE_SUCCESS(rv, rv);

                        *aResult = strRes;
                        txXPathNodeUtils::getNodeName(node, strRes->mValue);
                    }
                    else {
                        aContext->recycler()->getEmptyStringResult(aResult);
                    }

                    return NS_OK;
                }
                default:
                {
                    break;
                }
            }
        }
        case POSITION:
        {
            return aContext->recycler()->getNumberResult(aContext->position(),
                                                         aResult);
        }

        // String functions

        case CONCAT:
        {
            nsRefPtr<StringResult> strRes;
            rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
            NS_ENSURE_SUCCESS(rv, rv);

            PRUint32 i, len = mParams.Length();
            for (i = 0; i < len; ++i) {
                rv = mParams[i]->evaluateToString(aContext, strRes->mValue);
                NS_ENSURE_SUCCESS(rv, rv);
            }

            NS_ADDREF(*aResult = strRes);

            return NS_OK;
        }
        case CONTAINS:
        {
            nsAutoString arg2;
            rv = mParams[1]->evaluateToString(aContext, arg2);
            NS_ENSURE_SUCCESS(rv, rv);

            if (arg2.IsEmpty()) {
                aContext->recycler()->getBoolResult(PR_TRUE, aResult);
            }
            else {
                nsAutoString arg1;
                rv = mParams[0]->evaluateToString(aContext, arg1);
                NS_ENSURE_SUCCESS(rv, rv);

                aContext->recycler()->getBoolResult(FindInReadable(arg2, arg1),
                                                    aResult);
            }

            return NS_OK;
        }
        case NORMALIZE_SPACE:
        {
            nsAutoString resultStr;
            if (!mParams.IsEmpty()) {
                rv = mParams[0]->evaluateToString(aContext, resultStr);
                NS_ENSURE_SUCCESS(rv, rv);
            }
            else {
                txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
                                                  resultStr);
            }

            nsRefPtr<StringResult> strRes;
            rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
            NS_ENSURE_SUCCESS(rv, rv);

            MBool addSpace = MB_FALSE;
            MBool first = MB_TRUE;
            strRes->mValue.SetCapacity(resultStr.Length());
            PRUnichar c;
            PRUint32 src;
            for (src = 0; src < resultStr.Length(); src++) {
                c = resultStr.CharAt(src);
                if (XMLUtils::isWhitespace(c)) {
                    addSpace = MB_TRUE;
                }
                else {
                    if (addSpace && !first)
                        strRes->mValue.Append(PRUnichar(' '));

                    strRes->mValue.Append(c);
                    addSpace = MB_FALSE;
                    first = MB_FALSE;
                }
            }
            *aResult = strRes;
            NS_ADDREF(*aResult);

            return NS_OK;
        }
        case STARTS_WITH:
        {
            nsAutoString arg2;
            rv = mParams[1]->evaluateToString(aContext, arg2);
            NS_ENSURE_SUCCESS(rv, rv);

            PRBool result = PR_FALSE;
            if (arg2.IsEmpty()) {
                result = PR_TRUE;
            }
            else {
                nsAutoString arg1;
                rv = mParams[0]->evaluateToString(aContext, arg1);
                NS_ENSURE_SUCCESS(rv, rv);

                result = StringBeginsWith(arg1, arg2);
            }

            aContext->recycler()->getBoolResult(result, aResult);

            return NS_OK;
        }
        case STRING:
        {
            nsRefPtr<StringResult> strRes;
            rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
            NS_ENSURE_SUCCESS(rv, rv);

            if (!mParams.IsEmpty()) {
                rv = mParams[0]->evaluateToString(aContext, strRes->mValue);
                NS_ENSURE_SUCCESS(rv, rv);
            }
            else {
                txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
                                                  strRes->mValue);
            }

            NS_ADDREF(*aResult = strRes);

            return NS_OK;
        }
        case STRING_LENGTH:
        {
            nsAutoString resultStr;
            if (!mParams.IsEmpty()) {
                rv = mParams[0]->evaluateToString(aContext, resultStr);
                NS_ENSURE_SUCCESS(rv, rv);
            }
            else {
                txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
                                                  resultStr);
            }
            rv = aContext->recycler()->getNumberResult(resultStr.Length(),
                                                       aResult);
            NS_ENSURE_SUCCESS(rv, rv);

            return NS_OK;
        }
        case SUBSTRING:
        {
            nsAutoString src;
            rv = mParams[0]->evaluateToString(aContext, src);
            NS_ENSURE_SUCCESS(rv, rv);

            double start;
            rv = evaluateToNumber(mParams[1], aContext, &start);
            NS_ENSURE_SUCCESS(rv, rv);

            // check for NaN or +/-Inf
            if (Double::isNaN(start) ||
                Double::isInfinite(start) ||
                start >= src.Length() + 0.5) {
                aContext->recycler()->getEmptyStringResult(aResult);

                return NS_OK;
            }

            start = floor(start + 0.5) - 1;

            double end;
            if (mParams.Length() == 3) {
                rv = evaluateToNumber(mParams[2], aContext, &end);
                NS_ENSURE_SUCCESS(rv, rv);

                end += start;
                if (Double::isNaN(end) || end < 0) {
                    aContext->recycler()->getEmptyStringResult(aResult);

                    return NS_OK;
                }
                
                if (end > src.Length())
                    end = src.Length();
                else
                    end = floor(end + 0.5);
            }
            else {
                end = src.Length();
            }

            if (start < 0)
                start = 0;
 
            if (start > end) {
                aContext->recycler()->getEmptyStringResult(aResult);
                
                return NS_OK;
            }

            return aContext->recycler()->getStringResult(
                  Substring(src, (PRUint32)start, (PRUint32)(end - start)),
                  aResult);
        }
        case SUBSTRING_AFTER:
        {
            nsAutoString arg1;
            rv = mParams[0]->evaluateToString(aContext, arg1);
            NS_ENSURE_SUCCESS(rv, rv);

            nsAutoString arg2;
            rv = mParams[1]->evaluateToString(aContext, arg2);
            NS_ENSURE_SUCCESS(rv, rv);

            if (arg2.IsEmpty()) {
                return aContext->recycler()->getStringResult(arg1, aResult);
            }

            PRInt32 idx = arg1.Find(arg2);
            if (idx == kNotFound) {
                aContext->recycler()->getEmptyStringResult(aResult);
                
                return NS_OK;
            }

            const nsSubstring& result = Substring(arg1, idx + arg2.Length());
            return aContext->recycler()->getStringResult(result, aResult);
        }
        case SUBSTRING_BEFORE:
        {
            nsAutoString arg2;
            rv = mParams[1]->evaluateToString(aContext, arg2);
            NS_ENSURE_SUCCESS(rv, rv);

            if (arg2.IsEmpty()) {
                aContext->recycler()->getEmptyStringResult(aResult);

                return NS_OK;
            }

            nsAutoString arg1;
            rv = mParams[0]->evaluateToString(aContext, arg1);
            NS_ENSURE_SUCCESS(rv, rv);

            PRInt32 idx = arg1.Find(arg2);
            if (idx == kNotFound) {
                aContext->recycler()->getEmptyStringResult(aResult);
                
                return NS_OK;
            }

            return aContext->recycler()->getStringResult(StringHead(arg1, idx),
                                                         aResult);
        }
        case TRANSLATE:
        {
            nsAutoString src;
            rv = mParams[0]->evaluateToString(aContext, src);
            NS_ENSURE_SUCCESS(rv, rv);

            if (src.IsEmpty()) {
                aContext->recycler()->getEmptyStringResult(aResult);

                return NS_OK;
            }
            
            nsRefPtr<StringResult> strRes;
            rv = aContext->recycler()->getStringResult(getter_AddRefs(strRes));
            NS_ENSURE_SUCCESS(rv, rv);

            strRes->mValue.SetCapacity(src.Length());

            nsAutoString oldChars, newChars;
            rv = mParams[1]->evaluateToString(aContext, oldChars);
            NS_ENSURE_SUCCESS(rv, rv);

            rv = mParams[2]->evaluateToString(aContext, newChars);
            NS_ENSURE_SUCCESS(rv, rv);

            PRUint32 i;
            PRInt32 newCharsLength = (PRInt32)newChars.Length();
            for (i = 0; i < src.Length(); i++) {
                PRInt32 idx = oldChars.FindChar(src.CharAt(i));
                if (idx != kNotFound) {
                    if (idx < newCharsLength)
                        strRes->mValue.Append(newChars.CharAt((PRUint32)idx));
                }
                else {
                    strRes->mValue.Append(src.CharAt(i));
                }
            }

            NS_ADDREF(*aResult = strRes);

            return NS_OK;
        }
        
        // Number functions

        case NUMBER:
        {
            double res;
            if (!mParams.IsEmpty()) {
                rv = evaluateToNumber(mParams[0], aContext, &res);
                NS_ENSURE_SUCCESS(rv, rv);
            }
            else {
                nsAutoString resultStr;
                txXPathNodeUtils::appendNodeValue(aContext->getContextNode(),
                                                  resultStr);
                res = Double::toDouble(resultStr);
            }
            return aContext->recycler()->getNumberResult(res, aResult);
        }
        case ROUND:
        {
            double dbl;
            rv = evaluateToNumber(mParams[0], aContext, &dbl);
            NS_ENSURE_SUCCESS(rv, rv);

            if (!Double::isNaN(dbl) && !Double::isInfinite(dbl)) {
                if (Double::isNeg(dbl) && dbl >= -0.5) {
                    dbl *= 0;
                }
                else {
                    dbl = floor(dbl + 0.5);
                }
            }

            return aContext->recycler()->getNumberResult(dbl, aResult);
        }
        case FLOOR:
        {
            double dbl;
            rv = evaluateToNumber(mParams[0], aContext, &dbl);
            NS_ENSURE_SUCCESS(rv, rv);

            if (!Double::isNaN(dbl) &&
                !Double::isInfinite(dbl) &&
                !(dbl == 0 && Double::isNeg(dbl))) {
                dbl = floor(dbl);
            }

            return aContext->recycler()->getNumberResult(dbl, aResult);
        }
        case CEILING:
        {
            double dbl;
            rv = evaluateToNumber(mParams[0], aContext, &dbl);
            NS_ENSURE_SUCCESS(rv, rv);

            if (!Double::isNaN(dbl) && !Double::isInfinite(dbl)) {
                if (Double::isNeg(dbl) && dbl > -1) {
                    dbl *= 0;
                }
                else {
                    dbl = ceil(dbl);
                }
            }

            return aContext->recycler()->getNumberResult(dbl, aResult);
        }
        case SUM:
        {
            nsRefPtr<txNodeSet> nodes;
            nsresult rv = evaluateToNodeSet(mParams[0], aContext,
                                            getter_AddRefs(nodes));
            NS_ENSURE_SUCCESS(rv, rv);

            double res = 0;
            PRInt32 i;
            for (i = 0; i < nodes->size(); ++i) {
                nsAutoString resultStr;
                txXPathNodeUtils::appendNodeValue(nodes->get(i), resultStr);
                res += Double::toDouble(resultStr);
            }
            return aContext->recycler()->getNumberResult(res, aResult);
        }
        
        // Boolean functions
        
        case BOOLEAN:
        {
            PRBool result;
            nsresult rv = mParams[0]->evaluateToBool(aContext, result);
            NS_ENSURE_SUCCESS(rv, rv);

            aContext->recycler()->getBoolResult(result, aResult);

            return NS_OK;
        }
        case _FALSE:
        {
            aContext->recycler()->getBoolResult(PR_FALSE, aResult);

            return NS_OK;
        }
        case LANG:
        {
            txXPathTreeWalker walker(aContext->getContextNode());

            nsAutoString lang;
            PRBool found;
            do {
                found = walker.getAttr(txXMLAtoms::lang, kNameSpaceID_XML,
                                       lang);
            } while (!found && walker.moveToParent());

            if (!found) {
                aContext->recycler()->getBoolResult(PR_FALSE, aResult);

                return NS_OK;
            }

            nsAutoString arg;
            rv = mParams[0]->evaluateToString(aContext, arg);
            NS_ENSURE_SUCCESS(rv, rv);

            PRBool result =
                StringBeginsWith(lang, arg,
                                 txCaseInsensitiveStringComparator()) &&
                (lang.Length() == arg.Length() ||
                 lang.CharAt(arg.Length()) == '-');

            aContext->recycler()->getBoolResult(result, aResult);

            return NS_OK;
        }
        case _NOT:
        {
            PRBool result;
            rv = mParams[0]->evaluateToBool(aContext, result);
            NS_ENSURE_SUCCESS(rv, rv);

            aContext->recycler()->getBoolResult(!result, aResult);

            return NS_OK;
        }
        case _TRUE:
        {
            aContext->recycler()->getBoolResult(PR_TRUE, aResult);

            return NS_OK;
        }
    }

    aContext->receiveError(NS_LITERAL_STRING("Internal error"),
                           NS_ERROR_UNEXPECTED);
    return NS_ERROR_UNEXPECTED;
}
Esempio n. 4
0
        Expr* Parser::evaluateConfigDefinition(Str* ns, Expr* e)
        {
            // e is evaluated in an environment containing only config bindings.
            // ns is the default namespace: it will be used to qualify any unqualified name.
            
            switch (e->tag()) {
                case TAG_literalUndefined:
                case TAG_literalString:
                case TAG_literalNull:
                case TAG_literalUInt:
                case TAG_literalInt:
                case TAG_literalDouble:
                case TAG_literalBoolean:
                    return e;
                case TAG_simpleName: {
                    Expr* expr = findConfigBinding(ns, ((SimpleName*)e)->name);
                    if (expr == NULL)
                        compiler->syntaxError(expr->pos, SYNTAXERR_UNBOUND_CONST_NAME);
                    return expr;
                }
                case TAG_qualifiedName: {
                    QualifiedName* qname = (QualifiedName*)e;
                    if (qname->qualifier->tag() != TAG_simpleName || qname->name->tag() != TAG_simpleName)
                        compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME);    // Specifically an illegal const name
                    Str* ns = ((SimpleName*)qname->qualifier)->name;
                    Str* name = ((SimpleName*)qname->name)->name;
                    Expr* value = findConfigBinding(ns, name);
                    if (value == NULL)
                        compiler->syntaxError(e->pos, SYNTAXERR_UNBOUND_CONST_NAME);
                    return value;
                }
                case TAG_binaryExpr: {
                    // CLARIFICATION: no short-circuiting
                    //
                    // We evaluate both sides of && and || in order to uncover
                    // any undefined variables lurking in non-taken branches.
                    BinaryExpr* binary = (BinaryExpr*)e;
                    Expr* lhs = evaluateConfigDefinition(ns, binary->lhs);
                    Expr* rhs = evaluateConfigDefinition(ns, binary->rhs);
                    switch (binary->op) {
                        case OPR_plus:
                            if (lhs->tag() == TAG_literalString || rhs->tag() == TAG_literalString) {
                                StringBuilder b(compiler);
                                b.append(((LiteralString*)lhs)->value);
                                b.append(((LiteralString*)rhs)->value);
                                return boxString(b.str());
                            }
                            return boxDouble(evaluateToNumber(lhs) + evaluateToNumber(rhs));
                        case OPR_minus:
                            return boxDouble(evaluateToNumber(lhs) - evaluateToNumber(rhs));
                        case OPR_multiply:
                            return boxDouble(evaluateToNumber(lhs) * evaluateToNumber(rhs));
                        case OPR_divide:
                            return boxDouble(evaluateToNumber(lhs) / evaluateToNumber(rhs));
                        case OPR_remainder:
                            return boxDouble(fmod(evaluateToNumber(lhs), evaluateToNumber(rhs)));
                        case OPR_leftShift:
                            return boxInt(evaluateToInt32(lhs) << (evaluateToUInt32(rhs) & 0x1F));
                        case OPR_rightShift:
                            return boxInt(evaluateToInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F));
                        case OPR_rightShiftUnsigned:
                            return boxUInt(evaluateToUInt32(lhs) >> (evaluateToUInt32(rhs) & 0x1F));
                        case OPR_bitwiseAnd:
                            return boxInt(evaluateToInt32(lhs) & evaluateToInt32(rhs));
                        case OPR_bitwiseOr:
                            return boxInt(evaluateToInt32(lhs) | evaluateToInt32(rhs));
                        case OPR_bitwiseXor:
                            return boxInt(evaluateToInt32(lhs) ^ evaluateToInt32(rhs));
                        case OPR_logicalAnd:
                            return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) == 2);
                        case OPR_logicalOr:
                            return boxBoolean(int(evaluateToBoolean(lhs)) + int(evaluateToBoolean(rhs)) != 0);
                        case OPR_less: {
                            int r = evaluateRelational(lhs, rhs);
                            return boxBoolean(r == -1 || r == 0 ? false : true);
                        }
                        case OPR_greater: {
                            int r = evaluateRelational(rhs, lhs);
                            return boxBoolean(r == -1 || r == 0 ? false : true);
                        }
                        case OPR_lessOrEqual: {
                            int r = evaluateRelational(rhs, lhs);
                            return boxBoolean(r == -1 || r == 1 ? false : true);
                        }
                        case OPR_greaterOrEqual: {
                            int r = evaluateRelational(lhs, rhs);
                            return boxBoolean(r == -1 || r == 1 ? false : true);
                        }
                        case OPR_equal:
                        case OPR_notEqual:
                        case OPR_strictEqual:
                        case OPR_strictNotEqual: {
                            if (lhs->tag() == TAG_literalInt || lhs->tag() == TAG_literalUInt)
                                lhs = boxDouble(evaluateToNumber(lhs));
                            if (rhs->tag() == TAG_literalInt || rhs->tag() == TAG_literalUInt)
                                rhs = boxDouble(evaluateToNumber(rhs));

                            bool equality;
                            if (binary->op == OPR_equal || binary->op == OPR_notEqual)
                                equality = binary->op == OPR_equal;
                            else
                                equality = binary->op == OPR_strictEqual;
                            
                            if (lhs->tag() != rhs->tag()) {
                                if (binary->op == OPR_equal || binary->op == OPR_notEqual) {
                                    if ((lhs->tag() == TAG_literalUndefined && rhs->tag() == TAG_literalNull) ||
                                        (lhs->tag() == TAG_literalNull && rhs->tag() == TAG_literalUndefined))
                                        return boxBoolean(true == equality);
                                    if ((lhs->tag() == TAG_literalString && rhs->tag() == TAG_literalDouble) ||
                                        (lhs->tag() == TAG_literalDouble && rhs->tag() == TAG_literalString))
                                        return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality);
                                    if (lhs->tag() == TAG_literalBoolean || rhs->tag() == TAG_literalBoolean)
                                        return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality);
                                }
                                return boxBoolean(false == equality);
                            }
                            if (lhs->tag() == TAG_literalUndefined || lhs->tag() == TAG_literalNull)
                                return boxBoolean(true == equality);
                            if (lhs->tag() == TAG_literalDouble)
                                return boxBoolean((evaluateToNumber(lhs) == evaluateToNumber(rhs)) == equality);
                            if (lhs->tag() == TAG_literalBoolean)
                                return boxBoolean((evaluateToBoolean(lhs) == evaluateToBoolean(rhs)) == equality);
                            if (lhs->tag() == TAG_literalString)
                                return boxBoolean((evaluateToString(lhs) == evaluateToString(rhs)) == equality);
                            failNonConstant(lhs);
                            /*NOTREACHED*/
                            break;
                        }
                        default:
                            // "as", "is", "in", ",", "="
                            compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
                            /*NOTREACHED*/
                            break;
                    }
                    /*NOTREACHED*/
                    break;
                }
                case TAG_unaryExpr: {
                    // EXTENSION: typeof
                    //
                    // Supporting "typeof" makes some sort of sense (for example, the
                    // operand of typeof can be a config constant that can take on
                    // various values, and computing the name of the type into the
                    // program can be useful).
                    //
                    // EXTENSION: void
                    //
                    // Supporting "void" probably does not make a lot of sense, but it
                    // seems benign.
                    UnaryExpr* unary = (UnaryExpr*)e;
                    Expr* opd = evaluateConfigDefinition(ns, unary->expr);
                    switch (unary->op) {
                        case OPR_typeof:
                            switch (opd->tag()) {
                                case TAG_literalUndefined: return boxString("undefined");
                                case TAG_literalString:    return boxString("string");
                                case TAG_literalNull:      return boxString("object");
                                case TAG_literalUInt:
                                case TAG_literalInt:
                                case TAG_literalDouble:    return boxString("number");
                                case TAG_literalBoolean:   return boxString("boolean");
                                default:
                                    failNonConstant(opd);
                                    return NULL;
                            }
                        case OPR_bitwiseNot: return boxUInt(~evaluateToUInt32(opd));
                        case OPR_unminus:    return boxDouble(-evaluateToNumber(opd));
                        case OPR_unplus:     return boxDouble(evaluateToNumber(opd));
                        case OPR_not:        return boxBoolean(!evaluateToBoolean(opd));
                        case OPR_void:       return boxUndefined();
                        default:
                            // "delete", "++", "--"
                            compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
                            /*NOTREACHED*/
                            break;
                    }
                    /*NOTREACHED*/
                    break;
                }
                case TAG_conditionalExpr: {
                    // EXTENSION: conditional operator
                    //
                    // It seems totally sensible to support "... ? ... : ...", though
                    // it's not mentioned in the conditional compilation spec.
                    //
                    // We evaluate both arms in order to uncover references to undefined
                    // configuration variables, same as for && and ||.
                    ConditionalExpr* cond = (ConditionalExpr*)e;
                    Expr* e1 = evaluateConfigDefinition(ns, cond->e1);
                    Expr* e2 = evaluateConfigDefinition(ns, cond->e2);
                    Expr* e3 = evaluateConfigDefinition(ns, cond->e3);
                    return evaluateToBoolean(e1) ? e2 : e3;
                }
                default:
                    // Property references, 'new', 'call' - lots of things
                    compiler->syntaxError(position(), SYNTAXERR_ILLEGAL_OP_IN_CONSTEXPR);
                    /*NOTREACHED*/
                    break;
            }
            /*NOTREACHED*/
            return NULL;
        }
/*
 * Evaluates this Expr based on the given context node and processor state
 * @param context the context node for evaluation of this Expr
 * @param cs the ContextState containing the stack information needed
 * for evaluation
 * @return the result of the evaluation
 */
nsresult
txFormatNumberFunctionCall::evaluate(txIEvalContext* aContext,
                                     txAExprResult** aResult)
{
    *aResult = nullptr;
    if (!requireParams(2, 3, aContext))
        return NS_ERROR_XPATH_BAD_ARGUMENT_COUNT;

    // Get number and format
    double value;
    txExpandedName formatName;

    nsresult rv = evaluateToNumber(mParams[0], aContext, &value);
    NS_ENSURE_SUCCESS(rv, rv);

    nsAutoString formatStr;
    rv = mParams[1]->evaluateToString(aContext, formatStr);
    NS_ENSURE_SUCCESS(rv, rv);

    if (mParams.Length() == 3) {
        nsAutoString formatQName;
        rv = mParams[2]->evaluateToString(aContext, formatQName);
        NS_ENSURE_SUCCESS(rv, rv);

        rv = formatName.init(formatQName, mMappings, false);
        NS_ENSURE_SUCCESS(rv, rv);
    }

    txDecimalFormat* format = mStylesheet->getDecimalFormat(formatName);
    if (!format) {
        nsAutoString err(NS_LITERAL_STRING("unknown decimal format"));
#ifdef TX_TO_STRING
        err.AppendLiteral(" for: ");
        toString(err);
#endif
        aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
        return NS_ERROR_XPATH_INVALID_ARG;
    }

    // Special cases
    if (mozilla::IsNaN(value)) {
        return aContext->recycler()->getStringResult(format->mNaN, aResult);
    }

    if (value == mozilla::PositiveInfinity<double>()) {
        return aContext->recycler()->getStringResult(format->mInfinity,
                                                     aResult);
    }

    if (value == mozilla::NegativeInfinity<double>()) {
        nsAutoString res;
        res.Append(format->mMinusSign);
        res.Append(format->mInfinity);
        return aContext->recycler()->getStringResult(res, aResult);
    }

    // Value is a normal finite number
    nsAutoString prefix;
    nsAutoString suffix;
    int minIntegerSize=0;
    int minFractionSize=0;
    int maxFractionSize=0;
    int multiplier=1;
    int groupSize=-1;

    uint32_t pos = 0;
    uint32_t formatLen = formatStr.Length();
    bool inQuote;

    // Get right subexpression
    inQuote = false;
    if (mozilla::IsNegative(value)) {
        while (pos < formatLen &&
               (inQuote ||
                formatStr.CharAt(pos) != format->mPatternSeparator)) {
            if (formatStr.CharAt(pos) == FORMAT_QUOTE)
                inQuote = !inQuote;
            pos++;
        }

        if (pos == formatLen) {
            pos = 0;
            prefix.Append(format->mMinusSign);
        }
        else
            pos++;
    }

    // Parse the format string
    FormatParseState pState = Prefix;
    inQuote = false;

    char16_t c = 0;
    while (pos < formatLen && pState != Finished) {
        c=formatStr.CharAt(pos++);

        switch (pState) {

        case Prefix:
        case Suffix:
            if (!inQuote) {
                if (c == format->mPercent) {
                    if (multiplier == 1)
                        multiplier = 100;
                    else {
                        nsAutoString err(INVALID_PARAM_VALUE);
#ifdef TX_TO_STRING
                        err.AppendLiteral(": ");
                        toString(err);
#endif
                        aContext->receiveError(err,
                                               NS_ERROR_XPATH_INVALID_ARG);
                        return NS_ERROR_XPATH_INVALID_ARG;
                    }
                }
                else if (c == format->mPerMille) {
                    if (multiplier == 1)
                        multiplier = 1000;
                    else {
                        nsAutoString err(INVALID_PARAM_VALUE);
#ifdef TX_TO_STRING
                        err.AppendLiteral(": ");
                        toString(err);
#endif
                        aContext->receiveError(err,
                                               NS_ERROR_XPATH_INVALID_ARG);
                        return NS_ERROR_XPATH_INVALID_ARG;
                    }
                }
                else if (c == format->mDecimalSeparator ||
                         c == format->mGroupingSeparator ||
                         c == format->mZeroDigit ||
                         c == format->mDigit ||
                         c == format->mPatternSeparator) {
                    pState = pState == Prefix ? IntDigit : Finished;
                    pos--;
                    break;
                }
            }

            if (c == FORMAT_QUOTE)
                inQuote = !inQuote;
            else if (pState == Prefix)
                prefix.Append(c);
            else
                suffix.Append(c);
            break;

        case IntDigit:
            if (c == format->mGroupingSeparator)
                groupSize=0;
            else if (c == format->mDigit) {
                if (groupSize >= 0)
                    groupSize++;
            }
            else {
                pState = IntZero;
                pos--;
            }
            break;

        case IntZero:
            if (c == format->mGroupingSeparator)
                groupSize = 0;
            else if (c == format->mZeroDigit) {
                if (groupSize >= 0)
                    groupSize++;
                minIntegerSize++;
            }
            else if (c == format->mDecimalSeparator) {
                pState = FracZero;
            }
            else {
                pState = Suffix;
                pos--;
            }
            break;

        case FracZero:
            if (c == format->mZeroDigit) {
                maxFractionSize++;
                minFractionSize++;
            }
            else {
                pState = FracDigit;
                pos--;
            }
            break;

        case FracDigit:
            if (c == format->mDigit)
                maxFractionSize++;
            else {
                pState = Suffix;
                pos--;
            }
            break;

        case Finished:
            break;
        }
    }

    // Did we manage to parse the entire formatstring and was it valid
    if ((c != format->mPatternSeparator && pos < formatLen) ||
        inQuote ||
        groupSize == 0) {
        nsAutoString err(INVALID_PARAM_VALUE);
#ifdef TX_TO_STRING
        err.AppendLiteral(": ");
        toString(err);
#endif
        aContext->receiveError(err, NS_ERROR_XPATH_INVALID_ARG);
        return NS_ERROR_XPATH_INVALID_ARG;
    }


    /*
     * FINALLY we're done with the parsing
     * now build the result string
     */

    value = fabs(value) * multiplier;

    // Prefix
    nsAutoString res(prefix);

    int bufsize;
    if (value > 1)
        bufsize = (int)log10(value) + 30;
    else
        bufsize = 1 + 30;

    char* buf = new char[bufsize];
    NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);

    int bufIntDigits, sign;
    char* endp;
    PR_dtoa(value, 0, 0, &bufIntDigits, &sign, &endp, buf, bufsize-1);

    int buflen = endp - buf;
    int intDigits;
    intDigits = bufIntDigits > minIntegerSize ? bufIntDigits : minIntegerSize;

    if (groupSize < 0)
        groupSize = intDigits + 10; //to simplify grouping

    // XXX We shouldn't use SetLength.
    res.SetLength(res.Length() +
                  intDigits +               // integer digits
                  1 +                       // decimal separator
                  maxFractionSize +         // fractions
                  (intDigits-1)/groupSize); // group separators

    int32_t i = bufIntDigits + maxFractionSize - 1;
    bool carry = (0 <= i+1) && (i+1 < buflen) && (buf[i+1] >= '5');
    bool hasFraction = false;

    uint32_t resPos = res.Length()-1;

    // Fractions
    for (; i >= bufIntDigits; --i) {
        int digit;
        if (i >= buflen || i < 0) {
            digit = 0;
        }
        else {
            digit = buf[i] - '0';
        }
        
        if (carry) {
            digit = (digit + 1) % 10;
            carry = digit == 0;
        }

        if (hasFraction || digit != 0 || i < bufIntDigits+minFractionSize) {
            hasFraction = true;
            res.SetCharAt((char16_t)(digit + format->mZeroDigit),
                          resPos--);
        }
        else {
            res.Truncate(resPos--);
        }
    }

    // Decimal separator
    if (hasFraction) {
        res.SetCharAt(format->mDecimalSeparator, resPos--);
    }
    else {
        res.Truncate(resPos--);
    }

    // Integer digits
    for (i = 0; i < intDigits; ++i) {
        int digit;
        if (bufIntDigits-i-1 >= buflen || bufIntDigits-i-1 < 0) {
            digit = 0;
        }
        else {
            digit = buf[bufIntDigits-i-1] - '0';
        }
        
        if (carry) {
            digit = (digit + 1) % 10;
            carry = digit == 0;
        }

        if (i != 0 && i%groupSize == 0) {
            res.SetCharAt(format->mGroupingSeparator, resPos--);
        }

        res.SetCharAt((char16_t)(digit + format->mZeroDigit), resPos--);
    }

    if (carry) {
        if (i%groupSize == 0) {
            res.Insert(format->mGroupingSeparator, resPos + 1);
        }
        res.Insert((char16_t)(1 + format->mZeroDigit), resPos + 1);
    }

    if (!hasFraction && !intDigits && !carry) {
        // If we havn't added any characters we add a '0'
        // This can only happen for formats like '##.##'
        res.Append(format->mZeroDigit);
    }

    delete [] buf;

    // Build suffix
    res.Append(suffix);

    return aContext->recycler()->getStringResult(res, aResult);
} //-- evaluate