1 // -*- c-basic-offset: 2 -*-
3 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
4 * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved.
5 * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
34 #include <wtf/Assertions.h>
35 #include <wtf/unicode/Unicode.h>
38 using namespace Unicode
;
40 // we can't specify the namespace in yacc's C output, so do it here
48 #include "lexer.lut.h"
50 extern YYLTYPE kjsyylloc
; // global bison variable holding token info
52 // a bridge for yacc from the C world to C++
60 static bool isDecimalDigit(int);
62 static const size_t initialReadBufferCapacity
= 32;
63 static const size_t initialStringTableCapacity
= 64;
67 ASSERT(JSLock::currentThreadIsHoldingLock());
69 // FIXME: We'd like to avoid calling new here, but we don't currently
70 // support tearing down the Lexer at app quit time, since that would involve
71 // tearing down its UString data members without holding the JSLock.
72 static Lexer
* staticLexer
= new Lexer
;
79 , eatNextIdentifier(false)
91 m_buffer8
.reserveCapacity(initialReadBufferCapacity
);
92 m_buffer16
.reserveCapacity(initialReadBufferCapacity
);
93 m_strings
.reserveCapacity(initialStringTableCapacity
);
94 m_identifiers
.reserveCapacity(initialStringTableCapacity
);
97 void Lexer::setCode(int startingLineNumber
, const KJS::UChar
*c
, unsigned int len
)
99 yylineno
= 1 + startingLineNumber
;
100 restrKeyword
= false;
102 eatNextIdentifier
= false;
113 // read first characters
117 void Lexer::shift(unsigned p
)
119 // ECMA-262 calls for stripping Cf characters here, but we only do this for BOM,
120 // see <https://bugs.webkit.org/show_bug.cgi?id=4931>.
131 next3
= code
[pos
++].uc
;
132 } while (next3
== 0xFEFF);
136 // called on each new line
137 void Lexer::nextLine()
143 void Lexer::setDone(State s
)
153 unsigned short stringType
= 0; // either single or double quotes
161 // did we push a token on the stack previously ?
162 // (after an automatic semicolon insertion)
163 if (stackToken
>= 0) {
170 if (skipLF
&& current
!= '\n') // found \r but not \n afterwards
172 if (skipCR
&& current
!= '\r') // found \n but not \r afterwards
174 if (skipLF
|| skipCR
) // found \r\n or \n\r -> eat the second one
182 if (isWhiteSpace()) {
184 } else if (current
== '/' && next1
== '/') {
186 state
= InSingleLineComment
;
187 } else if (current
== '/' && next1
== '*') {
189 state
= InMultiLineComment
;
190 } else if (current
== -1) {
191 if (!terminator
&& !delimited
) {
192 // automatic semicolon insertion if program incomplete
198 } else if (isLineTerminator()) {
205 } else if (current
== '"' || current
== '\'') {
207 stringType
= static_cast<unsigned short>(current
);
208 } else if (isIdentStart(current
)) {
210 state
= InIdentifierOrKeyword
;
211 } else if (current
== '\\') {
212 state
= InIdentifierStartUnicodeEscapeStart
;
213 } else if (current
== '0') {
216 } else if (isDecimalDigit(current
)) {
219 } else if (current
== '.' && isDecimalDigit(next1
)) {
222 // <!-- marks the beginning of a line comment (for www usage)
223 } else if (current
== '<' && next1
== '!' &&
224 next2
== '-' && next3
== '-') {
226 state
= InSingleLineComment
;
228 } else if (atLineStart
&& current
== '-' && next1
== '-' && next2
== '>') {
230 state
= InSingleLineComment
;
232 token
= matchPunctuator(current
, next1
, next2
, next3
);
236 // cerr << "encountered unknown character" << endl;
242 if (current
== stringType
) {
245 } else if (isLineTerminator() || current
== -1) {
247 } else if (current
== '\\') {
248 state
= InEscapeSequence
;
253 // Escape Sequences inside of strings
254 case InEscapeSequence
:
255 if (isOctalDigit(current
)) {
256 if (current
>= '0' && current
<= '3' &&
257 isOctalDigit(next1
) && isOctalDigit(next2
)) {
258 record16(convertOctal(current
, next1
, next2
));
261 } else if (isOctalDigit(current
) && isOctalDigit(next1
)) {
262 record16(convertOctal('0', current
, next1
));
265 } else if (isOctalDigit(current
)) {
266 record16(convertOctal('0', '0', current
));
271 } else if (current
== 'x')
273 else if (current
== 'u')
274 state
= InUnicodeEscape
;
275 else if (isLineTerminator()) {
279 record16(singleEscape(static_cast<unsigned short>(current
)));
284 if (isHexDigit(current
) && isHexDigit(next1
)) {
286 record16(convertHex(current
, next1
));
288 } else if (current
== stringType
) {
298 case InUnicodeEscape
:
299 if (isHexDigit(current
) && isHexDigit(next1
) && isHexDigit(next2
) && isHexDigit(next3
)) {
300 record16(convertUnicode(current
, next1
, next2
, next3
));
303 } else if (current
== stringType
) {
311 case InSingleLineComment
:
312 if (isLineTerminator()) {
320 } else if (current
== -1) {
324 case InMultiLineComment
:
327 } else if (isLineTerminator()) {
329 } else if (current
== '*' && next1
== '/') {
334 case InIdentifierOrKeyword
:
336 if (isIdentPart(current
))
338 else if (current
== '\\')
339 state
= InIdentifierPartUnicodeEscapeStart
;
341 setDone(state
== InIdentifierOrKeyword
? IdentifierOrKeyword
: Identifier
);
344 if (current
== 'x' || current
== 'X') {
347 } else if (current
== '.') {
350 } else if (current
== 'e' || current
== 'E') {
352 state
= InExponentIndicator
;
353 } else if (isOctalDigit(current
)) {
356 } else if (isDecimalDigit(current
)) {
364 if (isHexDigit(current
)) {
371 if (isOctalDigit(current
)) {
374 else if (isDecimalDigit(current
)) {
381 if (isDecimalDigit(current
)) {
383 } else if (current
== '.') {
386 } else if (current
== 'e' || current
== 'E') {
388 state
= InExponentIndicator
;
393 if (isDecimalDigit(current
)) {
395 } else if (current
== 'e' || current
== 'E') {
397 state
= InExponentIndicator
;
401 case InExponentIndicator
:
402 if (current
== '+' || current
== '-') {
404 } else if (isDecimalDigit(current
)) {
411 if (isDecimalDigit(current
)) {
416 case InIdentifierStartUnicodeEscapeStart
:
418 state
= InIdentifierStartUnicodeEscape
;
422 case InIdentifierPartUnicodeEscapeStart
:
424 state
= InIdentifierPartUnicodeEscape
;
428 case InIdentifierStartUnicodeEscape
:
429 if (!isHexDigit(current
) || !isHexDigit(next1
) || !isHexDigit(next2
) || !isHexDigit(next3
)) {
433 token
= convertUnicode(current
, next1
, next2
, next3
).uc
;
435 if (!isIdentStart(token
)) {
440 state
= InIdentifier
;
442 case InIdentifierPartUnicodeEscape
:
443 if (!isHexDigit(current
) || !isHexDigit(next1
) || !isHexDigit(next2
) || !isHexDigit(next3
)) {
447 token
= convertUnicode(current
, next1
, next2
, next3
).uc
;
449 if (!isIdentPart(token
)) {
454 state
= InIdentifier
;
457 ASSERT(!"Unhandled state in switch statement");
460 // move on to the next character
463 if (state
!= Start
&& state
!= InSingleLineComment
)
467 // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
468 if ((state
== Number
|| state
== Octal
|| state
== Hex
) && isIdentStart(current
))
472 m_buffer8
.append('\0');
475 fprintf(stderr
, "line: %d ", lineNo());
476 fprintf(stderr
, "yytext (%x): ", m_buffer8
[0]);
477 fprintf(stderr
, "%s ", buffer8
.data());
481 if (state
== Number
) {
482 dval
= kjs_strtod(m_buffer8
.data(), 0L);
483 } else if (state
== Hex
) { // scan hex numbers
484 const char* p
= m_buffer8
.data() + 2;
485 while (char c
= *p
++) {
487 dval
+= convertHex(c
);
490 if (dval
>= mantissaOverflowLowerBound
)
491 dval
= parseIntOverflow(m_buffer8
.data() + 2, p
- (m_buffer8
.data() + 3), 16);
494 } else if (state
== Octal
) { // scan octal number
495 const char* p
= m_buffer8
.data() + 1;
496 while (char c
= *p
++) {
501 if (dval
>= mantissaOverflowLowerBound
)
502 dval
= parseIntOverflow(m_buffer8
.data() + 1, p
- (m_buffer8
.data() + 2), 8);
516 printf("(Identifier)/(Keyword)\n");
519 printf("(String)\n");
522 printf("(Number)\n");
529 if (state
!= Identifier
&& eatNextIdentifier
)
530 eatNextIdentifier
= false;
532 restrKeyword
= false;
534 kjsyylloc
.first_line
= yylineno
; // ???
535 kjsyylloc
.last_line
= yylineno
;
542 if(token
== '}' || token
== ';') {
546 case IdentifierOrKeyword
:
547 if ((token
= Lookup::find(&mainTable
, m_buffer16
.data(), m_buffer16
.size())) < 0) {
549 // Lookup for keyword failed, means this is an identifier
550 // Apply anonymous-function hack below (eat the identifier)
551 if (eatNextIdentifier
) {
552 eatNextIdentifier
= false;
556 kjsyylval
.ident
= makeIdentifier(m_buffer16
);
561 eatNextIdentifier
= false;
562 // Hack for "f = function somename() { ... }", too hard to get into the grammar
563 if (token
== FUNCTION
&& lastToken
== '=' )
564 eatNextIdentifier
= true;
566 if (token
== CONTINUE
|| token
== BREAK
||
567 token
== RETURN
|| token
== THROW
)
571 kjsyylval
.string
= makeUString(m_buffer16
);
575 kjsyylval
.doubleValue
= dval
;
580 fprintf(stderr
, "yylex: ERROR.\n");
585 ASSERT(!"unhandled numeration value in switch");
593 bool Lexer::isWhiteSpace() const
595 return current
== '\t' || current
== 0x0b || current
== 0x0c || isSeparatorSpace(current
);
598 bool Lexer::isLineTerminator()
600 bool cr
= (current
== '\r');
601 bool lf
= (current
== '\n');
606 return cr
|| lf
|| current
== 0x2028 || current
== 0x2029;
609 bool Lexer::isIdentStart(int c
)
611 return (category(c
) & (Letter_Uppercase
| Letter_Lowercase
| Letter_Titlecase
| Letter_Modifier
| Letter_Other
))
612 || c
== '$' || c
== '_';
615 bool Lexer::isIdentPart(int c
)
617 return (category(c
) & (Letter_Uppercase
| Letter_Lowercase
| Letter_Titlecase
| Letter_Modifier
| Letter_Other
618 | Mark_NonSpacing
| Mark_SpacingCombining
| Number_DecimalDigit
| Punctuation_Connector
))
619 || c
== '$' || c
== '_';
622 static bool isDecimalDigit(int c
)
624 return (c
>= '0' && c
<= '9');
627 bool Lexer::isHexDigit(int c
)
629 return (c
>= '0' && c
<= '9' ||
630 c
>= 'a' && c
<= 'f' ||
631 c
>= 'A' && c
<= 'F');
634 bool Lexer::isOctalDigit(int c
)
636 return (c
>= '0' && c
<= '7');
639 int Lexer::matchPunctuator(int c1
, int c2
, int c3
, int c4
)
641 if (c1
== '>' && c2
== '>' && c3
== '>' && c4
== '=') {
644 } else if (c1
== '=' && c2
== '=' && c3
== '=') {
647 } else if (c1
== '!' && c2
== '=' && c3
== '=') {
650 } else if (c1
== '>' && c2
== '>' && c3
== '>') {
653 } else if (c1
== '<' && c2
== '<' && c3
== '=') {
656 } else if (c1
== '>' && c2
== '>' && c3
== '=') {
659 } else if (c1
== '<' && c2
== '=') {
662 } else if (c1
== '>' && c2
== '=') {
665 } else if (c1
== '!' && c2
== '=') {
668 } else if (c1
== '+' && c2
== '+') {
674 } else if (c1
== '-' && c2
== '-') {
677 return AUTOMINUSMINUS
;
680 } else if (c1
== '=' && c2
== '=') {
683 } else if (c1
== '+' && c2
== '=') {
686 } else if (c1
== '-' && c2
== '=') {
689 } else if (c1
== '*' && c2
== '=') {
692 } else if (c1
== '/' && c2
== '=') {
695 } else if (c1
== '&' && c2
== '=') {
698 } else if (c1
== '^' && c2
== '=') {
701 } else if (c1
== '%' && c2
== '=') {
704 } else if (c1
== '|' && c2
== '=') {
707 } else if (c1
== '<' && c2
== '<') {
710 } else if (c1
== '>' && c2
== '>') {
713 } else if (c1
== '&' && c2
== '&') {
716 } else if (c1
== '|' && c2
== '|') {
747 return static_cast<int>(c1
);
753 unsigned short Lexer::singleEscape(unsigned short c
)
779 unsigned short Lexer::convertOctal(int c1
, int c2
, int c3
)
781 return static_cast<unsigned short>((c1
- '0') * 64 + (c2
- '0') * 8 + c3
- '0');
784 unsigned char Lexer::convertHex(int c
)
786 if (c
>= '0' && c
<= '9')
787 return static_cast<unsigned char>(c
- '0');
788 if (c
>= 'a' && c
<= 'f')
789 return static_cast<unsigned char>(c
- 'a' + 10);
790 return static_cast<unsigned char>(c
- 'A' + 10);
793 unsigned char Lexer::convertHex(int c1
, int c2
)
795 return ((convertHex(c1
) << 4) + convertHex(c2
));
798 KJS::UChar
Lexer::convertUnicode(int c1
, int c2
, int c3
, int c4
)
800 return KJS::UChar((convertHex(c1
) << 4) + convertHex(c2
),
801 (convertHex(c3
) << 4) + convertHex(c4
));
804 void Lexer::record8(int c
)
808 m_buffer8
.append(static_cast<char>(c
));
811 void Lexer::record16(int c
)
814 ASSERT(c
<= USHRT_MAX
);
815 record16(UChar(static_cast<unsigned short>(c
)));
818 void Lexer::record16(KJS::UChar c
)
820 m_buffer16
.append(c
);
823 bool Lexer::scanRegExp()
826 bool lastWasEscape
= false;
827 bool inBrackets
= false;
830 if (isLineTerminator() || current
== -1)
832 else if (current
!= '/' || lastWasEscape
== true || inBrackets
== true)
834 // keep track of '[' and ']'
835 if (!lastWasEscape
) {
836 if ( current
== '[' && !inBrackets
)
838 if ( current
== ']' && inBrackets
)
843 !lastWasEscape
&& (current
== '\\');
844 } else { // end of regexp
845 m_pattern
= UString(m_buffer16
);
853 while (isIdentPart(current
)) {
857 m_flags
= UString(m_buffer16
);
864 deleteAllValues(m_strings
);
865 Vector
<UString
*> newStrings
;
866 newStrings
.reserveCapacity(initialStringTableCapacity
);
867 m_strings
.swap(newStrings
);
869 deleteAllValues(m_identifiers
);
870 Vector
<KJS::Identifier
*> newIdentifiers
;
871 newIdentifiers
.reserveCapacity(initialStringTableCapacity
);
872 m_identifiers
.swap(newIdentifiers
);
874 Vector
<char> newBuffer8
;
875 newBuffer8
.reserveCapacity(initialReadBufferCapacity
);
876 m_buffer8
.swap(newBuffer8
);
878 Vector
<UChar
> newBuffer16
;
879 newBuffer16
.reserveCapacity(initialReadBufferCapacity
);
880 m_buffer16
.swap(newBuffer16
);
886 Identifier
* Lexer::makeIdentifier(const Vector
<KJS::UChar
>& buffer
)
888 KJS::Identifier
* identifier
= new KJS::Identifier(buffer
.data(), buffer
.size());
889 m_identifiers
.append(identifier
);
893 UString
* Lexer::makeUString(const Vector
<KJS::UChar
>& buffer
)
895 UString
* string
= new UString(buffer
);
896 m_strings
.append(string
);