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