Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "Tokenizer.h"
8 :
9 : #include "nsUnicharUtils.h"
10 : #include <algorithm>
11 :
12 : namespace mozilla {
13 :
14 : static const char sWhitespaces[] = " \t";
15 :
16 7105 : Tokenizer::Tokenizer(const nsACString& aSource,
17 : const char* aWhitespaces,
18 7105 : const char* aAdditionalWordChars)
19 7105 : : TokenizerBase(aWhitespaces, aAdditionalWordChars)
20 : {
21 7105 : mInputFinished = true;
22 7105 : aSource.BeginReading(mCursor);
23 7105 : mRecord = mRollback = mCursor;
24 7105 : aSource.EndReading(mEnd);
25 7105 : }
26 :
27 2 : Tokenizer::Tokenizer(const char* aSource,
28 : const char* aWhitespaces,
29 2 : const char* aAdditionalWordChars)
30 2 : : Tokenizer(nsDependentCString(aSource), aWhitespaces, aAdditionalWordChars)
31 : {
32 2 : }
33 :
34 : bool
35 22 : Tokenizer::Next(Token& aToken)
36 : {
37 22 : if (!HasInput()) {
38 0 : mHasFailed = true;
39 0 : return false;
40 : }
41 :
42 22 : mRollback = mCursor;
43 22 : mCursor = Parse(aToken);
44 :
45 22 : AssignFragment(aToken, mRollback, mCursor);
46 :
47 22 : mPastEof = aToken.Type() == TOKEN_EOF;
48 22 : mHasFailed = false;
49 22 : return true;
50 : }
51 :
52 : bool
53 16 : Tokenizer::Check(const TokenType aTokenType, Token& aResult)
54 : {
55 16 : if (!HasInput()) {
56 0 : mHasFailed = true;
57 0 : return false;
58 : }
59 :
60 16 : nsACString::const_char_iterator next = Parse(aResult);
61 16 : if (aTokenType != aResult.Type()) {
62 16 : mHasFailed = true;
63 16 : return false;
64 : }
65 :
66 0 : mRollback = mCursor;
67 0 : mCursor = next;
68 :
69 0 : AssignFragment(aResult, mRollback, mCursor);
70 :
71 0 : mPastEof = aResult.Type() == TOKEN_EOF;
72 0 : mHasFailed = false;
73 0 : return true;
74 : }
75 :
76 : bool
77 15018 : Tokenizer::Check(const Token& aToken)
78 : {
79 15018 : if (!HasInput()) {
80 0 : mHasFailed = true;
81 0 : return false;
82 : }
83 :
84 30036 : Token parsed;
85 15018 : nsACString::const_char_iterator next = Parse(parsed);
86 15018 : if (!aToken.Equals(parsed)) {
87 8071 : mHasFailed = true;
88 8071 : return false;
89 : }
90 :
91 6947 : mRollback = mCursor;
92 6947 : mCursor = next;
93 6947 : mPastEof = parsed.Type() == TOKEN_EOF;
94 6947 : mHasFailed = false;
95 6947 : return true;
96 : }
97 :
98 : void
99 472 : Tokenizer::SkipWhites(WhiteSkipping aIncludeNewLines)
100 : {
101 472 : if (!CheckWhite() && (aIncludeNewLines == DONT_INCLUDE_NEW_LINE || !CheckEOL())) {
102 471 : return;
103 : }
104 :
105 1 : nsACString::const_char_iterator rollback = mRollback;
106 1 : while (CheckWhite() || (aIncludeNewLines == INCLUDE_NEW_LINE && CheckEOL())) {
107 : }
108 :
109 1 : mHasFailed = false;
110 1 : mRollback = rollback;
111 : }
112 :
113 : void
114 0 : Tokenizer::SkipUntil(Token const& aToken)
115 : {
116 0 : nsACString::const_char_iterator rollback = mCursor;
117 0 : const Token eof = Token::EndOfFile();
118 :
119 0 : Token t;
120 0 : while (Next(t)) {
121 0 : if (aToken.Equals(t) || eof.Equals(t)) {
122 0 : Rollback();
123 0 : break;
124 : }
125 : }
126 :
127 0 : mRollback = rollback;
128 0 : }
129 :
130 : bool
131 45747 : Tokenizer::CheckChar(bool (*aClassifier)(const char aChar))
132 : {
133 45747 : if (!aClassifier) {
134 0 : MOZ_ASSERT(false);
135 : return false;
136 : }
137 :
138 45747 : if (!HasInput() || mCursor == mEnd) {
139 258 : mHasFailed = true;
140 258 : return false;
141 : }
142 :
143 45489 : if (!aClassifier(*mCursor)) {
144 6806 : mHasFailed = true;
145 6806 : return false;
146 : }
147 :
148 38683 : mRollback = mCursor;
149 38683 : ++mCursor;
150 38683 : mHasFailed = false;
151 38683 : return true;
152 : }
153 :
154 : bool
155 0 : Tokenizer::ReadChar(char* aValue)
156 : {
157 0 : MOZ_RELEASE_ASSERT(aValue);
158 :
159 0 : Token t;
160 0 : if (!Check(TOKEN_CHAR, t)) {
161 0 : return false;
162 : }
163 :
164 0 : *aValue = t.AsChar();
165 0 : return true;
166 : }
167 :
168 : bool
169 6 : Tokenizer::ReadChar(bool (*aClassifier)(const char aChar), char* aValue)
170 : {
171 6 : MOZ_RELEASE_ASSERT(aValue);
172 :
173 6 : if (!CheckChar(aClassifier)) {
174 0 : return false;
175 : }
176 :
177 6 : *aValue = *mRollback;
178 6 : return true;
179 : }
180 :
181 : bool
182 0 : Tokenizer::ReadWord(nsACString& aValue)
183 : {
184 0 : Token t;
185 0 : if (!Check(TOKEN_WORD, t)) {
186 0 : return false;
187 : }
188 :
189 0 : aValue.Assign(t.AsString());
190 0 : return true;
191 : }
192 :
193 : bool
194 0 : Tokenizer::ReadWord(nsDependentCSubstring& aValue)
195 : {
196 0 : Token t;
197 0 : if (!Check(TOKEN_WORD, t)) {
198 0 : return false;
199 : }
200 :
201 0 : aValue.Rebind(t.AsString().BeginReading(), t.AsString().Length());
202 0 : return true;
203 : }
204 :
205 : bool
206 0 : Tokenizer::ReadUntil(Token const& aToken, nsACString& aResult, ClaimInclusion aInclude)
207 : {
208 0 : nsDependentCSubstring substring;
209 0 : bool rv = ReadUntil(aToken, substring, aInclude);
210 0 : aResult.Assign(substring);
211 0 : return rv;
212 : }
213 :
214 : bool
215 0 : Tokenizer::ReadUntil(Token const& aToken, nsDependentCSubstring& aResult, ClaimInclusion aInclude)
216 : {
217 0 : nsACString::const_char_iterator record = mRecord;
218 0 : Record();
219 0 : nsACString::const_char_iterator rollback = mRollback = mCursor;
220 :
221 0 : bool found = false;
222 0 : Token t;
223 0 : while (Next(t)) {
224 0 : if (aToken.Equals(t)) {
225 0 : found = true;
226 0 : break;
227 : }
228 0 : if (t.Equals(Token::EndOfFile())) {
229 : // We don't want to eat it.
230 0 : Rollback();
231 0 : break;
232 : }
233 : }
234 :
235 0 : Claim(aResult, aInclude);
236 0 : mRollback = rollback;
237 0 : mRecord = record;
238 0 : return found;
239 : }
240 :
241 : void
242 1 : Tokenizer::Rollback()
243 : {
244 1 : MOZ_ASSERT(mCursor > mRollback || mPastEof,
245 : "Tokenizer::Rollback() cannot use twice or before any parsing");
246 :
247 1 : mPastEof = false;
248 1 : mHasFailed = false;
249 1 : mCursor = mRollback;
250 1 : }
251 :
252 : void
253 6553 : Tokenizer::Record(ClaimInclusion aInclude)
254 : {
255 6553 : mRecord = aInclude == INCLUDE_LAST
256 6553 : ? mRollback
257 : : mCursor;
258 6553 : }
259 :
260 : void
261 6245 : Tokenizer::Claim(nsACString& aResult, ClaimInclusion aInclusion)
262 : {
263 : nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
264 6245 : ? mRollback
265 6245 : : mCursor;
266 6245 : aResult.Assign(Substring(mRecord, close));
267 6245 : }
268 :
269 : void
270 0 : Tokenizer::Claim(nsDependentCSubstring& aResult, ClaimInclusion aInclusion)
271 : {
272 : nsACString::const_char_iterator close = aInclusion == EXCLUDE_LAST
273 0 : ? mRollback
274 0 : : mCursor;
275 :
276 0 : MOZ_RELEASE_ASSERT(close >= mRecord, "Overflow!");
277 0 : aResult.Rebind(mRecord, close - mRecord);
278 0 : }
279 :
280 : // TokenizerBase
281 :
282 7105 : TokenizerBase::TokenizerBase(const char* aWhitespaces,
283 7105 : const char* aAdditionalWordChars)
284 : : mPastEof(false)
285 : , mHasFailed(false)
286 : , mInputFinished(true)
287 : , mMode(Mode::FULL)
288 : , mMinRawDelivery(1024)
289 7105 : , mWhitespaces(aWhitespaces ? aWhitespaces : sWhitespaces)
290 : , mAdditionalWordChars(aAdditionalWordChars)
291 : , mCursor(nullptr)
292 : , mEnd(nullptr)
293 14210 : , mNextCustomTokenID(TOKEN_CUSTOM0)
294 : {
295 7105 : }
296 :
297 : TokenizerBase::Token
298 0 : TokenizerBase::AddCustomToken(const nsACString & aValue,
299 : ECaseSensitivity aCaseInsensitivity, bool aEnabled)
300 : {
301 0 : MOZ_ASSERT(!aValue.IsEmpty());
302 :
303 0 : UniquePtr<Token>& t = *mCustomTokens.AppendElement();
304 0 : t = MakeUnique<Token>();
305 :
306 0 : t->mType = static_cast<TokenType>(++mNextCustomTokenID);
307 0 : t->mCustomCaseInsensitivity = aCaseInsensitivity;
308 0 : t->mCustomEnabled = aEnabled;
309 0 : t->mCustom.Assign(aValue);
310 0 : return *t;
311 : }
312 :
313 : void
314 0 : TokenizerBase::RemoveCustomToken(Token& aToken)
315 : {
316 0 : if (aToken.mType == TOKEN_UNKNOWN) {
317 : // Already removed
318 0 : return;
319 : }
320 :
321 0 : for (UniquePtr<Token> const& custom : mCustomTokens) {
322 0 : if (custom->mType == aToken.mType) {
323 0 : mCustomTokens.RemoveElement(custom);
324 0 : aToken.mType = TOKEN_UNKNOWN;
325 0 : return;
326 : }
327 : }
328 :
329 0 : MOZ_ASSERT(false, "Token to remove not found");
330 : }
331 :
332 : void
333 0 : TokenizerBase::EnableCustomToken(Token const& aToken, bool aEnabled)
334 : {
335 0 : if (aToken.mType == TOKEN_UNKNOWN) {
336 : // Already removed
337 0 : return;
338 : }
339 :
340 0 : for (UniquePtr<Token> const& custom : mCustomTokens) {
341 0 : if (custom->Type() == aToken.Type()) {
342 : // This effectively destroys the token instance.
343 0 : custom->mCustomEnabled = aEnabled;
344 0 : return;
345 : }
346 : }
347 :
348 0 : MOZ_ASSERT(false, "Token to change not found");
349 : }
350 :
351 : void
352 0 : TokenizerBase::SetTokenizingMode(Mode aMode)
353 : {
354 0 : mMode = aMode;
355 0 : }
356 :
357 : bool
358 0 : TokenizerBase::HasFailed() const
359 : {
360 0 : return mHasFailed;
361 : }
362 :
363 : bool
364 60803 : TokenizerBase::HasInput() const
365 : {
366 60803 : return !mPastEof;
367 : }
368 :
369 : nsACString::const_char_iterator
370 15056 : TokenizerBase::Parse(Token& aToken) const
371 : {
372 15056 : if (mCursor == mEnd) {
373 555 : if (!mInputFinished) {
374 0 : return mCursor;
375 : }
376 :
377 555 : aToken = Token::EndOfFile();
378 555 : return mEnd;
379 : }
380 :
381 14501 : MOZ_RELEASE_ASSERT(mEnd >= mCursor, "Overflow!");
382 14501 : nsACString::size_type available = mEnd - mCursor;
383 :
384 14501 : uint32_t longestCustom = 0;
385 14501 : for (UniquePtr<Token> const& custom : mCustomTokens) {
386 0 : if (IsCustom(mCursor, *custom, &longestCustom)) {
387 0 : aToken = *custom;
388 0 : return mCursor + custom->mCustom.Length();
389 : }
390 : }
391 :
392 14501 : if (!mInputFinished && available < longestCustom) {
393 : // Not enough data to deterministically decide.
394 0 : return mCursor;
395 : }
396 :
397 14501 : nsACString::const_char_iterator next = mCursor;
398 :
399 14501 : if (mMode == Mode::CUSTOM_ONLY) {
400 : // We have to do a brute-force search for all of the enabled custom
401 : // tokens.
402 0 : while (next < mEnd) {
403 0 : ++next;
404 0 : for (UniquePtr<Token> const& custom : mCustomTokens) {
405 0 : if (IsCustom(next, *custom)) {
406 0 : aToken = Token::Raw();
407 0 : return next;
408 : }
409 : }
410 : }
411 :
412 0 : if (mInputFinished) {
413 : // End of the data reached.
414 0 : aToken = Token::Raw();
415 0 : return next;
416 : }
417 :
418 0 : if (longestCustom < available && available > mMinRawDelivery) {
419 : // We can return some data w/o waiting for either a custom token
420 : // or call to FinishData() when we leave the tail where all the
421 : // custom tokens potentially fit, so we can't lose only partially
422 : // delivered tokens. This preserves reasonable granularity.
423 0 : aToken = Token::Raw();
424 0 : return mEnd - longestCustom + 1;
425 : }
426 :
427 : // Not enough data to deterministically decide.
428 0 : return mCursor;
429 : }
430 :
431 : enum State {
432 : PARSE_INTEGER,
433 : PARSE_WORD,
434 : PARSE_CRLF,
435 : PARSE_LF,
436 : PARSE_WS,
437 : PARSE_CHAR,
438 : } state;
439 :
440 14501 : if (IsWordFirst(*next)) {
441 6 : state = PARSE_WORD;
442 14495 : } else if (IsNumber(*next)) {
443 13 : state = PARSE_INTEGER;
444 14482 : } else if (strchr(mWhitespaces, *next)) { // not UTF-8 friendly?
445 2 : state = PARSE_WS;
446 14480 : } else if (*next == '\r') {
447 0 : state = PARSE_CRLF;
448 14480 : } else if (*next == '\n') {
449 0 : state = PARSE_LF;
450 : } else {
451 14480 : state = PARSE_CHAR;
452 : }
453 :
454 14501 : mozilla::CheckedUint64 resultingNumber = 0;
455 :
456 14627 : while (next < mEnd) {
457 14564 : switch (state) {
458 : case PARSE_INTEGER:
459 : // Keep it simple for now
460 31 : resultingNumber *= 10;
461 31 : resultingNumber += static_cast<uint64_t>(*next - '0');
462 :
463 31 : ++next;
464 31 : if (IsPending(next)) {
465 0 : break;
466 : }
467 31 : if (IsEnd(next) || !IsNumber(*next)) {
468 13 : if (!resultingNumber.isValid()) {
469 0 : aToken = Token::Error();
470 : } else {
471 13 : aToken = Token::Number(resultingNumber.value());
472 : }
473 13 : return next;
474 : }
475 18 : break;
476 :
477 : case PARSE_WORD:
478 51 : ++next;
479 51 : if (IsPending(next)) {
480 0 : break;
481 : }
482 51 : if (IsEnd(next) || !IsWord(*next)) {
483 6 : aToken = Token::Word(Substring(mCursor, next));
484 6 : return next;
485 : }
486 45 : break;
487 :
488 : case PARSE_CRLF:
489 0 : ++next;
490 0 : if (IsPending(next)) {
491 0 : break;
492 : }
493 0 : if (!IsEnd(next) && *next == '\n') { // LF is optional
494 0 : ++next;
495 : }
496 0 : aToken = Token::NewLine();
497 0 : return next;
498 :
499 : case PARSE_LF:
500 0 : ++next;
501 0 : aToken = Token::NewLine();
502 0 : return next;
503 :
504 : case PARSE_WS:
505 2 : ++next;
506 2 : aToken = Token::Whitespace();
507 2 : return next;
508 :
509 : case PARSE_CHAR:
510 14480 : ++next;
511 14480 : aToken = Token::Char(*mCursor);
512 14480 : return next;
513 : } // switch (state)
514 : } // while (next < end)
515 :
516 0 : MOZ_ASSERT(!mInputFinished);
517 0 : return mCursor;
518 : }
519 :
520 : bool
521 164 : TokenizerBase::IsEnd(const nsACString::const_char_iterator& caret) const
522 : {
523 164 : return caret == mEnd;
524 : }
525 :
526 : bool
527 82 : TokenizerBase::IsPending(const nsACString::const_char_iterator& caret) const
528 : {
529 82 : return IsEnd(caret) && !mInputFinished;
530 : }
531 :
532 : bool
533 14552 : TokenizerBase::IsWordFirst(const char aInput) const
534 : {
535 : // TODO: make this fully work with unicode
536 14552 : return (ToLowerCase(static_cast<uint32_t>(aInput)) !=
537 29053 : ToUpperCase(static_cast<uint32_t>(aInput))) ||
538 29104 : '_' == aInput ||
539 29054 : (mAdditionalWordChars ? !!strchr(mAdditionalWordChars, aInput) : false);
540 : }
541 :
542 : bool
543 51 : TokenizerBase::IsWord(const char aInput) const
544 : {
545 51 : return IsWordFirst(aInput) || IsNumber(aInput);
546 : }
547 :
548 : bool
549 14526 : TokenizerBase::IsNumber(const char aInput) const
550 : {
551 : // TODO: are there unicode numbers?
552 14526 : return aInput >= '0' && aInput <= '9';
553 : }
554 :
555 : bool
556 0 : TokenizerBase::IsCustom(const nsACString::const_char_iterator & caret,
557 : const Token & aCustomToken,
558 : uint32_t * aLongest) const
559 : {
560 0 : MOZ_ASSERT(aCustomToken.mType > TOKEN_CUSTOM0);
561 0 : if (!aCustomToken.mCustomEnabled) {
562 0 : return false;
563 : }
564 :
565 0 : if (aLongest) {
566 0 : *aLongest = std::max(*aLongest, aCustomToken.mCustom.Length());
567 : }
568 :
569 : // This is not very likely to happen according to how we call this method
570 : // and since it's on a hot path, it's just a diagnostic assert,
571 : // not a release assert.
572 0 : MOZ_DIAGNOSTIC_ASSERT(mEnd >= caret, "Overflow?");
573 0 : uint32_t inputLength = mEnd - caret;
574 0 : if (aCustomToken.mCustom.Length() > inputLength) {
575 0 : return false;
576 : }
577 :
578 0 : nsDependentCSubstring inputFragment(caret, aCustomToken.mCustom.Length());
579 0 : if (aCustomToken.mCustomCaseInsensitivity == CASE_INSENSITIVE) {
580 0 : return inputFragment.Equals(aCustomToken.mCustom, nsCaseInsensitiveUTF8StringComparator());
581 : }
582 0 : return inputFragment.Equals(aCustomToken.mCustom);
583 : }
584 :
585 22 : void TokenizerBase::AssignFragment(Token& aToken,
586 : nsACString::const_char_iterator begin,
587 : nsACString::const_char_iterator end)
588 : {
589 22 : aToken.AssignFragment(begin, end);
590 22 : }
591 :
592 : // TokenizerBase::Token
593 :
594 45172 : TokenizerBase::Token::Token()
595 : : mType(TOKEN_UNKNOWN)
596 : , mChar(0)
597 : , mInteger(0)
598 : , mCustomCaseInsensitivity(CASE_SENSITIVE)
599 45172 : , mCustomEnabled(false)
600 : {
601 45172 : }
602 :
603 0 : TokenizerBase::Token::Token(const Token& aOther)
604 0 : : mType(aOther.mType)
605 : , mCustom(aOther.mCustom)
606 0 : , mChar(aOther.mChar)
607 0 : , mInteger(aOther.mInteger)
608 0 : , mCustomCaseInsensitivity(aOther.mCustomCaseInsensitivity)
609 0 : , mCustomEnabled(aOther.mCustomEnabled)
610 : {
611 0 : if (mType == TOKEN_WORD || mType > TOKEN_CUSTOM0) {
612 0 : mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
613 : }
614 0 : }
615 :
616 : TokenizerBase::Token&
617 15056 : TokenizerBase::Token::operator=(const Token& aOther)
618 : {
619 15056 : mType = aOther.mType;
620 15056 : mCustom = aOther.mCustom;
621 15056 : mChar = aOther.mChar;
622 15056 : mWord.Rebind(aOther.mWord.BeginReading(), aOther.mWord.Length());
623 15056 : mInteger = aOther.mInteger;
624 15056 : mCustomCaseInsensitivity = aOther.mCustomCaseInsensitivity;
625 15056 : mCustomEnabled = aOther.mCustomEnabled;
626 15056 : return *this;
627 : }
628 :
629 : void
630 22 : TokenizerBase::Token::AssignFragment(nsACString::const_char_iterator begin,
631 : nsACString::const_char_iterator end)
632 : {
633 22 : MOZ_RELEASE_ASSERT(end >= begin, "Overflow!");
634 22 : mFragment.Rebind(begin, end - begin);
635 22 : }
636 :
637 : // static
638 : TokenizerBase::Token
639 0 : TokenizerBase::Token::Raw()
640 : {
641 0 : Token t;
642 0 : t.mType = TOKEN_RAW;
643 0 : return t;
644 : }
645 :
646 : // static
647 : TokenizerBase::Token
648 6 : TokenizerBase::Token::Word(const nsACString& aValue)
649 : {
650 6 : Token t;
651 6 : t.mType = TOKEN_WORD;
652 6 : t.mWord.Rebind(aValue.BeginReading(), aValue.Length());
653 6 : return t;
654 : }
655 :
656 : // static
657 : TokenizerBase::Token
658 22020 : TokenizerBase::Token::Char(const char aValue)
659 : {
660 22020 : Token t;
661 22020 : t.mType = TOKEN_CHAR;
662 22020 : t.mChar = aValue;
663 22020 : return t;
664 : }
665 :
666 : // static
667 : TokenizerBase::Token
668 13 : TokenizerBase::Token::Number(const uint64_t aValue)
669 : {
670 13 : Token t;
671 13 : t.mType = TOKEN_INTEGER;
672 13 : t.mInteger = aValue;
673 13 : return t;
674 : }
675 :
676 : // static
677 : TokenizerBase::Token
678 7506 : TokenizerBase::Token::Whitespace()
679 : {
680 7506 : Token t;
681 7506 : t.mType = TOKEN_WS;
682 7506 : t.mChar = '\0';
683 7506 : return t;
684 : }
685 :
686 : // static
687 : TokenizerBase::Token
688 0 : TokenizerBase::Token::NewLine()
689 : {
690 0 : Token t;
691 0 : t.mType = TOKEN_EOL;
692 0 : return t;
693 : }
694 :
695 : // static
696 : TokenizerBase::Token
697 584 : TokenizerBase::Token::EndOfFile()
698 : {
699 584 : Token t;
700 584 : t.mType = TOKEN_EOF;
701 584 : return t;
702 : }
703 :
704 : // static
705 : TokenizerBase::Token
706 0 : TokenizerBase::Token::Error()
707 : {
708 0 : Token t;
709 0 : t.mType = TOKEN_ERROR;
710 0 : return t;
711 : }
712 :
713 : bool
714 15073 : TokenizerBase::Token::Equals(const Token& aOther) const
715 : {
716 15073 : if (mType != aOther.mType) {
717 7806 : return false;
718 : }
719 :
720 7267 : switch (mType) {
721 : case TOKEN_INTEGER:
722 0 : return AsInteger() == aOther.AsInteger();
723 : case TOKEN_WORD:
724 0 : return AsString() == aOther.AsString();
725 : case TOKEN_CHAR:
726 7253 : return AsChar() == aOther.AsChar();
727 : default:
728 14 : return true;
729 : }
730 : }
731 :
732 : char
733 14506 : TokenizerBase::Token::AsChar() const
734 : {
735 14506 : MOZ_ASSERT(mType == TOKEN_CHAR || mType == TOKEN_WS);
736 14506 : return mChar;
737 : }
738 :
739 : nsDependentCSubstring
740 0 : TokenizerBase::Token::AsString() const
741 : {
742 0 : MOZ_ASSERT(mType == TOKEN_WORD);
743 0 : return mWord;
744 : }
745 :
746 : uint64_t
747 14 : TokenizerBase::Token::AsInteger() const
748 : {
749 14 : MOZ_ASSERT(mType == TOKEN_INTEGER);
750 14 : return mInteger;
751 : }
752 :
753 : } // mozilla
|