/** * <strong>[11.6] Identifier Names and Identifiers</strong> * * <pre> * Identifier :: * IdentifierName but not ReservedWord * IdentifierName :: * IdentifierStart * IdentifierName IdentifierPart * </pre> */ private Token readIdentifier(int c) { assert isIdentifierStart(c); TokenStreamInput input = this.input; StringBuffer buffer = this.buffer(); buffer.addCodepoint(c); for (; ; ) { c = input.get(); if (isIdentifierPart(c)) { buffer.add(c); } else if (c == '\\') { mustMatch('u'); c = readUnicode(); if (!isIdentifierPart(c)) { throw error(Messages.Key.InvalidUnicodeEscapedIdentifierPart); } buffer.addCodepoint(c); continue; } else { input.unget(c); break; } } Token tok = readReservedWord(buffer); if (tok != null) { return tok; } return Token.NAME; }
/** * <strong>[11.8.6] Template Literal Lexical Components</strong> * * <pre> * Template :: * NoSubstitutionTemplate * TemplateHead * NoSubstitutionTemplate :: * ` TemplateCharacters<sub>opt</sub>` * TemplateHead :: * ` TemplateCharacters<sub>opt</sub>${ * TemplateSubstitutionTail :: * TemplateMiddle * TemplateTail * TemplateMiddle :: * } TemplateCharacters<sub>opt</sub>${ * TemplateTail :: * } TemplateCharacters<sub>opt</sub>` * TemplateCharacters :: * TemplateCharacter TemplateCharacters<sub>opt</sub> * TemplateCharacter :: * SourceCharacter but not one of ` or \ or $ * $ [LA ∉ { ] * \ EscapeSequence * LineContinuation * </pre> */ public String[] readTemplateLiteral(Token start) { assert start == Token.TEMPLATE || start == Token.RC; assert currentToken() == start; assert next == null : "template literal in lookahead"; final int EOF = TokenStreamInput.EOF; TokenStreamInput input = this.input; StringBuilder raw = new StringBuilder(); StringBuffer buffer = buffer(); int pos = input.position(); for (; ; ) { int c = input.get(); if (c == EOF) { throw eofError(Messages.Key.UnterminatedTemplateLiteral); } if (c == '`') { current = Token.TEMPLATE; raw.append(input.range(pos, input.position() - 1)); return new String[] {buffer.toString(), raw.toString()}; } if (c == '$' && match('{')) { current = Token.LC; raw.append(input.range(pos, input.position() - 2)); return new String[] {buffer.toString(), raw.toString()}; } if (c != '\\') { if (isLineTerminator(c)) { // line terminator sequence if (c == '\r') { // normalise \r and \r\n to \n raw.append(input.range(pos, input.position() - 1)).append('\n'); match('\n'); pos = input.position(); c = '\n'; } buffer.add(c); incrementLine(); continue; } // TODO: add substring range buffer.add(c); continue; } c = input.get(); if (c == EOF) { throw eofError(Messages.Key.UnterminatedTemplateLiteral); } // EscapeSequence if (isLineTerminator(c)) { // line continuation if (c == '\r') { // normalise \r and \r\n to \n raw.append(input.range(pos, input.position() - 1)).append('\n'); match('\n'); pos = input.position(); } incrementLine(); continue; } switch (c) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\u000B'; break; case '0': if (isDecimalDigit(input.peek(0))) { throw error(Messages.Key.InvalidNULLEscape); } c = '\0'; break; case 'x': c = (hexDigit(input.get()) << 4) | hexDigit(input.get()); if (c < 0) { throw error(Messages.Key.InvalidHexEscape); } break; case 'u': c = readUnicode(); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': throw error(Messages.Key.StrictModeOctalEscapeSequence); case '"': case '\'': case '\\': default: // fall-through } buffer.addCodepoint(c); } }
/** * <strong>[11.8.4] String Literals</strong> * * <pre> * StringLiteral :: * " DoubleStringCharacters<sub>opt</sub> " * ' SingleStringCharacters<sub>opt</sub> ' * DoubleStringCharacters :: * DoubleStringCharacter DoubleStringCharacters<sub>opt</sub> * SingleStringCharacters :: * SingleStringCharacter SingleStringCharacters<sub>opt</sub> * DoubleStringCharacter :: * SourceCharacter but not one of " or \ or LineTerminator * \ EscapeSequence * LineContinuation * SingleStringCharacter :: * SourceCharacter but not one of ' or \ or LineTerminator * \ EscapeSequence * LineContinuation * LineContinuation :: * \ LineTerminatorSequence * EscapeSequence :: * CharacterEscapeSequence * 0 [lookahead ∉ DecimalDigit] * HexEscapeSequence * UnicodeEscapeSequence * CharacterEscapeSequence :: * SingleEscapeCharacter * NonEscapeCharacter * SingleEscapeCharacter :: one of * ' " \ b f n r t v * NonEscapeCharacter :: * SourceCharacter but not one of EscapeCharacter or LineTerminator * EscapeCharacter :: * SingleEscapeCharacter * DecimalDigit * x * u * HexEscapeSequence :: * x HexDigit HexDigit * UnicodeEscapeSequence :: * u HexDigit HexDigit HexDigit HexDigit * u{ HexDigits } * </pre> * * <strong>[B.1.2] String Literals</strong> * * <pre> * EscapeSequence :: * CharacterEscapeSequence * OctalEscapeSequence * HexEscapeSequence * UnicodeEscapeSequence * </pre> */ private Token readString(int quoteChar) { assert quoteChar == '"' || quoteChar == '\''; final int EOF = TokenStreamInput.EOF; TokenStreamInput input = this.input; int start = input.position(); StringBuffer buffer = this.buffer(); hasEscape = false; for (; ; ) { int c = input.get(); if (c == EOF) { throw eofError(Messages.Key.UnterminatedStringLiteral); } if (c == quoteChar) { buffer.add(input.range(start, input.position() - 1)); break; } if (isLineTerminator(c)) { throw error(Messages.Key.UnterminatedStringLiteral); } if (c != '\\') { continue; } buffer.add(input.range(start, input.position() - 1)); hasEscape = true; c = input.get(); if (isLineTerminator(c)) { // line continuation if (c == '\r' && match('\n')) { // \r\n sequence } incrementLine(); start = input.position(); continue; } // escape sequences switch (c) { case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\u000B'; break; case 'x': c = (hexDigit(input.get()) << 4) | hexDigit(input.get()); if (c < 0) { throw error(Messages.Key.InvalidHexEscape); } break; case 'u': c = readUnicode(); break; case '0': if (isDecimalDigit(input.peek(0))) { if (!parser.isEnabled(CompatibilityOption.OctalEscapeSequence)) { throw error(Messages.Key.InvalidNULLEscape); } c = readOctalEscape(c); } else { c = '\0'; } break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': if (!parser.isEnabled(CompatibilityOption.OctalEscapeSequence)) { throw error(Messages.Key.StrictModeOctalEscapeSequence); } c = readOctalEscape(c); break; case '8': case '9': // FIXME: spec bug - undefined behaviour for \8 and \9 if (!parser.isEnabled(CompatibilityOption.OctalEscapeSequence)) { throw error(Messages.Key.StrictModeOctalEscapeSequence); } // fall-through case '"': case '\'': case '\\': default: // fall-through } buffer.addCodepoint(c); start = input.position(); } return Token.STRING; }