1 // -*- c-basic-offset: 2 -*-
3 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
4 * Copyright (C) 2006, 2007 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
114 current
= (length
> 0) ? code
[0].uc
: -1;
115 next1
= (length
> 1) ? code
[1].uc
: -1;
116 next2
= (length
> 2) ? code
[2].uc
: -1;
117 next3
= (length
> 3) ? code
[3].uc
: -1;
120 void Lexer::shift(unsigned int p
)
122 // Here would be a good place to strip Cf characters, but that has caused compatibility problems:
123 // <http://bugs.webkit.org/show_bug.cgi?id=10183>.
129 next3
= (pos
+ 3 < length
) ? code
[pos
+ 3].uc
: -1;
133 // called on each new line
134 void Lexer::nextLine()
140 void Lexer::setDone(State s
)
150 unsigned short stringType
= 0; // either single or double quotes
158 // did we push a token on the stack previously ?
159 // (after an automatic semicolon insertion)
160 if (stackToken
>= 0) {
167 if (skipLF
&& current
!= '\n') // found \r but not \n afterwards
169 if (skipCR
&& current
!= '\r') // found \n but not \r afterwards
171 if (skipLF
|| skipCR
) // found \r\n or \n\r -> eat the second one
179 if (isWhiteSpace()) {
181 } else if (current
== '/' && next1
== '/') {
183 state
= InSingleLineComment
;
184 } else if (current
== '/' && next1
== '*') {
186 state
= InMultiLineComment
;
187 } else if (current
== -1) {
188 if (!terminator
&& !delimited
) {
189 // automatic semicolon insertion if program incomplete
195 } else if (isLineTerminator()) {
202 } else if (current
== '"' || current
== '\'') {
204 stringType
= static_cast<unsigned short>(current
);
205 } else if (isIdentStart(current
)) {
207 state
= InIdentifierOrKeyword
;
208 } else if (current
== '\\') {
209 state
= InIdentifierStartUnicodeEscapeStart
;
210 } else if (current
== '0') {
213 } else if (isDecimalDigit(current
)) {
216 } else if (current
== '.' && isDecimalDigit(next1
)) {
219 // <!-- marks the beginning of a line comment (for www usage)
220 } else if (current
== '<' && next1
== '!' &&
221 next2
== '-' && next3
== '-') {
223 state
= InSingleLineComment
;
225 } else if (atLineStart
&& current
== '-' && next1
== '-' && next2
== '>') {
227 state
= InSingleLineComment
;
229 token
= matchPunctuator(current
, next1
, next2
, next3
);
233 // cerr << "encountered unknown character" << endl;
239 if (current
== stringType
) {
242 } else if (isLineTerminator() || current
== -1) {
244 } else if (current
== '\\') {
245 state
= InEscapeSequence
;
250 // Escape Sequences inside of strings
251 case InEscapeSequence
:
252 if (isOctalDigit(current
)) {
253 if (current
>= '0' && current
<= '3' &&
254 isOctalDigit(next1
) && isOctalDigit(next2
)) {
255 record16(convertOctal(current
, next1
, next2
));
258 } else if (isOctalDigit(current
) && isOctalDigit(next1
)) {
259 record16(convertOctal('0', current
, next1
));
262 } else if (isOctalDigit(current
)) {
263 record16(convertOctal('0', '0', current
));
268 } else if (current
== 'x')
270 else if (current
== 'u')
271 state
= InUnicodeEscape
;
272 else if (isLineTerminator()) {
276 record16(singleEscape(static_cast<unsigned short>(current
)));
281 if (isHexDigit(current
) && isHexDigit(next1
)) {
283 record16(convertHex(current
, next1
));
285 } else if (current
== stringType
) {
295 case InUnicodeEscape
:
296 if (isHexDigit(current
) && isHexDigit(next1
) && isHexDigit(next2
) && isHexDigit(next3
)) {
297 record16(convertUnicode(current
, next1
, next2
, next3
));
300 } else if (current
== stringType
) {
308 case InSingleLineComment
:
309 if (isLineTerminator()) {
317 } else if (current
== -1) {
321 case InMultiLineComment
:
324 } else if (isLineTerminator()) {
326 } else if (current
== '*' && next1
== '/') {
331 case InIdentifierOrKeyword
:
333 if (isIdentPart(current
))
335 else if (current
== '\\')
336 state
= InIdentifierPartUnicodeEscapeStart
;
338 setDone(state
== InIdentifierOrKeyword
? IdentifierOrKeyword
: Identifier
);
341 if (current
== 'x' || current
== 'X') {
344 } else if (current
== '.') {
347 } else if (current
== 'e' || current
== 'E') {
349 state
= InExponentIndicator
;
350 } else if (isOctalDigit(current
)) {
353 } else if (isDecimalDigit(current
)) {
361 if (isHexDigit(current
)) {
368 if (isOctalDigit(current
)) {
371 else if (isDecimalDigit(current
)) {
378 if (isDecimalDigit(current
)) {
380 } else if (current
== '.') {
383 } else if (current
== 'e' || current
== 'E') {
385 state
= InExponentIndicator
;
390 if (isDecimalDigit(current
)) {
392 } else if (current
== 'e' || current
== 'E') {
394 state
= InExponentIndicator
;
398 case InExponentIndicator
:
399 if (current
== '+' || current
== '-') {
401 } else if (isDecimalDigit(current
)) {
408 if (isDecimalDigit(current
)) {
413 case InIdentifierStartUnicodeEscapeStart
:
415 state
= InIdentifierStartUnicodeEscape
;
419 case InIdentifierPartUnicodeEscapeStart
:
421 state
= InIdentifierPartUnicodeEscape
;
425 case InIdentifierStartUnicodeEscape
:
426 if (!isHexDigit(current
) || !isHexDigit(next1
) || !isHexDigit(next2
) || !isHexDigit(next3
)) {
430 token
= convertUnicode(current
, next1
, next2
, next3
).uc
;
432 if (!isIdentStart(token
)) {
437 state
= InIdentifier
;
439 case InIdentifierPartUnicodeEscape
:
440 if (!isHexDigit(current
) || !isHexDigit(next1
) || !isHexDigit(next2
) || !isHexDigit(next3
)) {
444 token
= convertUnicode(current
, next1
, next2
, next3
).uc
;
446 if (!isIdentPart(token
)) {
451 state
= InIdentifier
;
454 ASSERT(!"Unhandled state in switch statement");
457 // move on to the next character
460 if (state
!= Start
&& state
!= InSingleLineComment
)
464 // no identifiers allowed directly after numeric literal, e.g. "3in" is bad
465 if ((state
== Number
|| state
== Octal
|| state
== Hex
) && isIdentStart(current
))
469 m_buffer8
.append('\0');
472 fprintf(stderr
, "line: %d ", lineNo());
473 fprintf(stderr
, "yytext (%x): ", m_buffer8
[0]);
474 fprintf(stderr
, "%s ", buffer8
.data());
478 if (state
== Number
) {
479 dval
= kjs_strtod(m_buffer8
.data(), 0L);
480 } else if (state
== Hex
) { // scan hex numbers
481 const char* p
= m_buffer8
.data() + 2;
482 while (char c
= *p
++) {
484 dval
+= convertHex(c
);
487 if (dval
>= mantissaOverflowLowerBound
)
488 dval
= parseIntOverflow(m_buffer8
.data() + 2, p
- (m_buffer8
.data() + 3), 16);
491 } else if (state
== Octal
) { // scan octal number
492 const char* p
= m_buffer8
.data() + 1;
493 while (char c
= *p
++) {
498 if (dval
>= mantissaOverflowLowerBound
)
499 dval
= parseIntOverflow(m_buffer8
.data() + 1, p
- (m_buffer8
.data() + 2), 8);
513 printf("(Identifier)/(Keyword)\n");
516 printf("(String)\n");
519 printf("(Number)\n");
526 if (state
!= Identifier
&& eatNextIdentifier
)
527 eatNextIdentifier
= false;
529 restrKeyword
= false;
531 kjsyylloc
.first_line
= yylineno
; // ???
532 kjsyylloc
.last_line
= yylineno
;
539 if(token
== '}' || token
== ';') {
543 case IdentifierOrKeyword
:
544 if ((token
= Lookup::find(&mainTable
, m_buffer16
.data(), m_buffer16
.size())) < 0) {
546 // Lookup for keyword failed, means this is an identifier
547 // Apply anonymous-function hack below (eat the identifier)
548 if (eatNextIdentifier
) {
549 eatNextIdentifier
= false;
553 kjsyylval
.ident
= makeIdentifier(m_buffer16
);
558 eatNextIdentifier
= false;
559 // Hack for "f = function somename() { ... }", too hard to get into the grammar
560 if (token
== FUNCTION
&& lastToken
== '=' )
561 eatNextIdentifier
= true;
563 if (token
== CONTINUE
|| token
== BREAK
||
564 token
== RETURN
|| token
== THROW
)
568 kjsyylval
.string
= makeUString(m_buffer16
);
572 kjsyylval
.doubleValue
= dval
;
577 fprintf(stderr
, "yylex: ERROR.\n");
582 ASSERT(!"unhandled numeration value in switch");
590 bool Lexer::isWhiteSpace() const
592 return current
== '\t' || current
== 0x0b || current
== 0x0c || isSeparatorSpace(current
);
595 bool Lexer::isLineTerminator()
597 bool cr
= (current
== '\r');
598 bool lf
= (current
== '\n');
603 return cr
|| lf
|| current
== 0x2028 || current
== 0x2029;
606 bool Lexer::isIdentStart(int c
)
608 return (category(c
) & (Letter_Uppercase
| Letter_Lowercase
| Letter_Titlecase
| Letter_Modifier
| Letter_Other
))
609 || c
== '$' || c
== '_';
612 bool Lexer::isIdentPart(int c
)
614 return (category(c
) & (Letter_Uppercase
| Letter_Lowercase
| Letter_Titlecase
| Letter_Modifier
| Letter_Other
615 | Mark_NonSpacing
| Mark_SpacingCombining
| Number_DecimalDigit
| Punctuation_Connector
))
616 || c
== '$' || c
== '_';
619 static bool isDecimalDigit(int c
)
621 return (c
>= '0' && c
<= '9');
624 bool Lexer::isHexDigit(int c
)
626 return (c
>= '0' && c
<= '9' ||
627 c
>= 'a' && c
<= 'f' ||
628 c
>= 'A' && c
<= 'F');
631 bool Lexer::isOctalDigit(int c
)
633 return (c
>= '0' && c
<= '7');
636 int Lexer::matchPunctuator(int c1
, int c2
, int c3
, int c4
)
638 if (c1
== '>' && c2
== '>' && c3
== '>' && c4
== '=') {
641 } else if (c1
== '=' && c2
== '=' && c3
== '=') {
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
== '=') {
659 } else if (c1
== '>' && c2
== '=') {
662 } else if (c1
== '!' && c2
== '=') {
665 } else if (c1
== '+' && c2
== '+') {
671 } else if (c1
== '-' && c2
== '-') {
674 return AUTOMINUSMINUS
;
677 } else if (c1
== '=' && c2
== '=') {
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
== '|') {
744 return static_cast<int>(c1
);
750 unsigned short Lexer::singleEscape(unsigned short c
)
776 unsigned short Lexer::convertOctal(int c1
, int c2
, int c3
)
778 return static_cast<unsigned short>((c1
- '0') * 64 + (c2
- '0') * 8 + c3
- '0');
781 unsigned char Lexer::convertHex(int c
)
783 if (c
>= '0' && c
<= '9')
784 return static_cast<unsigned char>(c
- '0');
785 if (c
>= 'a' && c
<= 'f')
786 return static_cast<unsigned char>(c
- 'a' + 10);
787 return static_cast<unsigned char>(c
- 'A' + 10);
790 unsigned char Lexer::convertHex(int c1
, int c2
)
792 return ((convertHex(c1
) << 4) + convertHex(c2
));
795 KJS::UChar
Lexer::convertUnicode(int c1
, int c2
, int c3
, int c4
)
797 return KJS::UChar((convertHex(c1
) << 4) + convertHex(c2
),
798 (convertHex(c3
) << 4) + convertHex(c4
));
801 void Lexer::record8(int c
)
805 m_buffer8
.append(static_cast<char>(c
));
808 void Lexer::record16(int c
)
811 ASSERT(c
<= USHRT_MAX
);
812 record16(UChar(static_cast<unsigned short>(c
)));
815 void Lexer::record16(KJS::UChar c
)
817 m_buffer16
.append(c
);
820 bool Lexer::scanRegExp()
823 bool lastWasEscape
= false;
824 bool inBrackets
= false;
827 if (isLineTerminator() || current
== -1)
829 else if (current
!= '/' || lastWasEscape
== true || inBrackets
== true)
831 // keep track of '[' and ']'
832 if (!lastWasEscape
) {
833 if ( current
== '[' && !inBrackets
)
835 if ( current
== ']' && inBrackets
)
840 !lastWasEscape
&& (current
== '\\');
841 } else { // end of regexp
842 m_pattern
= UString(m_buffer16
);
850 while (isIdentPart(current
)) {
854 m_flags
= UString(m_buffer16
);
861 deleteAllValues(m_strings
);
862 Vector
<UString
*> newStrings
;
863 newStrings
.reserveCapacity(initialStringTableCapacity
);
864 m_strings
.swap(newStrings
);
866 deleteAllValues(m_identifiers
);
867 Vector
<KJS::Identifier
*> newIdentifiers
;
868 newIdentifiers
.reserveCapacity(initialStringTableCapacity
);
869 m_identifiers
.swap(newIdentifiers
);
871 Vector
<char> newBuffer8
;
872 newBuffer8
.reserveCapacity(initialReadBufferCapacity
);
873 m_buffer8
.swap(newBuffer8
);
875 Vector
<UChar
> newBuffer16
;
876 newBuffer16
.reserveCapacity(initialReadBufferCapacity
);
877 m_buffer16
.swap(newBuffer16
);
883 Identifier
* Lexer::makeIdentifier(const Vector
<KJS::UChar
>& buffer
)
885 KJS::Identifier
* identifier
= new KJS::Identifier(buffer
.data(), buffer
.size());
886 m_identifiers
.append(identifier
);
890 UString
* Lexer::makeUString(const Vector
<KJS::UChar
>& buffer
)
892 UString
* string
= new UString(buffer
);
893 m_strings
.append(string
);