Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : /**
7 : * Lexical analyzer for XPath expressions
8 : */
9 :
10 : #include "txExprLexer.h"
11 : #include "nsGkAtoms.h"
12 : #include "nsString.h"
13 : #include "nsError.h"
14 : #include "txXMLUtils.h"
15 :
16 : /**
17 : * Creates a new ExprLexer
18 : */
19 0 : txExprLexer::txExprLexer()
20 : : mCurrentItem(nullptr),
21 : mFirstItem(nullptr),
22 : mLastItem(nullptr),
23 0 : mTokenCount(0)
24 : {
25 0 : }
26 :
27 : /**
28 : * Destroys this instance of an txExprLexer
29 : */
30 0 : txExprLexer::~txExprLexer()
31 : {
32 : //-- delete tokens
33 0 : Token* tok = mFirstItem;
34 0 : while (tok) {
35 0 : Token* temp = tok->mNext;
36 : delete tok;
37 0 : tok = temp;
38 : }
39 0 : mCurrentItem = nullptr;
40 0 : }
41 :
42 : Token*
43 0 : txExprLexer::nextToken()
44 : {
45 0 : if (!mCurrentItem) {
46 0 : NS_NOTREACHED("nextToken called on uninitialized lexer");
47 0 : return nullptr;
48 : }
49 :
50 0 : if (mCurrentItem->mType == Token::END) {
51 : // Do not progress beyond the end token
52 0 : return mCurrentItem;
53 : }
54 :
55 0 : Token* token = mCurrentItem;
56 0 : mCurrentItem = mCurrentItem->mNext;
57 0 : return token;
58 : }
59 :
60 : void
61 0 : txExprLexer::addToken(Token* aToken)
62 : {
63 0 : if (mLastItem) {
64 0 : mLastItem->mNext = aToken;
65 : }
66 0 : if (!mFirstItem) {
67 0 : mFirstItem = aToken;
68 0 : mCurrentItem = aToken;
69 : }
70 0 : mLastItem = aToken;
71 0 : ++mTokenCount;
72 0 : }
73 :
74 : /**
75 : * Returns true if the following Token should be an operator.
76 : * This is a helper for the first bullet of [XPath 3.7]
77 : * Lexical Structure
78 : */
79 : bool
80 0 : txExprLexer::nextIsOperatorToken(Token* aToken)
81 : {
82 0 : if (!aToken || aToken->mType == Token::NULL_TOKEN) {
83 0 : return false;
84 : }
85 : /* This relies on the tokens having the right order in txExprLexer.h */
86 0 : return aToken->mType < Token::COMMA ||
87 0 : aToken->mType > Token::UNION_OP;
88 :
89 : }
90 :
91 : /**
92 : * Parses the given string into a sequence of Tokens
93 : */
94 : nsresult
95 0 : txExprLexer::parse(const nsAString& aPattern)
96 : {
97 : iterator start, end;
98 0 : start = aPattern.BeginReading(mPosition);
99 0 : aPattern.EndReading(end);
100 :
101 : //-- initialize previous token, this will automatically get
102 : //-- deleted when it goes out of scope
103 0 : Token nullToken(nullptr, nullptr, Token::NULL_TOKEN);
104 :
105 : Token::Type defType;
106 0 : Token* newToken = nullptr;
107 0 : Token* prevToken = &nullToken;
108 : bool isToken;
109 :
110 0 : while (mPosition < end) {
111 :
112 0 : defType = Token::CNAME;
113 0 : isToken = true;
114 :
115 0 : if (*mPosition == DOLLAR_SIGN) {
116 0 : if (++mPosition == end || !XMLUtils::isLetter(*mPosition)) {
117 0 : return NS_ERROR_XPATH_INVALID_VAR_NAME;
118 : }
119 0 : defType = Token::VAR_REFERENCE;
120 : }
121 : // just reuse the QName parsing, which will use defType
122 : // the token to construct
123 :
124 0 : if (XMLUtils::isLetter(*mPosition)) {
125 : // NCName, can get QName or OperatorName;
126 : // FunctionName, NodeName, and AxisSpecifier may want whitespace,
127 : // and are dealt with below
128 0 : start = mPosition;
129 0 : while (++mPosition < end && XMLUtils::isNCNameChar(*mPosition)) {
130 : /* just go */
131 : }
132 0 : if (mPosition < end && *mPosition == COLON) {
133 : // try QName or wildcard, might need to step back for axis
134 0 : if (++mPosition == end) {
135 0 : return NS_ERROR_XPATH_UNEXPECTED_END;
136 : }
137 0 : if (XMLUtils::isLetter(*mPosition)) {
138 0 : while (++mPosition < end && XMLUtils::isNCNameChar(*mPosition)) {
139 : /* just go */
140 : }
141 : }
142 0 : else if (*mPosition == '*' && defType != Token::VAR_REFERENCE) {
143 : // eat wildcard for NameTest, bail for var ref at COLON
144 0 : ++mPosition;
145 : }
146 : else {
147 0 : --mPosition; // step back
148 : }
149 : }
150 0 : if (nextIsOperatorToken(prevToken)) {
151 0 : nsDependentSubstring op(Substring(start, mPosition));
152 0 : if (nsGkAtoms::_and->Equals(op)) {
153 0 : defType = Token::AND_OP;
154 : }
155 0 : else if (nsGkAtoms::_or->Equals(op)) {
156 0 : defType = Token::OR_OP;
157 : }
158 0 : else if (nsGkAtoms::mod->Equals(op)) {
159 0 : defType = Token::MODULUS_OP;
160 : }
161 0 : else if (nsGkAtoms::div->Equals(op)) {
162 0 : defType = Token::DIVIDE_OP;
163 : }
164 : else {
165 : // XXX QUESTION: spec is not too precise
166 : // badops is sure an error, but is bad:ops, too? We say yes!
167 0 : return NS_ERROR_XPATH_OPERATOR_EXPECTED;
168 : }
169 : }
170 0 : newToken = new Token(start, mPosition, defType);
171 : }
172 0 : else if (isXPathDigit(*mPosition)) {
173 0 : start = mPosition;
174 0 : while (++mPosition < end && isXPathDigit(*mPosition)) {
175 : /* just go */
176 : }
177 0 : if (mPosition < end && *mPosition == '.') {
178 0 : while (++mPosition < end && isXPathDigit(*mPosition)) {
179 : /* just go */
180 : }
181 : }
182 0 : newToken = new Token(start, mPosition, Token::NUMBER);
183 : }
184 : else {
185 0 : switch (*mPosition) {
186 : //-- ignore whitespace
187 : case SPACE:
188 : case TX_TAB:
189 : case TX_CR:
190 : case TX_LF:
191 0 : ++mPosition;
192 0 : isToken = false;
193 0 : break;
194 : case S_QUOTE :
195 : case D_QUOTE :
196 0 : start = mPosition;
197 0 : while (++mPosition < end && *mPosition != *start) {
198 : // eat literal
199 : }
200 0 : if (mPosition == end) {
201 0 : mPosition = start;
202 0 : return NS_ERROR_XPATH_UNCLOSED_LITERAL;
203 : }
204 0 : newToken = new Token(start + 1, mPosition, Token::LITERAL);
205 0 : ++mPosition;
206 0 : break;
207 : case PERIOD:
208 : // period can be .., .(DIGITS)+ or ., check next
209 0 : if (++mPosition == end) {
210 0 : newToken = new Token(mPosition - 1, Token::SELF_NODE);
211 : }
212 0 : else if (isXPathDigit(*mPosition)) {
213 0 : start = mPosition - 1;
214 0 : while (++mPosition < end && isXPathDigit(*mPosition)) {
215 : /* just go */
216 : }
217 0 : newToken = new Token(start, mPosition, Token::NUMBER);
218 : }
219 0 : else if (*mPosition == PERIOD) {
220 0 : ++mPosition;
221 0 : newToken = new Token(mPosition - 2, mPosition, Token::PARENT_NODE);
222 : }
223 : else {
224 0 : newToken = new Token(mPosition - 1, Token::SELF_NODE);
225 : }
226 0 : break;
227 : case COLON: // QNames are dealt above, must be axis ident
228 0 : if (++mPosition >= end || *mPosition != COLON ||
229 0 : prevToken->mType != Token::CNAME) {
230 0 : return NS_ERROR_XPATH_BAD_COLON;
231 : }
232 0 : prevToken->mType = Token::AXIS_IDENTIFIER;
233 0 : ++mPosition;
234 0 : isToken = false;
235 0 : break;
236 : case FORWARD_SLASH :
237 0 : if (++mPosition < end && *mPosition == FORWARD_SLASH) {
238 0 : ++mPosition;
239 0 : newToken = new Token(mPosition - 2, mPosition, Token::ANCESTOR_OP);
240 : }
241 : else {
242 0 : newToken = new Token(mPosition - 1, Token::PARENT_OP);
243 : }
244 0 : break;
245 : case BANG : // can only be !=
246 0 : if (++mPosition < end && *mPosition == EQUAL) {
247 0 : ++mPosition;
248 0 : newToken = new Token(mPosition - 2, mPosition, Token::NOT_EQUAL_OP);
249 0 : break;
250 : }
251 : // Error ! is not not()
252 0 : return NS_ERROR_XPATH_BAD_BANG;
253 : case EQUAL:
254 0 : newToken = new Token(mPosition, Token::EQUAL_OP);
255 0 : ++mPosition;
256 0 : break;
257 : case L_ANGLE:
258 0 : if (++mPosition == end) {
259 0 : return NS_ERROR_XPATH_UNEXPECTED_END;
260 : }
261 0 : if (*mPosition == EQUAL) {
262 0 : ++mPosition;
263 0 : newToken = new Token(mPosition - 2, mPosition,
264 0 : Token::LESS_OR_EQUAL_OP);
265 : }
266 : else {
267 0 : newToken = new Token(mPosition - 1, Token::LESS_THAN_OP);
268 : }
269 0 : break;
270 : case R_ANGLE:
271 0 : if (++mPosition == end) {
272 0 : return NS_ERROR_XPATH_UNEXPECTED_END;
273 : }
274 0 : if (*mPosition == EQUAL) {
275 0 : ++mPosition;
276 0 : newToken = new Token(mPosition - 2, mPosition,
277 0 : Token::GREATER_OR_EQUAL_OP);
278 : }
279 : else {
280 0 : newToken = new Token(mPosition - 1, Token::GREATER_THAN_OP);
281 : }
282 0 : break;
283 : case HYPHEN :
284 0 : newToken = new Token(mPosition, Token::SUBTRACTION_OP);
285 0 : ++mPosition;
286 0 : break;
287 : case ASTERISK:
288 0 : if (nextIsOperatorToken(prevToken)) {
289 0 : newToken = new Token(mPosition, Token::MULTIPLY_OP);
290 : }
291 : else {
292 0 : newToken = new Token(mPosition, Token::CNAME);
293 : }
294 0 : ++mPosition;
295 0 : break;
296 : case L_PAREN:
297 0 : if (prevToken->mType == Token::CNAME) {
298 0 : const nsDependentSubstring& val = prevToken->Value();
299 0 : if (val.EqualsLiteral("comment")) {
300 0 : prevToken->mType = Token::COMMENT_AND_PAREN;
301 : }
302 0 : else if (val.EqualsLiteral("node")) {
303 0 : prevToken->mType = Token::NODE_AND_PAREN;
304 : }
305 0 : else if (val.EqualsLiteral("processing-instruction")) {
306 0 : prevToken->mType = Token::PROC_INST_AND_PAREN;
307 : }
308 0 : else if (val.EqualsLiteral("text")) {
309 0 : prevToken->mType = Token::TEXT_AND_PAREN;
310 : }
311 : else {
312 0 : prevToken->mType = Token::FUNCTION_NAME_AND_PAREN;
313 : }
314 0 : isToken = false;
315 : }
316 : else {
317 0 : newToken = new Token(mPosition, Token::L_PAREN);
318 : }
319 0 : ++mPosition;
320 0 : break;
321 : case R_PAREN:
322 0 : newToken = new Token(mPosition, Token::R_PAREN);
323 0 : ++mPosition;
324 0 : break;
325 : case L_BRACKET:
326 0 : newToken = new Token(mPosition, Token::L_BRACKET);
327 0 : ++mPosition;
328 0 : break;
329 : case R_BRACKET:
330 0 : newToken = new Token(mPosition, Token::R_BRACKET);
331 0 : ++mPosition;
332 0 : break;
333 : case COMMA:
334 0 : newToken = new Token(mPosition, Token::COMMA);
335 0 : ++mPosition;
336 0 : break;
337 : case AT_SIGN :
338 0 : newToken = new Token(mPosition, Token::AT_SIGN);
339 0 : ++mPosition;
340 0 : break;
341 : case PLUS:
342 0 : newToken = new Token(mPosition, Token::ADDITION_OP);
343 0 : ++mPosition;
344 0 : break;
345 : case VERT_BAR:
346 0 : newToken = new Token(mPosition, Token::UNION_OP);
347 0 : ++mPosition;
348 0 : break;
349 : default:
350 : // Error, don't grok character :-(
351 0 : return NS_ERROR_XPATH_ILLEGAL_CHAR;
352 : }
353 : }
354 0 : if (isToken) {
355 0 : NS_ENSURE_TRUE(newToken, NS_ERROR_OUT_OF_MEMORY);
356 0 : NS_ENSURE_TRUE(newToken != mLastItem, NS_ERROR_FAILURE);
357 0 : prevToken = newToken;
358 0 : addToken(newToken);
359 : }
360 : }
361 :
362 : // add a endToken to the list
363 0 : newToken = new Token(end, end, Token::END);
364 0 : addToken(newToken);
365 :
366 0 : return NS_OK;
367 : }
|