1840 lines
49 KiB
TypeScript

/**
* Abstract syntax tree representing a source file once parsed.
* @module ast
*//***/
import {
CommonFlags,
PATH_DELIMITER,
STATIC_DELIMITER,
INSTANCE_DELIMITER,
LIBRARY_PREFIX
} from "./program";
import {
Token,
Tokenizer,
Range
} from "./tokenizer";
import {
normalizePath,
resolvePath
} from "./util";
export { Token, Range };
/** Indicates the kind of a node. */
export enum NodeKind {
SOURCE,
// types
TYPE,
TYPEPARAMETER,
PARAMETER,
SIGNATURE,
// expressions
IDENTIFIER,
ASSERTION,
BINARY,
CALL,
COMMA,
ELEMENTACCESS,
FALSE,
FUNCTION,
LITERAL,
NEW,
NULL,
PARENTHESIZED,
PROPERTYACCESS,
TERNARY,
SUPER,
THIS,
TRUE,
CONSTRUCTOR,
UNARYPOSTFIX,
UNARYPREFIX,
// statements
BLOCK,
BREAK,
CONTINUE,
DO,
EMPTY,
EXPORT,
EXPORTIMPORT,
EXPRESSION,
FOR,
IF,
IMPORT,
RETURN,
SWITCH,
THROW,
TRY,
VARIABLE,
VOID,
WHILE,
// declaration statements
CLASSDECLARATION,
ENUMDECLARATION,
ENUMVALUEDECLARATION,
FIELDDECLARATION,
FUNCTIONDECLARATION,
IMPORTDECLARATION,
INTERFACEDECLARATION,
METHODDECLARATION,
NAMESPACEDECLARATION,
TYPEDECLARATION,
VARIABLEDECLARATION,
// special
DECORATOR,
EXPORTMEMBER,
SWITCHCASE,
COMMENT
}
/** Base class of all nodes. */
export abstract class Node {
/** Node kind indicator. */
kind: NodeKind;
/** Source range. */
range: Range;
/** Parent node. */
parent: Node | null = null;
/** Common flags indicating specific traits. */
flags: CommonFlags = CommonFlags.NONE;
/** Tests if this node has a specific flag or flags. */
is(flag: CommonFlags): bool { return (this.flags & flag) == flag; }
/** Tests if this node has one of the specified flags. */
isAny(flag: CommonFlags): bool { return (this.flags & flag) != 0; }
/** Sets a specific flag or flags. */
set(flag: CommonFlags): void { this.flags |= flag; }
// types
static createType(
name: IdentifierExpression,
typeArguments: CommonTypeNode[] | null,
isNullable: bool,
range: Range
): TypeNode {
var type = new TypeNode();
type.range = range;
type.name = name; name.parent = type;
type.typeArguments = typeArguments; if (typeArguments) setParent(typeArguments, type);
type.isNullable = isNullable;
return type;
}
static createOmittedType(
range: Range
): TypeNode {
return Node.createType(
Node.createIdentifierExpression("", range),
null,
false,
range
);
}
static createTypeParameter(
name: IdentifierExpression,
extendsType: TypeNode | null,
range: Range
): TypeParameterNode {
var elem = new TypeParameterNode();
elem.range = range;
elem.name = name; name.parent = elem;
elem.extendsType = extendsType; if (extendsType) extendsType.parent = elem;
return elem;
}
static createParameter(
name: IdentifierExpression,
type: CommonTypeNode,
initializer: Expression | null,
kind: ParameterKind,
range: Range
): ParameterNode {
var elem = new ParameterNode();
elem.range = range;
elem.name = name; name.parent = elem;
elem.type = type; if (type) type.parent = elem;
elem.initializer = initializer; if (initializer) initializer.parent = elem;
elem.parameterKind = kind;
return elem;
}
static createSignature(
parameters: ParameterNode[],
returnType: CommonTypeNode,
explicitThisType: TypeNode | null,
isNullable: bool,
range: Range
): SignatureNode {
var sig = new SignatureNode();
sig.range = range;
sig.parameterTypes = parameters; setParent(parameters, sig);
sig.returnType = returnType; returnType.parent = sig;
sig.explicitThisType = explicitThisType; if (explicitThisType) explicitThisType.parent = sig;
sig.isNullable = isNullable;
return sig;
}
// special
static createDecorator(
expression: Expression,
args: Expression[] | null,
range: Range
): DecoratorNode {
var stmt = new DecoratorNode();
stmt.range = range;
stmt.name = expression; expression.parent = stmt;
stmt.arguments = args; if (args) setParent(args, stmt);
if (expression.kind == NodeKind.IDENTIFIER) {
switch ((<IdentifierExpression>expression).text) {
case "global": {
stmt.decoratorKind = DecoratorKind.GLOBAL;
break;
}
case "operator": {
stmt.decoratorKind = DecoratorKind.OPERATOR;
break;
}
case "unmanaged": {
stmt.decoratorKind = DecoratorKind.UNMANAGED;
break;
}
case "offset": {
stmt.decoratorKind = DecoratorKind.OFFSET;
break;
}
default: {
stmt.decoratorKind = DecoratorKind.CUSTOM;
break;
}
}
} else {
stmt.decoratorKind = DecoratorKind.CUSTOM;
}
return stmt;
}
static createComment(
text: string,
kind: CommentKind,
range: Range
): CommentNode {
var node = new CommentNode();
node.range = range;
node.commentKind = kind;
node.text = text;
return node;
}
// expressions
static createIdentifierExpression(
name: string,
range: Range
): IdentifierExpression {
var expr = new IdentifierExpression();
expr.range = range;
expr.text = name;
return expr;
}
static createEmptyIdentifierExpression(
range: Range
): IdentifierExpression {
var expr = new IdentifierExpression();
expr.range = range;
expr.text = "";
return expr;
}
static createArrayLiteralExpression(
elements: (Expression | null)[],
range: Range
): ArrayLiteralExpression {
var expr = new ArrayLiteralExpression();
expr.range = range;
expr.elementExpressions = elements; setParentIfNotNull(elements, expr);
return expr;
}
static createAssertionExpression(
assertionKind: AssertionKind,
expression: Expression,
toType: CommonTypeNode,
range: Range
): AssertionExpression {
var expr = new AssertionExpression();
expr.range = range;
expr.assertionKind = assertionKind;
expr.expression = expression; expression.parent = expr;
expr.toType = toType; toType.parent = expr;
return expr;
}
static createBinaryExpression(
operator: Token,
left: Expression,
right: Expression,
range: Range
): BinaryExpression {
var expr = new BinaryExpression();
expr.range = range;
expr.operator = operator;
expr.left = left; left.parent = expr;
expr.right = right; right.parent = expr;
return expr;
}
static createCallExpression(
expression: Expression,
typeArgs: CommonTypeNode[] | null,
args: Expression[],
range: Range
): CallExpression {
var expr = new CallExpression();
expr.range = range;
expr.expression = expression; expression.parent = expr;
expr.typeArguments = typeArgs; if (typeArgs) setParent(typeArgs, expr);
expr.arguments = args; setParent(args, expr);
return expr;
}
static createCommaExpression(
expressions: Expression[],
range: Range
): CommaExpression {
var expr = new CommaExpression();
expr.range = range;
expr.expressions = expressions; setParent(expressions, expr);
return expr;
}
static createConstructorExpression(
range: Range
): ConstructorExpression {
var expr = new ConstructorExpression();
expr.range = range;
return expr;
}
static createElementAccessExpression(
expression: Expression,
element: Expression,
range: Range
): ElementAccessExpression {
var expr = new ElementAccessExpression();
expr.range = range;
expr.expression = expression; expression.parent = expr;
expr.elementExpression = element; element.parent = expr;
return expr;
}
static createFalseExpression(
range: Range
): FalseExpression {
var expr = new FalseExpression();
expr.range = range;
return expr;
}
static createFloatLiteralExpression(
value: f64,
range: Range
): FloatLiteralExpression {
var expr = new FloatLiteralExpression();
expr.range = range;
expr.value = value;
return expr;
}
static createFunctionExpression(
declaration: FunctionDeclaration
): FunctionExpression {
var expr = new FunctionExpression();
expr.flags = declaration.flags & CommonFlags.ARROW;
expr.range = declaration.range;
expr.declaration = declaration;
return expr;
}
static createIntegerLiteralExpression(
value: I64,
range: Range
): IntegerLiteralExpression {
var expr = new IntegerLiteralExpression();
expr.range = range;
expr.value = value;
return expr;
}
static createNewExpression(
expression: Expression,
typeArgs: CommonTypeNode[] | null,
args: Expression[],
range: Range
): NewExpression {
var expr = new NewExpression();
expr.range = range;
expr.expression = expression; expression.parent = expr;
expr.typeArguments = typeArgs; if (typeArgs) setParent(typeArgs, expr);
expr.arguments = args; setParent(args, expr);
return expr;
}
static createNullExpression(
range: Range
): NullExpression {
var expr = new NullExpression();
expr.range = range;
return expr;
}
static createParenthesizedExpression(
expression: Expression,
range: Range
): ParenthesizedExpression {
var expr = new ParenthesizedExpression();
expr.range = range;
expr.expression = expression; expression.parent = expr;
return expr;
}
static createPropertyAccessExpression(
expression: Expression,
property: IdentifierExpression,
range: Range
): PropertyAccessExpression {
var expr = new PropertyAccessExpression();
expr.range = range;
expr.expression = expression; expression.parent = expr;
expr.property = property; property.parent = expr;
return expr;
}
static createRegexpLiteralExpression(
pattern: string,
flags: string,
range: Range
): RegexpLiteralExpression {
var expr = new RegexpLiteralExpression();
expr.range = range;
expr.pattern = pattern;
expr.patternFlags = flags;
return expr;
}
static createTernaryExpression(
condition: Expression,
ifThen: Expression,
ifElse: Expression,
range: Range
): TernaryExpression {
var expr = new TernaryExpression();
expr.range = range;
expr.condition = condition; condition.parent = expr;
expr.ifThen = ifThen; ifThen.parent = expr;
expr.ifElse = ifElse; ifElse.parent = expr;
return expr;
}
static createStringLiteralExpression(
value: string,
range: Range
): StringLiteralExpression {
var expr = new StringLiteralExpression();
expr.range = range;
expr.value = value;
return expr;
}
static createSuperExpression(
range: Range
): SuperExpression {
var expr = new SuperExpression();
expr.range = range;
return expr;
}
static createThisExpression(
range: Range
): ThisExpression {
var expr = new ThisExpression();
expr.range = range;
return expr;
}
static createTrueExpression(
range: Range
): TrueExpression {
var expr = new TrueExpression();
expr.range = range;
return expr;
}
static createUnaryPostfixExpression(
operator: Token,
operand: Expression,
range: Range
): UnaryPostfixExpression {
var expr = new UnaryPostfixExpression();
expr.range = range;
expr.operator = operator;
expr.operand = operand; operand.parent = expr;
return expr;
}
static createUnaryPrefixExpression(
operator: Token,
operand: Expression,
range: Range
): UnaryPrefixExpression {
var expr = new UnaryPrefixExpression();
expr.range = range;
expr.operator = operator;
expr.operand = operand; operand.parent = expr;
return expr;
}
// statements
static createBlockStatement(
statements: Statement[],
range: Range
): BlockStatement {
var stmt = new BlockStatement();
stmt.range = range;
stmt.statements = statements; setParent(statements, stmt);
return stmt;
}
static createBreakStatement(
label: IdentifierExpression | null,
range: Range
): BreakStatement {
var stmt = new BreakStatement();
stmt.range = range;
stmt.label = label; if (label) label.parent = stmt;
return stmt;
}
static createClassDeclaration(
identifier: IdentifierExpression,
typeParameters: TypeParameterNode[],
extendsType: TypeNode | null, // can't be a function
implementsTypes: TypeNode[], // can't be a function
members: DeclarationStatement[],
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): ClassDeclaration {
var stmt = new ClassDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = identifier; identifier.parent = stmt;
stmt.typeParameters = typeParameters; setParent(typeParameters, stmt);
stmt.extendsType = extendsType; if (extendsType) extendsType.parent = stmt;
stmt.implementsTypes = implementsTypes; setParent(implementsTypes, stmt);
stmt.members = members; setParent(members, stmt);
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createContinueStatement(
label: IdentifierExpression | null,
range: Range
): ContinueStatement {
var stmt = new ContinueStatement();
stmt.range = range;
stmt.label = label; if (label) label.parent = stmt;
return stmt;
}
static createDoStatement(
statement: Statement,
condition: Expression,
range: Range
): DoStatement {
var stmt = new DoStatement();
stmt.range = range;
stmt.statement = statement; statement.parent = stmt;
stmt.condition = condition; condition.parent = stmt;
return stmt;
}
static createEmptyStatement(
range: Range
): EmptyStatement {
var stmt = new EmptyStatement();
stmt.range = range;
return stmt;
}
static createEnumDeclaration(
name: IdentifierExpression,
members: EnumValueDeclaration[],
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): EnumDeclaration {
var stmt = new EnumDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.values = members; setParent(members, stmt);
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createEnumValueDeclaration(
name: IdentifierExpression,
value: Expression | null,
flags: CommonFlags,
range: Range
): EnumValueDeclaration {
var stmt = new EnumValueDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.value = value; if (value) value.parent = stmt;
return stmt;
}
static createExportStatement(
members: ExportMember[],
path: StringLiteralExpression | null,
flags: CommonFlags,
range: Range
): ExportStatement {
var stmt = new ExportStatement();
stmt.range = range;
stmt.flags = flags;
stmt.members = members; setParent(members, stmt);
stmt.path = path;
if (path) {
let normalizedPath = normalizePath(path.value);
if (path.value.startsWith(".")) { // relative
stmt.normalizedPath = resolvePath(
normalizedPath,
range.source.normalizedPath
);
} else { // absolute
stmt.normalizedPath = normalizedPath;
}
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
} else {
stmt.normalizedPath = null;
stmt.internalPath = null;
}
return stmt;
}
static createExportImportStatement(
name: IdentifierExpression,
externalName: IdentifierExpression,
range: Range
): ExportImportStatement {
var stmt = new ExportImportStatement();
stmt.range = range;
stmt.name = name; name.parent = stmt;
stmt.externalName = externalName; externalName.parent = stmt;
return stmt;
}
static createExportMember(
name: IdentifierExpression,
externalName: IdentifierExpression | null,
range: Range
): ExportMember {
var elem = new ExportMember();
elem.range = range;
elem.name = name; name.parent = elem;
if (!externalName) {
externalName = name;
} else {
externalName.parent = elem;
}
elem.externalName = externalName;
return elem;
}
static createExpressionStatement(
expression: Expression
): ExpressionStatement {
var stmt = new ExpressionStatement();
stmt.range = expression.range;
stmt.expression = expression; expression.parent = stmt;
return stmt;
}
static createIfStatement(
condition: Expression,
ifTrue: Statement,
ifFalse: Statement | null,
range: Range
): IfStatement {
var stmt = new IfStatement();
stmt.range = range;
stmt.condition = condition; condition.parent = stmt;
stmt.ifTrue = ifTrue; ifTrue.parent = stmt;
stmt.ifFalse = ifFalse; if (ifFalse) ifFalse.parent = stmt;
return stmt;
}
static createImportStatement(
decls: ImportDeclaration[] | null,
path: StringLiteralExpression,
range: Range
): ImportStatement {
var stmt = new ImportStatement();
stmt.range = range;
stmt.declarations = decls; if (decls) setParent(decls, stmt);
stmt.namespaceName = null;
stmt.path = path;
var normalizedPath = normalizePath(path.value);
if (path.value.startsWith(".")) { // relative in project
stmt.normalizedPath = resolvePath(
normalizedPath,
range.source.normalizedPath
);
} else { // absolute in library
if (!normalizedPath.startsWith(LIBRARY_PREFIX)) {
normalizedPath = LIBRARY_PREFIX + normalizedPath;
}
stmt.normalizedPath = normalizedPath;
}
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
return stmt;
}
static createImportStatementWithWildcard(
identifier: IdentifierExpression,
path: StringLiteralExpression,
range: Range
): ImportStatement {
var stmt = new ImportStatement();
stmt.range = range;
stmt.declarations = null;
stmt.namespaceName = identifier;
stmt.path = path;
stmt.normalizedPath = resolvePath(
normalizePath(path.value),
range.source.normalizedPath
);
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
return stmt;
}
static createImportDeclaration(
externalName: IdentifierExpression,
name: IdentifierExpression | null,
range: Range
): ImportDeclaration {
var elem = new ImportDeclaration();
elem.range = range;
elem.externalName = externalName; externalName.parent = elem;
if (!name) {
name = externalName;
} else {
name.parent = elem;
}
elem.name = name;
return elem;
}
static createInterfaceDeclaration(
name: IdentifierExpression,
extendsType: TypeNode | null, // can't be a function
members: DeclarationStatement[],
flags: CommonFlags,
range: Range
): InterfaceDeclaration {
var stmt = new InterfaceDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.extendsType = extendsType; if (extendsType) extendsType.parent = stmt;
stmt.members = members; setParent(members, stmt);
return stmt;
}
static createFieldDeclaration(
name: IdentifierExpression,
type: CommonTypeNode | null,
initializer: Expression | null,
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): FieldDeclaration {
var stmt = new FieldDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.type = type; if (type) type.parent = stmt;
stmt.initializer = initializer; if (initializer) initializer.parent = stmt;
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createForStatement(
initializer: Statement | null,
condition: Expression | null,
incrementor: Expression | null,
statement: Statement,
range: Range
): ForStatement {
var stmt = new ForStatement();
stmt.range = range;
stmt.initializer = initializer; if (initializer) initializer.parent = stmt;
stmt.condition = condition; if (condition) condition.parent = stmt;
stmt.incrementor = incrementor; if (incrementor) incrementor.parent = stmt;
stmt.statement = statement; statement.parent = stmt;
return stmt;
}
static createFunctionDeclaration(
name: IdentifierExpression,
typeParameters: TypeParameterNode[] | null,
signature: SignatureNode,
body: Statement | null,
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): FunctionDeclaration {
var stmt = new FunctionDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.typeParameters = typeParameters; if (typeParameters) setParent(typeParameters, stmt);
stmt.signature = signature; signature.parent = stmt;
stmt.body = body; if (body) body.parent = stmt;
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createMethodDeclaration(
name: IdentifierExpression,
typeParameters: TypeParameterNode[] | null,
signature: SignatureNode,
body: Statement | null,
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): MethodDeclaration {
var stmt = new MethodDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.typeParameters = typeParameters; if (typeParameters) setParent(typeParameters, stmt);
stmt.signature = signature; signature.parent = stmt;
stmt.body = body; if (body) body.parent = stmt;
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createNamespaceDeclaration(
name: IdentifierExpression,
members: Statement[],
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): NamespaceDeclaration {
var stmt = new NamespaceDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.members = members; setParent(members, stmt);
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createReturnStatement(
value: Expression | null,
range: Range
): ReturnStatement {
var stmt = new ReturnStatement();
stmt.range = range;
stmt.value = value; if (value) value.parent = stmt;
return stmt;
}
static createSwitchStatement(
condition: Expression,
cases: SwitchCase[],
range: Range
): SwitchStatement {
var stmt = new SwitchStatement();
stmt.range = range;
stmt.condition = condition; condition.parent = stmt;
stmt.cases = cases; setParent(cases, stmt);
return stmt;
}
static createSwitchCase(
label: Expression | null,
statements: Statement[],
range: Range
): SwitchCase {
var elem = new SwitchCase();
elem.range = range;
elem.label = label; if (label) label.parent = elem;
elem.statements = statements; setParent(statements, elem);
return elem;
}
static createThrowStatement(
value: Expression,
range: Range
): ThrowStatement {
var stmt = new ThrowStatement();
stmt.range = range;
stmt.value = value; value.parent = stmt;
return stmt;
}
static createTryStatement(
statements: Statement[],
catchVariable: IdentifierExpression | null,
catchStatements: Statement[] | null,
finallyStatements: Statement[] | null,
range: Range
): TryStatement {
var stmt = new TryStatement();
stmt.range = range;
stmt.statements = statements; setParent(statements, stmt);
stmt.catchVariable = catchVariable;
if (catchVariable) catchVariable.parent = stmt;
stmt.catchStatements = catchStatements;
if (catchStatements) setParent(catchStatements, stmt);
stmt.finallyStatements = finallyStatements;
if (finallyStatements) setParent(finallyStatements, stmt);
return stmt;
}
static createTypeDeclaration(
name: IdentifierExpression,
typeParameters: TypeParameterNode[] | null,
alias: CommonTypeNode,
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): TypeDeclaration {
var stmt = new TypeDeclaration();
stmt.range = range;
stmt.flags = flags;
stmt.name = name; name.parent = stmt;
stmt.typeParameters = typeParameters; if (typeParameters) setParent(typeParameters, stmt);
stmt.type = alias; alias.parent = stmt;
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createVariableStatement(
declarations: VariableDeclaration[],
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): VariableStatement {
var stmt = new VariableStatement();
stmt.range = range;
stmt.flags = flags;
stmt.declarations = declarations; setParent(declarations, stmt);
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
}
static createVariableDeclaration(
name: IdentifierExpression,
type: CommonTypeNode | null,
initializer: Expression | null,
decorators: DecoratorNode[] | null,
flags: CommonFlags,
range: Range
): VariableDeclaration {
var elem = new VariableDeclaration();
elem.range = range;
elem.flags = flags;
elem.name = name; name.parent = elem;
elem.type = type; if (type) type.parent = elem;
elem.initializer = initializer; if (initializer) initializer.parent = elem;
elem.decorators = decorators; // inherited
return elem;
}
static createVoidStatement(
expression: Expression,
range: Range
): VoidStatement {
var stmt = new VoidStatement();
stmt.range = range;
stmt.expression = expression;
return stmt;
}
static createWhileStatement(
condition: Expression,
statement: Statement,
range: Range
): WhileStatement {
var stmt = new WhileStatement();
stmt.range = range;
stmt.condition = condition; condition.parent = stmt;
stmt.statement = statement; statement.parent = stmt;
return stmt;
}
}
// types
export abstract class CommonTypeNode extends Node {
// kind varies
/** Whether nullable or not. */
isNullable: bool;
}
/** Represents a type annotation. */
export class TypeNode extends CommonTypeNode {
kind = NodeKind.TYPE;
/** Identifier reference. */
name: IdentifierExpression;
/** Type argument references. */
typeArguments: CommonTypeNode[] | null;
}
/** Represents a type parameter. */
export class TypeParameterNode extends Node {
kind = NodeKind.TYPEPARAMETER;
/** Identifier reference. */
name: IdentifierExpression;
/** Extended type reference, if any. */
extendsType: TypeNode | null; // can't be a function
}
/** Represents the kind of a parameter. */
export enum ParameterKind {
/** No specific flags. */
DEFAULT,
/** Is an optional parameter. */
OPTIONAL,
/** Is a rest parameter. */
REST
}
/** Represents a function parameter. */
export class ParameterNode extends Node {
kind = NodeKind.PARAMETER;
/** Parameter kind. */
parameterKind: ParameterKind;
/** Parameter name. */
name: IdentifierExpression;
/** Parameter type. */
type: CommonTypeNode;
/** Initializer expression, if present. */
initializer: Expression | null;
}
/** Represents a function signature. */
export class SignatureNode extends CommonTypeNode {
kind = NodeKind.SIGNATURE;
/** Accepted parameters. */
parameterTypes: ParameterNode[];
/** Return type. */
returnType: CommonTypeNode;
/** Explicitly provided this type, if any. */
explicitThisType: TypeNode | null; // can't be a function
}
// special
/** Built-in decorator kinds. */
export enum DecoratorKind {
CUSTOM,
GLOBAL,
OPERATOR,
UNMANAGED,
OFFSET
}
/** Represents a decorator. */
export class DecoratorNode extends Node {
kind = NodeKind.DECORATOR;
/** Built-in kind, if applicable. */
decoratorKind: DecoratorKind;
/** Name expression. */
name: Expression;
/** Argument expressions. */
arguments: Expression[] | null;
}
/** Comment kinds. */
export enum CommentKind {
/** Line comment. */
LINE,
/** Triple-slash comment. */
TRIPLE,
/** Block comment. */
BLOCK
}
/** Represents a comment. */
export class CommentNode extends Node {
kind = NodeKind.COMMENT;
/** Comment kind. */
commentKind: CommentKind;
/** Comment text. */
text: string;
}
// expressions
/** Base class of all expression nodes. */
export abstract class Expression extends Node { }
/** Represents an identifier expression. */
export class IdentifierExpression extends Expression {
kind = NodeKind.IDENTIFIER;
/** Textual name. */
text: string;
}
/** Indicates the kind of a literal. */
export enum LiteralKind {
FLOAT,
INTEGER,
STRING,
REGEXP,
ARRAY,
OBJECT
}
/** Base class of all literal expressions. */
export abstract class LiteralExpression extends Expression {
kind = NodeKind.LITERAL;
/** Specific literal kind. */
literalKind: LiteralKind;
}
/** Represents an `[]` literal expression. */
export class ArrayLiteralExpression extends LiteralExpression {
literalKind = LiteralKind.ARRAY;
/** Nested element expressions. */
elementExpressions: (Expression | null)[];
}
/** Indicates the kind of an assertion. */
export enum AssertionKind {
PREFIX,
AS
}
/** Represents an assertion expression. */
export class AssertionExpression extends Expression {
kind = NodeKind.ASSERTION;
/** Specific kind of this assertion. */
assertionKind: AssertionKind;
/** Expression being asserted. */
expression: Expression;
/** Target type. */
toType: CommonTypeNode;
}
/** Represents a binary expression. */
export class BinaryExpression extends Expression {
kind = NodeKind.BINARY;
/** Operator token. */
operator: Token;
/** Left-hand side expression */
left: Expression;
/** Right-hand side expression. */
right: Expression;
}
/** Represents a call expression. */
export class CallExpression extends Expression {
kind = NodeKind.CALL;
/** Called expression. Usually an identifier or property access expression. */
expression: Expression;
/** Provided type arguments. */
typeArguments: CommonTypeNode[] | null;
/** Provided arguments. */
arguments: Expression[];
}
/** Represents a comma expression composed of multiple expressions. */
export class CommaExpression extends Expression {
kind = NodeKind.COMMA;
/** Sequential expressions. */
expressions: Expression[];
}
/** Represents a `constructor` expression. */
export class ConstructorExpression extends IdentifierExpression {
kind = NodeKind.CONSTRUCTOR;
text = "constructor";
}
/** Represents an element access expression, e.g., array access. */
export class ElementAccessExpression extends Expression {
kind = NodeKind.ELEMENTACCESS;
/** Expression being accessed. */
expression: Expression;
/** Element of the expression being accessed. */
elementExpression: Expression;
}
/** Represents a float literal expression. */
export class FloatLiteralExpression extends LiteralExpression {
literalKind = LiteralKind.FLOAT;
/** Float value. */
value: f64;
}
/** Represents a function expression using the 'function' keyword. */
export class FunctionExpression extends Expression {
kind = NodeKind.FUNCTION;
/** Inline function declaration. */
declaration: FunctionDeclaration;
}
/** Represents an integer literal expression. */
export class IntegerLiteralExpression extends LiteralExpression {
literalKind = LiteralKind.INTEGER;
/** Integer value. */
value: I64;
}
/** Represents a `new` expression. Like a call but with its own kind. */
export class NewExpression extends CallExpression {
kind = NodeKind.NEW;
}
/** Represents a `null` expression. */
export class NullExpression extends IdentifierExpression {
kind = NodeKind.NULL;
text = "null";
}
/** Represents a parenthesized expression. */
export class ParenthesizedExpression extends Expression {
kind = NodeKind.PARENTHESIZED;
/** Expression in parenthesis. */
expression: Expression;
}
/** Represents a property access expression. */
export class PropertyAccessExpression extends Expression {
kind = NodeKind.PROPERTYACCESS;
/** Expression being accessed. */
expression: Expression;
/** Property of the expression being accessed. */
property: IdentifierExpression;
}
/** Represents a regular expression literal expression. */
export class RegexpLiteralExpression extends LiteralExpression {
literalKind = LiteralKind.REGEXP;
/** Regular expression pattern. */
pattern: string;
/** Regular expression flags. */
patternFlags: string;
}
/** Represents a ternary expression, i.e., short if notation. */
export class TernaryExpression extends Expression {
kind = NodeKind.TERNARY;
/** Condition expression. */
condition: Expression;
/** Expression executed when condition is `true`. */
ifThen: Expression;
/** Expression executed when condition is `false`. */
ifElse: Expression;
}
/** Represents a string literal expression. */
export class StringLiteralExpression extends LiteralExpression {
literalKind = LiteralKind.STRING;
/** String value without quotes. */
value: string;
}
/** Represents a `super` expression. */
export class SuperExpression extends IdentifierExpression {
kind = NodeKind.SUPER;
text = "super";
}
/** Represents a `this` expression. */
export class ThisExpression extends IdentifierExpression {
kind = NodeKind.THIS;
text = "this";
}
/** Represents a `true` expression. */
export class TrueExpression extends IdentifierExpression {
kind = NodeKind.TRUE;
text = "true";
}
/** Represents a `false` expression. */
export class FalseExpression extends IdentifierExpression {
kind = NodeKind.FALSE;
text = "false";
}
/** Base class of all unary expressions. */
export abstract class UnaryExpression extends Expression {
/** Operator token. */
operator: Token;
/** Operand expression. */
operand: Expression;
}
/** Represents a unary postfix expression, e.g. a postfix increment. */
export class UnaryPostfixExpression extends UnaryExpression {
kind = NodeKind.UNARYPOSTFIX;
}
/** Represents a unary prefix expression, e.g. a negation. */
export class UnaryPrefixExpression extends UnaryExpression {
kind = NodeKind.UNARYPREFIX;
}
// statements
/** Base class of all statement nodes. */
export abstract class Statement extends Node { }
/** Indicates the specific kind of a source. */
export enum SourceKind {
/** Default source. Usually imported from an entry file. */
DEFAULT,
/** Entry file. */
ENTRY,
/** Library file. */
LIBRARY
}
/** A top-level source node. */
export class Source extends Node {
kind = NodeKind.SOURCE;
parent = null;
/** Source kind. */
sourceKind: SourceKind;
/** Normalized path. */
normalizedPath: string;
/** Path used internally. */
internalPath: string;
/** Contained statements. */
statements: Statement[];
/** Full source text. */
text: string;
/** Tokenizer reference. */
tokenizer: Tokenizer | null = null;
/** Source map index. */
debugInfoIndex: i32 = -1;
/** Constructs a new source node. */
constructor(normalizedPath: string, text: string, kind: SourceKind) {
super();
this.sourceKind = kind;
this.normalizedPath = normalizedPath;
this.internalPath = mangleInternalPath(this.normalizedPath);
this.statements = new Array();
this.range = new Range(this, 0, text.length);
this.text = text;
}
/** Tests if this source is an entry file. */
get isEntry(): bool { return this.sourceKind == SourceKind.ENTRY; }
/** Tests if this source is a stdlib file. */
get isLibrary(): bool { return this.sourceKind == SourceKind.LIBRARY; }
}
/** Base class of all declaration statements. */
export abstract class DeclarationStatement extends Statement {
/** Simple name being declared. */
name: IdentifierExpression;
/** Array of decorators. */
decorators: DecoratorNode[] | null = null;
protected cachedProgramLevelInternalName: string | null = null;
protected cachedFileLevelInternalName: string | null = null;
/** Gets the mangled program-level internal name of this declaration. */
get programLevelInternalName(): string {
if (!this.cachedProgramLevelInternalName) {
this.cachedProgramLevelInternalName = mangleInternalName(this, true);
}
return this.cachedProgramLevelInternalName;
}
/** Gets the mangled file-level internal name of this declaration. */
get fileLevelInternalName(): string {
if (!this.cachedFileLevelInternalName) {
this.cachedFileLevelInternalName = mangleInternalName(this, false);
}
return this.cachedFileLevelInternalName;
}
/** Tests if this is a top-level declaration within its source file. */
get isTopLevel(): bool {
var parent = this.parent;
if (!parent) {
return false;
}
if (parent.kind == NodeKind.VARIABLE && !(parent = parent.parent)) {
return false;
}
return parent.kind == NodeKind.SOURCE;
}
/** Tests if this declaration is a top-level export within its source file. */
get isTopLevelExport(): bool {
var parent = this.parent;
if (!parent || (parent.kind == NodeKind.VARIABLE && !(parent = parent.parent))) {
return false;
}
if (parent.kind == NodeKind.NAMESPACEDECLARATION) {
return this.is(CommonFlags.EXPORT) && (<NamespaceDeclaration>parent).isTopLevelExport;
}
if (parent.kind == NodeKind.CLASSDECLARATION) {
return this.is(CommonFlags.STATIC) && (<ClassDeclaration>parent).isTopLevelExport;
}
return parent.kind == NodeKind.SOURCE && this.is(CommonFlags.EXPORT);
}
/** Tests if this declaration needs an explicit export. */
needsExplicitExport(member: ExportMember): bool {
// This is necessary because module-level exports are automatically created
// for top level declarations of all sorts. This function essentially tests
// that there isn't a otherwise duplicate top-level export already.
return (
member.name.text != member.externalName.text || // if aliased
this.range.source != member.range.source || // if a re-export
!this.isTopLevelExport // if not top-level
);
}
}
/** Base class of all variable-like declaration statements. */
export abstract class VariableLikeDeclarationStatement extends DeclarationStatement {
/** Variable type. */
type: CommonTypeNode | null;
/** Variable initializer. */
initializer: Expression | null;
}
/** Represents a block statement. */
export class BlockStatement extends Statement {
kind = NodeKind.BLOCK;
/** Contained statements. */
statements: Statement[];
}
/** Represents a `break` statement. */
export class BreakStatement extends Statement {
kind = NodeKind.BREAK;
/** Target label, if applicable. */
label: IdentifierExpression | null;
}
/** Represents a `class` declaration. */
export class ClassDeclaration extends DeclarationStatement {
kind = NodeKind.CLASSDECLARATION;
/** Accepted type parameters. */
typeParameters: TypeParameterNode[];
/** Base class type being extended. */
extendsType: TypeNode | null; // can't be a function
/** Interface types being implemented. */
implementsTypes: TypeNode[]; // can't be a function
/** Class member declarations. */
members: DeclarationStatement[];
get isGeneric(): bool {
var typeParameters = this.typeParameters;
return typeParameters != null && typeParameters.length > 0;
}
}
/** Represents a `continue` statement. */
export class ContinueStatement extends Statement {
kind = NodeKind.CONTINUE;
/** Target label, if applicable. */
label: IdentifierExpression | null;
}
/** Represents a `do` statement. */
export class DoStatement extends Statement {
kind = NodeKind.DO;
/** Statement being looped over. */
statement: Statement;
/** Condition when to repeat. */
condition: Expression;
}
/** Represents an empty statement, i.e., a semicolon terminating nothing. */
export class EmptyStatement extends Statement {
kind = NodeKind.EMPTY;
}
/** Represents an `enum` declaration. */
export class EnumDeclaration extends DeclarationStatement {
kind = NodeKind.ENUMDECLARATION;
/** Enum value declarations. */
values: EnumValueDeclaration[];
}
/** Represents a value of an `enum` declaration. */
export class EnumValueDeclaration extends DeclarationStatement {
kind = NodeKind.ENUMVALUEDECLARATION;
// name is inherited
/** Value expression. */
value: Expression | null;
}
/** Represents an `export import` statement of an interface. */
export class ExportImportStatement extends Node {
kind = NodeKind.EXPORTIMPORT;
/** Identifier being imported. */
name: IdentifierExpression;
/** Identifier being exported. */
externalName: IdentifierExpression;
}
/** Represents a member of an `export` statement. */
export class ExportMember extends Node {
kind = NodeKind.EXPORTMEMBER;
/** Identifier being exported. */
name: IdentifierExpression;
/** Identifier seen when imported again. */
externalName: IdentifierExpression;
}
/** Represents an `export` statement. */
export class ExportStatement extends Statement {
kind = NodeKind.EXPORT;
/** Array of members. */
members: ExportMember[];
/** Path being exported from, if applicable. */
path: StringLiteralExpression | null;
/** Normalized path, if `path` is set. */
normalizedPath: string | null;
/** Mangled internal path being referenced, if `path` is set. */
internalPath: string | null;
}
/** Represents an expression that is used as a statement. */
export class ExpressionStatement extends Statement {
kind = NodeKind.EXPRESSION;
/** Expression being used as a statement.*/
expression: Expression;
}
/** Represents a field declaration within a `class`. */
export class FieldDeclaration extends VariableLikeDeclarationStatement {
kind = NodeKind.FIELDDECLARATION;
}
/** Represents a `for` statement. */
export class ForStatement extends Statement {
kind = NodeKind.FOR;
/**
* Initializer statement, if present.
* Either a {@link VariableStatement} or {@link ExpressionStatement}.
*/
initializer: Statement | null;
/** Condition expression, if present. */
condition: Expression | null;
/** Incrementor expression, if present. */
incrementor: Expression | null;
/** Statement being looped over. */
statement: Statement;
}
/** Represents a `function` declaration. */
export class FunctionDeclaration extends DeclarationStatement {
kind = NodeKind.FUNCTIONDECLARATION;
/** Type parameters, if any. */
typeParameters: TypeParameterNode[] | null;
/** Function signature. */
signature: SignatureNode;
/** Body statement. Usually a block. */
body: Statement | null;
get isGeneric(): bool {
var typeParameters = this.typeParameters;
return typeParameters != null && typeParameters.length > 0;
}
}
/** Represents an `if` statement. */
export class IfStatement extends Statement {
kind = NodeKind.IF;
/** Condition. */
condition: Expression;
/** Statement executed when condition is `true`. */
ifTrue: Statement;
/** Statement executed when condition is `false`. */
ifFalse: Statement | null;
}
/** Represents an `import` declaration part of an {@link ImportStatement}. */
export class ImportDeclaration extends DeclarationStatement {
kind = NodeKind.IMPORTDECLARATION;
/** Identifier being imported. */
externalName: IdentifierExpression;
}
/** Represents an `import` statement. */
export class ImportStatement extends Statement {
kind = NodeKind.IMPORT;
/** Array of member declarations or `null` if an asterisk import. */
declarations: ImportDeclaration[] | null;
/** Name of the local namespace, if an asterisk import. */
namespaceName: IdentifierExpression | null;
/** Path being imported from. */
path: StringLiteralExpression;
/** Normalized path. */
normalizedPath: string;
/** Mangled internal path being referenced. */
internalPath: string;
}
/** Represents an `interfarce` declaration. */
export class InterfaceDeclaration extends ClassDeclaration {
kind = NodeKind.INTERFACEDECLARATION;
}
/** Represents a method declaration within a `class`. */
export class MethodDeclaration extends FunctionDeclaration {
kind = NodeKind.METHODDECLARATION;
}
/** Represents a `namespace` declaration. */
export class NamespaceDeclaration extends DeclarationStatement {
kind = NodeKind.NAMESPACEDECLARATION;
/** Array of namespace members. */
members: Statement[];
}
/** Represents a `return` statement. */
export class ReturnStatement extends Statement {
kind = NodeKind.RETURN;
/** Value expression being returned, if present. */
value: Expression | null;
}
/** Represents a single `case` within a `switch` statement. */
export class SwitchCase extends Node {
kind = NodeKind.SWITCHCASE;
/** Label expression. `null` indicates the default case. */
label: Expression | null;
/** Contained statements. */
statements: Statement[];
}
/** Represents a `switch` statement. */
export class SwitchStatement extends Statement {
kind = NodeKind.SWITCH;
/** Condition expression. */
condition: Expression;
/** Contained cases. */
cases: SwitchCase[];
}
/** Represents a `throw` statement. */
export class ThrowStatement extends Statement {
kind = NodeKind.THROW;
/** Value expression being thrown. */
value: Expression;
}
/** Represents a `try` statement. */
export class TryStatement extends Statement {
kind = NodeKind.TRY;
/** Contained statements. */
statements: Statement[];
/** Exception variable name, if a `catch` clause is present. */
catchVariable: IdentifierExpression | null;
/** Statements being executed on catch, if a `catch` clause is present. */
catchStatements: Statement[] | null;
/** Statements being executed afterwards, if a `finally` clause is present. */
finallyStatements: Statement[] | null;
}
/** Represents a `type` declaration. */
export class TypeDeclaration extends DeclarationStatement {
kind = NodeKind.TYPEDECLARATION;
/** Type parameters, if any. */
typeParameters: TypeParameterNode[] | null;
/** Type being aliased. */
type: CommonTypeNode;
}
/** Represents a variable declaration part of a {@link VariableStatement}. */
export class VariableDeclaration extends VariableLikeDeclarationStatement {
kind = NodeKind.VARIABLEDECLARATION;
}
/** Represents a variable statement wrapping {@link VariableDeclaration}s. */
export class VariableStatement extends Statement {
kind = NodeKind.VARIABLE;
/** Array of decorators. */
decorators: DecoratorNode[] | null;
/** Array of member declarations. */
declarations: VariableDeclaration[];
}
/** Represents a void statement dropping an expression's value. */
export class VoidStatement extends Statement {
kind = NodeKind.VOID;
/** Expression being dropped. */
expression: Expression;
}
/** Represents a `while` statement. */
export class WhileStatement extends Statement {
kind = NodeKind.WHILE;
/** Condition expression. */
condition: Expression;
/** Statement being looped over. */
statement: Statement;
}
/** Gets the first decorator by name within at set of decorators, if present. */
export function getFirstDecorator(name: string, decorators: DecoratorNode[] | null): DecoratorNode | null {
if (decorators) {
for (let i = 0, k = decorators.length; i < k; ++i) {
let decorator = decorators[i];
let expression = decorator.name;
if (expression.kind == NodeKind.IDENTIFIER && (<IdentifierExpression>expression).text == name) {
return decorator;
}
}
}
return null;
}
/** Tests if a specific decorator is present within the specified decorators. */
export function hasDecorator(name: string, decorators: DecoratorNode[] | null): bool {
return getFirstDecorator(name, decorators) != null;
}
/** Mangles a declaration's name to an internal name. */
export function mangleInternalName(declaration: DeclarationStatement, asGlobal: bool = false): string {
var name = declaration.name.text;
var parent = declaration.parent;
if (!parent) return name;
if (
declaration.kind == NodeKind.VARIABLEDECLARATION &&
parent.kind == NodeKind.VARIABLE
) { // skip over
if (!(parent = parent.parent)) return name;
}
if (parent.kind == NodeKind.CLASSDECLARATION) {
return mangleInternalName(<ClassDeclaration>parent, asGlobal) + (
declaration.is(CommonFlags.STATIC)
? STATIC_DELIMITER
: INSTANCE_DELIMITER
) + name;
}
if (
parent.kind == NodeKind.NAMESPACEDECLARATION ||
parent.kind == NodeKind.ENUMDECLARATION
) {
return mangleInternalName(<DeclarationStatement>parent, asGlobal) +
STATIC_DELIMITER + name;
}
return asGlobal
? name
: declaration.range.source.internalPath + PATH_DELIMITER + name;
}
/** Mangles an external to an internal path. */
export function mangleInternalPath(path: string): string {
if (path.endsWith(".ts")) path = path.substring(0, path.length - 3);
return path;
}
// Helpers
/** Sets the parent node on an array of nodes. */
function setParent(nodes: Node[], parent: Node): void {
for (let i = 0, k = nodes.length; i < k; ++i) {
nodes[i].parent = parent;
}
}
/** Sets the parent node on an array of nullable nodes. */
function setParentIfNotNull(nodes: (Node | null)[], parent: Node): void {
for (let i = 0, k = nodes.length; i < k; ++i) {
let node = nodes[i];
if (node) node.parent = parent;
}
}