Files
Mozilla/mozilla/extensions/transformiix/source/xpath/ExprParser.cpp
peterv%netscape.com f21892e8db Landing TX_BRIDGE_1_1_BRANCH.
Rewrite pretty much all variable and parameter handling. Makes global and local variables be handled differently. Global variables are no longer part of the variables stack, and are lazily evaluated. Fixes bugs 117658, 92929 and some unfiled bugs and fixes remaining parts of bugs 83651 and 96802. Patch by sicking, r=Pike sr=bz.

Fix for bug 156464: fix rounding problems in module, allow patterns without any '0's in the integer part and fix problems with grouping for standalone. Patch by sicking, r=Pike sr=bz.

Fix for bug 157340 (Probable bugs in extensions/transformiix/source/base/txMozillaString.h). Patch by peterv, r=Pike, sr=bz.

Fix for bug 146967 (Clean up Transformiix strings). Patch by peterv, r=sicking, sr=jst.

Fix for bug 156464 (Remove static strings from Transformiix). Patch by peterv, r=Pike, sr=jst.


git-svn-id: svn://10.0.0.236/trunk@126495 18797224-902f-48f8-a5cc-f745e15eee43
2002-08-06 12:28:27 +00:00

1046 lines
33 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is TransforMiiX XSLT processor.
*
* The Initial Developer of the Original Code is The MITRE Corporation.
* Portions created by MITRE are Copyright (C) 1999 The MITRE Corporation.
*
* Portions created by Keith Visco as a Non MITRE employee,
* (C) 1999 Keith Visco. All Rights Reserved.
*
* Contributor(s):
* Keith Visco, kvisco@ziplink.net
* -- original author.
*
* Olivier Gerardin, ogerardin@vo.lu
* -- added support for number function calls
* -- fixed a bug in CreateExpr (@xxx=/yyy was parsed as @xxx=@xxx/yyy)
*
* Marina Mechtcheriakova
* -- added support for lang()
* -- fixed bug in ::parsePredicates,
* made sure we continue looking for more predicates.
*
*/
/**
* ExprParser
* This class is used to parse XSL Expressions
* @see ExprLexer
**/
#include "ExprParser.h"
#include "FunctionLib.h"
#include "Names.h"
#include "txAtoms.h"
#include "txIXPathContext.h"
// XXX This is ugly, but this is the last file to use them,
// once we convert the parser to directly compare with
// atoms we should remove these.
class XPathNames {
public:
static const String BOOLEAN_FN;
static const String CONCAT_FN;
static const String CONTAINS_FN;
static const String COUNT_FN ;
static const String FALSE_FN;
static const String ID_FN;
static const String LANG_FN;
static const String LAST_FN;
static const String LOCAL_NAME_FN;
static const String NAME_FN;
static const String NAMESPACE_URI_FN;
static const String NORMALIZE_SPACE_FN;
static const String NOT_FN;
static const String POSITION_FN;
static const String STARTS_WITH_FN;
static const String STRING_FN;
static const String STRING_LENGTH_FN;
static const String SUBSTRING_FN;
static const String SUBSTRING_AFTER_FN;
static const String SUBSTRING_BEFORE_FN;
static const String SUM_FN;
static const String TRANSLATE_FN;
static const String TRUE_FN;
static const String NUMBER_FN;
static const String ROUND_FN;
static const String CEILING_FN;
static const String FLOOR_FN;
};
const String XPathNames::BOOLEAN_FN("boolean");
const String XPathNames::CONCAT_FN("concat");
const String XPathNames::CONTAINS_FN("contains");
const String XPathNames::COUNT_FN("count");
const String XPathNames::FALSE_FN("false");
const String XPathNames::ID_FN("id");
const String XPathNames::LAST_FN("last");
const String XPathNames::LOCAL_NAME_FN("local-name");
const String XPathNames::NAME_FN("name");
const String XPathNames::NAMESPACE_URI_FN("namespace-uri");
const String XPathNames::NORMALIZE_SPACE_FN("normalize-space");
const String XPathNames::NOT_FN("not");
const String XPathNames::POSITION_FN("position");
const String XPathNames::STARTS_WITH_FN("starts-with");
const String XPathNames::STRING_FN("string");
const String XPathNames::STRING_LENGTH_FN("string-length");
const String XPathNames::SUBSTRING_FN("substring");
const String XPathNames::SUBSTRING_AFTER_FN("substring-after");
const String XPathNames::SUBSTRING_BEFORE_FN("substring-before");
const String XPathNames::SUM_FN("sum");
const String XPathNames::TRANSLATE_FN("translate");
const String XPathNames::TRUE_FN("true");
const String XPathNames::NUMBER_FN("number");
const String XPathNames::ROUND_FN("round");
const String XPathNames::CEILING_FN("ceiling");
const String XPathNames::FLOOR_FN("floor");
const String XPathNames::LANG_FN("lang");
/**
* Creates an Attribute Value Template using the given value
* This should move to XSLProcessor class
**/
AttributeValueTemplate* ExprParser::createAttributeValueTemplate
(const String& attValue, txIParseContext* aContext)
{
AttributeValueTemplate* avt = new AttributeValueTemplate();
if (attValue.isEmpty())
return avt; //XXX should return 0, but that causes crash in lre12
PRUint32 size = attValue.length();
PRUint32 cc = 0;
UNICODE_CHAR nextCh;
UNICODE_CHAR ch;
String buffer;
MBool inExpr = MB_FALSE;
MBool inLiteral = MB_FALSE;
UNICODE_CHAR endLiteral = 0;
nextCh = attValue.charAt(cc);
while (cc++ < size) {
ch = nextCh;
nextCh = cc != size ? attValue.charAt(cc) : 0;
// if in literal just add ch to buffer
if (inLiteral && (ch != endLiteral)) {
buffer.append(ch);
continue;
}
switch ( ch ) {
case '\'' :
case '"' :
buffer.append(ch);
if (inLiteral)
inLiteral = MB_FALSE;
else if (inExpr) {
inLiteral = MB_TRUE;
endLiteral = ch;
}
break;
case '{' :
if (!inExpr) {
// Ignore case where we find two {
if (nextCh == ch) {
buffer.append(ch); //-- append '{'
cc++;
nextCh = cc != size ? attValue.charAt(cc) : 0;
}
else {
if (!buffer.isEmpty())
avt->addExpr(new StringExpr(buffer));
buffer.clear();
inExpr = MB_TRUE;
}
}
else
buffer.append(ch); //-- simply append '{'
break;
case '}':
if (inExpr) {
inExpr = MB_FALSE;
ExprLexer lexer(buffer);
Expr* expr = createExpr(lexer, aContext);
if (!expr) {
delete avt;
return 0;
}
avt->addExpr(expr);
buffer.clear();
}
else if (nextCh == ch) {
buffer.append(ch);
cc++;
nextCh = cc != size ? attValue.charAt(cc) : 0;
}
else {
//XXX ErrorReport: unmatched '}' found
delete avt;
return 0;
}
break;
default:
buffer.append(ch);
break;
}
}
if (inExpr) {
//XXX ErrorReport: ending '}' missing
delete avt;
return 0;
}
if (!buffer.isEmpty())
avt->addExpr(new StringExpr(buffer));
return avt;
} //-- createAttributeValueTemplate
Expr* ExprParser::createExpr(const String& aExpression,
txIParseContext* aContext)
{
ExprLexer lexer(aExpression);
Expr* expr = createExpr(lexer, aContext);
return expr;
} //-- createExpr
//--------------------/
//- Private Methods -/
//-------------------/
/**
* Creates a binary Expr for the given operator
**/
Expr* ExprParser::createBinaryExpr (Expr* left, Expr* right, Token* op) {
if (!op)
return 0;
switch (op->type) {
//-- additive ops
case Token::ADDITION_OP :
return new AdditiveExpr(left, right, AdditiveExpr::ADDITION);
case Token::SUBTRACTION_OP:
return new AdditiveExpr(left, right, AdditiveExpr::SUBTRACTION);
//-- case boolean ops
case Token::AND_OP:
return new BooleanExpr(left, right, BooleanExpr::AND);
case Token::OR_OP:
return new BooleanExpr(left, right, BooleanExpr::OR);
//-- equality ops
case Token::EQUAL_OP :
return new RelationalExpr(left, right, RelationalExpr::EQUAL);
case Token::NOT_EQUAL_OP :
return new RelationalExpr(left, right, RelationalExpr::NOT_EQUAL);
//-- relational ops
case Token::LESS_THAN_OP:
return new RelationalExpr(left, right, RelationalExpr::LESS_THAN);
case Token::GREATER_THAN_OP:
return new RelationalExpr(left, right, RelationalExpr::GREATER_THAN);
case Token::LESS_OR_EQUAL_OP:
return new RelationalExpr(left, right, RelationalExpr::LESS_OR_EQUAL);
case Token::GREATER_OR_EQUAL_OP:
return new RelationalExpr(left, right, RelationalExpr::GREATER_OR_EQUAL);
//-- multiplicative ops
case Token::DIVIDE_OP :
return new MultiplicativeExpr(left, right, MultiplicativeExpr::DIVIDE);
case Token::MODULUS_OP :
return new MultiplicativeExpr(left, right, MultiplicativeExpr::MODULUS);
case Token::MULTIPLY_OP :
return new MultiplicativeExpr(left, right, MultiplicativeExpr::MULTIPLY);
default:
break;
}
return 0;
//return new ErrorExpr();
} //-- createBinaryExpr
Expr* ExprParser::createExpr(ExprLexer& lexer, txIParseContext* aContext)
{
MBool done = MB_FALSE;
Expr* expr = 0;
Stack exprs;
Stack ops;
while (!done) {
MBool unary = MB_FALSE;
while (lexer.peek()->type == Token::SUBTRACTION_OP) {
unary = !unary;
lexer.nextToken();
}
expr = createUnionExpr(lexer, aContext);
if (!expr)
break;
if (unary)
expr = new UnaryExpr(expr);
Token* tok = lexer.nextToken();
switch (tok->type) {
case Token::ADDITION_OP:
case Token::DIVIDE_OP:
//-- boolean ops
case Token::AND_OP :
case Token::OR_OP :
//-- equality ops
case Token::EQUAL_OP:
case Token::NOT_EQUAL_OP:
//-- relational ops
case Token::LESS_THAN_OP:
case Token::GREATER_THAN_OP:
case Token::LESS_OR_EQUAL_OP:
case Token::GREATER_OR_EQUAL_OP:
//-- multiplicative ops
case Token::MODULUS_OP:
case Token::MULTIPLY_OP:
case Token::SUBTRACTION_OP:
{
while (!exprs.empty() &&
precedenceLevel(tok->type)
<= precedenceLevel(((Token*)ops.peek())->type)) {
expr = createBinaryExpr((Expr*)exprs.pop(),
expr,
(Token*)ops.pop());
}
exprs.push(expr);
ops.push(tok);
break;
}
default:
lexer.pushBack();
done = MB_TRUE;
break;
}
}
// make sure expr != 0
if (!expr) {
while (!exprs.empty()) {
delete (Expr*)exprs.pop();
}
return 0;
}
while (!exprs.empty()) {
expr = createBinaryExpr((Expr*)exprs.pop(), expr, (Token*)ops.pop());
}
return expr;
} //-- createExpr
Expr* ExprParser::createFilterExpr(ExprLexer& lexer, txIParseContext* aContext)
{
Token* tok = lexer.nextToken();
Expr* expr = 0;
switch (tok->type) {
case Token::FUNCTION_NAME :
lexer.pushBack();
expr = createFunctionCall(lexer, aContext);
break;
case Token::VAR_REFERENCE :
{
txAtom *prefix, *lName;
PRInt32 nspace;
nsresult rv = resolveQName(tok->value, prefix, aContext,
lName, nspace);
if (NS_FAILED(rv)) {
// XXX error report namespace resolve failed
return 0;
}
expr = new VariableRefExpr(prefix, lName, nspace);
TX_IF_RELEASE_ATOM(prefix);
TX_IF_RELEASE_ATOM(lName);
}
break;
case Token::L_PAREN:
expr = createExpr(lexer, aContext);
if (!expr)
return 0;
if (lexer.nextToken()->type != Token::R_PAREN) {
lexer.pushBack();
//XXX ErrorReport: right parenthesis expected
delete expr;
return 0;
}
break;
case Token::LITERAL :
expr = new StringExpr(tok->value);
break;
case Token::NUMBER:
{
expr = new NumberExpr(Double::toDouble(tok->value));
break;
}
default:
// this should never ever happen.
lexer.pushBack();
//XXX ErrorReport: error in parser, please report on bugzilla.mozilla.org
return 0;
break;
}
if (!expr)
return 0;
if (lexer.peek()->type == Token::L_BRACKET) {
FilterExpr* filterExpr = new FilterExpr(expr);
//-- handle predicates
if (!parsePredicates(filterExpr, lexer, aContext)) {
delete filterExpr;
return 0;
}
expr = filterExpr;
}
return expr;
} //-- createFilterExpr
Expr* ExprParser::createFunctionCall(ExprLexer& lexer,
txIParseContext* aContext)
{
FunctionCall* fnCall = 0;
Token* tok = lexer.nextToken();
if (tok->type != Token::FUNCTION_NAME) {
//XXX ErrorReport: error in parser, please report on bugzilla.mozilla.org
return 0;
}
//-- compare function names
//-- * we should hash these names for speed
nsresult rv = NS_OK;
if (XPathNames::BOOLEAN_FN.isEqual(tok->value)) {
fnCall = new BooleanFunctionCall(BooleanFunctionCall::TX_BOOLEAN);
}
else if (XPathNames::CONCAT_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::CONCAT);
}
else if (XPathNames::CONTAINS_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::CONTAINS);
}
else if (XPathNames::COUNT_FN.isEqual(tok->value)) {
fnCall = new NodeSetFunctionCall(NodeSetFunctionCall::COUNT);
}
else if (XPathNames::FALSE_FN.isEqual(tok->value)) {
fnCall = new BooleanFunctionCall(BooleanFunctionCall::TX_FALSE);
}
else if (XPathNames::ID_FN.isEqual(tok->value)) {
fnCall = new NodeSetFunctionCall(NodeSetFunctionCall::ID);
}
else if (XPathNames::LANG_FN.isEqual(tok->value)) {
fnCall = new BooleanFunctionCall(BooleanFunctionCall::TX_LANG);
}
else if (XPathNames::LAST_FN.isEqual(tok->value)) {
fnCall = new NodeSetFunctionCall(NodeSetFunctionCall::LAST);
}
else if (XPathNames::LOCAL_NAME_FN.isEqual(tok->value)) {
fnCall = new NodeSetFunctionCall(NodeSetFunctionCall::LOCAL_NAME);
}
else if (XPathNames::NAME_FN.isEqual(tok->value)) {
fnCall = new NodeSetFunctionCall(NodeSetFunctionCall::NAME);
}
else if (XPathNames::NAMESPACE_URI_FN.isEqual(tok->value)) {
fnCall = new NodeSetFunctionCall(NodeSetFunctionCall::NAMESPACE_URI);
}
else if (XPathNames::NORMALIZE_SPACE_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::NORMALIZE_SPACE);
}
else if (XPathNames::NOT_FN.isEqual(tok->value)) {
fnCall = new BooleanFunctionCall(BooleanFunctionCall::TX_NOT);
}
else if (XPathNames::POSITION_FN.isEqual(tok->value)) {
fnCall = new NodeSetFunctionCall(NodeSetFunctionCall::POSITION);
}
else if (XPathNames::STARTS_WITH_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::STARTS_WITH);
}
else if (XPathNames::STRING_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::STRING);
}
else if (XPathNames::STRING_LENGTH_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::STRING_LENGTH);
}
else if (XPathNames::SUBSTRING_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::SUBSTRING);
}
else if (XPathNames::SUBSTRING_AFTER_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::SUBSTRING_AFTER);
}
else if (XPathNames::SUBSTRING_BEFORE_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::SUBSTRING_BEFORE);
}
else if (XPathNames::SUM_FN.isEqual(tok->value)) {
fnCall = new NumberFunctionCall(NumberFunctionCall::SUM);
}
else if (XPathNames::TRANSLATE_FN.isEqual(tok->value)) {
fnCall = new StringFunctionCall(StringFunctionCall::TRANSLATE);
}
else if (XPathNames::TRUE_FN.isEqual(tok->value)) {
fnCall = new BooleanFunctionCall(BooleanFunctionCall::TX_TRUE);
}
else if (XPathNames::NUMBER_FN.isEqual(tok->value)) {
fnCall = new NumberFunctionCall(NumberFunctionCall::NUMBER);
}
else if (XPathNames::ROUND_FN.isEqual(tok->value)) {
fnCall = new NumberFunctionCall(NumberFunctionCall::ROUND);
}
else if (XPathNames::CEILING_FN.isEqual(tok->value)) {
fnCall = new NumberFunctionCall(NumberFunctionCall::CEILING);
}
else if (XPathNames::FLOOR_FN.isEqual(tok->value)) {
fnCall = new NumberFunctionCall(NumberFunctionCall::FLOOR);
}
else {
txAtom *prefix, *lName;
PRInt32 namespaceID;
rv = resolveQName(tok->value, prefix, aContext, lName, namespaceID);
if (NS_FAILED(rv)) {
// XXX error report namespace resolve failed
return 0;
}
rv = aContext->resolveFunctionCall(lName, namespaceID, fnCall);
TX_IF_RELEASE_ATOM(prefix);
TX_IF_RELEASE_ATOM(lName);
if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
// XXX report error unknown function call
return 0;
}
}
//-- handle parametes
if (!parseParameters(fnCall, lexer, aContext)) {
delete fnCall;
return 0;
}
if (rv == NS_ERROR_NOT_IMPLEMENTED) {
NS_ASSERTION(!fnCall, "Now is it implemented or not?");
String err(tok->value);
err.append(" not implemented.");
return new StringExpr(err);
}
return fnCall;
} //-- createFunctionCall
LocationStep* ExprParser::createLocationStep(ExprLexer& lexer,
txIParseContext* aContext)
{
//-- child axis is default
LocationStep::LocationStepType axisIdentifier = LocationStep::CHILD_AXIS;
txNodeTest* nodeTest = 0;
//-- get Axis Identifier or AbbreviatedStep, if present
Token* tok = lexer.peek();
switch (tok->type) {
case Token::AXIS_IDENTIFIER:
{
//-- eat token
lexer.nextToken();
//-- should switch to a hash here for speed if necessary
if (ANCESTOR_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::ANCESTOR_AXIS;
}
else if (ANCESTOR_OR_SELF_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::ANCESTOR_OR_SELF_AXIS;
}
else if (ATTRIBUTE_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::ATTRIBUTE_AXIS;
}
else if (CHILD_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::CHILD_AXIS;
}
else if (DESCENDANT_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::DESCENDANT_AXIS;
}
else if (DESCENDANT_OR_SELF_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::DESCENDANT_OR_SELF_AXIS;
}
else if (FOLLOWING_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::FOLLOWING_AXIS;
}
else if (FOLLOWING_SIBLING_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::FOLLOWING_SIBLING_AXIS;
}
else if (NAMESPACE_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::NAMESPACE_AXIS;
}
else if (PARENT_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::PARENT_AXIS;
}
else if (PRECEDING_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::PRECEDING_AXIS;
}
else if (PRECEDING_SIBLING_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::PRECEDING_SIBLING_AXIS;
}
else if (SELF_AXIS.isEqual(tok->value)) {
axisIdentifier = LocationStep::SELF_AXIS;
}
else {
//XXX ErrorReport: unknow axis
return 0;
}
break;
}
case Token::AT_SIGN:
//-- eat token
lexer.nextToken();
axisIdentifier = LocationStep::ATTRIBUTE_AXIS;
break;
case Token::PARENT_NODE :
//-- eat token
lexer.nextToken();
axisIdentifier = LocationStep::PARENT_AXIS;
nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
if (!nodeTest) {
//XXX out of memory
return 0;
}
break;
case Token::SELF_NODE :
//-- eat token
lexer.nextToken();
axisIdentifier = LocationStep::SELF_AXIS;
nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
if (!nodeTest) {
//XXX out of memory
return 0;
}
break;
default:
break;
}
//-- get NodeTest unless an AbbreviatedStep was found
if (!nodeTest) {
tok = lexer.nextToken();
switch (tok->type) {
case Token::CNAME :
{
// resolve QName
txAtom *prefix, *lName;
PRInt32 nspace;
nsresult rv = resolveQName(tok->value, prefix, aContext,
lName, nspace);
if (NS_FAILED(rv)) {
// XXX error report namespace resolve failed
return 0;
}
switch (axisIdentifier) {
case LocationStep::ATTRIBUTE_AXIS:
nodeTest = new txNameTest(prefix, lName, nspace,
Node::ATTRIBUTE_NODE);
break;
default:
nodeTest = new txNameTest(prefix, lName, nspace,
Node::ELEMENT_NODE);
break;
}
TX_IF_RELEASE_ATOM(prefix);
TX_IF_RELEASE_ATOM(lName);
}
if (!nodeTest) {
//XXX ErrorReport: out of memory
return 0;
}
break;
default:
lexer.pushBack();
nodeTest = createNodeTypeTest(lexer);
if (!nodeTest) {
return 0;
}
}
}
LocationStep* lstep = new LocationStep(nodeTest, axisIdentifier);
if (!lstep) {
//XXX out of memory
delete nodeTest;
return 0;
}
//-- handle predicates
if (!parsePredicates(lstep, lexer, aContext)) {
delete lstep;
return 0;
}
return lstep;
} //-- createLocationPath
/**
* This method only handles comment(), text(), processing-instructing() and node()
*
**/
txNodeTypeTest* ExprParser::createNodeTypeTest(ExprLexer& lexer) {
txNodeTypeTest* nodeTest = 0;
Token* nodeTok = lexer.nextToken();
switch (nodeTok->type) {
case Token::COMMENT:
nodeTest = new txNodeTypeTest(txNodeTypeTest::COMMENT_TYPE);
break;
case Token::NODE :
nodeTest = new txNodeTypeTest(txNodeTypeTest::NODE_TYPE);
break;
case Token::PROC_INST :
nodeTest = new txNodeTypeTest(txNodeTypeTest::PI_TYPE);
break;
case Token::TEXT :
nodeTest = new txNodeTypeTest(txNodeTypeTest::TEXT_TYPE);
break;
default:
lexer.pushBack();
// XXX ErrorReport: unexpected token
return 0;
}
if (!nodeTest) {
//XXX out of memory
return 0;
}
if (lexer.nextToken()->type != Token::L_PAREN) {
lexer.pushBack();
//XXX ErrorReport: left parenthesis expected
delete nodeTest;
return 0;
}
if (nodeTok->type == Token::PROC_INST &&
lexer.peek()->type == Token::LITERAL) {
Token* tok = lexer.nextToken();
nodeTest->setNodeName(tok->value);
}
if (lexer.nextToken()->type != Token::R_PAREN) {
lexer.pushBack();
//XXX ErrorReport: right parenthesis expected (or literal for pi)
delete nodeTest;
return 0;
}
return nodeTest;
} //-- createNodeTypeTest
/**
* Creates a PathExpr using the given ExprLexer
* @param lexer the ExprLexer for retrieving Tokens
**/
Expr* ExprParser::createPathExpr(ExprLexer& lexer, txIParseContext* aContext)
{
Expr* expr = 0;
Token* tok = lexer.peek();
// is this a root expression?
if (tok->type == Token::PARENT_OP) {
lexer.nextToken();
if (!isLocationStepToken(lexer.peek()))
return new RootExpr(MB_TRUE);
lexer.pushBack();
}
// parse first step (possibly a FilterExpr)
if (tok->type != Token::PARENT_OP &&
tok->type != Token::ANCESTOR_OP) {
if (isFilterExprToken(tok)) {
expr = createFilterExpr(lexer, aContext);
}
else
expr = createLocationStep(lexer, aContext);
if (!expr)
return 0;
// is this a singlestep path expression?
tok = lexer.peek();
if (tok->type != Token::PARENT_OP &&
tok->type != Token::ANCESTOR_OP)
return expr;
}
else {
expr = new RootExpr(MB_FALSE);
if (!expr) {
// XXX ErrorReport: out of memory
return 0;
}
}
// We have a PathExpr containing several steps
PathExpr* pathExpr = new PathExpr();
if (!pathExpr) {
// XXX ErrorReport: out of memory
delete expr;
return 0;
}
pathExpr->addExpr(expr, PathExpr::RELATIVE_OP);
// this is ugly
while (1) {
PathExpr::PathOperator pathOp;
tok = lexer.nextToken();
switch (tok->type) {
case Token::ANCESTOR_OP :
pathOp = PathExpr::DESCENDANT_OP;
break;
case Token::PARENT_OP :
pathOp = PathExpr::RELATIVE_OP;
break;
default:
lexer.pushBack();
return pathExpr;
}
expr = createLocationStep(lexer, aContext);
if (!expr) {
delete pathExpr;
return 0;
}
pathExpr->addExpr(expr, pathOp);
}
return pathExpr;
} //-- createPathExpr
/**
* Creates a PathExpr using the given ExprLexer
* XXX temporary use as top of XSLT Pattern
* @param lexer the ExprLexer for retrieving Tokens
**/
Expr* ExprParser::createUnionExpr(ExprLexer& lexer, txIParseContext* aContext)
{
Expr* expr = createPathExpr(lexer, aContext);
if (!expr)
return 0;
if (lexer.peek()->type != Token::UNION_OP)
return expr;
UnionExpr* unionExpr = new UnionExpr();
unionExpr->addExpr(expr);
while (lexer.peek()->type == Token::UNION_OP) {
lexer.nextToken(); //-- eat token
expr = createPathExpr(lexer, aContext);
if (!expr) {
delete unionExpr;
return 0;
}
unionExpr->addExpr(expr);
}
return unionExpr;
} //-- createUnionExpr
MBool ExprParser::isFilterExprToken(Token* token) {
switch (token->type) {
case Token::LITERAL:
case Token::NUMBER:
case Token::FUNCTION_NAME:
case Token::VAR_REFERENCE:
case Token::L_PAREN: // grouping expr
return MB_TRUE;
default:
return MB_FALSE;
}
} //-- isFilterExprToken
MBool ExprParser::isLocationStepToken(Token* token) {
switch (token->type) {
case Token::AXIS_IDENTIFIER :
case Token::AT_SIGN :
case Token::PARENT_NODE :
case Token::SELF_NODE :
return MB_TRUE;
default:
return isNodeTypeToken(token);
}
} //-- isLocationStepToken
MBool ExprParser::isNodeTypeToken(Token* token) {
switch (token->type) {
case Token::CNAME:
case Token::COMMENT:
case Token::NODE :
case Token::PROC_INST :
case Token::TEXT :
return MB_TRUE;
default:
return MB_FALSE;
}
} //-- isNodeTypeToken
/**
* Using the given lexer, parses the tokens if they represent a predicate list
* If an error occurs a non-zero String pointer will be returned containing the
* error message.
* @param predicateList, the PredicateList to add predicate expressions to
* @param lexer the ExprLexer to use for parsing tokens
* @return 0 if successful, or a String pointer to the error message
**/
MBool ExprParser::parsePredicates(PredicateList* predicateList,
ExprLexer& lexer, txIParseContext* aContext)
{
while (lexer.peek()->type == Token::L_BRACKET) {
//-- eat Token
lexer.nextToken();
Expr* expr = createExpr(lexer, aContext);
if (!expr)
return MB_FALSE;
predicateList->add(expr);
if (lexer.nextToken()->type != Token::R_BRACKET) {
lexer.pushBack();
//XXX ErrorReport: right bracket expected
return MB_FALSE;
}
}
return MB_TRUE;
} //-- parsePredicates
/**
* Using the given lexer, parses the tokens if they represent a parameter list
* If an error occurs a non-zero String pointer will be returned containing the
* error message.
* @param list, the List to add parameter expressions to
* @param lexer the ExprLexer to use for parsing tokens
* @return MB_TRUE if successful, or a MB_FALSE otherwise
**/
MBool ExprParser::parseParameters(FunctionCall* fnCall, ExprLexer& lexer,
txIParseContext* aContext)
{
if (lexer.nextToken()->type != Token::L_PAREN) {
lexer.pushBack();
//XXX ErrorReport: left parenthesis expected
return MB_FALSE;
}
if (lexer.peek()->type == Token::R_PAREN) {
lexer.nextToken();
return MB_TRUE;
}
while (1) {
Expr* expr = createExpr(lexer, aContext);
if (!expr)
return MB_FALSE;
if (fnCall)
fnCall->addParam(expr);
switch (lexer.nextToken()->type) {
case Token::R_PAREN :
return MB_TRUE;
case Token::COMMA: //-- param separator
break;
default:
lexer.pushBack();
//XXX ErrorReport: right parenthesis or comma expected
return MB_FALSE;
}
}
return MB_FALSE;
} //-- parseParameters
short ExprParser::precedenceLevel(short tokenType) {
switch (tokenType) {
case Token::OR_OP:
return 1;
case Token::AND_OP:
return 2;
//-- equality
case Token::EQUAL_OP:
case Token::NOT_EQUAL_OP:
return 3;
//-- relational
case Token::LESS_THAN_OP:
case Token::GREATER_THAN_OP:
case Token::LESS_OR_EQUAL_OP:
case Token::GREATER_OR_EQUAL_OP:
return 4;
//-- additive operators
case Token::ADDITION_OP:
case Token::SUBTRACTION_OP:
return 5;
//-- multiplicative
case Token::DIVIDE_OP:
case Token::MULTIPLY_OP:
case Token::MODULUS_OP:
return 6;
default:
break;
}
return 0;
}
nsresult ExprParser::resolveQName(const String& aQName,
txAtom*& aPrefix, txIParseContext* aContext,
txAtom*& aLocalName, PRInt32& aNamespace)
{
aNamespace = kNameSpaceID_None;
String prefix, lName;
PRInt32 idx = aQName.indexOf(':');
if (idx > 0) {
aQName.subString(0, (PRUint32)idx, prefix);
aPrefix = TX_GET_ATOM(prefix);
if (!aPrefix) {
return NS_ERROR_OUT_OF_MEMORY;
}
aQName.subString((PRUint32)idx + 1, lName);
aLocalName = TX_GET_ATOM(lName);
if (!aLocalName) {
TX_RELEASE_ATOM(aPrefix);
aPrefix = 0;
return NS_ERROR_OUT_OF_MEMORY;
}
return aContext->resolveNamespacePrefix(aPrefix, aNamespace);
}
// the lexer dealt with idx == 0
aPrefix = 0;
aLocalName = TX_GET_ATOM(aQName);
if (!aLocalName) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}