futurescript
Version:
A functional style, but highly readable language that compiles to JavaScript.
996 lines (937 loc) • 37.5 kB
JavaScript
// `isJoint=true` means the token can't be the start or end of something.
// That is, it must act as a "joint".
// Note: this doesn't apply to a token before `:` or after `as`. For example,
// these are legal:
// ..: something
// something as ..
import * as $tools from "./tools.mjs";
import * as $lockedApi from "../locked-api.mjs";
export class Lex {
// Both `raw` and `path` are optional, but at least one should exist.
// If `raw` is absent, it will return a promise rather than an instance.
constructor(raw, path) {
let internalConstructor = () => {
this.path = path;
this.raw = raw;
// In fact this is "round 0". Source map and error message need line and column.
// Note: "\n" may be in strings. We must add all "\n".
let rawLinePositions = [0];
for (let i = 0; i < raw.length; i++) {
if (raw[i] === "\n") {
rawLinePositions.push(i + 1);
}
}
this._rawLinePositions = rawLinePositions;
this._round1();
this._massiveRounds();
};
if (raw === undefined) {
return new Promise((resolve, reject) => {(async () => {try {
raw = await $lockedApi.readTextFile(path);
internalConstructor();
resolve(this);
} catch (ex) {reject(ex);}})();});
}
else {
internalConstructor();
}
}
_round1() {
let raw = this.raw;
// Note: we can only append things to the raw end. If we modify raw's middle,
// raw positions are lost.
if (!raw.endsWith("\n")) {
raw += "\n";
}
let result = [];
let pendingToken = null;
let versionDirectiveInBraces = false;
let numberForcesInt = false;
let numberIsHex = false;
let stringType = null;
let stringInterpolationParenthesisLevel = null;
let indentValue = null;
let stringIndentValue = null;
let stringIndentValueRemaining = null;
let stringIndentFinished = false;
let stringLine = {content: ""};
let postQuote = null;
let i = 0;
while (i < raw.length) {
let char = raw[i];
// process length-unfixed tokens
if (pendingToken instanceof VersionDirective) {
if (char === "{") {
versionDirectiveInBraces = true;
pendingToken.value += char;
i++;
}
else if (char === "}") {
versionDirectiveInBraces = false;
pendingToken.value += char;
i++;
}
else if (!versionDirectiveInBraces && (char === "\r" || char === "\n")) {
result.push(pendingToken.end(i - 1));
pendingToken = null;
}
else {
pendingToken.value += char;
i++;
}
}
else if (pendingToken instanceof Indent) {
if (char === " " || char === "\t") {
pendingToken.value++;
i++;
}
else {
indentValue = pendingToken.value;
result.push(pendingToken.end(i - 1));
pendingToken = null;
}
}
else if (pendingToken instanceof Num) {
if (!numberIsHex && char === "e" && (raw[i + 1] === "+" || raw[i + 1] === "-")) {
pendingToken.value += "e" + raw[i + 1];
i += 2;
}
else if ((
numberForcesInt ?
char.search(/[0-9A-Fa-fx]/) :
char.search(/[0-9A-Fa-fx.]/)
) !== -1) {
pendingToken.value += char;
i++;
}
else {
result.push(pendingToken.end(i - 1));
pendingToken = null;
numberForcesInt = false;
numberIsHex = false;
}
}
else if (pendingToken instanceof Str) {
let indentOffset = stringIndentValue - indentValue;
if (postQuote !== null) {
if (char.search(/[a-z]/) !== -1) {
postQuote.value += char;
i++;
}
else {
result.push(postQuote.end(i - 1));
result.push(new PseudoCallRightParenthesis());
postQuote = null;
pendingToken = null;
}
}
else if (!(char === "\"" && (
stringType.isInline || (!stringType.isInline && indentOffset === 0)
))) {
let interpolationChar = null;
if (stringType === InlineNormalString || stringType === FormattedNormalString) {
interpolationChar = "\\";
}
else if (stringType === InlineRegex || stringType === FormattedRegex) {
interpolationChar = "#";
}
let quoteEscapeEnabled =
stringType === InlineNormalString ||
stringType === FormattedNormalString ||
stringType === InlineRegex;
let joinEscapeEnabled =
stringType === InlineNormalString ||
stringType === FormattedNormalString ||
stringType === InlineRegex ||
stringType === FormattedRegex;
if (char === interpolationChar && raw[i + 1] === "(") {
stringLine.indentOffset = indentOffset;
stringLine.isJoin = false;
pendingToken.value.push(stringLine);
result.push(pendingToken.end(i - 1));
result.push(new Plus());
result.push(new NormalLeftParenthesis().start(i).end(i + 1));
pendingToken = null;
stringLine = {content: ""};
stringInterpolationParenthesisLevel = 1;
stringIndentFinished = true;
i += 2;
}
else if (
quoteEscapeEnabled &&
char === "\\" && raw[i + 1] === "\""
) {
stringLine.content += "\\\"";
stringIndentFinished = true;
i += 2;
}
else if (
joinEscapeEnabled &&
char === "\\" && (raw[i + 1] === "\r" || raw[i + 1] === "\n")
) {
stringLine.indentOffset = indentOffset;
stringLine.isJoin = true;
pendingToken.value.push(stringLine);
stringLine = {content: ""};
stringIndentValue = 0;
stringIndentFinished = false;
i += 2;
}
else if (
stringIndentValue !== null &&
(char === " " || char === "\t") &&
!stringIndentFinished
) {
stringIndentValue++;
i++;
}
else if (char === "\r" || char === "\n") {
stringLine.indentOffset = indentOffset;
stringLine.isJoin = false;
pendingToken.value.push(stringLine);
stringLine = {content: ""};
stringIndentValue = 0;
stringIndentFinished = false;
i++;
}
else {
stringLine.content += char;
stringIndentFinished = true;
i++;
}
}
else {
stringLine.indentOffset = indentOffset;
stringLine.isJoin = false;
pendingToken.value.push(stringLine);
pendingToken.isLast = true;
result.push(pendingToken.end(i - 1));
if (raw[i + 1].search(/[a-z]/) !== -1) {
result.push(new Comma());
postQuote = new PostQuote("").start(i + 1);
}
else {
result.push(new PseudoCallRightParenthesis());
pendingToken = null;
}
stringType = null;
stringIndentValue = null;
stringLine = {content: ""};
stringIndentFinished = false;
i++;
}
}
else if (pendingToken instanceof InlineComment) {
if (char !== "\r" && char !== "\n") {
pendingToken.value += char;
i++;
}
else {
result.push(pendingToken.end(i - 1));
pendingToken = null;
}
}
else if (pendingToken instanceof FormattedComment) {
if (!(char === "#" && raw[i + 1] === "#" && raw[i + 2] === "#")) {
pendingToken.value += char;
i++;
}
else {
result.push(pendingToken.end(i + 2));
pendingToken = null;
i += 3;
}
}
else if (pendingToken instanceof NormalToken) {
if (char.search(/[A-Za-z0-9_$]/) !== -1) {
pendingToken.value += char;
i++;
}
else {
result.push(pendingToken.end(i - 1));
pendingToken = null;
}
}
// characters that won't be processed
else if (char === " ") {
i++;
}
else if (char === "\r" && raw[i + 1] === "\n") {
i++;
}
// recognize length-unfixed tokens: version directive
else if (i === 0) {
pendingToken = new VersionDirective(char).start(i);
i++;
}
// recognize length-unfixed tokens: three characters
else if (char === "j" && raw[i + 1] === "s" && raw[i + 2] === "\"") {
if (raw[i + 3] === "\r" || raw[i + 3] === "\n") {
stringType = FormattedJs;
result.push(new FormattedJs().start(i).end(i + 1));
}
else {
stringType = InlineJs;
result.push(new InlineJs().start(i).end(i + 1));
}
result.push(new PseudoCallLeftParenthesis());
pendingToken = new Str([]).start(i + 3);
pendingToken.type = stringType;
pendingToken.isFirst = true;
stringIndentValue = indentValue;
i += 3;
}
else if (char === "#" && raw[i + 1] === "#" && raw[i + 2] === "#") {
pendingToken = new FormattedComment("").start(i);
i += 3;
}
// recognize length-fixed tokens: three characters
else if (char === "." && raw[i + 1] === "." && raw[i + 2] === ".") {
result.push(new Spread().start(i).end(i + 2));
i += 3;
}
// recognize length-unfixed tokens: double character
else if (char === "v" && raw[i + 1] === "\"") {
if (raw[i + 2] === "\r" || raw[i + 2] === "\n") {
stringType = FormattedVerbatimString;
result.push(new FormattedVerbatimString().start(i).end(i));
}
else {
stringType = InlineVerbatimString;
result.push(new InlineVerbatimString().start(i).end(i));
}
result.push(new PseudoCallLeftParenthesis());
pendingToken = new Str([]).start(i + 2);
pendingToken.type = stringType;
pendingToken.isFirst = true;
stringIndentValue = indentValue;
i += 2;
}
else if (char === "r" && raw[i + 1] === "\"") {
if (raw[i + 2] === "\r" || raw[i + 2] === "\n") {
stringType = FormattedRegex;
result.push(new FormattedRegex().start(i).end(i));
}
else {
stringType = InlineRegex;
result.push(new InlineRegex().start(i).end(i));
}
result.push(new PseudoCallLeftParenthesis());
pendingToken = new Str([]).start(i + 2);
pendingToken.type = stringType;
pendingToken.isFirst = true;
stringIndentValue = indentValue;
i += 2;
}
// recognize length-fixed tokens: double character
else if (char === "<" && raw[i + 1] === "<") {
result.push(new LeftChevron().start(i).end(i + 1));
i += 2;
}
else if (char === ">" && raw[i + 1] === ">") {
result.push(new RightChevron().start(i).end(i + 1));
i += 2;
}
else if (char === "-" && raw[i + 1] === "-") {
result.push(new DashFunction().start(i).end(i + 1));
i += 2;
}
else if (char === "-" && raw[i + 1] === ">") {
result.push(new ArrowFunction().start(i).end(i + 1));
i += 2;
}
else if (char === "<" && raw[i + 1] === ">") {
result.push(new DiamondFunction().start(i).end(i + 1));
i += 2;
}
else if (char === "|" && raw[i + 1] === ">") {
result.push(new Pipe().start(i).end(i + 1));
i += 2;
}
else if (char === ":" && raw[i + 1] === ":") {
result.push(new FatDot().start(i).end(i + 1));
i += 2;
}
else if (char === "." && raw[i + 1] === ".") {
if (raw[i + 2].search(/[A-Za-z_$]/) !== -1) {
result.push(new DotDot().start(i).end(i + 1));
}
else {
// TODO: Anti-conflict string should be in a more general module so that the lex
// module can also use it.
result.push(new NormalToken("dotDot_573300145710716007").start(i).end(i + 1));
}
i += 2;
}
else if (char === "/" && raw[i + 1] === "=") {
result.push(new NotEqual().start(i).end(i + 1));
i += 2;
}
else if (char === "<" && raw[i + 1] === "=") {
result.push(new LessThanOrEqual().start(i).end(i + 1));
i += 2;
}
else if (char === ">" && raw[i + 1] === "=") {
result.push(new GreaterThanOrEqual().start(i).end(i + 1));
i += 2;
}
else if (char === "*" && raw[i + 1] === "*") {
result.push(new Power().start(i).end(i + 1));
i += 2;
}
// recognize length-unfixed tokens: single character
else if (char === "\n") {
pendingToken = new Indent(0).start(i + 1);
i++;
}
else if (char.search(/[0-9]/) !== -1) {
pendingToken = new Num(char).start(i);
if (raw[i - 1] === "." || raw[i - 1] === "@") {
numberForcesInt = true;
}
if (char === "0" && raw[i + 1] === "x") {
numberIsHex = true;
}
i++;
}
else if (char === "\"") {
if (raw[i + 1] === "\r" || raw[i + 1] === "\n") {
stringType = FormattedNormalString;
result.push(new FormattedNormalString());
}
else {
stringType = InlineNormalString;
result.push(new InlineNormalString());
}
result.push(new PseudoCallLeftParenthesis());
pendingToken = new Str([]).start(i + 1);
pendingToken.type = stringType;
pendingToken.isFirst = true;
stringIndentValue = indentValue;
i++;
}
else if (char === "#") {
pendingToken = new InlineComment("").start(i);
i++;
}
else if (char.search(/[A-Za-z_$]/) !== -1) {
pendingToken = new NormalToken(char).start(i);
i++;
}
// recognize length-fixed tokens: single character
else if (char === ";") {
result.push(new Semicolon().start(i).end(i));
i++;
}
else if (char === ":") {
result.push(new Colon().start(i).end(i));
i++;
}
else if (char === ",") {
result.push(new Comma().start(i).end(i));
i++;
}
else if (char === ".") {
result.push(new Dot().start(i).end(i));
i++;
}
else if (char === "(") {
if (stringType !== null) {
stringInterpolationParenthesisLevel++;
}
if (raw[i - 1].search(/["')@A-Za-z0-9_$]/) !== -1) {
result.push(new CallLeftParenthesis().start(i).end(i));
}
else {
result.push(new NormalLeftParenthesis().start(i).end(i));
}
i++;
}
else if (char === ")") {
if (stringType !== null) {
stringInterpolationParenthesisLevel--;
}
if (stringType !== null && stringInterpolationParenthesisLevel === 0) {
result.push(new RightParenthesis().start(i).end(i));
result.push(new Plus());
stringInterpolationParenthesisLevel = null;
pendingToken = new Str([]).start(i + 1);
pendingToken.type = stringType;
}
else {
result.push(new RightParenthesis().start(i).end(i));
}
i++;
}
else if (char === "[") {
if (raw[i - 1].search(/["')@A-Za-z0-9_$]/) !== -1) {
result.push(new CallLeftBracket().start(i).end(i));
}
else {
result.push(new NormalLeftBracket().start(i).end(i));
}
i++;
}
else if (char === "]") {
result.push(new RightBracket().start(i).end(i));
i++;
}
else if (char === "{") {
if (raw[i - 1].search(/["')@A-Za-z0-9_$]/) !== -1) {
result.push(new CallLeftBrace().start(i).end(i));
}
else {
result.push(new NormalLeftBrace().start(i).end(i));
}
i++;
}
else if (char === "}") {
result.push(new RightBrace().start(i).end(i));
i++;
}
else if (char === "=") {
result.push(new Equal().start(i).end(i));
i++;
}
else if (char === "≠") {
result.push(new NotEqual().start(i).end(i));
i++;
}
else if (char === "<") {
result.push(new LessThan().start(i).end(i));
i++;
}
else if (char === ">") {
result.push(new GreaterThan().start(i).end(i));
i++;
}
else if (char === "≤") {
result.push(new LessThanOrEqual().start(i).end(i));
i++;
}
else if (char === "≥") {
result.push(new GreaterThanOrEqual().start(i).end(i));
i++;
}
else if (char === "+") {
if (
raw[i - 1].search(/[")\]}@A-Za-z0-9_$]/) === -1 &&
raw[i + 1] !== " " && raw[i + 1] !== "\t" &&
raw[i + 1] !== "\r" && raw[i + 1] !== "\n"
) {
result.push(new Positive().start(i).end(i));
}
else {
result.push(new Plus().start(i).end(i));
}
i++;
}
else if (char === "-") {
if (
raw[i - 1].search(/[")\]}@A-Za-z0-9_$]/) === -1 &&
raw[i + 1] !== " " && raw[i + 1] !== "\t" &&
raw[i + 1] !== "\r" && raw[i + 1] !== "\n"
) {
result.push(new Negative().start(i).end(i));
}
else {
result.push(new Minus().start(i).end(i));
}
i++;
}
else if (char === "*") {
result.push(new Times().start(i).end(i));
i++;
}
else if (char === "/") {
result.push(new Over().start(i).end(i));
i++;
}
else if (char === "?") {
result.push(new Question().start(i).end(i));
i++;
}
else if (char === "|") {
result.push(new Stick().start(i).end(i));
i++;
}
else if (char === "@") {
result.push(new Arg().start(i).end(i));
if (raw[i + 1].search(/[A-Za-z0-9_$]/) !== -1) {
result.push(new Dot());
}
i++;
}
else if (char === "'") {
if (raw[i + 1].search(/[A-Za-z0-9]/) !== -1) {
result.push(new NormalVariant().start(i).end(i));
}
else {
result.push(new FunctionVariant().start(i).end(i));
}
i++;
}
else if (char === "\\" && (raw[i + 1] === "\r" || raw[i + 1] === "\n")) {
result.push(new Join().start(i).end(i));
i++;
}
else {
throw new CharacterError([i, i], this.raw);
}
}
this.value = result;
}
at(index) {
return this.value[index];
}
count() {
return this.value.length;
}
part() {
if (arguments[0] === null) {
return null;
}
else if (arguments[0] instanceof Object) {
return new LexPart(this, arguments[0].startIndex, arguments[0].endIndex);
}
else {
return new LexPart(this, arguments[0], arguments[1]);
}
}
rawLineColumn(rawPosition) {
let line = $tools.findSortedNumber(this._rawLinePositions, rawPosition, false);
let column = rawPosition - this._rawLinePositions[line];
return [line, column];
}
toString(part) {
if (part === undefined) {
part = this.part();
}
let arr = [];
for (let i = part.startIndex; i <= part.endIndex; i++) {
let token = this.value[i];
let part1 = token.constructor.name;
let part2 = "";
if (token.value !== undefined) {
part2 = " " + JSON.stringify(token.value);
}
arr.push(part1 + part2);
}
return arr.join(", ");
}
}
export class LexPart {
constructor(lex, startIndex, endIndex) {
this.lex = lex;
this.startIndex = startIndex === undefined ? 0 : startIndex;
this.endIndex = endIndex === undefined ? lex.count() - 1 : endIndex;
}
shrink() {
return new LexPart(this.lex, this.startIndex + 1, this.endIndex - 1);
}
changeTo() {
return this.lex.part(...arguments);
}
// Returns the first token in the lex part.
token() {
return this.lex.at(this.startIndex);
}
addStyle(style) {
for (let i = this.startIndex; i <= this.endIndex; i++) {
this.lex.at(i).addStyle(style);
}
}
toString() {
return this.lex.toString(this);
}
}
export class RawError extends $lockedApi.SyntaxError {
// `rawPart` is a 2-element array: [start, end]
constructor(rawPart, raw, message) {
if (rawPart === undefined) {
super(message);
return;
}
let start = null;
let end = null;
let line = 0;
let column = 0;
for (let i = 0; i < raw.length; i++) {
if (i === rawPart[0]) {
start = [line, column];
}
if (i === rawPart[1]) {
end = [line, column];
break;
}
if (raw[i] === "\n") {
line++;
column = 0;
}
else {
column++;
}
}
let positionStr =
"From line " + (start[0] + 1) + " column " + (start[1] + 1) +
" to line " + (end[0] + 1) + " column " + (end[1] + 1) + "\n";
super(positionStr + message);
this.rawPart = rawPart;
this.rawStart = start;
this.rawEnd = end;
}
}
export class CharacterError extends RawError {
constructor(rawPart, raw) {
super(rawPart, raw, "Illegal character.");
}
}
export class SyntaxError extends $lockedApi.SyntaxError {
constructor(lexPart, message) {
if (lexPart === undefined) {
super(message);
return;
}
let lex = lexPart.lex;
let startIndex = 0;
let endIndex = lexPart.lex.raw.length - 1;
let t = lex.at(lexPart.startIndex).rawStartIndex;
if (t === undefined) {
for (let i = lexPart.startIndex - 1; i >= 0; i--) {
if (lex.at(i).rawEndIndex !== undefined) {
startIndex = lex.at(i).rawEndIndex + 1;
break;
}
}
}
else {
startIndex = t;
}
t = lex.at(lexPart.endIndex).rawEndIndex;
if (t === undefined) {
for (let i = lexPart.endIndex + 1; i < lex.count(); i++) {
if (lex.at(i).rawStartIndex !== undefined) {
endIndex = lex.at(i).rawStartIndex - 1;
break;
}
}
}
else {
endIndex = t;
}
let start = lex.rawLineColumn(startIndex);
let end = lex.rawLineColumn(endIndex);
let positionStr =
"From line " + (start[0] + 1) + " column " + (start[1] + 1) +
" to line " + (end[0] + 1) + " column " + (end[1] + 1) + "\n";
super(positionStr + message);
// We have `lexPart`, Why still exists `rawPart`: because this error may happen
// in middle rounds of lex initialization, at which time the lex is not the final
// well-formed lex. So in that case we better use `rawPart` in test code. Test
// shouldn't check middle round lex, otherwise the test code is confusing.
this.rawPart = [startIndex, endIndex];
this.rawStart = start;
this.rawEnd = end;
this.lexPart = lexPart;
}
}
export class PunctuationPairError extends SyntaxError {
constructor(lexPart) {
super(lexPart, "(...), [...], {...}, <<...>> or \"...\" pair mismatch.");
}
}
export class IndentError extends SyntaxError {
constructor(lexPart) {
super(lexPart, "Indent error.");
}
}
export class MatchNoBodyError extends SyntaxError {
constructor(lexPart) {
super(lexPart, "`match` must have body represented by indents (or `<<` and `>>`).");
}
}
// If a token doesn't occupy any length of the raw input, then `rawStartIndex` and `rawEndIndex`
// should be undefined.
export class Token {
constructor(value) {
if (value !== undefined) {
this.value = value;
}
this.style = 0;
this._originalTokens = null;
}
start(index) {
this.rawStartIndex = index;
return this;
}
end(index) {
this.rawEndIndex = index;
return this;
}
copyPositionFrom(token) {
return this.start(token.rawStartIndex).end(token.rawEndIndex);
}
// Create a new instance with the same type, position, style and original tokens,
// but different value, returning the new instance.
replaceWith(value) {
return new this.constructor(value)
.copyPositionFrom(this)
.addStyle(this.style)
.setOriginalTokens(this._originalTokens);
}
addStyle(style) {
this.style |= style;
return this;
}
setOriginalTokens(tokens) {
if (tokens === null) {
this._originalTokens = null;
}
else {
let r = [];
let traverse = tokens => {
tokens.forEach(token => {
if (token._originalTokens !== null) {
traverse(token._originalTokens);
}
else {
r.push(token);
}
});
};
traverse(tokens);
this._originalTokens = r;
}
return this;
}
getOriginalTokens() {
return this._originalTokens;
}
}
export class NormalToken extends Token {} NormalToken.canBeCalleeEnd = true;
export class VersionDirective extends Token {}
export class Indent extends Token {}
export class BlockStart extends Token {}
export class BlockEnd extends Token {}
export class ClassStart extends Token {}
export class ClassEnd extends Token {}
export class Semicolon extends Token {}
export class Chevron extends Token {}
export class LeftChevron extends Chevron {}
export class RightChevron extends Chevron {}
export class Parenthesis extends Token {}
export class LeftParenthesis extends Parenthesis {}
export class NormalLeftParenthesis extends LeftParenthesis {}
export class CallLeftParenthesis extends LeftParenthesis {}
export class PseudoCallLeftParenthesis extends LeftParenthesis {}
export class RightParenthesis extends Parenthesis {}
export class NormalRightParenthesis extends RightParenthesis {}
export class CallRightParenthesis extends RightParenthesis {}
export class PseudoCallRightParenthesis extends RightParenthesis {}
export class Bracket extends Token {}
export class LeftBracket extends Bracket {}
export class NormalLeftBracket extends LeftBracket {}
export class CallLeftBracket extends LeftBracket {}
export class RightBracket extends Bracket {}
export class NormalRightBracket extends RightBracket {}
export class CallRightBracket extends RightBracket {}
export class Brace extends Token {}
export class LeftBrace extends Brace {}
export class NormalLeftBrace extends LeftBrace {}
export class CallLeftBrace extends LeftBrace {}
export class RightBrace extends Brace {}
export class NormalRightBrace extends RightBrace {}
export class CallRightBrace extends RightBrace {}
export class SpaceCall extends Token {}
export class Comma extends Token {} Comma.isJoint = true;
export class Dot extends Token {} Dot.isJoint = true;
export class Colon extends Token {} Colon.isJoint = true;
export class Equal extends Token {} Equal.isJoint = true;
export class NotEqual extends Token {} NotEqual.isJoint = true;
export class LessThan extends Token {} LessThan.isJoint = true;
export class GreaterThan extends Token {} GreaterThan.isJoint = true;
export class LessThanOrEqual extends Token {} LessThanOrEqual.isJoint = true;
export class GreaterThanOrEqual extends Token {} GreaterThanOrEqual.isJoint = true;
export class Plus extends Token {} Plus.isJoint = true;
export class Minus extends Token {} Minus.isJoint = true;
export class Times extends Token {} Times.isJoint = true;
export class Over extends Token {} Over.isJoint = true;
export class Power extends Token {} Power.isJoint = true;
export class Positive extends Token {}
export class Negative extends Token {}
export class Num extends Token {} Num.canBeCalleeEnd = true;
export class Str extends Token {} Str.canBeCalleeEnd = true;
export class InlineNormalString extends Token {} InlineNormalString.isInline = true;
export class FormattedNormalString extends Token {} FormattedNormalString.isInline = false;
export class InlineVerbatimString extends Token {} InlineVerbatimString.isInline = true;
export class FormattedVerbatimString extends Token {} FormattedVerbatimString.isInline = false;
export class InlineRegex extends Token {} InlineRegex.isInline = true;
export class FormattedRegex extends Token {} FormattedRegex.isInline = false;
export class InlineJs extends Token {} InlineJs.isInline = true;
export class FormattedJs extends Token {} FormattedJs.isInline = false;
export class InlineComment extends Token {}
export class FormattedComment extends Token {}
export class DashFunction extends Token {}
export class ArrowFunction extends Token {} ArrowFunction.expressionStartForbidden = true;
export class DiamondFunction extends Token {}
export class Arg extends Token {} Arg.canBeCalleeEnd = true;
export class NormalVariant extends Token {} NormalVariant.isJoint = true;
export class FunctionVariant extends Token {}
FunctionVariant.canBeCalleeEnd = true;
FunctionVariant.expressionStartForbidden = true;
export class Join extends Token {}
export class Pipe extends Token {} Pipe.isJoint = true;
export class FatDot extends Token {} FatDot.isJoint = true;
export class DotDot extends Token {} DotDot.isJoint = true;
export class PostQuote extends Token {}
export class Stick extends Token {}
export class Question extends Token {}
export class Spread extends Token {} Spread.expressionStartForbidden = true;
export class Keyword extends Token {}
export class Above extends Keyword {}
export class And extends Keyword {} And.isJoint = true;
export class As extends Keyword {} As.isJoint = true;
export class Catch extends Keyword {} Catch.expressionStartForbidden = true;
export class Class extends Keyword {}
export class Delete extends Keyword {}
Delete.isCommand = true;
Delete.expressionStartForbidden = true;
export class Do extends Keyword {}
export class Else extends Keyword {} Else.expressionStartForbidden = true;
export class Export extends Keyword {}
Export.isCommand = true;
Export.expressionStartForbidden = true;
export class False extends Keyword {}
export class Finally extends Keyword {} Finally.expressionStartForbidden = true;
export class Fun extends Keyword {} Fun.canBeCalleeEnd = true;
export class If extends Keyword {}
export class Ifnull extends Keyword {} Ifnull.isJoint = true;
export class Ifvoid extends Keyword {} Ifvoid.isJoint = true;
export class Import extends Keyword {}
export class In extends Keyword {} In.isJoint = true;
export class Is extends Keyword {} Is.isJoint = true;
export class Isnt extends Keyword {} Isnt.isJoint = true;
export class Match extends Keyword {}
export class Me extends Keyword {}
export class ClassMe extends Keyword {} ClassMe.canBeCalleeEnd = true;
export class Mod extends Keyword {} Mod.isJoint = true;
export class New extends Keyword {}
export class Nonew extends Keyword {}
export class Not extends Keyword {}
export class Null extends Keyword {}
export class Or extends Keyword {} Or.isJoint = true;
export class Pause extends Keyword {}
Pause.isCommand = true;
Pause.expressionStartForbidden = true;
export class Rem extends Keyword {} Rem.isJoint = true;
export class Self extends Keyword {} Self.canBeCalleeEnd = true;
export class Super extends Keyword {} Super.canBeCalleeEnd = true;
export class Then extends Keyword {} Then.expressionStartForbidden = true;
export class Throw extends Keyword {}
Throw.isCommand = true;
Throw.expressionStartForbidden = true;
export class True extends Keyword {}
export class Try extends Keyword {}
export class Void extends Keyword {}
export class ExportAs extends Token {} ExportAs.isJoint = true;
export class NotIn extends Token {} NotIn.isJoint = true;