@angular/compiler
Version:
Angular - the compiler library
936 lines • 134 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import * as chars from '../chars';
import { ParseError, ParseLocation, ParseSourceFile, ParseSourceSpan } from '../parse_util';
import { NAMED_ENTITIES } from './entities';
import { DEFAULT_INTERPOLATION_CONFIG } from './interpolation_config';
import { TagContentType } from './tags';
export var TokenType;
(function (TokenType) {
TokenType[TokenType["TAG_OPEN_START"] = 0] = "TAG_OPEN_START";
TokenType[TokenType["TAG_OPEN_END"] = 1] = "TAG_OPEN_END";
TokenType[TokenType["TAG_OPEN_END_VOID"] = 2] = "TAG_OPEN_END_VOID";
TokenType[TokenType["TAG_CLOSE"] = 3] = "TAG_CLOSE";
TokenType[TokenType["INCOMPLETE_TAG_OPEN"] = 4] = "INCOMPLETE_TAG_OPEN";
TokenType[TokenType["TEXT"] = 5] = "TEXT";
TokenType[TokenType["ESCAPABLE_RAW_TEXT"] = 6] = "ESCAPABLE_RAW_TEXT";
TokenType[TokenType["RAW_TEXT"] = 7] = "RAW_TEXT";
TokenType[TokenType["COMMENT_START"] = 8] = "COMMENT_START";
TokenType[TokenType["COMMENT_END"] = 9] = "COMMENT_END";
TokenType[TokenType["CDATA_START"] = 10] = "CDATA_START";
TokenType[TokenType["CDATA_END"] = 11] = "CDATA_END";
TokenType[TokenType["ATTR_NAME"] = 12] = "ATTR_NAME";
TokenType[TokenType["ATTR_QUOTE"] = 13] = "ATTR_QUOTE";
TokenType[TokenType["ATTR_VALUE"] = 14] = "ATTR_VALUE";
TokenType[TokenType["DOC_TYPE"] = 15] = "DOC_TYPE";
TokenType[TokenType["EXPANSION_FORM_START"] = 16] = "EXPANSION_FORM_START";
TokenType[TokenType["EXPANSION_CASE_VALUE"] = 17] = "EXPANSION_CASE_VALUE";
TokenType[TokenType["EXPANSION_CASE_EXP_START"] = 18] = "EXPANSION_CASE_EXP_START";
TokenType[TokenType["EXPANSION_CASE_EXP_END"] = 19] = "EXPANSION_CASE_EXP_END";
TokenType[TokenType["EXPANSION_FORM_END"] = 20] = "EXPANSION_FORM_END";
TokenType[TokenType["EOF"] = 21] = "EOF";
})(TokenType || (TokenType = {}));
export class Token {
constructor(type, parts, sourceSpan) {
this.type = type;
this.parts = parts;
this.sourceSpan = sourceSpan;
}
}
export class TokenError extends ParseError {
constructor(errorMsg, tokenType, span) {
super(span, errorMsg);
this.tokenType = tokenType;
}
}
export class TokenizeResult {
constructor(tokens, errors, nonNormalizedIcuExpressions) {
this.tokens = tokens;
this.errors = errors;
this.nonNormalizedIcuExpressions = nonNormalizedIcuExpressions;
}
}
export function tokenize(source, url, getTagDefinition, options = {}) {
const tokenizer = new _Tokenizer(new ParseSourceFile(source, url), getTagDefinition, options);
tokenizer.tokenize();
return new TokenizeResult(mergeTextTokens(tokenizer.tokens), tokenizer.errors, tokenizer.nonNormalizedIcuExpressions);
}
const _CR_OR_CRLF_REGEXP = /\r\n?/g;
function _unexpectedCharacterErrorMsg(charCode) {
const char = charCode === chars.$EOF ? 'EOF' : String.fromCharCode(charCode);
return `Unexpected character "${char}"`;
}
function _unknownEntityErrorMsg(entitySrc) {
return `Unknown entity "${entitySrc}" - use the "&#<decimal>;" or "&#x<hex>;" syntax`;
}
function _unparsableEntityErrorMsg(type, entityStr) {
return `Unable to parse entity "${entityStr}" - ${type} character reference entities must end with ";"`;
}
var CharacterReferenceType;
(function (CharacterReferenceType) {
CharacterReferenceType["HEX"] = "hexadecimal";
CharacterReferenceType["DEC"] = "decimal";
})(CharacterReferenceType || (CharacterReferenceType = {}));
class _ControlFlowError {
constructor(error) {
this.error = error;
}
}
// See https://www.w3.org/TR/html51/syntax.html#writing-html-documents
class _Tokenizer {
/**
* @param _file The html source file being tokenized.
* @param _getTagDefinition A function that will retrieve a tag definition for a given tag name.
* @param options Configuration of the tokenization.
*/
constructor(_file, _getTagDefinition, options) {
this._getTagDefinition = _getTagDefinition;
this._currentTokenStart = null;
this._currentTokenType = null;
this._expansionCaseStack = [];
this._inInterpolation = false;
this.tokens = [];
this.errors = [];
this.nonNormalizedIcuExpressions = [];
this._tokenizeIcu = options.tokenizeExpansionForms || false;
this._interpolationConfig = options.interpolationConfig || DEFAULT_INTERPOLATION_CONFIG;
this._leadingTriviaCodePoints =
options.leadingTriviaChars && options.leadingTriviaChars.map(c => c.codePointAt(0) || 0);
const range = options.range || { endPos: _file.content.length, startPos: 0, startLine: 0, startCol: 0 };
this._cursor = options.escapedString ? new EscapedCharacterCursor(_file, range) :
new PlainCharacterCursor(_file, range);
this._preserveLineEndings = options.preserveLineEndings || false;
this._escapedString = options.escapedString || false;
this._i18nNormalizeLineEndingsInICUs = options.i18nNormalizeLineEndingsInICUs || false;
try {
this._cursor.init();
}
catch (e) {
this.handleError(e);
}
}
_processCarriageReturns(content) {
if (this._preserveLineEndings) {
return content;
}
// https://www.w3.org/TR/html51/syntax.html#preprocessing-the-input-stream
// In order to keep the original position in the source, we can not
// pre-process it.
// Instead CRs are processed right before instantiating the tokens.
return content.replace(_CR_OR_CRLF_REGEXP, '\n');
}
tokenize() {
while (this._cursor.peek() !== chars.$EOF) {
const start = this._cursor.clone();
try {
if (this._attemptCharCode(chars.$LT)) {
if (this._attemptCharCode(chars.$BANG)) {
if (this._attemptCharCode(chars.$LBRACKET)) {
this._consumeCdata(start);
}
else if (this._attemptCharCode(chars.$MINUS)) {
this._consumeComment(start);
}
else {
this._consumeDocType(start);
}
}
else if (this._attemptCharCode(chars.$SLASH)) {
this._consumeTagClose(start);
}
else {
this._consumeTagOpen(start);
}
}
else if (!(this._tokenizeIcu && this._tokenizeExpansionForm())) {
this._consumeText();
}
}
catch (e) {
this.handleError(e);
}
}
this._beginToken(TokenType.EOF);
this._endToken([]);
}
/**
* @returns whether an ICU token has been created
* @internal
*/
_tokenizeExpansionForm() {
if (this.isExpansionFormStart()) {
this._consumeExpansionFormStart();
return true;
}
if (isExpansionCaseStart(this._cursor.peek()) && this._isInExpansionForm()) {
this._consumeExpansionCaseStart();
return true;
}
if (this._cursor.peek() === chars.$RBRACE) {
if (this._isInExpansionCase()) {
this._consumeExpansionCaseEnd();
return true;
}
if (this._isInExpansionForm()) {
this._consumeExpansionFormEnd();
return true;
}
}
return false;
}
_beginToken(type, start = this._cursor.clone()) {
this._currentTokenStart = start;
this._currentTokenType = type;
}
_endToken(parts, end) {
if (this._currentTokenStart === null) {
throw new TokenError('Programming error - attempted to end a token when there was no start to the token', this._currentTokenType, this._cursor.getSpan(end));
}
if (this._currentTokenType === null) {
throw new TokenError('Programming error - attempted to end a token which has no token type', null, this._cursor.getSpan(this._currentTokenStart));
}
const token = new Token(this._currentTokenType, parts, this._cursor.getSpan(this._currentTokenStart, this._leadingTriviaCodePoints));
this.tokens.push(token);
this._currentTokenStart = null;
this._currentTokenType = null;
return token;
}
_createError(msg, span) {
if (this._isInExpansionForm()) {
msg += ` (Do you have an unescaped "{" in your template? Use "{{ '{' }}") to escape it.)`;
}
const error = new TokenError(msg, this._currentTokenType, span);
this._currentTokenStart = null;
this._currentTokenType = null;
return new _ControlFlowError(error);
}
handleError(e) {
if (e instanceof CursorError) {
e = this._createError(e.msg, this._cursor.getSpan(e.cursor));
}
if (e instanceof _ControlFlowError) {
this.errors.push(e.error);
}
else {
throw e;
}
}
_attemptCharCode(charCode) {
if (this._cursor.peek() === charCode) {
this._cursor.advance();
return true;
}
return false;
}
_attemptCharCodeCaseInsensitive(charCode) {
if (compareCharCodeCaseInsensitive(this._cursor.peek(), charCode)) {
this._cursor.advance();
return true;
}
return false;
}
_requireCharCode(charCode) {
const location = this._cursor.clone();
if (!this._attemptCharCode(charCode)) {
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
}
}
_attemptStr(chars) {
const len = chars.length;
if (this._cursor.charsLeft() < len) {
return false;
}
const initialPosition = this._cursor.clone();
for (let i = 0; i < len; i++) {
if (!this._attemptCharCode(chars.charCodeAt(i))) {
// If attempting to parse the string fails, we want to reset the parser
// to where it was before the attempt
this._cursor = initialPosition;
return false;
}
}
return true;
}
_attemptStrCaseInsensitive(chars) {
for (let i = 0; i < chars.length; i++) {
if (!this._attemptCharCodeCaseInsensitive(chars.charCodeAt(i))) {
return false;
}
}
return true;
}
_requireStr(chars) {
const location = this._cursor.clone();
if (!this._attemptStr(chars)) {
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(location));
}
}
_attemptCharCodeUntilFn(predicate) {
while (!predicate(this._cursor.peek())) {
this._cursor.advance();
}
}
_requireCharCodeUntilFn(predicate, len) {
const start = this._cursor.clone();
this._attemptCharCodeUntilFn(predicate);
if (this._cursor.diff(start) < len) {
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
}
}
_attemptUntilChar(char) {
while (this._cursor.peek() !== char) {
this._cursor.advance();
}
}
_readChar(decodeEntities) {
if (decodeEntities && this._cursor.peek() === chars.$AMPERSAND) {
return this._decodeEntity();
}
else {
// Don't rely upon reading directly from `_input` as the actual char value
// may have been generated from an escape sequence.
const char = String.fromCodePoint(this._cursor.peek());
this._cursor.advance();
return char;
}
}
_decodeEntity() {
const start = this._cursor.clone();
this._cursor.advance();
if (this._attemptCharCode(chars.$HASH)) {
const isHex = this._attemptCharCode(chars.$x) || this._attemptCharCode(chars.$X);
const codeStart = this._cursor.clone();
this._attemptCharCodeUntilFn(isDigitEntityEnd);
if (this._cursor.peek() != chars.$SEMICOLON) {
// Advance cursor to include the peeked character in the string provided to the error
// message.
this._cursor.advance();
const entityType = isHex ? CharacterReferenceType.HEX : CharacterReferenceType.DEC;
throw this._createError(_unparsableEntityErrorMsg(entityType, this._cursor.getChars(start)), this._cursor.getSpan());
}
const strNum = this._cursor.getChars(codeStart);
this._cursor.advance();
try {
const charCode = parseInt(strNum, isHex ? 16 : 10);
return String.fromCharCode(charCode);
}
catch (_a) {
throw this._createError(_unknownEntityErrorMsg(this._cursor.getChars(start)), this._cursor.getSpan());
}
}
else {
const nameStart = this._cursor.clone();
this._attemptCharCodeUntilFn(isNamedEntityEnd);
if (this._cursor.peek() != chars.$SEMICOLON) {
this._cursor = nameStart;
return '&';
}
const name = this._cursor.getChars(nameStart);
this._cursor.advance();
const char = NAMED_ENTITIES[name];
if (!char) {
throw this._createError(_unknownEntityErrorMsg(name), this._cursor.getSpan(start));
}
return char;
}
}
_consumeRawText(decodeEntities, endMarkerPredicate) {
this._beginToken(decodeEntities ? TokenType.ESCAPABLE_RAW_TEXT : TokenType.RAW_TEXT);
const parts = [];
while (true) {
const tagCloseStart = this._cursor.clone();
const foundEndMarker = endMarkerPredicate();
this._cursor = tagCloseStart;
if (foundEndMarker) {
break;
}
parts.push(this._readChar(decodeEntities));
}
return this._endToken([this._processCarriageReturns(parts.join(''))]);
}
_consumeComment(start) {
this._beginToken(TokenType.COMMENT_START, start);
this._requireCharCode(chars.$MINUS);
this._endToken([]);
this._consumeRawText(false, () => this._attemptStr('-->'));
this._beginToken(TokenType.COMMENT_END);
this._requireStr('-->');
this._endToken([]);
}
_consumeCdata(start) {
this._beginToken(TokenType.CDATA_START, start);
this._requireStr('CDATA[');
this._endToken([]);
this._consumeRawText(false, () => this._attemptStr(']]>'));
this._beginToken(TokenType.CDATA_END);
this._requireStr(']]>');
this._endToken([]);
}
_consumeDocType(start) {
this._beginToken(TokenType.DOC_TYPE, start);
const contentStart = this._cursor.clone();
this._attemptUntilChar(chars.$GT);
const content = this._cursor.getChars(contentStart);
this._cursor.advance();
this._endToken([content]);
}
_consumePrefixAndName() {
const nameOrPrefixStart = this._cursor.clone();
let prefix = '';
while (this._cursor.peek() !== chars.$COLON && !isPrefixEnd(this._cursor.peek())) {
this._cursor.advance();
}
let nameStart;
if (this._cursor.peek() === chars.$COLON) {
prefix = this._cursor.getChars(nameOrPrefixStart);
this._cursor.advance();
nameStart = this._cursor.clone();
}
else {
nameStart = nameOrPrefixStart;
}
this._requireCharCodeUntilFn(isNameEnd, prefix === '' ? 0 : 1);
const name = this._cursor.getChars(nameStart);
return [prefix, name];
}
_consumeTagOpen(start) {
let tagName;
let prefix;
let openTagToken;
try {
if (!chars.isAsciiLetter(this._cursor.peek())) {
throw this._createError(_unexpectedCharacterErrorMsg(this._cursor.peek()), this._cursor.getSpan(start));
}
openTagToken = this._consumeTagOpenStart(start);
prefix = openTagToken.parts[0];
tagName = openTagToken.parts[1];
this._attemptCharCodeUntilFn(isNotWhitespace);
while (this._cursor.peek() !== chars.$SLASH && this._cursor.peek() !== chars.$GT &&
this._cursor.peek() !== chars.$LT && this._cursor.peek() !== chars.$EOF) {
this._consumeAttributeName();
this._attemptCharCodeUntilFn(isNotWhitespace);
if (this._attemptCharCode(chars.$EQ)) {
this._attemptCharCodeUntilFn(isNotWhitespace);
this._consumeAttributeValue();
}
this._attemptCharCodeUntilFn(isNotWhitespace);
}
this._consumeTagOpenEnd();
}
catch (e) {
if (e instanceof _ControlFlowError) {
if (openTagToken) {
// We errored before we could close the opening tag, so it is incomplete.
openTagToken.type = TokenType.INCOMPLETE_TAG_OPEN;
}
else {
// When the start tag is invalid, assume we want a "<" as text.
// Back to back text tokens are merged at the end.
this._beginToken(TokenType.TEXT, start);
this._endToken(['<']);
}
return;
}
throw e;
}
const contentTokenType = this._getTagDefinition(tagName).getContentType(prefix);
if (contentTokenType === TagContentType.RAW_TEXT) {
this._consumeRawTextWithTagClose(prefix, tagName, false);
}
else if (contentTokenType === TagContentType.ESCAPABLE_RAW_TEXT) {
this._consumeRawTextWithTagClose(prefix, tagName, true);
}
}
_consumeRawTextWithTagClose(prefix, tagName, decodeEntities) {
this._consumeRawText(decodeEntities, () => {
if (!this._attemptCharCode(chars.$LT))
return false;
if (!this._attemptCharCode(chars.$SLASH))
return false;
this._attemptCharCodeUntilFn(isNotWhitespace);
if (!this._attemptStrCaseInsensitive(tagName))
return false;
this._attemptCharCodeUntilFn(isNotWhitespace);
return this._attemptCharCode(chars.$GT);
});
this._beginToken(TokenType.TAG_CLOSE);
this._requireCharCodeUntilFn(code => code === chars.$GT, 3);
this._cursor.advance(); // Consume the `>`
this._endToken([prefix, tagName]);
}
_consumeTagOpenStart(start) {
this._beginToken(TokenType.TAG_OPEN_START, start);
const parts = this._consumePrefixAndName();
return this._endToken(parts);
}
_consumeAttributeName() {
const attrNameStart = this._cursor.peek();
if (attrNameStart === chars.$SQ || attrNameStart === chars.$DQ) {
throw this._createError(_unexpectedCharacterErrorMsg(attrNameStart), this._cursor.getSpan());
}
this._beginToken(TokenType.ATTR_NAME);
const prefixAndName = this._consumePrefixAndName();
this._endToken(prefixAndName);
}
_consumeAttributeValue() {
let value;
if (this._cursor.peek() === chars.$SQ || this._cursor.peek() === chars.$DQ) {
this._beginToken(TokenType.ATTR_QUOTE);
const quoteChar = this._cursor.peek();
this._cursor.advance();
this._endToken([String.fromCodePoint(quoteChar)]);
this._beginToken(TokenType.ATTR_VALUE);
const parts = [];
while (this._cursor.peek() !== quoteChar) {
parts.push(this._readChar(true));
}
value = parts.join('');
this._endToken([this._processCarriageReturns(value)]);
this._beginToken(TokenType.ATTR_QUOTE);
this._cursor.advance();
this._endToken([String.fromCodePoint(quoteChar)]);
}
else {
this._beginToken(TokenType.ATTR_VALUE);
const valueStart = this._cursor.clone();
this._requireCharCodeUntilFn(isNameEnd, 1);
value = this._cursor.getChars(valueStart);
this._endToken([this._processCarriageReturns(value)]);
}
}
_consumeTagOpenEnd() {
const tokenType = this._attemptCharCode(chars.$SLASH) ? TokenType.TAG_OPEN_END_VOID : TokenType.TAG_OPEN_END;
this._beginToken(tokenType);
this._requireCharCode(chars.$GT);
this._endToken([]);
}
_consumeTagClose(start) {
this._beginToken(TokenType.TAG_CLOSE, start);
this._attemptCharCodeUntilFn(isNotWhitespace);
const prefixAndName = this._consumePrefixAndName();
this._attemptCharCodeUntilFn(isNotWhitespace);
this._requireCharCode(chars.$GT);
this._endToken(prefixAndName);
}
_consumeExpansionFormStart() {
this._beginToken(TokenType.EXPANSION_FORM_START);
this._requireCharCode(chars.$LBRACE);
this._endToken([]);
this._expansionCaseStack.push(TokenType.EXPANSION_FORM_START);
this._beginToken(TokenType.RAW_TEXT);
const condition = this._readUntil(chars.$COMMA);
const normalizedCondition = this._processCarriageReturns(condition);
if (this._i18nNormalizeLineEndingsInICUs) {
// We explicitly want to normalize line endings for this text.
this._endToken([normalizedCondition]);
}
else {
// We are not normalizing line endings.
const conditionToken = this._endToken([condition]);
if (normalizedCondition !== condition) {
this.nonNormalizedIcuExpressions.push(conditionToken);
}
}
this._requireCharCode(chars.$COMMA);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._beginToken(TokenType.RAW_TEXT);
const type = this._readUntil(chars.$COMMA);
this._endToken([type]);
this._requireCharCode(chars.$COMMA);
this._attemptCharCodeUntilFn(isNotWhitespace);
}
_consumeExpansionCaseStart() {
this._beginToken(TokenType.EXPANSION_CASE_VALUE);
const value = this._readUntil(chars.$LBRACE).trim();
this._endToken([value]);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._beginToken(TokenType.EXPANSION_CASE_EXP_START);
this._requireCharCode(chars.$LBRACE);
this._endToken([]);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._expansionCaseStack.push(TokenType.EXPANSION_CASE_EXP_START);
}
_consumeExpansionCaseEnd() {
this._beginToken(TokenType.EXPANSION_CASE_EXP_END);
this._requireCharCode(chars.$RBRACE);
this._endToken([]);
this._attemptCharCodeUntilFn(isNotWhitespace);
this._expansionCaseStack.pop();
}
_consumeExpansionFormEnd() {
this._beginToken(TokenType.EXPANSION_FORM_END);
this._requireCharCode(chars.$RBRACE);
this._endToken([]);
this._expansionCaseStack.pop();
}
_consumeText() {
const start = this._cursor.clone();
this._beginToken(TokenType.TEXT, start);
const parts = [];
do {
if (this._interpolationConfig && this._attemptStr(this._interpolationConfig.start)) {
parts.push(this._interpolationConfig.start);
this._inInterpolation = true;
}
else if (this._interpolationConfig && this._inInterpolation &&
this._attemptStr(this._interpolationConfig.end)) {
parts.push(this._interpolationConfig.end);
this._inInterpolation = false;
}
else {
parts.push(this._readChar(true));
}
} while (!this._isTextEnd());
// It is possible that an interpolation was started but not ended inside this text token.
// Make sure that we reset the state of the lexer correctly.
this._inInterpolation = false;
this._endToken([this._processCarriageReturns(parts.join(''))]);
}
_isTextEnd() {
if (this._isTagStart() || this._cursor.peek() === chars.$EOF) {
return true;
}
if (this._tokenizeIcu && !this._inInterpolation) {
if (this.isExpansionFormStart()) {
// start of an expansion form
return true;
}
if (this._cursor.peek() === chars.$RBRACE && this._isInExpansionCase()) {
// end of and expansion case
return true;
}
}
return false;
}
/**
* Returns true if the current cursor is pointing to the start of a tag
* (opening/closing/comments/cdata/etc).
*/
_isTagStart() {
if (this._cursor.peek() === chars.$LT) {
// We assume that `<` followed by whitespace is not the start of an HTML element.
const tmp = this._cursor.clone();
tmp.advance();
// If the next character is alphabetic, ! nor / then it is a tag start
const code = tmp.peek();
if ((chars.$a <= code && code <= chars.$z) || (chars.$A <= code && code <= chars.$Z) ||
code === chars.$SLASH || code === chars.$BANG) {
return true;
}
}
return false;
}
_readUntil(char) {
const start = this._cursor.clone();
this._attemptUntilChar(char);
return this._cursor.getChars(start);
}
_isInExpansionCase() {
return this._expansionCaseStack.length > 0 &&
this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
TokenType.EXPANSION_CASE_EXP_START;
}
_isInExpansionForm() {
return this._expansionCaseStack.length > 0 &&
this._expansionCaseStack[this._expansionCaseStack.length - 1] ===
TokenType.EXPANSION_FORM_START;
}
isExpansionFormStart() {
if (this._cursor.peek() !== chars.$LBRACE) {
return false;
}
if (this._interpolationConfig) {
const start = this._cursor.clone();
const isInterpolation = this._attemptStr(this._interpolationConfig.start);
this._cursor = start;
return !isInterpolation;
}
return true;
}
}
function isNotWhitespace(code) {
return !chars.isWhitespace(code) || code === chars.$EOF;
}
function isNameEnd(code) {
return chars.isWhitespace(code) || code === chars.$GT || code === chars.$LT ||
code === chars.$SLASH || code === chars.$SQ || code === chars.$DQ || code === chars.$EQ ||
code === chars.$EOF;
}
function isPrefixEnd(code) {
return (code < chars.$a || chars.$z < code) && (code < chars.$A || chars.$Z < code) &&
(code < chars.$0 || code > chars.$9);
}
function isDigitEntityEnd(code) {
return code == chars.$SEMICOLON || code == chars.$EOF || !chars.isAsciiHexDigit(code);
}
function isNamedEntityEnd(code) {
return code == chars.$SEMICOLON || code == chars.$EOF || !chars.isAsciiLetter(code);
}
function isExpansionCaseStart(peek) {
return peek !== chars.$RBRACE;
}
function compareCharCodeCaseInsensitive(code1, code2) {
return toUpperCaseCharCode(code1) == toUpperCaseCharCode(code2);
}
function toUpperCaseCharCode(code) {
return code >= chars.$a && code <= chars.$z ? code - chars.$a + chars.$A : code;
}
function mergeTextTokens(srcTokens) {
const dstTokens = [];
let lastDstToken = undefined;
for (let i = 0; i < srcTokens.length; i++) {
const token = srcTokens[i];
if (lastDstToken && lastDstToken.type == TokenType.TEXT && token.type == TokenType.TEXT) {
lastDstToken.parts[0] += token.parts[0];
lastDstToken.sourceSpan.end = token.sourceSpan.end;
}
else {
lastDstToken = token;
dstTokens.push(lastDstToken);
}
}
return dstTokens;
}
class PlainCharacterCursor {
constructor(fileOrCursor, range) {
if (fileOrCursor instanceof PlainCharacterCursor) {
this.file = fileOrCursor.file;
this.input = fileOrCursor.input;
this.end = fileOrCursor.end;
const state = fileOrCursor.state;
// Note: avoid using `{...fileOrCursor.state}` here as that has a severe performance penalty.
// In ES5 bundles the object spread operator is translated into the `__assign` helper, which
// is not optimized by VMs as efficiently as a raw object literal. Since this constructor is
// called in tight loops, this difference matters.
this.state = {
peek: state.peek,
offset: state.offset,
line: state.line,
column: state.column,
};
}
else {
if (!range) {
throw new Error('Programming error: the range argument must be provided with a file argument.');
}
this.file = fileOrCursor;
this.input = fileOrCursor.content;
this.end = range.endPos;
this.state = {
peek: -1,
offset: range.startPos,
line: range.startLine,
column: range.startCol,
};
}
}
clone() {
return new PlainCharacterCursor(this);
}
peek() {
return this.state.peek;
}
charsLeft() {
return this.end - this.state.offset;
}
diff(other) {
return this.state.offset - other.state.offset;
}
advance() {
this.advanceState(this.state);
}
init() {
this.updatePeek(this.state);
}
getSpan(start, leadingTriviaCodePoints) {
start = start || this;
let fullStart = start;
if (leadingTriviaCodePoints) {
while (this.diff(start) > 0 && leadingTriviaCodePoints.indexOf(start.peek()) !== -1) {
if (fullStart === start) {
start = start.clone();
}
start.advance();
}
}
const startLocation = this.locationFromCursor(start);
const endLocation = this.locationFromCursor(this);
const fullStartLocation = fullStart !== start ? this.locationFromCursor(fullStart) : startLocation;
return new ParseSourceSpan(startLocation, endLocation, fullStartLocation);
}
getChars(start) {
return this.input.substring(start.state.offset, this.state.offset);
}
charAt(pos) {
return this.input.charCodeAt(pos);
}
advanceState(state) {
if (state.offset >= this.end) {
this.state = state;
throw new CursorError('Unexpected character "EOF"', this);
}
const currentChar = this.charAt(state.offset);
if (currentChar === chars.$LF) {
state.line++;
state.column = 0;
}
else if (!chars.isNewLine(currentChar)) {
state.column++;
}
state.offset++;
this.updatePeek(state);
}
updatePeek(state) {
state.peek = state.offset >= this.end ? chars.$EOF : this.charAt(state.offset);
}
locationFromCursor(cursor) {
return new ParseLocation(cursor.file, cursor.state.offset, cursor.state.line, cursor.state.column);
}
}
class EscapedCharacterCursor extends PlainCharacterCursor {
constructor(fileOrCursor, range) {
if (fileOrCursor instanceof EscapedCharacterCursor) {
super(fileOrCursor);
this.internalState = Object.assign({}, fileOrCursor.internalState);
}
else {
super(fileOrCursor, range);
this.internalState = this.state;
}
}
advance() {
this.state = this.internalState;
super.advance();
this.processEscapeSequence();
}
init() {
super.init();
this.processEscapeSequence();
}
clone() {
return new EscapedCharacterCursor(this);
}
getChars(start) {
const cursor = start.clone();
let chars = '';
while (cursor.internalState.offset < this.internalState.offset) {
chars += String.fromCodePoint(cursor.peek());
cursor.advance();
}
return chars;
}
/**
* Process the escape sequence that starts at the current position in the text.
*
* This method is called to ensure that `peek` has the unescaped value of escape sequences.
*/
processEscapeSequence() {
const peek = () => this.internalState.peek;
if (peek() === chars.$BACKSLASH) {
// We have hit an escape sequence so we need the internal state to become independent
// of the external state.
this.internalState = Object.assign({}, this.state);
// Move past the backslash
this.advanceState(this.internalState);
// First check for standard control char sequences
if (peek() === chars.$n) {
this.state.peek = chars.$LF;
}
else if (peek() === chars.$r) {
this.state.peek = chars.$CR;
}
else if (peek() === chars.$v) {
this.state.peek = chars.$VTAB;
}
else if (peek() === chars.$t) {
this.state.peek = chars.$TAB;
}
else if (peek() === chars.$b) {
this.state.peek = chars.$BSPACE;
}
else if (peek() === chars.$f) {
this.state.peek = chars.$FF;
}
// Now consider more complex sequences
else if (peek() === chars.$u) {
// Unicode code-point sequence
this.advanceState(this.internalState); // advance past the `u` char
if (peek() === chars.$LBRACE) {
// Variable length Unicode, e.g. `\x{123}`
this.advanceState(this.internalState); // advance past the `{` char
// Advance past the variable number of hex digits until we hit a `}` char
const digitStart = this.clone();
let length = 0;
while (peek() !== chars.$RBRACE) {
this.advanceState(this.internalState);
length++;
}
this.state.peek = this.decodeHexDigits(digitStart, length);
}
else {
// Fixed length Unicode, e.g. `\u1234`
const digitStart = this.clone();
this.advanceState(this.internalState);
this.advanceState(this.internalState);
this.advanceState(this.internalState);
this.state.peek = this.decodeHexDigits(digitStart, 4);
}
}
else if (peek() === chars.$x) {
// Hex char code, e.g. `\x2F`
this.advanceState(this.internalState); // advance past the `x` char
const digitStart = this.clone();
this.advanceState(this.internalState);
this.state.peek = this.decodeHexDigits(digitStart, 2);
}
else if (chars.isOctalDigit(peek())) {
// Octal char code, e.g. `\012`,
let octal = '';
let length = 0;
let previous = this.clone();
while (chars.isOctalDigit(peek()) && length < 3) {
previous = this.clone();
octal += String.fromCodePoint(peek());
this.advanceState(this.internalState);
length++;
}
this.state.peek = parseInt(octal, 8);
// Backup one char
this.internalState = previous.internalState;
}
else if (chars.isNewLine(this.internalState.peek)) {
// Line continuation `\` followed by a new line
this.advanceState(this.internalState); // advance over the newline
this.state = this.internalState;
}
else {
// If none of the `if` blocks were executed then we just have an escaped normal character.
// In that case we just, effectively, skip the backslash from the character.
this.state.peek = this.internalState.peek;
}
}
}
decodeHexDigits(start, length) {
const hex = this.input.substr(start.internalState.offset, length);
const charCode = parseInt(hex, 16);
if (!isNaN(charCode)) {
return charCode;
}
else {
start.state = start.internalState;
throw new CursorError('Invalid hexadecimal escape sequence', start);
}
}
}
export class CursorError {
constructor(msg, cursor) {
this.msg = msg;
this.cursor = cursor;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGV4ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb21waWxlci9zcmMvbWxfcGFyc2VyL2xleGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7R0FNRztBQUVILE9BQU8sS0FBSyxLQUFLLE1BQU0sVUFBVSxDQUFDO0FBQ2xDLE9BQU8sRUFBQyxVQUFVLEVBQUUsYUFBYSxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFDMUYsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLFlBQVksQ0FBQztBQUUxQyxPQUFPLEVBQUMsNEJBQTRCLEVBQXNCLE1BQU0sd0JBQXdCLENBQUM7QUFDekYsT0FBTyxFQUFDLGNBQWMsRUFBZ0IsTUFBTSxRQUFRLENBQUM7QUFFckQsTUFBTSxDQUFOLElBQVksU0F1Qlg7QUF2QkQsV0FBWSxTQUFTO0lBQ25CLDZEQUFjLENBQUE7SUFDZCx5REFBWSxDQUFBO0lBQ1osbUVBQWlCLENBQUE7SUFDakIsbURBQVMsQ0FBQTtJQUNULHVFQUFtQixDQUFBO0lBQ25CLHlDQUFJLENBQUE7SUFDSixxRUFBa0IsQ0FBQTtJQUNsQixpREFBUSxDQUFBO0lBQ1IsMkRBQWEsQ0FBQTtJQUNiLHVEQUFXLENBQUE7SUFDWCx3REFBVyxDQUFBO0lBQ1gsb0RBQVMsQ0FBQTtJQUNULG9EQUFTLENBQUE7SUFDVCxzREFBVSxDQUFBO0lBQ1Ysc0RBQVUsQ0FBQTtJQUNWLGtEQUFRLENBQUE7SUFDUiwwRUFBb0IsQ0FBQTtJQUNwQiwwRUFBb0IsQ0FBQTtJQUNwQixrRkFBd0IsQ0FBQTtJQUN4Qiw4RUFBc0IsQ0FBQTtJQUN0QixzRUFBa0IsQ0FBQTtJQUNsQix3Q0FBRyxDQUFBO0FBQ0wsQ0FBQyxFQXZCVyxTQUFTLEtBQVQsU0FBUyxRQXVCcEI7QUFFRCxNQUFNLE9BQU8sS0FBSztJQUNoQixZQUNXLElBQW9CLEVBQVMsS0FBZSxFQUFTLFVB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