From 12c02a7a645e0bc0a2acdddfe3fdb9911b8c6a61 Mon Sep 17 00:00:00 2001 From: Bob Jamison Date: Sun, 21 May 2006 21:51:51 +0000 Subject: Unix-ify the sources (bzr r928) --- src/dom/xpathparser.cpp | 4094 +++++++++++++++++++++++------------------------ 1 file changed, 2047 insertions(+), 2047 deletions(-) (limited to 'src/dom/xpathparser.cpp') diff --git a/src/dom/xpathparser.cpp b/src/dom/xpathparser.cpp index cc6488eed..f0617795e 100644 --- a/src/dom/xpathparser.cpp +++ b/src/dom/xpathparser.cpp @@ -1,2047 +1,2047 @@ -/** - * Phoebe DOM Implementation. - * - * This is a C++ approximation of the W3C DOM model, which follows - * fairly closely the specifications in the various .idl files, copies of - * which are provided for reference. Most important is this one: - * - * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html - * - * Authors: - * Bob Jamison - * - * Copyright (C) 2006 Bob Jamison - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - - -#include "charclass.h" -#include "xpathparser.h" - - -namespace org -{ -namespace w3c -{ -namespace dom -{ -namespace xpath -{ - - -//######################################################################### -//# M E S S A G E S -//######################################################################### - - - -void XPathParser::trace(const char *fmt, ...) -{ - if (!debug) - return; - - FILE *f = stdout; - - va_list args; - va_start(args, fmt); - fprintf(f, "XPathParser: "); - vfprintf(f, fmt, args); - fprintf(f, "\n"); - va_end(args); -} - - - -void XPathParser::error(const char *fmt, ...) -{ - FILE *f = stdout; - va_list args; - va_start(args, fmt); - fprintf(f, "XPathParser ERROR: "); - vfprintf(f, fmt, args); - fprintf(f, "\n"); - va_end(args); - - //Print location in string - fprintf(f, "%s\n", parsebuf); - for (int i=0 ; i=(int)lexicalTokens.size()) - { - LexTok tok; - return tok; - } - return lexicalTokens[p]; -} - -int XPathParser::lexTokType(int p) -{ - if (p < 0 || p>=(int)lexicalTokens.size()) - return -1; - return lexicalTokens[p].getType(); -} - - - - - - - - -int XPathParser::peek(int p) -{ - if (p >= parselen) - return -1; - position = p; - return parsebuf[p] ; -} - - -int XPathParser::get(int p) -{ - if (p >= parselen) - return -1; - position = p; - return parsebuf[p]; -} - -int XPathParser::skipwhite(int p0) -{ - int p = p0; - - while (p < parselen) - { - int ch = peek(p); - if (!isWhitespace(ch)) - break; - ch = get(p++); - } - return p; -} - -int XPathParser::getword(int p0, DOMString &str) -{ - int p = p0; - while (p < parselen) - { - int ch = peek(p); - if (!isLetterOrDigit(ch)) - break; - ch = get(p++); - str.push_back((XMLCh)ch); - } - return p; -} - -int XPathParser::match(int p, const char *str) -{ - while (*str) - { - if (p >= parselen) - return -1; - if (parsebuf[p] != *str) - return -1; - p++; str++; - } - return p; -} - - - - -int XPathParser::getNumber(int p0, double &dresult) -{ - int p = p0; - if (p >= parselen) - return p0;/*need at least x*/ - - bool isdouble = false; - bool negative = false; - - int ch = parsebuf[p]; - if (ch=='-') - { - p++; - negative = true; - if (p >= parselen) return p0; - } - - bool seen_dot = false; - bool seen_e = false; - bool seen_eminus = false; - - DOMString num; - - int i = p; - while (i < parselen) - { - ch = parsebuf[i]; - if (ch=='.') - { - if (seen_dot) - return p0; - seen_dot = true; - isdouble = true; - } - else if (ch=='e' || ch=='E') - { - if (seen_e || !seen_dot) - return p0; - seen_e = true; - } - else if (ch=='-' && seen_e) - { - if (seen_eminus || !seen_dot) - return p0; - seen_eminus = true; - } - else if (!isDigit(ch)) - break; - num.push_back((XMLCh)ch); - i++; - } - - if (i == p)/*no digits*/ - return p0; - if (isdouble) - { - const char *begin = num.c_str(); - char *end; - dresult = strtod(begin,&end); - if (!end)/*not a number?*/ - { - error("Error formatting double: %s\n", num.c_str()); - return p0; - } - } - else - { - const char *begin = num.c_str(); - char *end; - dresult = (double)strtol(begin,&end,10); - if (!end)/*not a number?*/ - { - error("Error formatting integer: %s\n", num.c_str()); - return p0; - } - } - p = i; - return p; -} - - - -int XPathParser::getLiteral(int p0, DOMString &result) -{ - int p = p0; - int ch = peek(p); - int quotechar = 0; - if (ch == '"' || ch == '\'') - { - quotechar = ch; - } - else - return p0; - p++; - while (true) - { - if (p >= parselen) - { - error("Unterminated literal string"); - return -1; - } - ch = peek(p); - if (ch == quotechar) - break; - result.push_back((XMLCh)ch); - p++; - } - p++; //skip over closing " - return p; -} - - - -/** - * NCName is a 'non-colonized' name - */ -int XPathParser::getNCName(int p0, DOMString &result) -{ - int p = p0; - int ch = peek(p); - if (ch != '_' && !isLetter(ch)) - return p0; - - result.push_back((XMLCh)ch); - p++; - while (p < parselen) - { - ch = peek(p); - if ( isLetterOrDigit(ch) || - isCombiningChar(ch) || - isExtender(ch) || - ch == '.' || ch == '-' || ch == '_' ) - { - result.push_back((XMLCh)ch); - p++; - } - else - break; - } - return p; -} - - - -/** - * Name parsing with post-parsing - */ -int XPathParser::getNameTest(int p0, DOMString &result) -{ - int p = p0; - int ch = peek(p); - if (ch == '*') - { - result.push_back((XMLCh)ch); - p++; - return p; - } - - DOMString ncName; - int p2 = getNCName(p, ncName); - if (p2 <= p) - return p0; - - result = ncName; - p = p2; - - ch = peek(p); - if (ch != ':' )//short name. we are done - { - return p; - } - - if (peek(p+1) == ':') //was name:: which is ok - return p; - - result.push_back(':'); - - p++; - ch = peek(p); - if (ch == '*') - { - result.push_back((XMLCh)ch); - p++; - return p; - } - - DOMString ncName2; - p2 = getNCName(p, ncName2); - if (p2 <= p) - { - if (peek(p) == ':') //was name:: which is ok - return p0; - error("Nothing after ':' in QName"); - return -1; - } - - result.append(ncName2); - - p = p2; - - return p; -} - - - -int XPathParser::lexicalScan() -{ - lexicalTokens.clear(); - - int p = 0; - int p2 = p; - - while (p < parselen) - { - p2 = skipwhite(p); - p = p2; - - //trace("nextChar:%c", peek(p)); - bool selected = false; - - //### LITERAL EXPR TOKENS - for (int i=2 ; i<=10 ; i++) - { - p2 = match(p, exprTokenTable[i].sval); - if (p2 > p) - { - lexTokAdd(exprTokenTable[i].ival, p); - p = p2; - selected = true; - break; - } - } - if (selected) - continue; - - //### OPERATORS - for (LookupEntry *entry = operatorTable; entry->sval ; entry++) - { - p2 = match(p, entry->sval); - if (p2 > p) - { - long op = (long)entry->ival; - //according to the disambiguating rule for * in the spec - if (op == MULTIPLY && lexicalTokens.size() > 0) - { - int ltyp = lexTokType(lexicalTokens.size()-1); - if (ltyp != AMPR && ltyp != DOUBLE_COLON && - ltyp != LPAREN && ltyp != RBRACKET && - ltyp != COMMA && ltyp != OPERATOR ) - { - lexTokAdd(OPERATOR, p, (long)entry->ival); - p = p2; - selected = true; - break; - } - } - else - { - lexTokAdd(OPERATOR, p, (long)entry->ival); - p = p2; - selected = true; - break; - } - } - } - if (selected) - continue; - - //### NODE TYPES - for (LookupEntry *entry = nodeTypeTable; entry->sval ; entry++) - { - p2 = match(p, entry->sval); - if (p2 > p) - { - lexTokAdd(NODE_TYPE, p, (long)entry->ival); - p = p2; - selected = true; - break; - } - } - if (selected) - continue; - - //### AXIS NAMES - for (LookupEntry *entry = axisNameTable; entry->sval ; entry++) - { - p2 = match(p, entry->sval); - if (p2 > p) - { - lexTokAdd(AXIS_NAME, p, (long)entry->ival); - p = p2; - selected = true; - break; - } - } - if (selected) - continue; - - //### NAME TEST - DOMString ntResult; - p2 = getNameTest(p, ntResult); - if (p2 > p) - { - int p3 = skipwhite(p2); - if (peek(p3) == '(') - lexTokAdd(FUNCTION_NAME, p, ntResult); - else - lexTokAdd(NAME_TEST, p, ntResult); - p = p2; - selected = true; - } - if (selected) - continue; - - //### VARIABLE REFERENCE - if (peek(p) == '$') - { - p++; - DOMString qnResult; - p2 = getNCName(p, qnResult); - if (p2 > p) - { - lexTokAdd(VARIABLE_REFERENCE, p, qnResult); - p = p2; - selected = true; - } - else - { - error("Variable referenced with '$' requires a qualified name\n"); - return -1; - } - } - if (selected) - continue; - - //### NUMBER - double numval; - p2 = getNumber(p, numval); - if (p2 > p) - { - lexTokAdd(NUMBER, p, numval); - p = p2; - selected = true; - } - if (selected) - continue; - - //### LITERAL - DOMString strval; - p2 = getLiteral(p, strval); - if (p2 > p) - { - lexTokAdd(LITERAL, p, strval); - p = p2; - selected = true; - } - if (selected) - continue; - - //### CHAR (default, none of the above) - lexTokAdd(CHAR, p, (long) peek(p)); - p++; - - }//while p - - - return p; -} - - - - - - - - - - - - - - - - - - - - - - -//######################################################################### -//# X P A T H G R A M M A R P A R S I N G -//######################################################################### - - -void XPathParser::tokAdd(Token *tok) -{ - tokens.add(tok); -} - -/** - * [1] LocationPath ::= - * RelativeLocationPath - * | AbsoluteLocationPath - */ -int XPathParser::getLocationPath(int p0, int depth) -{ - traceStack("getLocationPath", p0, depth); - int p = p0; - - p = skipwhite(p); - - int p2 = getAbsoluteLocationPath(p, depth+1); - if (p2 > p) - { - tokens.add(new TokAbsolute()); - return p2; - } - - p2 = getRelativeLocationPath(p, depth+1); - if (p2 > p) - { - tokens.add(new TokRelative()); - return p2; - } - - return p0; -} - - -/** - * [2] AbsoluteLocationPath ::= - * '/' RelativeLocationPath? - * | AbbreviatedAbsoluteLocationPath - */ -int XPathParser::getAbsoluteLocationPath(int p0, int depth) -{ - traceStack("getAbsoluteLocationPath", p0, depth); - - int p = p0; - LexTok t = lexTok(p); - if (t.getType() == OPERATOR && t.getIntValue()==SLASH) - { - p++; - int p2 = getRelativeLocationPath(p, depth+1); - if (p2 <= p) - { - error("Relative path after '/'"); - return -1; - } - p = p2; - return p; - } - - //AbbreviatedAbsoluteLocationPath - if (t.getType() == OPERATOR && t.getIntValue()==DOUBLE_SLASH) - { - p++; - int p2 = getRelativeLocationPath(p, depth+1); - if (p2 <= p) - { - error("Relative path after '//'"); - return -1; - } - p = p2; - return p; - } - - - return p0; -} - - -/** - * [3] RelativeLocationPath ::= - * Step - * | RelativeLocationPath '/' Step - * | AbbreviatedRelativeLocationPath - */ -int XPathParser::getRelativeLocationPath(int p0, int depth) -{ - traceStack("getRelativeLocationPath", p0, depth); - int p = p0; - int p2 = getStep(p, depth+1); - if (p2 < 0) - return -1; - if (p2 > p) - { - p = p2; - LexTok t = lexTok(p); - if (t.getType() == OPERATOR && t.getIntValue()==SLASH) - { - p++; - p2 = getRelativeLocationPath(p, depth+1); - if (p2 < 0) - { - error("Relative path after '/'"); - return -1; - } - p = p2; - return p; - } - //AbbreviatedRelativeLocationPath - if (t.getType() == OPERATOR && t.getIntValue()==DOUBLE_SLASH) - { - p++; - // a '//' is an abbreviation for /descendant-or-self:node()/ - tokAdd(new TokAxisDescendantOrSelf()); - p2 = getRelativeLocationPath(p, depth+1); - if (p2 < 0) - { - error("Relative path after '//'"); - return -1; - } - p = p2; - return p; - } - return p; - } - - - return p0; -} - - -/** - * [4] Step ::= - * AxisSpecifier NodeTest Predicate* - * | AbbreviatedStep - */ -int XPathParser::getStep(int p0, int depth) -{ - traceStack("getStep", p0, depth); - - int p = p0; - - lexTok(p).print(); - - //This can be (and usually is) 0-length - int p2 = getAxisSpecifier(p, depth+1); - if (p2 < 0) - { - error("Axis specifier in step section"); - return -1; - } - p = p2; - p2 = getNodeTest(p, depth+1); - if (p2 < 0) - { - error("Node test in step section"); - return -1; - } - - if (p2 > p) - { - p = p2; - p2 = getPredicate(p, depth+1); - if (p2 < 0) - { - error("Predicate in step section"); - return -1; - } - p = p2; - return p; - } - - //AbbreviatedStep - if (lexTokType(p) == DOT) - { - p++; - return p; - } - - //AbbreviatedStep - if (lexTokType(p) == DOUBLE_DOT) - { - p++; - return p; - } - - return p0; -} - - -/** - * [5] AxisSpecifier ::= - * AxisName '::' - * | AbbreviatedAxisSpecifier - */ -int XPathParser::getAxisSpecifier(int p0, int depth) -{ - traceStack("getAxisSpecifier", p0, depth); - int p = p0; - if (lexTokType(p) == AXIS_NAME) - { - LexTok t = lexTok(p); - int axisType = t.getIntValue(); - p++; - if (lexTokType(p) != DOUBLE_COLON) - { - error("'::' required after axis name literal"); - return -1; - } - p++; - switch (axisType) - { - case ANCESTOR_OR_SELF: - tokAdd(new TokAxisAncestorOrSelf()); - case ANCESTOR: - tokAdd(new TokAxisAncestor()); - case ATTRIBUTE: - tokAdd(new TokAxisAttribute()); - case CHILD: - tokAdd(new TokAxisChild()); - case DESCENDANT_OR_SELF: - tokAdd(new TokAxisDescendantOrSelf()); - case DESCENDANT: - tokAdd(new TokAxisDescendant()); - case FOLLOWING_SIBLING: - tokAdd(new TokAxisFollowingSibling()); - case FOLLOWING: - tokAdd(new TokAxisFollowing()); - case NAMESPACE: - tokAdd(new TokAxisNamespace()); - case PARENT: - tokAdd(new TokAxisParent()); - case PRECEDING_SIBLING: - tokAdd(new TokAxisPrecedingSibling()); - case PRECEDING: - tokAdd(new TokAxisPreceding()); - case SELF: - tokAdd(new TokAxisSelf()); - default: - { - error("unknown axis type %d", axisType); - return -1; - } - } - return p; - } - - //AbbreviatedAxisSpecifier - if (lexTokType(p) == AMPR) - { - p++; - return p; - } - - return p0; -} - - -/** - * [6] AxisName ::= - * 'ancestor' - * | 'ancestor-or-self' - * | 'attribute' - * | 'child' - * | 'descendant' - * | 'descendant-or-self' - * | 'following' - * | 'following-sibling' - * | 'namespace' - * | 'parent' - * | 'preceding' - * | 'preceding-sibling' - * | 'self' - * NOTE: This definition, and those at the bottom, is not - * needed. Its functionality is handled by lexical scanning. - * It is left here for reference. - */ -int XPathParser::getAxisName(int p0, int depth) -{ - traceStack("getAxisName", p0, depth); - return p0; -} - - -/** - * [7] NodeTest ::= - * NameTest - * | NodeType '(' ')' - * | 'processing-instruction' '(' Literal ')' - */ -int XPathParser::getNodeTest(int p0, int depth) -{ - traceStack("getNodeTest", p0, depth); - int p = p0; - - LexTok t = lexTok(p); - if (t.getType() == NAME_TEST) - { - p++; - tokAdd(new TokNameTest(t.getStringValue())); - return p; - } - if (t.getType() == NODE_TYPE) - { - if (t.getIntValue() == PROCESSING_INSTRUCTION) - { - if (lexTokType(p) != LPAREN || - lexTokType(p+1) != LITERAL || - lexTokType(p+2) != RPAREN ) - { - error("processing instruction requires (\"literal string\")"); - return -1; - } - p += 3; - } - else - { - if (lexTokType(p+1) != LPAREN || - lexTokType(p+2) != RPAREN ) - { - error("processing instruction requires ()"); - return -1; - } - p += 2; - } - return p; - } - - return p0; -} - - -/** - * [8] Predicate ::= - * '[' PredicateExpr ']' - */ -int XPathParser::getPredicate(int p0, int depth) -{ - traceStack("getPredicate", p0, depth); - - int p = p0; - if (lexTokType(p) != LBRACKET) - return p0; - - p++; - int p2 = getPredicateExpr(p, depth+1); - if (p2 <= p) - { - error("Predicate expression in predicate"); - return -1; - } - - p = p2; - lexTok(p).print(); - if (lexTokType(p) != RBRACKET) - { - error("Predicate expression requires closing ']'"); - return -1; - } - p++; - return p; -} - - -/** - * [9] PredicateExpr ::= - * Expr - */ -int XPathParser::getPredicateExpr(int p0, int depth) -{ - traceStack("getPredicateExpr", p0, depth); - int p = p0; - int p2 = getExpr(p, depth+1); - if (p2 < 0) - { - error("Expression in predicate expression"); - return -1; - } - p = p2; - return p; -} - - -/** - * [10] AbbreviatedAbsoluteLocationPath ::= - * '//' RelativeLocationPath - * NOTE: not used. handled in getAbsoluteLocationPath() - */ -int XPathParser::getAbbreviatedAbsoluteLocationPath(int p0, int depth) -{ - traceStack("getAbbreviatedAbsoluteLocationPath", p0, depth); - - return p0; -} - -/** - * [11] AbbreviatedRelativeLocationPath ::= - * RelativeLocationPath '//' Step - * NOTE: not used. handled in getRelativeLocationPath() - */ -int XPathParser::getAbbreviatedRelativeLocationPath(int p0, int depth) -{ - traceStack("getAbbreviatedRelativeLocationPath", p0, depth); - return p0; -} - -/** - * [12] AbbreviatedStep ::= - * '.' - * | '..' - * NOTE: not used. handled in getStep() - */ -int XPathParser::getAbbreviatedStep(int p0, int depth) -{ - traceStack("getAbbreviatedStep", p0, depth); - return p0; -} - - -/** - * [13] AbbreviatedAxisSpecifier ::= - * '@'? - * NOTE: not used. handled in getAxisSpecifier() - */ -int XPathParser::getAbbreviatedAxisSpecifier(int p0, int depth) -{ - traceStack("getAbbreviatedAxisSpecifier", p0, depth); - return p0; -} - - -/** - * [14] Expr ::= - * OrExpr - */ -int XPathParser::getExpr(int p0, int depth) -{ - traceStack("getExpr", p0, depth); - - int p = p0; - - int p2 = getOrExpr(p, depth+1); - if (p2 < 0) - { - error("OR expression in expression"); - return -1; - } - p = p2; - - return p; -} - - -/** - * [15] PrimaryExpr ::= - * VariableReference - * | '(' Expr ')' - * | Literal - * | Number - * | FunctionCall - */ -int XPathParser::getPrimaryExpr(int p0, int depth) -{ - traceStack("getPrimaryExpr", p0, depth); - int p = p0; - int p2 = p; - - if (lexTokType(p) == VARIABLE_REFERENCE) - { - p++; - return p; - } - - if (lexTokType(p) == LPAREN) - { - p++; - p2 = getExpr(p, depth+1); - if (p2 <= p) - { - error("Expression in primary expression"); - return -1; - } - p += p2; - if (lexTokType(p) != RPAREN) - { - error("Primary expression requires closing ')'"); - return -1; - } - } - - if (lexTokType(p) == LITERAL) - { - tokens.add(new TokStr(lexTok(p).getStringValue())); - p++; - return p; - } - - if (lexTokType(p) == NUMBER) - { - tokens.add(new TokFloat(lexTok(p).getDoubleValue())); - p++; - return p; - } - - p2 = getFunctionCall(p, depth+1); - if (p2 < 0) - { - error("Function call in primary expression"); - return -1; - } - if (p2 > p) - { - p = p2; - return p; - } - - return p0; -} - - -/** - * [16] FunctionCall ::= - * FunctionName '(' ( Argument ( ',' Argument )* )? ')' - */ -int XPathParser::getFunctionCall(int p0, int depth) -{ - traceStack("getFunctionCall", p0, depth); - int p = p0; - - if (lexTokType(p) != FUNCTION_NAME) - return p0; - - DOMString name = lexTok(p).getStringValue(); - - p++; - - if (lexTokType(p) != LPAREN) //this makes a function - return p0; - p++; - - int argCount = 0; - - int p2 = getArgument(p, depth+1); - if (p2 < 0) - { - error("Error in function argument"); - return -1; - } - if (p2 > p) - { - argCount++; - p = p2; - while (lexTokType(p) == COMMA) - { - p++; - p2 = getArgument(p, depth+1); - if (p2 <= p) - { - error("Error in function argument"); - return -1; - } - if (p2 > p) - argCount++; - //do we add a token here? i dont think so - p = p2; - } - } - - if (lexTokType(p) != RPAREN) //mandatory - { - error("Function requires closing ')'"); - return -1; - } - p++; - - // Function names from http://www.w3.org/TR/xpath#NT-FunctionName - if (name == "last") - tokens.add(new TokFuncLast()); - else if (name == "position") - tokens.add(new TokFuncPosition()); - else if (name == "count") - tokens.add(new TokFuncCount()); - else if (name == "id") - tokens.add(new TokFuncId()); - else if (name == "local-name") - tokens.add(new TokFuncLocalName()); - else if (name == "namespace-uri") - tokens.add(new TokFuncNamespaceUri()); - else if (name == "name") - tokens.add(new TokFuncName()); - else if (name == "string") - tokens.add(new TokFuncString()); - else if (name == "concat") - tokens.add(new TokFuncConcat()); - else if (name == "starts-with") - tokens.add(new TokFuncStartsWith()); - else if (name == "contains") - tokens.add(new TokFuncContains()); - else if (name == "substring-before") - tokens.add(new TokFuncSubstringBefore()); - else if (name == "substring-after") - tokens.add(new TokFuncSubstringAfter()); - else if (name == "substring") - tokens.add(new TokFuncSubstring()); - else if (name == "string-length") - tokens.add(new TokFuncStringLength()); - else if (name == "normalize-space") - tokens.add(new TokFuncNormalizeSpace()); - else if (name == "translate") - tokens.add(new TokFuncTranslate()); - else if (name == "boolean") - tokens.add(new TokFuncBoolean()); - else if (name == "not") - tokens.add(new TokFuncNot()); - else if (name == "true") - tokens.add(new TokFuncTrue()); - else if (name == "false") - tokens.add(new TokFuncFalse()); - else if (name == "lang") - tokens.add(new TokFuncLang()); - else if (name == "number") - tokens.add(new TokFuncNumber()); - else if (name == "sum") - tokens.add(new TokFuncSum()); - else if (name == "floor") - tokens.add(new TokFuncFloor()); - else if (name == "ceiling") - tokens.add(new TokFuncCeiling()); - else if (name == "round") - tokens.add(new TokFuncRound()); - else - { - error("unknown function name:'%s'", name.c_str()); - return -1; - } - return p; -} - - -/** - * [17] Argument ::= - * Expr - */ -int XPathParser::getArgument(int p0, int depth) -{ - traceStack("getArgument", p0, depth); - int p = p0; - int p2 = getExpr(p, depth+1); - if (p2 < 0) - { - error("Argument expression"); - return -1; - } - p = p2; - return p; -} - - -/** - * [18] UnionExpr ::= - * PathExpr - * | UnionExpr '|' PathExpr - */ -int XPathParser::getUnionExpr(int p0, int depth) -{ - traceStack("getUnionExpr", p0, depth); - int p = p0; - int p2 = getPathExpr(p, depth+1); - if (p2 < 0) - { - error("Path expression for union"); - return -1; - } - p = p2; - LexTok t = lexTok(p); - if (t.getType() == OPERATOR && t.getIntValue() == PIPE) - { - p++; - p2 = getUnionExpr(p, depth+1); - if (p2 < 0) - { - error("OR (|) requires union expression on the left"); - return -1; - } - tokens.add(new TokUnion()); - p = p2; - } - return p; -} - - -/** - * [19] PathExpr ::= - * LocationPath - * | FilterExpr - * | FilterExpr '/' RelativeLocationPath - * | FilterExpr '//' RelativeLocationPath - */ -int XPathParser::getPathExpr(int p0, int depth) -{ - traceStack("getPathExpr", p0, depth); - int p = p0; - int p2; - - p2 = getLocationPath(p, depth+1); - if (p2 < 0) - { - error("Location path in path expression"); - return -1; - } - if (p2 > p) - { - p = p2; - return p; - } - - p2 = getFilterExpr(p, depth+1); - if (p2 < 0) - { - error("Filter expression in path expression"); - return -1; - } - if (p2 <= p) - return p0; - p = p2; - - LexTok t = lexTok(p); - if (t.getType() == OPERATOR && t.getIntValue() == SLASH) - { - p++; - p2 = getRelativeLocationPath(p, depth+1); - if (p2 < 0) - { - error("Relative location after / in path expression"); - return -1; - } - p = p2; - return p; - } - - if (t.getType() == OPERATOR && t.getIntValue() == DOUBLE_SLASH) - { - p++; - p2 = getRelativeLocationPath(p, depth+1); - if (p2 < 0) - { - error("Relative location after // in path expression"); - return -1; - } - p = p2; - return p; - } - return p; -} - - -/** - * [20] FilterExpr ::= - * PrimaryExpr - * | FilterExpr Predicate - */ -int XPathParser::getFilterExpr(int p0, int depth) -{ - traceStack("getFilterExpr", p0, depth); - int p = p0; - - int p2 = getPrimaryExpr(p, depth+1); - if (p2 < 0) - { - error("Primary expression in path expression"); - return -1; - } - if (p2 > p) - { - p = p2; - while (true) - { - p2 = getPredicate(p, depth+1); - if (p2 < 0) - { - error("Predicate in primary expression"); - return -1; - } - if (p2 > p) - { - p = p2; - } - else - break; - } - return p; - } - - return p0; -} - - -/** - * [21] OrExpr ::= - * AndExpr - * | OrExpr 'or' AndExpr - */ -int XPathParser::getOrExpr(int p0, int depth) -{ - traceStack("getOrExpr", p0, depth); - int p = p0; - int p2 = getAndExpr(p, depth+1); - if (p2 < 0) - { - error("AND expression in OR expression"); - return -1; - } - if (p2 > p) - { - p = p2; - LexTok t = lexTok(p); - if (t.getType() == OPERATOR && t.getIntValue() == OR) - { - p++; - p2 = getAndExpr(p, depth+1); - if (p2 <= p) - { - error("AND expression in OR expression"); - return -1; - } - p = p2; - return p; - } - tokens.add(new TokOr()); - return p; - } - - return p0; -} - - -/** - * [22] AndExpr ::= - * EqualityExpr - * | AndExpr 'and' EqualityExpr - */ -int XPathParser::getAndExpr(int p0, int depth) -{ - traceStack("getAndExpr", p0, depth); - int p = p0; - int p2 = getEqualityExpr(p, depth+1); - if (p2 < 0) - { - error("Equality expression in AND expression"); - return -1; - } - if (p2 > p) - { - p = p2; - LexTok t = lexTok(p); - if (t.getType() == OPERATOR && t.getIntValue() == AND) - { - p++; - p2 = getAndExpr(p, depth+1); - if (p2 <= p) - { - error("AND expression after 'and'"); - return -1; - } - p = p2; - return p; - } - tokens.add(new TokAnd()); - return p; - } - - return p0; -} - - -/** - * [23] EqualityExpr ::= - * RelationalExpr - * | EqualityExpr '=' RelationalExpr - * | EqualityExpr '!=' RelationalExpr - */ -int XPathParser::getEqualityExpr(int p0, int depth) -{ - traceStack("getEqualityExpr", p0, depth); - int p = p0; - int p2 = getRelationalExpr(p, depth+1); - if (p2 < 0) - { - error("Relation expression in equality expression"); - return -1; - } - if (p2 > p) - { - p = p2; - LexTok t = lexTok(p); - if (t.getType() == OPERATOR && t.getIntValue() == EQUALS) - { - p++; - p2 = getEqualityExpr(p, depth+1); - if (p2 <= p) - { - error("Equality expression expected after =="); - return -1; - } - tokens.add(new TokEquals()); - p = p2; - return p; - } - - if (t.getType() == OPERATOR && t.getIntValue() == NOT_EQUALS) - { - p++; - p2 = getEqualityExpr(p, depth+1); - if (p2 <= p) - { - error("Equality expression expected after !="); - return -1; - } - tokens.add(new TokNotEquals()); - p = p2; - return p; - } - - return p; - } - - return p0; -} - - -/** - * [24] RelationalExpr ::= - * AdditiveExpr - * | RelationalExpr '<' AdditiveExpr - * | RelationalExpr '>' AdditiveExpr - * | RelationalExpr '<=' AdditiveExpr - * | RelationalExpr '>=' AdditiveExpr - */ -int XPathParser::getRelationalExpr(int p0, int depth) -{ - traceStack("getRelationalExpr", p0, depth); - int p = p0; - int p2 = getAdditiveExpr(p, depth+1); - if (p2 < 0) - { - error("Additive expression in relational expression"); - return -1; - } - if (p2 > p) - { - p = p2; - LexTok t = lexTok(p); - - if (t.getType() == OPERATOR && t.getIntValue() == GREATER_THAN) - { - p++; - p2 = getRelationalExpr(p, depth+1); - if (p2 <= p) - { - error("Relational expression after '>'"); - return -1; - } - tokens.add(new TokGreaterThan()); - p = p2; - return p; - } - if (t.getType() == OPERATOR && t.getIntValue() == LESS_THAN) - { - p++; - p2 = getRelationalExpr(p, depth+1); - if (p2 <= p) - { - error("Relational expression after '<'"); - return -1; - } - tokens.add(new TokLessThan()); - p = p2; - return p; - } - if (t.getType() == OPERATOR && t.getIntValue() == GREATER_THAN_EQUALS) - { - p++; - p2 = getRelationalExpr(p, depth+1); - if (p2 <= p) - { - error("Relational expression after '>='"); - return -1; - } - tokens.add(new TokGreaterThanEquals()); - p = p2; - return p; - } - if (t.getType() == OPERATOR && t.getIntValue() == LESS_THAN_EQUALS) - { - p++; - p2 = getRelationalExpr(p, depth+1); - if (p2 <= p) - { - error("Relational expression after '<='"); - return -1; - } - tokens.add(new TokLessThanEquals()); - p = p2; - return p; - } - - - return p; - } - - return p0; -} - - -/** - * [25] AdditiveExp ::= - * MultiplicativeExpr - * | AdditiveExpr '+' MultiplicativeExpr - * | AdditiveExpr '-' MultiplicativeExpr - */ -int XPathParser::getAdditiveExpr(int p0, int depth) -{ - traceStack("getAdditiveExpr", p0, depth); - int p = p0; - int p2 = getMultiplicativeExpr(p, depth+1); - if (p2 < 0) - { - error("Multiplicative expression in additive expression"); - return -1; - } - if (p2 > p) - { - p = p2; - LexTok t = lexTok(p); - - if (t.getType() == OPERATOR && t.getIntValue() == PLUS) - { - p++; - p2 = getAdditiveExpr(p, depth+1); - if (p2 <= p) - { - error("Additive expression after '+'"); - return -1; - } - tokens.add(new TokPlus()); - p = p2; - return p; - } - if (t.getType() == OPERATOR && t.getIntValue() == MINUS) - { - p++; - p2 = getAdditiveExpr(p, depth+1); - if (p2 <= p) - { - error("Additive expression after '-'"); - return -1; - } - tokens.add(new TokMinus()); - p = p2; - return p; - } - - - return p; - } - - return p0; -} - - -/** - * [26] MultiplicativeExpr ::= - * UnaryExpr - * | MultiplicativeExpr MultiplyOperator UnaryExpr - * | MultiplicativeExpr 'div' UnaryExpr - * | MultiplicativeExpr 'mod' UnaryExpr - */ -int XPathParser::getMultiplicativeExpr(int p0, int depth) -{ - traceStack("getMultiplicativeExpr", p0, depth); - int p = p0; - int p2 = getUnaryExpr(p, depth+1); - if (p2 < 0) - { - error("Unary expression in multiplicative expression"); - return -1; - } - if (p2 > p) - { - p = p2; - LexTok t = lexTok(p); - - if (t.getType() == OPERATOR && t.getIntValue() == MULTIPLY) - { - p++; - p2 = getMultiplicativeExpr(p, depth+1); - if (p2 <= p) - { - error("Multiplicative expression after '*'"); - return -1; - } - tokens.add(new TokMul()); - p = p2; - return p; - } - - if (t.getType() == OPERATOR && t.getIntValue() == DIV) - { - p++; - p2 = getMultiplicativeExpr(p, depth+1); - if (p2 <= p) - { - error("Multiplicative expression after 'div'"); - return -1; - } - tokens.add(new TokDiv()); - p = p2; - return p; - } - - if (t.getType() == OPERATOR && t.getIntValue() == MOD) - { - p++; - p2 = getMultiplicativeExpr(p, depth+1); - if (p2 <= p) - { - error("Multiplicative expression after 'mod'"); - return -1; - } - tokens.add(new TokMod()); - p = p2; - return p; - } - - - return p; - } - - return p0; -} - - -/** - * [27] UnaryExpr ::= - * UnionExpr - * | '-' UnaryExpr - */ -int XPathParser::getUnaryExpr(int p0, int depth) -{ - traceStack("getUnaryExpr", p0, depth); - int p = p0; - int p2 = getUnionExpr(p, depth+1); - if (p2 < 0) - { - error("Union expression in unary expression"); - return -1; - } - if (p2 > p) - { - p = p2; - return p; - } - - if (lexTokType(p) == '-') - { - p++; - p2 = getUnaryExpr(p, depth+1); - if (p2 < 0) - { - error("Unary expression after '-'"); - return -1; - } - tokens.add(new TokNeg()); - p = p2; - return p; - } - - return p0; -} - - -//###################################################### -//# NOT USED!!! -//## The grammar definitions below are -//## handled by lexical parsing, and will not be used -//###################################################### - -/** - * [28] ExprToken ::= - * '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::' - * | NameTest - * | NodeType - * | Operator - * | FunctionName - * | AxisName - * | Literal - * | Number - * | VariableReference - */ -int XPathParser::getExprToken(int p0, int depth) -{ - traceStack("getExprToken", p0, depth); - return p0; -} - - -/** - * [29] Literal ::= - * '"' [^"]* '"' - * | "'" [^']* "'" - */ -int XPathParser::getLiteral(int p0, int depth) -{ - traceStack("getLiteral", p0, depth); - return p0; -} - - -/** - * [30] Number ::= - * Digits ('.' Digits?)? - * | '.' Digits - */ -int XPathParser::getNumber(int p0, int depth) -{ - traceStack("getNumber", p0, depth); - return p0; -} - - -/** - * [31] Digits ::= - * [0-9]+ - */ -int XPathParser::getDigits(int p0, int depth) -{ - traceStack("getDigits", p0, depth); - return p0; -} - - -/** - * [32] Operator ::= - * OperatorName - * | MultiplyOperator - * | '/' | '//' | '|' | '+' | '-' | '=' - * | '!=' | '<' | '<=' | '>' | '>=' - */ -int XPathParser::getOperator(int p0, int depth) -{ - traceStack("getOperator", p0, depth); - return p0; -} - - -/** - * [33] OperatorName ::= - * 'and' | 'or' | 'mod' | 'div' - */ -int XPathParser::getOperatorName(int p0, int depth) -{ - traceStack("getOperatorName", p0, depth); - return p0; -} - - -/** - * [34] MultiplyOperator ::= - * '*' - */ -int XPathParser::getMultiplyOperator(int p0, int depth) -{ - traceStack("getMultiplyOperator", p0, depth); - return p0; -} - - -/** - * [35] FunctionName ::= - * QName - NodeType - */ -int XPathParser::getFunctionName(int p0, int depth) -{ - traceStack("getFunctionName", p0, depth); - return p0; -} - - -/** - * [36] VariableReference ::= - * '$' QName - */ -int XPathParser::getVariableReference(int p0, int depth) -{ - traceStack("getVariableReference", p0, depth); - return p0; -} - - -/** - * [37] NameTest ::= - * '*' - * | NCName ':' '*' - * | QName - */ -int XPathParser::getNameTest(int p0, int depth) -{ - traceStack("getNameTest", p0, depth); - return p0; -} - - -/** - * [38] NodeType ::= - * 'comment' - * | 'text' - * | 'processing-instruction' - * | 'node' - */ -int XPathParser::getNodeType(int p0, int depth) -{ - traceStack("getNodeType", p0, depth); - return p0; -} - - -/** - * [39] ExprWhitespace ::= - * S - */ -int XPathParser::getExprWhitespace(int p0, int depth) -{ - traceStack("getExprWhitespace", p0, depth); - return p0; -} - - - - - -//######################################################################### -//# H I G H L E V E L P A R S I N G -//######################################################################### - -/** - * Parse a candidate XPath string. Leave a copy in 'tokens.' - */ -bool XPathParser::parse(const DOMString &xpathString) -{ - int p0 = 0; - - DOMString str = xpathString; - - parsebuf = (char *)str.c_str(); - parselen = (int) str.size(); - position = 0; - - trace("## parsing string: '%s'", parsebuf); - - lexicalScan(); - lexicalTokenDump(); - - tokens.clear();//Get ready to store new tokens - - int p = getLocationPath(p0, 0); - - parsebuf = NULL; - parselen = 0; - - if (p <= p0) - { - //return false; - } - - return true; -} - - - - - -//######################################################################### -//# E V A L U A T E -//######################################################################### - - - - -/** - * This wraps the two-step call to parse(), then execute() to get a NodeList - * of matching DOM nodes - */ -NodeList XPathParser::evaluate(const Node *root, - const DOMString &xpathString) -{ - NodeList list; - - //### Maybe do caching for speed here - - //### Parse and execute - //### Error message can be generated as a side effect - if (!parse(xpathString)) - return list; - - if (debug) - tokens.dump(); - - //### Execute the token list - TokenExecutor executor; - list = executor.execute(tokens, root); - - return list; -} - - - -} // namespace xpath -} // namespace dom -} // namespace w3c -} // namespace org -//######################################################################### -//# E N D O F F I L E -//######################################################################### - - - - +/** + * Phoebe DOM Implementation. + * + * This is a C++ approximation of the W3C DOM model, which follows + * fairly closely the specifications in the various .idl files, copies of + * which are provided for reference. Most important is this one: + * + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2006 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "charclass.h" +#include "xpathparser.h" + + +namespace org +{ +namespace w3c +{ +namespace dom +{ +namespace xpath +{ + + +//######################################################################### +//# M E S S A G E S +//######################################################################### + + + +void XPathParser::trace(const char *fmt, ...) +{ + if (!debug) + return; + + FILE *f = stdout; + + va_list args; + va_start(args, fmt); + fprintf(f, "XPathParser: "); + vfprintf(f, fmt, args); + fprintf(f, "\n"); + va_end(args); +} + + + +void XPathParser::error(const char *fmt, ...) +{ + FILE *f = stdout; + va_list args; + va_start(args, fmt); + fprintf(f, "XPathParser ERROR: "); + vfprintf(f, fmt, args); + fprintf(f, "\n"); + va_end(args); + + //Print location in string + fprintf(f, "%s\n", parsebuf); + for (int i=0 ; i=(int)lexicalTokens.size()) + { + LexTok tok; + return tok; + } + return lexicalTokens[p]; +} + +int XPathParser::lexTokType(int p) +{ + if (p < 0 || p>=(int)lexicalTokens.size()) + return -1; + return lexicalTokens[p].getType(); +} + + + + + + + + +int XPathParser::peek(int p) +{ + if (p >= parselen) + return -1; + position = p; + return parsebuf[p] ; +} + + +int XPathParser::get(int p) +{ + if (p >= parselen) + return -1; + position = p; + return parsebuf[p]; +} + +int XPathParser::skipwhite(int p0) +{ + int p = p0; + + while (p < parselen) + { + int ch = peek(p); + if (!isWhitespace(ch)) + break; + ch = get(p++); + } + return p; +} + +int XPathParser::getword(int p0, DOMString &str) +{ + int p = p0; + while (p < parselen) + { + int ch = peek(p); + if (!isLetterOrDigit(ch)) + break; + ch = get(p++); + str.push_back((XMLCh)ch); + } + return p; +} + +int XPathParser::match(int p, const char *str) +{ + while (*str) + { + if (p >= parselen) + return -1; + if (parsebuf[p] != *str) + return -1; + p++; str++; + } + return p; +} + + + + +int XPathParser::getNumber(int p0, double &dresult) +{ + int p = p0; + if (p >= parselen) + return p0;/*need at least x*/ + + bool isdouble = false; + bool negative = false; + + int ch = parsebuf[p]; + if (ch=='-') + { + p++; + negative = true; + if (p >= parselen) return p0; + } + + bool seen_dot = false; + bool seen_e = false; + bool seen_eminus = false; + + DOMString num; + + int i = p; + while (i < parselen) + { + ch = parsebuf[i]; + if (ch=='.') + { + if (seen_dot) + return p0; + seen_dot = true; + isdouble = true; + } + else if (ch=='e' || ch=='E') + { + if (seen_e || !seen_dot) + return p0; + seen_e = true; + } + else if (ch=='-' && seen_e) + { + if (seen_eminus || !seen_dot) + return p0; + seen_eminus = true; + } + else if (!isDigit(ch)) + break; + num.push_back((XMLCh)ch); + i++; + } + + if (i == p)/*no digits*/ + return p0; + if (isdouble) + { + const char *begin = num.c_str(); + char *end; + dresult = strtod(begin,&end); + if (!end)/*not a number?*/ + { + error("Error formatting double: %s\n", num.c_str()); + return p0; + } + } + else + { + const char *begin = num.c_str(); + char *end; + dresult = (double)strtol(begin,&end,10); + if (!end)/*not a number?*/ + { + error("Error formatting integer: %s\n", num.c_str()); + return p0; + } + } + p = i; + return p; +} + + + +int XPathParser::getLiteral(int p0, DOMString &result) +{ + int p = p0; + int ch = peek(p); + int quotechar = 0; + if (ch == '"' || ch == '\'') + { + quotechar = ch; + } + else + return p0; + p++; + while (true) + { + if (p >= parselen) + { + error("Unterminated literal string"); + return -1; + } + ch = peek(p); + if (ch == quotechar) + break; + result.push_back((XMLCh)ch); + p++; + } + p++; //skip over closing " + return p; +} + + + +/** + * NCName is a 'non-colonized' name + */ +int XPathParser::getNCName(int p0, DOMString &result) +{ + int p = p0; + int ch = peek(p); + if (ch != '_' && !isLetter(ch)) + return p0; + + result.push_back((XMLCh)ch); + p++; + while (p < parselen) + { + ch = peek(p); + if ( isLetterOrDigit(ch) || + isCombiningChar(ch) || + isExtender(ch) || + ch == '.' || ch == '-' || ch == '_' ) + { + result.push_back((XMLCh)ch); + p++; + } + else + break; + } + return p; +} + + + +/** + * Name parsing with post-parsing + */ +int XPathParser::getNameTest(int p0, DOMString &result) +{ + int p = p0; + int ch = peek(p); + if (ch == '*') + { + result.push_back((XMLCh)ch); + p++; + return p; + } + + DOMString ncName; + int p2 = getNCName(p, ncName); + if (p2 <= p) + return p0; + + result = ncName; + p = p2; + + ch = peek(p); + if (ch != ':' )//short name. we are done + { + return p; + } + + if (peek(p+1) == ':') //was name:: which is ok + return p; + + result.push_back(':'); + + p++; + ch = peek(p); + if (ch == '*') + { + result.push_back((XMLCh)ch); + p++; + return p; + } + + DOMString ncName2; + p2 = getNCName(p, ncName2); + if (p2 <= p) + { + if (peek(p) == ':') //was name:: which is ok + return p0; + error("Nothing after ':' in QName"); + return -1; + } + + result.append(ncName2); + + p = p2; + + return p; +} + + + +int XPathParser::lexicalScan() +{ + lexicalTokens.clear(); + + int p = 0; + int p2 = p; + + while (p < parselen) + { + p2 = skipwhite(p); + p = p2; + + //trace("nextChar:%c", peek(p)); + bool selected = false; + + //### LITERAL EXPR TOKENS + for (int i=2 ; i<=10 ; i++) + { + p2 = match(p, exprTokenTable[i].sval); + if (p2 > p) + { + lexTokAdd(exprTokenTable[i].ival, p); + p = p2; + selected = true; + break; + } + } + if (selected) + continue; + + //### OPERATORS + for (LookupEntry *entry = operatorTable; entry->sval ; entry++) + { + p2 = match(p, entry->sval); + if (p2 > p) + { + long op = (long)entry->ival; + //according to the disambiguating rule for * in the spec + if (op == MULTIPLY && lexicalTokens.size() > 0) + { + int ltyp = lexTokType(lexicalTokens.size()-1); + if (ltyp != AMPR && ltyp != DOUBLE_COLON && + ltyp != LPAREN && ltyp != RBRACKET && + ltyp != COMMA && ltyp != OPERATOR ) + { + lexTokAdd(OPERATOR, p, (long)entry->ival); + p = p2; + selected = true; + break; + } + } + else + { + lexTokAdd(OPERATOR, p, (long)entry->ival); + p = p2; + selected = true; + break; + } + } + } + if (selected) + continue; + + //### NODE TYPES + for (LookupEntry *entry = nodeTypeTable; entry->sval ; entry++) + { + p2 = match(p, entry->sval); + if (p2 > p) + { + lexTokAdd(NODE_TYPE, p, (long)entry->ival); + p = p2; + selected = true; + break; + } + } + if (selected) + continue; + + //### AXIS NAMES + for (LookupEntry *entry = axisNameTable; entry->sval ; entry++) + { + p2 = match(p, entry->sval); + if (p2 > p) + { + lexTokAdd(AXIS_NAME, p, (long)entry->ival); + p = p2; + selected = true; + break; + } + } + if (selected) + continue; + + //### NAME TEST + DOMString ntResult; + p2 = getNameTest(p, ntResult); + if (p2 > p) + { + int p3 = skipwhite(p2); + if (peek(p3) == '(') + lexTokAdd(FUNCTION_NAME, p, ntResult); + else + lexTokAdd(NAME_TEST, p, ntResult); + p = p2; + selected = true; + } + if (selected) + continue; + + //### VARIABLE REFERENCE + if (peek(p) == '$') + { + p++; + DOMString qnResult; + p2 = getNCName(p, qnResult); + if (p2 > p) + { + lexTokAdd(VARIABLE_REFERENCE, p, qnResult); + p = p2; + selected = true; + } + else + { + error("Variable referenced with '$' requires a qualified name\n"); + return -1; + } + } + if (selected) + continue; + + //### NUMBER + double numval; + p2 = getNumber(p, numval); + if (p2 > p) + { + lexTokAdd(NUMBER, p, numval); + p = p2; + selected = true; + } + if (selected) + continue; + + //### LITERAL + DOMString strval; + p2 = getLiteral(p, strval); + if (p2 > p) + { + lexTokAdd(LITERAL, p, strval); + p = p2; + selected = true; + } + if (selected) + continue; + + //### CHAR (default, none of the above) + lexTokAdd(CHAR, p, (long) peek(p)); + p++; + + }//while p + + + return p; +} + + + + + + + + + + + + + + + + + + + + + + +//######################################################################### +//# X P A T H G R A M M A R P A R S I N G +//######################################################################### + + +void XPathParser::tokAdd(Token *tok) +{ + tokens.add(tok); +} + +/** + * [1] LocationPath ::= + * RelativeLocationPath + * | AbsoluteLocationPath + */ +int XPathParser::getLocationPath(int p0, int depth) +{ + traceStack("getLocationPath", p0, depth); + int p = p0; + + p = skipwhite(p); + + int p2 = getAbsoluteLocationPath(p, depth+1); + if (p2 > p) + { + tokens.add(new TokAbsolute()); + return p2; + } + + p2 = getRelativeLocationPath(p, depth+1); + if (p2 > p) + { + tokens.add(new TokRelative()); + return p2; + } + + return p0; +} + + +/** + * [2] AbsoluteLocationPath ::= + * '/' RelativeLocationPath? + * | AbbreviatedAbsoluteLocationPath + */ +int XPathParser::getAbsoluteLocationPath(int p0, int depth) +{ + traceStack("getAbsoluteLocationPath", p0, depth); + + int p = p0; + LexTok t = lexTok(p); + if (t.getType() == OPERATOR && t.getIntValue()==SLASH) + { + p++; + int p2 = getRelativeLocationPath(p, depth+1); + if (p2 <= p) + { + error("Relative path after '/'"); + return -1; + } + p = p2; + return p; + } + + //AbbreviatedAbsoluteLocationPath + if (t.getType() == OPERATOR && t.getIntValue()==DOUBLE_SLASH) + { + p++; + int p2 = getRelativeLocationPath(p, depth+1); + if (p2 <= p) + { + error("Relative path after '//'"); + return -1; + } + p = p2; + return p; + } + + + return p0; +} + + +/** + * [3] RelativeLocationPath ::= + * Step + * | RelativeLocationPath '/' Step + * | AbbreviatedRelativeLocationPath + */ +int XPathParser::getRelativeLocationPath(int p0, int depth) +{ + traceStack("getRelativeLocationPath", p0, depth); + int p = p0; + int p2 = getStep(p, depth+1); + if (p2 < 0) + return -1; + if (p2 > p) + { + p = p2; + LexTok t = lexTok(p); + if (t.getType() == OPERATOR && t.getIntValue()==SLASH) + { + p++; + p2 = getRelativeLocationPath(p, depth+1); + if (p2 < 0) + { + error("Relative path after '/'"); + return -1; + } + p = p2; + return p; + } + //AbbreviatedRelativeLocationPath + if (t.getType() == OPERATOR && t.getIntValue()==DOUBLE_SLASH) + { + p++; + // a '//' is an abbreviation for /descendant-or-self:node()/ + tokAdd(new TokAxisDescendantOrSelf()); + p2 = getRelativeLocationPath(p, depth+1); + if (p2 < 0) + { + error("Relative path after '//'"); + return -1; + } + p = p2; + return p; + } + return p; + } + + + return p0; +} + + +/** + * [4] Step ::= + * AxisSpecifier NodeTest Predicate* + * | AbbreviatedStep + */ +int XPathParser::getStep(int p0, int depth) +{ + traceStack("getStep", p0, depth); + + int p = p0; + + lexTok(p).print(); + + //This can be (and usually is) 0-length + int p2 = getAxisSpecifier(p, depth+1); + if (p2 < 0) + { + error("Axis specifier in step section"); + return -1; + } + p = p2; + p2 = getNodeTest(p, depth+1); + if (p2 < 0) + { + error("Node test in step section"); + return -1; + } + + if (p2 > p) + { + p = p2; + p2 = getPredicate(p, depth+1); + if (p2 < 0) + { + error("Predicate in step section"); + return -1; + } + p = p2; + return p; + } + + //AbbreviatedStep + if (lexTokType(p) == DOT) + { + p++; + return p; + } + + //AbbreviatedStep + if (lexTokType(p) == DOUBLE_DOT) + { + p++; + return p; + } + + return p0; +} + + +/** + * [5] AxisSpecifier ::= + * AxisName '::' + * | AbbreviatedAxisSpecifier + */ +int XPathParser::getAxisSpecifier(int p0, int depth) +{ + traceStack("getAxisSpecifier", p0, depth); + int p = p0; + if (lexTokType(p) == AXIS_NAME) + { + LexTok t = lexTok(p); + int axisType = t.getIntValue(); + p++; + if (lexTokType(p) != DOUBLE_COLON) + { + error("'::' required after axis name literal"); + return -1; + } + p++; + switch (axisType) + { + case ANCESTOR_OR_SELF: + tokAdd(new TokAxisAncestorOrSelf()); + case ANCESTOR: + tokAdd(new TokAxisAncestor()); + case ATTRIBUTE: + tokAdd(new TokAxisAttribute()); + case CHILD: + tokAdd(new TokAxisChild()); + case DESCENDANT_OR_SELF: + tokAdd(new TokAxisDescendantOrSelf()); + case DESCENDANT: + tokAdd(new TokAxisDescendant()); + case FOLLOWING_SIBLING: + tokAdd(new TokAxisFollowingSibling()); + case FOLLOWING: + tokAdd(new TokAxisFollowing()); + case NAMESPACE: + tokAdd(new TokAxisNamespace()); + case PARENT: + tokAdd(new TokAxisParent()); + case PRECEDING_SIBLING: + tokAdd(new TokAxisPrecedingSibling()); + case PRECEDING: + tokAdd(new TokAxisPreceding()); + case SELF: + tokAdd(new TokAxisSelf()); + default: + { + error("unknown axis type %d", axisType); + return -1; + } + } + return p; + } + + //AbbreviatedAxisSpecifier + if (lexTokType(p) == AMPR) + { + p++; + return p; + } + + return p0; +} + + +/** + * [6] AxisName ::= + * 'ancestor' + * | 'ancestor-or-self' + * | 'attribute' + * | 'child' + * | 'descendant' + * | 'descendant-or-self' + * | 'following' + * | 'following-sibling' + * | 'namespace' + * | 'parent' + * | 'preceding' + * | 'preceding-sibling' + * | 'self' + * NOTE: This definition, and those at the bottom, is not + * needed. Its functionality is handled by lexical scanning. + * It is left here for reference. + */ +int XPathParser::getAxisName(int p0, int depth) +{ + traceStack("getAxisName", p0, depth); + return p0; +} + + +/** + * [7] NodeTest ::= + * NameTest + * | NodeType '(' ')' + * | 'processing-instruction' '(' Literal ')' + */ +int XPathParser::getNodeTest(int p0, int depth) +{ + traceStack("getNodeTest", p0, depth); + int p = p0; + + LexTok t = lexTok(p); + if (t.getType() == NAME_TEST) + { + p++; + tokAdd(new TokNameTest(t.getStringValue())); + return p; + } + if (t.getType() == NODE_TYPE) + { + if (t.getIntValue() == PROCESSING_INSTRUCTION) + { + if (lexTokType(p) != LPAREN || + lexTokType(p+1) != LITERAL || + lexTokType(p+2) != RPAREN ) + { + error("processing instruction requires (\"literal string\")"); + return -1; + } + p += 3; + } + else + { + if (lexTokType(p+1) != LPAREN || + lexTokType(p+2) != RPAREN ) + { + error("processing instruction requires ()"); + return -1; + } + p += 2; + } + return p; + } + + return p0; +} + + +/** + * [8] Predicate ::= + * '[' PredicateExpr ']' + */ +int XPathParser::getPredicate(int p0, int depth) +{ + traceStack("getPredicate", p0, depth); + + int p = p0; + if (lexTokType(p) != LBRACKET) + return p0; + + p++; + int p2 = getPredicateExpr(p, depth+1); + if (p2 <= p) + { + error("Predicate expression in predicate"); + return -1; + } + + p = p2; + lexTok(p).print(); + if (lexTokType(p) != RBRACKET) + { + error("Predicate expression requires closing ']'"); + return -1; + } + p++; + return p; +} + + +/** + * [9] PredicateExpr ::= + * Expr + */ +int XPathParser::getPredicateExpr(int p0, int depth) +{ + traceStack("getPredicateExpr", p0, depth); + int p = p0; + int p2 = getExpr(p, depth+1); + if (p2 < 0) + { + error("Expression in predicate expression"); + return -1; + } + p = p2; + return p; +} + + +/** + * [10] AbbreviatedAbsoluteLocationPath ::= + * '//' RelativeLocationPath + * NOTE: not used. handled in getAbsoluteLocationPath() + */ +int XPathParser::getAbbreviatedAbsoluteLocationPath(int p0, int depth) +{ + traceStack("getAbbreviatedAbsoluteLocationPath", p0, depth); + + return p0; +} + +/** + * [11] AbbreviatedRelativeLocationPath ::= + * RelativeLocationPath '//' Step + * NOTE: not used. handled in getRelativeLocationPath() + */ +int XPathParser::getAbbreviatedRelativeLocationPath(int p0, int depth) +{ + traceStack("getAbbreviatedRelativeLocationPath", p0, depth); + return p0; +} + +/** + * [12] AbbreviatedStep ::= + * '.' + * | '..' + * NOTE: not used. handled in getStep() + */ +int XPathParser::getAbbreviatedStep(int p0, int depth) +{ + traceStack("getAbbreviatedStep", p0, depth); + return p0; +} + + +/** + * [13] AbbreviatedAxisSpecifier ::= + * '@'? + * NOTE: not used. handled in getAxisSpecifier() + */ +int XPathParser::getAbbreviatedAxisSpecifier(int p0, int depth) +{ + traceStack("getAbbreviatedAxisSpecifier", p0, depth); + return p0; +} + + +/** + * [14] Expr ::= + * OrExpr + */ +int XPathParser::getExpr(int p0, int depth) +{ + traceStack("getExpr", p0, depth); + + int p = p0; + + int p2 = getOrExpr(p, depth+1); + if (p2 < 0) + { + error("OR expression in expression"); + return -1; + } + p = p2; + + return p; +} + + +/** + * [15] PrimaryExpr ::= + * VariableReference + * | '(' Expr ')' + * | Literal + * | Number + * | FunctionCall + */ +int XPathParser::getPrimaryExpr(int p0, int depth) +{ + traceStack("getPrimaryExpr", p0, depth); + int p = p0; + int p2 = p; + + if (lexTokType(p) == VARIABLE_REFERENCE) + { + p++; + return p; + } + + if (lexTokType(p) == LPAREN) + { + p++; + p2 = getExpr(p, depth+1); + if (p2 <= p) + { + error("Expression in primary expression"); + return -1; + } + p += p2; + if (lexTokType(p) != RPAREN) + { + error("Primary expression requires closing ')'"); + return -1; + } + } + + if (lexTokType(p) == LITERAL) + { + tokens.add(new TokStr(lexTok(p).getStringValue())); + p++; + return p; + } + + if (lexTokType(p) == NUMBER) + { + tokens.add(new TokFloat(lexTok(p).getDoubleValue())); + p++; + return p; + } + + p2 = getFunctionCall(p, depth+1); + if (p2 < 0) + { + error("Function call in primary expression"); + return -1; + } + if (p2 > p) + { + p = p2; + return p; + } + + return p0; +} + + +/** + * [16] FunctionCall ::= + * FunctionName '(' ( Argument ( ',' Argument )* )? ')' + */ +int XPathParser::getFunctionCall(int p0, int depth) +{ + traceStack("getFunctionCall", p0, depth); + int p = p0; + + if (lexTokType(p) != FUNCTION_NAME) + return p0; + + DOMString name = lexTok(p).getStringValue(); + + p++; + + if (lexTokType(p) != LPAREN) //this makes a function + return p0; + p++; + + int argCount = 0; + + int p2 = getArgument(p, depth+1); + if (p2 < 0) + { + error("Error in function argument"); + return -1; + } + if (p2 > p) + { + argCount++; + p = p2; + while (lexTokType(p) == COMMA) + { + p++; + p2 = getArgument(p, depth+1); + if (p2 <= p) + { + error("Error in function argument"); + return -1; + } + if (p2 > p) + argCount++; + //do we add a token here? i dont think so + p = p2; + } + } + + if (lexTokType(p) != RPAREN) //mandatory + { + error("Function requires closing ')'"); + return -1; + } + p++; + + // Function names from http://www.w3.org/TR/xpath#NT-FunctionName + if (name == "last") + tokens.add(new TokFuncLast()); + else if (name == "position") + tokens.add(new TokFuncPosition()); + else if (name == "count") + tokens.add(new TokFuncCount()); + else if (name == "id") + tokens.add(new TokFuncId()); + else if (name == "local-name") + tokens.add(new TokFuncLocalName()); + else if (name == "namespace-uri") + tokens.add(new TokFuncNamespaceUri()); + else if (name == "name") + tokens.add(new TokFuncName()); + else if (name == "string") + tokens.add(new TokFuncString()); + else if (name == "concat") + tokens.add(new TokFuncConcat()); + else if (name == "starts-with") + tokens.add(new TokFuncStartsWith()); + else if (name == "contains") + tokens.add(new TokFuncContains()); + else if (name == "substring-before") + tokens.add(new TokFuncSubstringBefore()); + else if (name == "substring-after") + tokens.add(new TokFuncSubstringAfter()); + else if (name == "substring") + tokens.add(new TokFuncSubstring()); + else if (name == "string-length") + tokens.add(new TokFuncStringLength()); + else if (name == "normalize-space") + tokens.add(new TokFuncNormalizeSpace()); + else if (name == "translate") + tokens.add(new TokFuncTranslate()); + else if (name == "boolean") + tokens.add(new TokFuncBoolean()); + else if (name == "not") + tokens.add(new TokFuncNot()); + else if (name == "true") + tokens.add(new TokFuncTrue()); + else if (name == "false") + tokens.add(new TokFuncFalse()); + else if (name == "lang") + tokens.add(new TokFuncLang()); + else if (name == "number") + tokens.add(new TokFuncNumber()); + else if (name == "sum") + tokens.add(new TokFuncSum()); + else if (name == "floor") + tokens.add(new TokFuncFloor()); + else if (name == "ceiling") + tokens.add(new TokFuncCeiling()); + else if (name == "round") + tokens.add(new TokFuncRound()); + else + { + error("unknown function name:'%s'", name.c_str()); + return -1; + } + return p; +} + + +/** + * [17] Argument ::= + * Expr + */ +int XPathParser::getArgument(int p0, int depth) +{ + traceStack("getArgument", p0, depth); + int p = p0; + int p2 = getExpr(p, depth+1); + if (p2 < 0) + { + error("Argument expression"); + return -1; + } + p = p2; + return p; +} + + +/** + * [18] UnionExpr ::= + * PathExpr + * | UnionExpr '|' PathExpr + */ +int XPathParser::getUnionExpr(int p0, int depth) +{ + traceStack("getUnionExpr", p0, depth); + int p = p0; + int p2 = getPathExpr(p, depth+1); + if (p2 < 0) + { + error("Path expression for union"); + return -1; + } + p = p2; + LexTok t = lexTok(p); + if (t.getType() == OPERATOR && t.getIntValue() == PIPE) + { + p++; + p2 = getUnionExpr(p, depth+1); + if (p2 < 0) + { + error("OR (|) requires union expression on the left"); + return -1; + } + tokens.add(new TokUnion()); + p = p2; + } + return p; +} + + +/** + * [19] PathExpr ::= + * LocationPath + * | FilterExpr + * | FilterExpr '/' RelativeLocationPath + * | FilterExpr '//' RelativeLocationPath + */ +int XPathParser::getPathExpr(int p0, int depth) +{ + traceStack("getPathExpr", p0, depth); + int p = p0; + int p2; + + p2 = getLocationPath(p, depth+1); + if (p2 < 0) + { + error("Location path in path expression"); + return -1; + } + if (p2 > p) + { + p = p2; + return p; + } + + p2 = getFilterExpr(p, depth+1); + if (p2 < 0) + { + error("Filter expression in path expression"); + return -1; + } + if (p2 <= p) + return p0; + p = p2; + + LexTok t = lexTok(p); + if (t.getType() == OPERATOR && t.getIntValue() == SLASH) + { + p++; + p2 = getRelativeLocationPath(p, depth+1); + if (p2 < 0) + { + error("Relative location after / in path expression"); + return -1; + } + p = p2; + return p; + } + + if (t.getType() == OPERATOR && t.getIntValue() == DOUBLE_SLASH) + { + p++; + p2 = getRelativeLocationPath(p, depth+1); + if (p2 < 0) + { + error("Relative location after // in path expression"); + return -1; + } + p = p2; + return p; + } + return p; +} + + +/** + * [20] FilterExpr ::= + * PrimaryExpr + * | FilterExpr Predicate + */ +int XPathParser::getFilterExpr(int p0, int depth) +{ + traceStack("getFilterExpr", p0, depth); + int p = p0; + + int p2 = getPrimaryExpr(p, depth+1); + if (p2 < 0) + { + error("Primary expression in path expression"); + return -1; + } + if (p2 > p) + { + p = p2; + while (true) + { + p2 = getPredicate(p, depth+1); + if (p2 < 0) + { + error("Predicate in primary expression"); + return -1; + } + if (p2 > p) + { + p = p2; + } + else + break; + } + return p; + } + + return p0; +} + + +/** + * [21] OrExpr ::= + * AndExpr + * | OrExpr 'or' AndExpr + */ +int XPathParser::getOrExpr(int p0, int depth) +{ + traceStack("getOrExpr", p0, depth); + int p = p0; + int p2 = getAndExpr(p, depth+1); + if (p2 < 0) + { + error("AND expression in OR expression"); + return -1; + } + if (p2 > p) + { + p = p2; + LexTok t = lexTok(p); + if (t.getType() == OPERATOR && t.getIntValue() == OR) + { + p++; + p2 = getAndExpr(p, depth+1); + if (p2 <= p) + { + error("AND expression in OR expression"); + return -1; + } + p = p2; + return p; + } + tokens.add(new TokOr()); + return p; + } + + return p0; +} + + +/** + * [22] AndExpr ::= + * EqualityExpr + * | AndExpr 'and' EqualityExpr + */ +int XPathParser::getAndExpr(int p0, int depth) +{ + traceStack("getAndExpr", p0, depth); + int p = p0; + int p2 = getEqualityExpr(p, depth+1); + if (p2 < 0) + { + error("Equality expression in AND expression"); + return -1; + } + if (p2 > p) + { + p = p2; + LexTok t = lexTok(p); + if (t.getType() == OPERATOR && t.getIntValue() == AND) + { + p++; + p2 = getAndExpr(p, depth+1); + if (p2 <= p) + { + error("AND expression after 'and'"); + return -1; + } + p = p2; + return p; + } + tokens.add(new TokAnd()); + return p; + } + + return p0; +} + + +/** + * [23] EqualityExpr ::= + * RelationalExpr + * | EqualityExpr '=' RelationalExpr + * | EqualityExpr '!=' RelationalExpr + */ +int XPathParser::getEqualityExpr(int p0, int depth) +{ + traceStack("getEqualityExpr", p0, depth); + int p = p0; + int p2 = getRelationalExpr(p, depth+1); + if (p2 < 0) + { + error("Relation expression in equality expression"); + return -1; + } + if (p2 > p) + { + p = p2; + LexTok t = lexTok(p); + if (t.getType() == OPERATOR && t.getIntValue() == EQUALS) + { + p++; + p2 = getEqualityExpr(p, depth+1); + if (p2 <= p) + { + error("Equality expression expected after =="); + return -1; + } + tokens.add(new TokEquals()); + p = p2; + return p; + } + + if (t.getType() == OPERATOR && t.getIntValue() == NOT_EQUALS) + { + p++; + p2 = getEqualityExpr(p, depth+1); + if (p2 <= p) + { + error("Equality expression expected after !="); + return -1; + } + tokens.add(new TokNotEquals()); + p = p2; + return p; + } + + return p; + } + + return p0; +} + + +/** + * [24] RelationalExpr ::= + * AdditiveExpr + * | RelationalExpr '<' AdditiveExpr + * | RelationalExpr '>' AdditiveExpr + * | RelationalExpr '<=' AdditiveExpr + * | RelationalExpr '>=' AdditiveExpr + */ +int XPathParser::getRelationalExpr(int p0, int depth) +{ + traceStack("getRelationalExpr", p0, depth); + int p = p0; + int p2 = getAdditiveExpr(p, depth+1); + if (p2 < 0) + { + error("Additive expression in relational expression"); + return -1; + } + if (p2 > p) + { + p = p2; + LexTok t = lexTok(p); + + if (t.getType() == OPERATOR && t.getIntValue() == GREATER_THAN) + { + p++; + p2 = getRelationalExpr(p, depth+1); + if (p2 <= p) + { + error("Relational expression after '>'"); + return -1; + } + tokens.add(new TokGreaterThan()); + p = p2; + return p; + } + if (t.getType() == OPERATOR && t.getIntValue() == LESS_THAN) + { + p++; + p2 = getRelationalExpr(p, depth+1); + if (p2 <= p) + { + error("Relational expression after '<'"); + return -1; + } + tokens.add(new TokLessThan()); + p = p2; + return p; + } + if (t.getType() == OPERATOR && t.getIntValue() == GREATER_THAN_EQUALS) + { + p++; + p2 = getRelationalExpr(p, depth+1); + if (p2 <= p) + { + error("Relational expression after '>='"); + return -1; + } + tokens.add(new TokGreaterThanEquals()); + p = p2; + return p; + } + if (t.getType() == OPERATOR && t.getIntValue() == LESS_THAN_EQUALS) + { + p++; + p2 = getRelationalExpr(p, depth+1); + if (p2 <= p) + { + error("Relational expression after '<='"); + return -1; + } + tokens.add(new TokLessThanEquals()); + p = p2; + return p; + } + + + return p; + } + + return p0; +} + + +/** + * [25] AdditiveExp ::= + * MultiplicativeExpr + * | AdditiveExpr '+' MultiplicativeExpr + * | AdditiveExpr '-' MultiplicativeExpr + */ +int XPathParser::getAdditiveExpr(int p0, int depth) +{ + traceStack("getAdditiveExpr", p0, depth); + int p = p0; + int p2 = getMultiplicativeExpr(p, depth+1); + if (p2 < 0) + { + error("Multiplicative expression in additive expression"); + return -1; + } + if (p2 > p) + { + p = p2; + LexTok t = lexTok(p); + + if (t.getType() == OPERATOR && t.getIntValue() == PLUS) + { + p++; + p2 = getAdditiveExpr(p, depth+1); + if (p2 <= p) + { + error("Additive expression after '+'"); + return -1; + } + tokens.add(new TokPlus()); + p = p2; + return p; + } + if (t.getType() == OPERATOR && t.getIntValue() == MINUS) + { + p++; + p2 = getAdditiveExpr(p, depth+1); + if (p2 <= p) + { + error("Additive expression after '-'"); + return -1; + } + tokens.add(new TokMinus()); + p = p2; + return p; + } + + + return p; + } + + return p0; +} + + +/** + * [26] MultiplicativeExpr ::= + * UnaryExpr + * | MultiplicativeExpr MultiplyOperator UnaryExpr + * | MultiplicativeExpr 'div' UnaryExpr + * | MultiplicativeExpr 'mod' UnaryExpr + */ +int XPathParser::getMultiplicativeExpr(int p0, int depth) +{ + traceStack("getMultiplicativeExpr", p0, depth); + int p = p0; + int p2 = getUnaryExpr(p, depth+1); + if (p2 < 0) + { + error("Unary expression in multiplicative expression"); + return -1; + } + if (p2 > p) + { + p = p2; + LexTok t = lexTok(p); + + if (t.getType() == OPERATOR && t.getIntValue() == MULTIPLY) + { + p++; + p2 = getMultiplicativeExpr(p, depth+1); + if (p2 <= p) + { + error("Multiplicative expression after '*'"); + return -1; + } + tokens.add(new TokMul()); + p = p2; + return p; + } + + if (t.getType() == OPERATOR && t.getIntValue() == DIV) + { + p++; + p2 = getMultiplicativeExpr(p, depth+1); + if (p2 <= p) + { + error("Multiplicative expression after 'div'"); + return -1; + } + tokens.add(new TokDiv()); + p = p2; + return p; + } + + if (t.getType() == OPERATOR && t.getIntValue() == MOD) + { + p++; + p2 = getMultiplicativeExpr(p, depth+1); + if (p2 <= p) + { + error("Multiplicative expression after 'mod'"); + return -1; + } + tokens.add(new TokMod()); + p = p2; + return p; + } + + + return p; + } + + return p0; +} + + +/** + * [27] UnaryExpr ::= + * UnionExpr + * | '-' UnaryExpr + */ +int XPathParser::getUnaryExpr(int p0, int depth) +{ + traceStack("getUnaryExpr", p0, depth); + int p = p0; + int p2 = getUnionExpr(p, depth+1); + if (p2 < 0) + { + error("Union expression in unary expression"); + return -1; + } + if (p2 > p) + { + p = p2; + return p; + } + + if (lexTokType(p) == '-') + { + p++; + p2 = getUnaryExpr(p, depth+1); + if (p2 < 0) + { + error("Unary expression after '-'"); + return -1; + } + tokens.add(new TokNeg()); + p = p2; + return p; + } + + return p0; +} + + +//###################################################### +//# NOT USED!!! +//## The grammar definitions below are +//## handled by lexical parsing, and will not be used +//###################################################### + +/** + * [28] ExprToken ::= + * '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::' + * | NameTest + * | NodeType + * | Operator + * | FunctionName + * | AxisName + * | Literal + * | Number + * | VariableReference + */ +int XPathParser::getExprToken(int p0, int depth) +{ + traceStack("getExprToken", p0, depth); + return p0; +} + + +/** + * [29] Literal ::= + * '"' [^"]* '"' + * | "'" [^']* "'" + */ +int XPathParser::getLiteral(int p0, int depth) +{ + traceStack("getLiteral", p0, depth); + return p0; +} + + +/** + * [30] Number ::= + * Digits ('.' Digits?)? + * | '.' Digits + */ +int XPathParser::getNumber(int p0, int depth) +{ + traceStack("getNumber", p0, depth); + return p0; +} + + +/** + * [31] Digits ::= + * [0-9]+ + */ +int XPathParser::getDigits(int p0, int depth) +{ + traceStack("getDigits", p0, depth); + return p0; +} + + +/** + * [32] Operator ::= + * OperatorName + * | MultiplyOperator + * | '/' | '//' | '|' | '+' | '-' | '=' + * | '!=' | '<' | '<=' | '>' | '>=' + */ +int XPathParser::getOperator(int p0, int depth) +{ + traceStack("getOperator", p0, depth); + return p0; +} + + +/** + * [33] OperatorName ::= + * 'and' | 'or' | 'mod' | 'div' + */ +int XPathParser::getOperatorName(int p0, int depth) +{ + traceStack("getOperatorName", p0, depth); + return p0; +} + + +/** + * [34] MultiplyOperator ::= + * '*' + */ +int XPathParser::getMultiplyOperator(int p0, int depth) +{ + traceStack("getMultiplyOperator", p0, depth); + return p0; +} + + +/** + * [35] FunctionName ::= + * QName - NodeType + */ +int XPathParser::getFunctionName(int p0, int depth) +{ + traceStack("getFunctionName", p0, depth); + return p0; +} + + +/** + * [36] VariableReference ::= + * '$' QName + */ +int XPathParser::getVariableReference(int p0, int depth) +{ + traceStack("getVariableReference", p0, depth); + return p0; +} + + +/** + * [37] NameTest ::= + * '*' + * | NCName ':' '*' + * | QName + */ +int XPathParser::getNameTest(int p0, int depth) +{ + traceStack("getNameTest", p0, depth); + return p0; +} + + +/** + * [38] NodeType ::= + * 'comment' + * | 'text' + * | 'processing-instruction' + * | 'node' + */ +int XPathParser::getNodeType(int p0, int depth) +{ + traceStack("getNodeType", p0, depth); + return p0; +} + + +/** + * [39] ExprWhitespace ::= + * S + */ +int XPathParser::getExprWhitespace(int p0, int depth) +{ + traceStack("getExprWhitespace", p0, depth); + return p0; +} + + + + + +//######################################################################### +//# H I G H L E V E L P A R S I N G +//######################################################################### + +/** + * Parse a candidate XPath string. Leave a copy in 'tokens.' + */ +bool XPathParser::parse(const DOMString &xpathString) +{ + int p0 = 0; + + DOMString str = xpathString; + + parsebuf = (char *)str.c_str(); + parselen = (int) str.size(); + position = 0; + + trace("## parsing string: '%s'", parsebuf); + + lexicalScan(); + lexicalTokenDump(); + + tokens.clear();//Get ready to store new tokens + + int p = getLocationPath(p0, 0); + + parsebuf = NULL; + parselen = 0; + + if (p <= p0) + { + //return false; + } + + return true; +} + + + + + +//######################################################################### +//# E V A L U A T E +//######################################################################### + + + + +/** + * This wraps the two-step call to parse(), then execute() to get a NodeList + * of matching DOM nodes + */ +NodeList XPathParser::evaluate(const Node *root, + const DOMString &xpathString) +{ + NodeList list; + + //### Maybe do caching for speed here + + //### Parse and execute + //### Error message can be generated as a side effect + if (!parse(xpathString)) + return list; + + if (debug) + tokens.dump(); + + //### Execute the token list + TokenExecutor executor; + list = executor.execute(tokens, root); + + return list; +} + + + +} // namespace xpath +} // namespace dom +} // namespace w3c +} // namespace org +//######################################################################### +//# E N D O F F I L E +//######################################################################### + + + + -- cgit v1.2.3