assemblyscript/src/tokenizer.ts

1630 lines
43 KiB
TypeScript
Raw Normal View History

2018-03-17 23:41:48 +01:00
/**
2018-03-19 01:12:18 +01:00
* A TypeScript tokenizer modified for AssemblyScript.
2018-03-17 23:41:48 +01:00
*
* Skips over trivia and provides a general mark/reset mechanism for the parser to utilize on
* ambiguous tokens.
2018-03-19 01:12:18 +01:00
*
* @module tokenizer
*//***/
2017-09-28 13:08:25 +02:00
2017-12-24 03:19:47 +01:00
import {
DiagnosticCode,
DiagnosticMessage,
DiagnosticEmitter
} from "./diagnostics";
import {
Source,
CommentKind
2017-12-24 03:19:47 +01:00
} from "./ast";
import {
CharCode,
isLineBreak,
isWhiteSpace,
isIdentifierStart,
isIdentifierPart,
isDecimalDigit,
isOctalDigit,
isKeywordCharacter
2018-03-19 01:12:18 +01:00
} from "./util";
2017-12-24 03:19:47 +01:00
/** Named token types. */
2017-09-28 13:08:25 +02:00
export enum Token {
// keywords
// discarded: ANY, BOOLEAN, NEVER, NUMBER, STRING, SYMBOL, UNDEFINED, LESSTHAN_SLASH
ABSTRACT,
AS,
ASYNC,
AWAIT, // ES2017
BREAK, // ES2017
CASE, // ES2017
CATCH, // ES2017
CLASS, // ES2017
CONST, // ES2017
CONTINUE, // ES2017
CONSTRUCTOR,
DEBUGGER, // ES2017
DECLARE,
DEFAULT, // ES2017
DELETE, // ES2017
DO, // ES2017
ELSE, // ES2017
ENUM, // ES2017 future
EXPORT, // ES2017
EXTENDS, // ES2017
FALSE, // ES
FINALLY, // ES2017
FOR, // ES2017
FROM, // AS possible identifier
FUNCTION, // ES2017
GET,
IF, // ES2017
IMPLEMENTS, // ES2017 non-lexical
IMPORT, // ES2017
IN, // ES2017
INSTANCEOF, // ES2017
INTERFACE, // ES2017 non-lexical
IS,
KEYOF,
LET, // ES2017 non-lexical
MODULE, // AS possible identifier
NAMESPACE, // AS possible identifier
NEW, // ES2017
NULL, // ES
OF,
PACKAGE, // ES2017 non-lexical
PRIVATE, // ES2017 non-lexical
PROTECTED, // ES2017 non-lexical
PUBLIC, // ES2017 non-lexical
READONLY,
RETURN, // ES2017
SET,
STATIC, // ES2017 non-lexical
SUPER, // ES2017
SWITCH, // ES2017
THIS, // ES2017
THROW, // ES2017
TRUE, // ES
TRY, // ES2017
TYPE, // AS possible identifier
TYPEOF, // ES2017
VAR, // ES2017
VOID, // ES2017
WHILE, // ES2017
WITH, // ES2017
YIELD, // ES2017
// punctuation
OPENBRACE,
CLOSEBRACE,
OPENPAREN,
CLOSEPAREN,
OPENBRACKET,
CLOSEBRACKET,
DOT,
DOT_DOT_DOT,
SEMICOLON,
COMMA,
LESSTHAN,
GREATERTHAN,
LESSTHAN_EQUALS,
GREATERTHAN_EQUALS,
EQUALS_EQUALS,
EXCLAMATION_EQUALS,
EQUALS_EQUALS_EQUALS,
EXCLAMATION_EQUALS_EQUALS,
EQUALS_GREATERTHAN,
PLUS,
MINUS,
ASTERISK_ASTERISK,
ASTERISK,
SLASH,
PERCENT,
PLUS_PLUS,
MINUS_MINUS,
LESSTHAN_LESSTHAN,
GREATERTHAN_GREATERTHAN,
GREATERTHAN_GREATERTHAN_GREATERTHAN,
AMPERSAND,
BAR,
CARET,
EXCLAMATION,
TILDE,
AMPERSAND_AMPERSAND,
BAR_BAR,
QUESTION,
COLON,
EQUALS,
PLUS_EQUALS,
MINUS_EQUALS,
ASTERISK_EQUALS,
ASTERISK_ASTERISK_EQUALS,
SLASH_EQUALS,
PERCENT_EQUALS,
LESSTHAN_LESSTHAN_EQUALS,
GREATERTHAN_GREATERTHAN_EQUALS,
GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS,
AMPERSAND_EQUALS,
BAR_EQUALS,
CARET_EQUALS,
AT,
// literals
IDENTIFIER,
STRINGLITERAL,
INTEGERLITERAL,
FLOATLITERAL,
// meta
INVALID,
ENDOFFILE
}
export enum IdentifierHandling {
DEFAULT,
PREFER,
ALWAYS
}
export function tokenFromKeyword(text: string): Token {
assert(text.length);
switch (text.charCodeAt(0)) {
case CharCode.a: {
switch (text) {
case "abstract": return Token.ABSTRACT;
case "as": return Token.AS;
case "async": return Token.ASYNC;
case "await": return Token.AWAIT;
}
break;
}
case CharCode.b: {
switch (text) {
case "break": return Token.BREAK;
}
break;
}
case CharCode.c: {
switch (text) {
case "case": return Token.CASE;
case "catch": return Token.CATCH;
case "class": return Token.CLASS;
case "continue": return Token.CONTINUE;
case "const": return Token.CONST;
case "constructor": return Token.CONSTRUCTOR;
}
break;
}
case CharCode.d: {
switch (text) {
case "debugger": return Token.DEBUGGER;
case "declare": return Token.DECLARE;
case "default": return Token.DEFAULT;
case "delete": return Token.DELETE;
case "do": return Token.DO;
}
break;
}
case CharCode.e: {
switch (text) {
case "else": return Token.ELSE;
case "enum": return Token.ENUM;
case "export": return Token.EXPORT;
case "extends": return Token.EXTENDS;
}
break;
}
case CharCode.f: {
switch (text) {
case "false": return Token.FALSE;
case "finally": return Token.FINALLY;
case "for": return Token.FOR;
case "from": return Token.FROM;
case "function": return Token.FUNCTION;
}
break;
}
case CharCode.g: {
switch (text) {
case "get": return Token.GET;
}
break;
}
case CharCode.i: {
switch (text) {
case "if": return Token.IF;
case "implements": return Token.IMPLEMENTS;
case "import": return Token.IMPORT;
case "in": return Token.IN;
case "instanceof": return Token.INSTANCEOF;
case "interface": return Token.INTERFACE;
case "is": return Token.IS;
}
break;
}
case CharCode.k: {
switch (text) {
case "keyof": return Token.KEYOF;
}
break;
}
case CharCode.l: {
switch (text) {
case "let": return Token.LET;
}
break;
}
case CharCode.m: {
switch (text) {
case "module": return Token.MODULE;
}
break;
}
case CharCode.n: {
switch (text) {
case "namespace": return Token.NAMESPACE;
case "new": return Token.NEW;
case "null": return Token.NULL;
}
break;
}
case CharCode.o: {
switch (text) {
case "of": return Token.OF;
}
break;
}
case CharCode.p: {
switch (text) {
case "package": return Token.PACKAGE;
case "private": return Token.PRIVATE;
case "protected": return Token.PROTECTED;
case "public": return Token.PUBLIC;
}
break;
}
case CharCode.r: {
switch (text) {
case "readonly": return Token.READONLY;
case "return": return Token.RETURN;
}
break;
}
case CharCode.s: {
switch (text) {
case "set": return Token.SET;
case "static": return Token.STATIC;
case "super": return Token.SUPER;
case "switch": return Token.SWITCH;
}
break;
}
case CharCode.t: {
switch (text) {
case "this": return Token.THIS;
case "throw": return Token.THROW;
case "true": return Token.TRUE;
case "try": return Token.TRY;
case "type": return Token.TYPE;
case "typeof": return Token.TYPEOF;
}
break;
}
case CharCode.v: {
switch (text) {
case "var": return Token.VAR;
case "void": return Token.VOID;
}
break;
}
case CharCode.w: {
switch (text) {
case "while": return Token.WHILE;
case "with": return Token.WITH;
}
break;
}
case CharCode.y: {
switch (text) {
case "yield": return Token.YIELD;
}
break;
}
}
return Token.INVALID;
}
2017-09-28 13:08:25 +02:00
export function tokenIsAlsoIdentifier(token: Token): bool {
switch (token) {
case Token.ABSTRACT:
case Token.AS:
case Token.CONSTRUCTOR:
case Token.DECLARE:
case Token.DELETE:
case Token.FROM:
case Token.FOR:
case Token.GET:
2019-06-05 23:15:39 +02:00
case Token.INSTANCEOF:
case Token.IS:
case Token.KEYOF:
case Token.MODULE:
case Token.NAMESPACE:
case Token.NULL:
case Token.READONLY:
case Token.SET:
case Token.TYPE:
case Token.VOID: return true;
default: return false;
}
}
export function isIllegalVariableIdentifier(name: string): bool {
assert(name.length);
switch (name.charCodeAt(0)) {
case CharCode.d: return name == "delete";
case CharCode.f: return name == "for";
case CharCode.i: return name == "instanceof";
case CharCode.n: return name == "null";
case CharCode.v: return name == "void";
}
return false;
}
export function operatorTokenToString(token: Token): string {
switch (token) {
case Token.DELETE: return "delete";
case Token.IN: return "in";
case Token.INSTANCEOF: return "instanceof";
case Token.NEW: return "new";
case Token.TYPEOF: return "typeof";
case Token.VOID: return "void";
case Token.YIELD: return "yield";
case Token.DOT_DOT_DOT: return "...";
case Token.COMMA: return ",";
case Token.LESSTHAN: return "<";
case Token.GREATERTHAN: return ">";
case Token.LESSTHAN_EQUALS: return "<=";
case Token.GREATERTHAN_EQUALS: return ">=";
case Token.EQUALS_EQUALS: return "==";
case Token.EXCLAMATION_EQUALS: return "!=";
case Token.EQUALS_EQUALS_EQUALS: return "===";
case Token.EXCLAMATION_EQUALS_EQUALS: return "!==";
case Token.PLUS: return "+";
case Token.MINUS: return "-";
case Token.ASTERISK_ASTERISK: return "**";
case Token.ASTERISK: return "*";
case Token.SLASH: return "/";
case Token.PERCENT: return "%";
case Token.PLUS_PLUS: return "++";
case Token.MINUS_MINUS: return "--";
case Token.LESSTHAN_LESSTHAN: return "<<";
case Token.GREATERTHAN_GREATERTHAN: return ">>";
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: return ">>>";
case Token.AMPERSAND: return "&";
case Token.BAR: return "|";
case Token.CARET: return "^";
case Token.EXCLAMATION: return "!";
case Token.TILDE: return "~";
case Token.AMPERSAND_AMPERSAND: return "&&";
case Token.BAR_BAR: return "||";
case Token.EQUALS: return "=";
case Token.PLUS_EQUALS: return "+=";
case Token.MINUS_EQUALS: return "-=";
case Token.ASTERISK_EQUALS: return "*=";
case Token.ASTERISK_ASTERISK_EQUALS: return "**=";
case Token.SLASH_EQUALS: return "/=";
case Token.PERCENT_EQUALS: return "%=";
case Token.LESSTHAN_LESSTHAN_EQUALS: return "<<=";
case Token.GREATERTHAN_GREATERTHAN_EQUALS: return ">>=";
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: return ">>>=";
case Token.AMPERSAND_EQUALS: return "&=";
case Token.BAR_EQUALS: return "|=";
case Token.CARET_EQUALS: return "^=";
default: {
assert(false);
return "";
}
}
}
2017-09-28 13:08:25 +02:00
export class Range {
source: Source;
start: i32;
end: i32;
2018-01-28 06:18:27 +01:00
// TODO: set these while tokenizing
// line: i32;
// column: i32;
2017-09-28 13:08:25 +02:00
constructor(source: Source, start: i32, end: i32) {
this.source = source;
this.start = start;
this.end = end;
}
static join(a: Range, b: Range): Range {
2018-02-25 00:13:39 +01:00
if (a.source != b.source) throw new Error("source mismatch");
return new Range(a.source,
a.start < b.start ? a.start : b.start,
a.end > b.end ? a.end : b.end
);
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
get atStart(): Range {
return new Range(this.source, this.start, this.start);
}
2019-06-05 23:15:39 +02:00
2018-02-25 00:13:39 +01:00
get atEnd(): Range {
return new Range(this.source, this.end, this.end);
}
get line(): i32 {
var text = this.source.text;
var line = 1;
for (let pos = this.start; pos >= 0; --pos) {
if (text.charCodeAt(pos) == CharCode.LINEFEED) line++;
2018-02-25 00:13:39 +01:00
}
return line;
}
get column(): i32 {
var text = this.source.text;
var column = 0;
for (let pos = this.start - 1; pos >= 0; --pos) {
2018-02-25 00:13:39 +01:00
if (text.charCodeAt(pos) == CharCode.LINEFEED) break;
++column;
}
return column;
}
toString(): string {
return this.source.text.substring(this.start, this.end);
}
debugInfoRef: usize = 0;
2017-09-28 13:08:25 +02:00
}
declare function parseFloat(str: string): f64;
2018-03-17 23:41:48 +01:00
/** Handler for intercepting comments while tokenizing. */
export type CommentHandler = (kind: CommentKind, text: string, range: Range) => void;
/** Tokenizes a source to individual {@link Token}s. */
2017-09-28 13:08:25 +02:00
export class Tokenizer extends DiagnosticEmitter {
source: Source;
end: i32 = 0;
pos: i32 = 0;
token: Token = -1;
tokenPos: i32 = 0;
nextToken: Token = -1;
nextTokenPos: i32 = 0;
2017-09-28 13:08:25 +02:00
nextTokenOnNewLine: bool = false;
2018-03-17 23:41:48 +01:00
onComment: CommentHandler | null = null;
2018-03-17 23:41:48 +01:00
/** Constructs a new tokenizer. */
2017-09-28 13:08:25 +02:00
constructor(source: Source, diagnostics: DiagnosticMessage[] | null = null) {
super(diagnostics);
2019-02-22 15:03:33 +02:00
2017-09-28 13:08:25 +02:00
this.source = source;
this.pos = 0;
this.end = source.text.length;
2019-06-05 23:15:39 +02:00
this.diagnostics = diagnostics || new Array();
2017-09-28 13:08:25 +02:00
2019-06-05 23:15:39 +02:00
var end = this.end;
var text = source.text;
2017-09-28 13:08:25 +02:00
// skip bom
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.BYTEORDERMARK
) {
++this.pos;
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
// skip shebang
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
this.pos + 1 < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.HASH &&
text.charCodeAt(this.pos + 1) == CharCode.EXCLAMATION
) {
2017-09-28 13:08:25 +02:00
this.pos += 2;
2018-02-25 00:13:39 +01:00
while (
2019-06-05 23:15:39 +02:00
this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) != CharCode.LINEFEED
) {
++this.pos;
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
// 'next' now starts at lf or eof
}
}
next(identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT): Token {
2017-09-28 13:08:25 +02:00
this.nextToken = -1;
return this.token = this.unsafeNext(identifierHandling);
2017-09-28 13:08:25 +02:00
}
private unsafeNext(
identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT,
maxTokenLength: i32 = i32.MAX_VALUE
): Token {
2019-06-05 23:15:39 +02:00
var end = this.end;
var text = this.source.text;
2019-06-05 23:15:39 +02:00
while (this.pos < end) {
2017-09-28 13:08:25 +02:00
this.tokenPos = this.pos;
let c = text.charCodeAt(this.pos);
2017-09-28 13:08:25 +02:00
switch (c) {
case CharCode.CARRIAGERETURN: {
if (!(
2019-06-05 23:15:39 +02:00
++this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.LINEFEED
)) break;
// otherwise fall-through
}
2017-09-28 13:08:25 +02:00
case CharCode.LINEFEED:
case CharCode.TAB:
case CharCode.VERTICALTAB:
case CharCode.FORMFEED:
case CharCode.SPACE: {
++this.pos;
2017-09-28 13:08:25 +02:00
break;
}
case CharCode.EXCLAMATION: {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
maxTokenLength > 1 && this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
maxTokenLength > 2 && this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.EXCLAMATION_EQUALS_EQUALS;
}
return Token.EXCLAMATION_EQUALS;
}
return Token.EXCLAMATION;
}
2017-09-28 13:08:25 +02:00
case CharCode.DOUBLEQUOTE:
case CharCode.SINGLEQUOTE:
case CharCode.BACKTICK: { // TODO
2017-09-28 13:08:25 +02:00
return Token.STRINGLITERAL; // expects a call to readString
}
case CharCode.PERCENT: {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
maxTokenLength > 1 && this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.PERCENT_EQUALS;
}
return Token.PERCENT;
}
case CharCode.AMPERSAND: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.AMPERSAND) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.AMPERSAND_AMPERSAND;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.AMPERSAND_EQUALS;
}
}
return Token.AMPERSAND;
}
case CharCode.OPENPAREN: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.OPENPAREN;
}
case CharCode.CLOSEPAREN: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.CLOSEPAREN;
}
case CharCode.ASTERISK: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.ASTERISK_EQUALS;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.ASTERISK) {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
maxTokenLength > 2 && this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.ASTERISK_ASTERISK_EQUALS;
}
return Token.ASTERISK_ASTERISK;
}
}
return Token.ASTERISK;
}
case CharCode.PLUS: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.PLUS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.PLUS_PLUS;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.PLUS_EQUALS;
}
}
return Token.PLUS;
}
case CharCode.COMMA: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.COMMA;
}
case CharCode.MINUS: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.MINUS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.MINUS_MINUS;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.MINUS_EQUALS;
}
}
return Token.MINUS;
}
case CharCode.DOT: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (isDecimalDigit(chr)) {
--this.pos;
2017-09-28 13:08:25 +02:00
return Token.FLOATLITERAL; // expects a call to readFloat
}
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
maxTokenLength > 2 && this.pos + 1 < end &&
2019-02-22 15:03:33 +02:00
chr == CharCode.DOT &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos + 1) == CharCode.DOT
) {
2017-09-28 13:08:25 +02:00
this.pos += 2;
return Token.DOT_DOT_DOT;
}
}
return Token.DOT;
}
case CharCode.SLASH: {
let commentStartPos = this.pos;
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.SLASH) { // single-line
let commentKind = CommentKind.LINE;
if (
2019-06-05 23:15:39 +02:00
this.pos + 1 < end &&
text.charCodeAt(this.pos + 1) == CharCode.SLASH
) {
++this.pos;
commentKind = CommentKind.TRIPLE;
}
2019-06-05 23:15:39 +02:00
while (++this.pos < end) {
if (text.charCodeAt(this.pos) == CharCode.LINEFEED) {
++this.pos;
break;
}
2017-09-28 13:08:25 +02:00
}
if (this.onComment) {
this.onComment(
commentKind,
text.substring(commentStartPos, this.pos),
this.range(commentStartPos, this.pos)
);
}
break;
2017-09-28 13:08:25 +02:00
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.ASTERISK) { // multi-line
let closed = false;
2019-06-05 23:15:39 +02:00
while (++this.pos < end) {
2017-09-28 13:08:25 +02:00
c = text.charCodeAt(this.pos);
2018-02-25 00:13:39 +01:00
if (
c == CharCode.ASTERISK &&
2019-06-05 23:15:39 +02:00
this.pos + 1 < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos + 1) == CharCode.SLASH
) {
2017-09-28 13:08:25 +02:00
this.pos += 2;
closed = true;
break;
}
}
2018-02-25 00:13:39 +01:00
if (!closed) {
this.error(
DiagnosticCode._0_expected,
this.range(this.pos), "*/"
);
} else if (this.onComment) {
this.onComment(
CommentKind.BLOCK,
text.substring(commentStartPos, this.pos),
this.range(commentStartPos, this.pos)
);
2018-02-25 00:13:39 +01:00
}
break;
2017-09-28 13:08:25 +02:00
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.SLASH_EQUALS;
}
}
return Token.SLASH;
}
2017-09-28 13:08:25 +02:00
case CharCode._0:
case CharCode._1:
case CharCode._2:
case CharCode._3:
case CharCode._4:
case CharCode._5:
case CharCode._6:
case CharCode._7:
case CharCode._8:
case CharCode._9: {
2017-09-28 13:08:25 +02:00
return this.testInteger()
? Token.INTEGERLITERAL // expects a call to readInteger
: Token.FLOATLITERAL; // expects a call to readFloat
}
case CharCode.COLON: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.COLON;
}
case CharCode.SEMICOLON: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.SEMICOLON;
}
case CharCode.LESSTHAN: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.LESSTHAN) {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
maxTokenLength > 2 &&
2019-06-05 23:15:39 +02:00
this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.LESSTHAN_LESSTHAN_EQUALS;
}
return Token.LESSTHAN_LESSTHAN;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.LESSTHAN_EQUALS;
}
}
return Token.LESSTHAN;
}
case CharCode.EQUALS: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.EQUALS) {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
maxTokenLength > 2 &&
2019-06-05 23:15:39 +02:00
this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.EQUALS_EQUALS_EQUALS;
}
return Token.EQUALS_EQUALS;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.GREATERTHAN) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.EQUALS_GREATERTHAN;
}
}
return Token.EQUALS;
}
case CharCode.GREATERTHAN: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.GREATERTHAN) {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 2 && this.pos < end) {
2019-02-22 15:03:33 +02:00
chr = text.charCodeAt(this.pos);
if (chr == CharCode.GREATERTHAN) {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
maxTokenLength > 3 && this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS;
}
return Token.GREATERTHAN_GREATERTHAN_GREATERTHAN;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.GREATERTHAN_GREATERTHAN_EQUALS;
}
}
return Token.GREATERTHAN_GREATERTHAN;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.GREATERTHAN_EQUALS;
}
}
return Token.GREATERTHAN;
}
case CharCode.QUESTION: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.QUESTION;
}
case CharCode.OPENBRACKET: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.OPENBRACKET;
}
case CharCode.CLOSEBRACKET: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.CLOSEBRACKET;
}
case CharCode.CARET: {
++this.pos;
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
maxTokenLength > 1 && this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.EQUALS
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.CARET_EQUALS;
}
return Token.CARET;
}
case CharCode.OPENBRACE: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.OPENBRACE;
}
case CharCode.BAR: {
++this.pos;
2019-06-05 23:15:39 +02:00
if (maxTokenLength > 1 && this.pos < end) {
2019-02-22 15:03:33 +02:00
let chr = text.charCodeAt(this.pos);
if (chr == CharCode.BAR) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.BAR_BAR;
}
2019-02-22 15:03:33 +02:00
if (chr == CharCode.EQUALS) {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.BAR_EQUALS;
}
}
return Token.BAR;
}
case CharCode.CLOSEBRACE: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.CLOSEBRACE;
}
case CharCode.TILDE: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.TILDE;
}
case CharCode.AT: {
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.AT;
}
default: {
2017-09-28 13:08:25 +02:00
if (isIdentifierStart(c)) {
if (isKeywordCharacter(c)) {
let posBefore = this.pos;
2018-02-25 00:13:39 +01:00
while (
2019-06-05 23:15:39 +02:00
++this.pos < end &&
2018-02-25 00:13:39 +01:00
isIdentifierPart(c = text.charCodeAt(this.pos))
) {
2017-09-28 13:08:25 +02:00
if (!isKeywordCharacter(c)) {
this.pos = posBefore;
return Token.IDENTIFIER;
}
}
let keywordText = text.substring(posBefore, this.pos);
let keywordToken = tokenFromKeyword(keywordText);
2018-02-25 00:13:39 +01:00
if (
2019-02-22 15:03:33 +02:00
keywordToken !== Token.INVALID &&
identifierHandling !== IdentifierHandling.ALWAYS &&
!(
identifierHandling === IdentifierHandling.PREFER &&
tokenIsAlsoIdentifier(keywordToken)
)
2018-02-25 00:13:39 +01:00
) {
return keywordToken;
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
this.pos = posBefore;
}
return Token.IDENTIFIER; // expects a call to readIdentifier
} else if (isWhiteSpace(c)) {
++this.pos;
2017-09-28 13:08:25 +02:00
break;
}
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Invalid_character,
this.range(this.pos, this.pos + 1)
);
++this.pos;
2017-09-28 13:08:25 +02:00
return Token.INVALID;
}
2017-09-28 13:08:25 +02:00
}
}
2018-02-25 00:13:39 +01:00
return Token.ENDOFFILE;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
peek(
checkOnNewLine: bool = false,
identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT,
2018-02-25 00:13:39 +01:00
maxCompoundLength: i32 = i32.MAX_VALUE
): Token {
var text = this.source.text;
2017-09-28 13:08:25 +02:00
if (this.nextToken < 0) {
let posBefore = this.pos;
let tokenBefore = this.token;
let tokenPosBefore = this.tokenPos;
this.nextToken = this.unsafeNext(identifierHandling, maxCompoundLength);
this.nextTokenPos = this.tokenPos;
2017-09-28 13:08:25 +02:00
if (checkOnNewLine) {
this.nextTokenOnNewLine = false;
for (let pos = posBefore, end = this.nextTokenPos; pos < end; ++pos) {
if (isLineBreak(text.charCodeAt(pos))) {
2017-09-28 13:08:25 +02:00
this.nextTokenOnNewLine = true;
break;
}
}
}
this.pos = posBefore;
this.token = tokenBefore;
this.tokenPos = tokenPosBefore;
}
return this.nextToken;
}
skipIdentifier(identifierHandling: IdentifierHandling = IdentifierHandling.PREFER): bool {
return this.skip(Token.IDENTIFIER, identifierHandling);
}
skip(token: Token, identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT): bool {
var posBefore = this.pos;
var tokenBefore = this.token;
var tokenPosBefore = this.tokenPos;
var maxCompoundLength = i32.MAX_VALUE;
switch (token) {
case Token.GREATERTHAN: { // where parsing type arguments
maxCompoundLength = 1;
break;
}
}
this.token = this.unsafeNext(identifierHandling, maxCompoundLength);
2018-02-25 00:13:39 +01:00
if (this.token == token) {
2017-09-28 13:08:25 +02:00
this.nextToken = -1;
return true;
} else {
this.pos = posBefore;
this.token = tokenBefore;
this.tokenPos = tokenPosBefore;
return false;
}
}
mark(): State {
var state: State;
if (reusableState) {
state = reusableState;
reusableState = null;
} else {
state = new State();
}
state.pos = this.pos;
state.token = this.token;
state.tokenPos = this.tokenPos;
return state;
2017-09-28 13:08:25 +02:00
}
discard(state: State): void {
reusableState = state;
}
reset(state: State): void {
this.pos = state.pos;
this.token = state.token;
this.tokenPos = state.tokenPos;
2017-09-28 13:08:25 +02:00
this.nextToken = -1;
}
range(start: i32 = -1, end: i32 = -1): Range {
if (start < 0) {
start = this.tokenPos;
end = this.pos;
2018-02-25 00:13:39 +01:00
} else if (end < 0) {
end = start;
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
return new Range(this.source, start, end);
}
readIdentifier(): string {
var text = this.source.text;
var start = this.pos;
2019-06-05 23:15:39 +02:00
var end = this.end;
2018-02-25 00:13:39 +01:00
while (
2019-06-05 23:15:39 +02:00
++this.pos < end &&
2018-02-25 00:13:39 +01:00
isIdentifierPart(text.charCodeAt(this.pos))
);
2017-09-28 13:08:25 +02:00
return text.substring(start, this.pos);
}
readString(): string {
var text = this.source.text;
var quote = text.charCodeAt(this.pos++);
var start = this.pos;
2019-06-05 23:15:39 +02:00
var end = this.end;
var result = "";
2017-09-28 13:08:25 +02:00
while (true) {
2019-06-05 23:15:39 +02:00
if (this.pos >= end) {
2017-09-28 13:08:25 +02:00
result += text.substring(start, this.pos);
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unterminated_string_literal,
2019-06-05 23:15:39 +02:00
this.range(start - 1, end)
2018-02-25 00:13:39 +01:00
);
2017-09-28 13:08:25 +02:00
break;
}
let c = text.charCodeAt(this.pos);
2017-09-28 13:08:25 +02:00
if (c == quote) {
result += text.substring(start, this.pos++);
break;
}
if (c == CharCode.BACKSLASH) {
result += text.substring(start, this.pos);
result += this.readEscapeSequence();
start = this.pos;
continue;
}
if (isLineBreak(c)) {
result += text.substring(start, this.pos);
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unterminated_string_literal,
this.range(start - 1, this.pos)
);
2017-09-28 13:08:25 +02:00
break;
}
++this.pos;
2017-09-28 13:08:25 +02:00
}
return result;
}
readEscapeSequence(): string {
2019-06-05 23:15:39 +02:00
var end = this.end;
if (++this.pos >= end) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unexpected_end_of_text,
2019-06-05 23:15:39 +02:00
this.range(end)
2018-02-25 00:13:39 +01:00
);
2017-09-28 13:08:25 +02:00
return "";
}
var text = this.source.text;
var c = text.charCodeAt(this.pos++);
2017-09-28 13:08:25 +02:00
switch (c) {
case CharCode._0: return "\0";
case CharCode.b: return "\b";
case CharCode.t: return "\t";
case CharCode.n: return "\n";
case CharCode.v: return "\v";
case CharCode.f: return "\f";
case CharCode.r: return "\r";
case CharCode.SINGLEQUOTE: return "'";
case CharCode.DOUBLEQUOTE: return "\"";
2017-09-28 13:08:25 +02:00
case CharCode.u: {
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.OPENBRACE
) {
++this.pos;
2017-09-28 13:08:25 +02:00
return this.readExtendedUnicodeEscape(); // \u{DDDDDDDD}
}
return this.readUnicodeEscape(); // \uDDDD
}
case CharCode.CARRIAGERETURN: {
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
this.pos < end &&
2018-02-25 00:13:39 +01:00
text.charCodeAt(this.pos) == CharCode.LINEFEED
) {
++this.pos;
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
// fall through
}
2017-09-28 13:08:25 +02:00
case CharCode.LINEFEED:
case CharCode.LINESEPARATOR:
case CharCode.PARAGRAPHSEPARATOR: return "";
default: return String.fromCharCode(c);
2017-09-28 13:08:25 +02:00
}
}
2018-01-07 18:15:21 +01:00
readRegexpPattern(): string {
var text = this.source.text;
var start = this.pos;
2019-06-05 23:15:39 +02:00
var end = this.end;
var escaped = false;
2017-09-28 13:08:25 +02:00
while (true) {
2019-06-05 23:15:39 +02:00
if (this.pos >= end) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unterminated_regular_expression_literal,
2019-06-05 23:15:39 +02:00
this.range(start, end)
2018-02-25 00:13:39 +01:00
);
2018-01-07 18:15:21 +01:00
break;
2017-09-28 13:08:25 +02:00
}
if (text.charCodeAt(this.pos) == CharCode.BACKSLASH) {
++this.pos;
2017-09-28 13:08:25 +02:00
escaped = true;
continue;
}
let c = text.charCodeAt(this.pos);
2019-02-22 15:03:33 +02:00
if (!escaped && c == CharCode.SLASH) break;
2017-09-28 13:08:25 +02:00
if (isLineBreak(c)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unterminated_regular_expression_literal,
this.range(start, this.pos)
);
2018-01-07 18:15:21 +01:00
break;
2017-09-28 13:08:25 +02:00
}
++this.pos;
escaped = false;
2017-09-28 13:08:25 +02:00
}
return text.substring(start, this.pos);
}
2018-01-07 18:15:21 +01:00
readRegexpFlags(): string {
var text = this.source.text;
var start = this.pos;
2019-06-05 23:15:39 +02:00
var end = this.end;
2018-01-07 18:15:21 +01:00
var flags = 0;
2019-06-05 23:15:39 +02:00
while (this.pos < end) {
let c: i32 = text.charCodeAt(this.pos);
2018-02-25 00:13:39 +01:00
if (!isIdentifierPart(c)) break;
2018-01-07 18:15:21 +01:00
++this.pos;
2018-02-25 00:13:39 +01:00
// make sure each supported flag is unique
2018-01-07 18:15:21 +01:00
switch (c) {
case CharCode.g: {
flags |= flags & 1 ? -1 : 1;
2018-01-07 18:15:21 +01:00
break;
}
case CharCode.i: {
flags |= flags & 2 ? -1 : 2;
2018-01-07 18:15:21 +01:00
break;
}
case CharCode.m: {
flags |= flags & 4 ? -1 : 4;
break;
}
default: {
2018-01-07 18:15:21 +01:00
flags = -1;
break;
}
}
}
2018-02-25 00:13:39 +01:00
if (flags == -1) {
this.error(
DiagnosticCode.Invalid_regular_expression_flags,
this.range(start, this.pos)
);
}
return text.substring(start, this.pos);
2017-09-28 13:08:25 +02:00
}
testInteger(): bool {
2019-06-05 23:15:39 +02:00
var end = this.end;
var text = this.source.text;
2019-06-05 23:15:39 +02:00
if (this.pos + 1 < end && text.charCodeAt(this.pos) == CharCode._0) {
2017-09-28 13:08:25 +02:00
switch (text.charCodeAt(this.pos + 2)) {
case CharCode.x:
2019-02-22 15:03:33 +02:00
case CharCode.X:
2017-09-28 13:08:25 +02:00
case CharCode.b:
2019-02-22 15:03:33 +02:00
case CharCode.B:
case CharCode.o:
case CharCode.O: return true;
2017-09-28 13:08:25 +02:00
}
}
var pos = this.pos;
2019-06-05 23:15:39 +02:00
while (pos < end) {
let c = text.charCodeAt(pos);
2019-06-05 23:15:39 +02:00
if (c == CharCode.DOT || c == CharCode.e || c == CharCode.E) return false;
if ((c < CharCode._0 || c > CharCode._9) && c != CharCode._) break;
// does not validate separator placement (this is done in readXYInteger)
2017-09-28 13:08:25 +02:00
pos++;
}
return true;
}
readInteger(): I64 {
var text = this.source.text;
2019-02-22 15:03:33 +02:00
if (this.pos + 2 < this.end && text.charCodeAt(this.pos) == CharCode._0) {
2017-09-28 13:08:25 +02:00
switch (text.charCodeAt(this.pos + 1)) {
2019-02-22 15:03:33 +02:00
case CharCode.x:
case CharCode.X: {
2017-09-28 13:08:25 +02:00
this.pos += 2;
return this.readHexInteger();
}
2019-02-22 15:03:33 +02:00
case CharCode.b:
case CharCode.B: {
2017-09-28 13:08:25 +02:00
this.pos += 2;
return this.readBinaryInteger();
}
2019-02-22 15:03:33 +02:00
case CharCode.o:
case CharCode.O: {
2017-09-28 13:08:25 +02:00
this.pos += 2;
return this.readOctalInteger();
}
2017-09-28 13:08:25 +02:00
}
if (isOctalDigit(text.charCodeAt(this.pos + 1))) {
let start = this.pos;
++this.pos;
let value = this.readOctalInteger();
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Octal_literals_are_not_allowed_in_strict_mode,
this.range(start, this.pos)
);
2017-09-28 13:08:25 +02:00
return value;
}
}
return this.readDecimalInteger();
}
readHexInteger(): I64 {
var text = this.source.text;
var start = this.pos;
2019-02-22 15:03:33 +02:00
var value = i64_new(0);
var i64_4 = i64_new(4);
var sepEnd = start;
2019-06-05 23:15:39 +02:00
var end = this.end;
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
2017-09-28 13:08:25 +02:00
if (c >= CharCode._0 && c <= CharCode._9) {
2019-02-22 15:03:33 +02:00
// value = (value << 4) + c - CharCode._0;
2018-02-25 00:13:39 +01:00
value = i64_add(
2019-02-22 15:03:33 +02:00
i64_shl(value, i64_4),
i64_new(c - CharCode._0)
2018-02-25 00:13:39 +01:00
);
} else if (c >= CharCode.A && c <= CharCode.F) {
2019-02-22 15:03:33 +02:00
// value = (value << 4) + 10 + c - CharCode.A;
2018-02-25 00:13:39 +01:00
value = i64_add(
2019-02-22 15:03:33 +02:00
i64_shl(value, i64_4),
i64_new(10 + c - CharCode.A)
2018-02-25 00:13:39 +01:00
);
2017-09-28 13:08:25 +02:00
} else if (c >= CharCode.a && c <= CharCode.f) {
2019-02-22 15:03:33 +02:00
// value = (value << 4) + 10 + c - CharCode.a;
2018-02-25 00:13:39 +01:00
value = i64_add(
2019-02-22 15:03:33 +02:00
i64_shl(value, i64_4),
i64_new(10 + c - CharCode.a)
2018-02-25 00:13:39 +01:00
);
} else if (c == CharCode._) {
if (sepEnd == pos) {
this.error(
sepEnd == start
? DiagnosticCode.Numeric_separators_are_not_allowed_here
: DiagnosticCode.Multiple_consecutive_numeric_separators_are_not_permitted,
this.range(pos)
);
}
sepEnd = pos + 1;
2018-02-25 00:13:39 +01:00
} else {
2017-09-28 13:08:25 +02:00
break;
2018-02-25 00:13:39 +01:00
}
this.pos = pos + 1;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
if (this.pos == start) {
this.error(
DiagnosticCode.Hexadecimal_digit_expected,
this.range(start)
);
} else if (sepEnd == this.pos) {
this.error(
DiagnosticCode.Numeric_separators_are_not_allowed_here,
this.range(sepEnd - 1)
);
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
return value;
}
readDecimalInteger(): I64 {
var text = this.source.text;
var start = this.pos;
2019-06-05 23:15:39 +02:00
var end = this.end;
2019-02-22 15:03:33 +02:00
var value = i64_new(0);
var i64_10 = i64_new(10);
var sepEnd = start;
2019-06-05 23:15:39 +02:00
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
2017-09-28 13:08:25 +02:00
if (c >= CharCode._0 && c <= CharCode._9) {
// value = value * 10 + c - CharCode._0;
2018-02-25 00:13:39 +01:00
value = i64_add(
i64_mul(value, i64_10),
2019-02-22 15:03:33 +02:00
i64_new(c - CharCode._0)
2018-02-25 00:13:39 +01:00
);
} else if (c == CharCode._) {
if (sepEnd == pos) {
this.error(
sepEnd == start
? DiagnosticCode.Numeric_separators_are_not_allowed_here
: DiagnosticCode.Multiple_consecutive_numeric_separators_are_not_permitted,
this.range(pos)
);
}
sepEnd = pos + 1;
2018-02-25 00:13:39 +01:00
} else {
2017-09-28 13:08:25 +02:00
break;
2018-02-25 00:13:39 +01:00
}
this.pos = pos + 1;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
if (this.pos == start) {
this.error(
DiagnosticCode.Digit_expected,
this.range(start)
);
} else if (sepEnd == this.pos) {
this.error(
DiagnosticCode.Numeric_separators_are_not_allowed_here,
this.range(sepEnd - 1)
);
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
return value;
}
readOctalInteger(): I64 {
var text = this.source.text;
var start = this.pos;
2019-02-22 15:03:33 +02:00
var value = i64_new(0);
var i64_3 = i64_new(3);
var sepEnd = start;
2019-06-05 23:15:39 +02:00
var end = this.end;
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
2017-09-28 13:08:25 +02:00
if (c >= CharCode._0 && c <= CharCode._7) {
2019-02-22 15:03:33 +02:00
// value = (value << 3) + c - CharCode._0;
2018-02-25 00:13:39 +01:00
value = i64_add(
2019-02-22 15:03:33 +02:00
i64_shl(value, i64_3),
i64_new(c - CharCode._0)
2018-02-25 00:13:39 +01:00
);
} else if (c == CharCode._) {
if (sepEnd == pos) {
this.error(
sepEnd == start
? DiagnosticCode.Numeric_separators_are_not_allowed_here
: DiagnosticCode.Multiple_consecutive_numeric_separators_are_not_permitted,
this.range(pos)
);
}
sepEnd = pos + 1;
2018-02-25 00:13:39 +01:00
} else {
2017-09-28 13:08:25 +02:00
break;
2018-02-25 00:13:39 +01:00
}
++this.pos;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
if (this.pos == start) {
this.error(
DiagnosticCode.Octal_digit_expected,
this.range(start)
);
} else if (sepEnd == this.pos) {
this.error(
DiagnosticCode.Numeric_separators_are_not_allowed_here,
this.range(sepEnd - 1)
);
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
return value;
}
readBinaryInteger(): I64 {
var text = this.source.text;
var start = this.pos;
2019-02-22 15:03:33 +02:00
var value = i64_new(0);
var i64_1 = i64_new(1);
var sepEnd = start;
2019-06-05 23:15:39 +02:00
var end = this.end;
while (this.pos < end) {
let pos = this.pos;
let c = text.charCodeAt(pos);
2017-09-28 13:08:25 +02:00
if (c == CharCode._0) {
2019-02-22 15:03:33 +02:00
// value = (value << 1);
value = i64_shl(value, i64_1);
2017-09-28 13:08:25 +02:00
} else if (c == CharCode._1) {
2019-02-22 15:03:33 +02:00
// value = (value << 1) + 1;
2018-02-25 00:13:39 +01:00
value = i64_add(
2019-02-22 15:03:33 +02:00
i64_shl(value, i64_1),
2018-02-25 00:13:39 +01:00
i64_1
);
} else if (c == CharCode._) {
if (sepEnd == pos) {
this.error(
sepEnd == start
? DiagnosticCode.Numeric_separators_are_not_allowed_here
: DiagnosticCode.Multiple_consecutive_numeric_separators_are_not_permitted,
this.range(pos)
);
}
sepEnd = pos + 1;
2018-02-25 00:13:39 +01:00
} else {
2017-09-28 13:08:25 +02:00
break;
2018-02-25 00:13:39 +01:00
}
this.pos = pos + 1;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
if (this.pos == start) {
this.error(
DiagnosticCode.Binary_digit_expected,
this.range(start)
);
} else if (sepEnd == this.pos) {
this.error(
DiagnosticCode.Numeric_separators_are_not_allowed_here,
this.range(sepEnd - 1)
);
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
return value;
}
readFloat(): f64 {
// var text = this.source.text;
// if (text.charCodeAt(this.pos) == CharCode._0 && this.pos + 2 < this.end) {
// switch (text.charCodeAt(this.pos + 1)) {
// case CharCode.X:
// case CharCode.x: {
// this.pos += 2;
// return this.readHexFloat();
// }
// }
// }
return this.readDecimalFloat();
}
readDecimalFloat(): f64 {
// TODO: numeric separators (parseFloat can't handle these)
var start = this.pos;
2019-06-05 23:15:39 +02:00
var end = this.end;
var text = this.source.text;
2019-06-05 23:15:39 +02:00
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
++this.pos;
2018-02-25 00:13:39 +01:00
}
2019-06-05 23:15:39 +02:00
if (this.pos < end && text.charCodeAt(this.pos) == CharCode.DOT) {
++this.pos;
2019-06-05 23:15:39 +02:00
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
++this.pos;
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
}
2019-06-05 23:15:39 +02:00
if (this.pos < end) {
let c = text.charCodeAt(this.pos);
2019-02-22 15:03:33 +02:00
if (c == CharCode.e || c == CharCode.E) {
2018-02-25 00:13:39 +01:00
if (
2019-06-05 23:15:39 +02:00
++this.pos < end &&
2019-02-22 15:03:33 +02:00
(c = text.charCodeAt(this.pos)) == CharCode.MINUS || c == CharCode.PLUS &&
2018-02-25 00:13:39 +01:00
isDecimalDigit(text.charCodeAt(this.pos + 1))
) {
++this.pos;
2018-02-25 00:13:39 +01:00
}
2019-06-05 23:15:39 +02:00
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
++this.pos;
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
}
}
return parseFloat(text.substring(start, this.pos));
}
readHexFloat(): f64 {
throw new Error("not implemented"); // TBD
}
2017-09-28 13:08:25 +02:00
readUnicodeEscape(): string {
var remain = 4;
var value = 0;
2019-06-05 23:15:39 +02:00
var end = this.end;
var text = this.source.text;
2019-06-05 23:15:39 +02:00
while (this.pos < end) {
let c = text.charCodeAt(this.pos++);
2018-02-25 00:13:39 +01:00
if (c >= CharCode._0 && c <= CharCode._9) {
2019-02-22 15:03:33 +02:00
value = (value << 4) + c - CharCode._0;
2018-02-25 00:13:39 +01:00
} else if (c >= CharCode.A && c <= CharCode.F) {
2019-02-22 15:03:33 +02:00
value = (value << 4) + c + (10 - CharCode.A);
2018-02-25 00:13:39 +01:00
} else if (c >= CharCode.a && c <= CharCode.f) {
2019-02-22 15:03:33 +02:00
value = (value << 4) + c + (10 - CharCode.a);
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.Hexadecimal_digit_expected,
this.range(this.pos - 1, this.pos)
);
2017-09-28 13:08:25 +02:00
return "";
}
2018-02-25 00:13:39 +01:00
if (--remain == 0) break;
2017-09-28 13:08:25 +02:00
}
if (remain) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unexpected_end_of_text,
this.range(this.pos)
);
2017-09-28 13:08:25 +02:00
return "";
}
return String.fromCharCode(value);
}
private readExtendedUnicodeEscape(): string {
var start = this.pos;
var value = this.readHexInteger();
var value32 = i64_low(value);
var invalid = false;
2017-09-28 13:08:25 +02:00
assert(!i64_high(value));
if (value32 > 0x10FFFF) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.An_extended_Unicode_escape_value_must_be_between_0x0_and_0x10FFFF_inclusive,
this.range(start, this.pos)
);
2017-09-28 13:08:25 +02:00
invalid = true;
}
2019-06-05 23:15:39 +02:00
var end = this.end;
var text = this.source.text;
2019-06-05 23:15:39 +02:00
if (this.pos >= end) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unexpected_end_of_text,
2019-06-05 23:15:39 +02:00
this.range(start, end)
2018-02-25 00:13:39 +01:00
);
2017-09-28 13:08:25 +02:00
invalid = true;
} else if (text.charCodeAt(this.pos) == CharCode.CLOSEBRACE) {
++this.pos;
2017-09-28 13:08:25 +02:00
} else {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Unterminated_Unicode_escape_sequence,
this.range(start, this.pos)
);
2017-09-28 13:08:25 +02:00
invalid = true;
}
2018-02-25 00:13:39 +01:00
if (invalid) return "";
2017-09-28 13:08:25 +02:00
return value32 < 65536
? String.fromCharCode(value32)
2018-02-25 00:13:39 +01:00
: String.fromCharCode(
2019-02-22 15:03:33 +02:00
((value32 - 65536) >>> 10) + 0xD800,
((value32 - 65536) & 1023) + 0xDC00
2018-02-25 00:13:39 +01:00
);
}
finish(): void {
2017-09-28 13:08:25 +02:00
}
}
/** Tokenizer state as returned by {@link Tokenizer#mark} and consumed by {@link Tokenizer#reset}. */
export class State {
/** Current position. */
pos: i32;
/** Current token. */
token: Token;
/** Current token's position. */
tokenPos: i32;
}
// Reusable state object to reduce allocations
var reusableState: State | null = null;