mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-09 21:11:27 +00:00
Program elements and resolve infrastructure; Stdlib ideas; Restructuring
This commit is contained in:
parent
6e98c52f76
commit
d1c1178f25
107
assembly.d.ts
vendored
Normal file
107
assembly.d.ts
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
// types
|
||||
|
||||
/** An 8-bit signed integer. */
|
||||
declare type i8 = number;
|
||||
/** A 16-bit signed integer. */
|
||||
declare type i16 = number;
|
||||
/** A 32-bit signed integer. */
|
||||
declare type i32 = number;
|
||||
/** A 64-bit signed integer. */
|
||||
declare type i64 = number;
|
||||
/** A 32-bit signed integer when targeting 32-bit WebAssembly or a 64-bit signed integer when targeting 64-bit WebAssembly. */
|
||||
declare type isize = number;
|
||||
/** An 8-bit unsigned integer. */
|
||||
declare type u8 = number;
|
||||
/** A 16-bit unsigned integer. */
|
||||
declare type u16 = number;
|
||||
/** A 32-bit unsigned integer. */
|
||||
declare type u32 = number;
|
||||
/** A 64-bit unsigned integer. */
|
||||
declare type u64 = number;
|
||||
/** A 32-bit unsigned integer when targeting 32-bit WebAssembly or a 64-bit unsigned integer when targeting 64-bit WebAssembly. */
|
||||
declare type usize = number;
|
||||
/** A 1-bit unsigned integer. */
|
||||
declare type bool = any; // sic
|
||||
/** A 32-bit float. */
|
||||
declare type f32 = number;
|
||||
/** A 64-bit float. */
|
||||
declare type f64 = number;
|
||||
|
||||
// builtins
|
||||
|
||||
/** Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer. */
|
||||
declare function rotl<T>(value: T, shift: T): T;
|
||||
/** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */
|
||||
declare function rotr<T>(value: T, shift: T): T;
|
||||
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
|
||||
declare function clz<T>(value: T): T;
|
||||
/** Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero. */
|
||||
declare function ctz<T>(value: T): T;
|
||||
/** Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer. */
|
||||
declare function popcnt<T>(value: T): T;
|
||||
|
||||
/** Computes the absolute value of a 32-bit or 64-bit float. */
|
||||
declare function abs<T>(value: T): T;
|
||||
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
|
||||
declare function ceil<T>(value: T): T;
|
||||
/** Performs the floor operation on a 32-bit or 64-bit float. */
|
||||
declare function floor<T>(value: T): T;
|
||||
/** Calculates the square root of a 32-bit or 64-bit float. */
|
||||
declare function sqrt<T>(value: T): T;
|
||||
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
|
||||
declare function trunc<T>(value: T): T;
|
||||
/** Rounds to the nearest integer tied to even of a 32-bit or 64-bit float. */
|
||||
declare function nearest<T>(value: T): T;
|
||||
/** Determines the minimum of two 32-bit or 64-bit floats. If either operand is `NaN`, returns `NaN`. */
|
||||
declare function min<T>(left: T, right: T): T;
|
||||
/** Determines the maximum of two 32-bit or 64-bit floats. If either operand is `NaN`, returns `NaN`. */
|
||||
declare function max<T>(left: T, right: T): T;
|
||||
/** Composes a 32-bit or 64-bit float from the magnitude of `x` and the sign of `y`. */
|
||||
declare function copysign<T>(x: T, y: T): T;
|
||||
|
||||
/** Reinterprets the bits of a value of type `T1` as type `T2`. Valid reinterpretations are i32 to/from f32 and i64 to/from f64. */
|
||||
declare function reinterpret<T1,T2>(value: T1): T2;
|
||||
/** Returns the current memory size in units of pages. One page is 64kb. */
|
||||
declare function current_memory(): i32;
|
||||
/** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */
|
||||
declare function grow_memory(value: i32): i32;
|
||||
/** Emits an unreachable operation that results in a runtime error when executed. */
|
||||
declare function unreachable(): void;
|
||||
|
||||
/** Loads a value of the specified type from memory. */
|
||||
declare function load<T>(offset: usize): T;
|
||||
/** Stores a value of the specified type to memory. */
|
||||
declare function store<T>(offset: usize, value: T): void;
|
||||
/** Determines the byte size of the specified core or class type. Compiles to a constant. */
|
||||
declare function sizeof<T>(): usize;
|
||||
/** Gets the underlying pointer value of a class type. */
|
||||
declare function pointerof<T>(cls: T): usize;
|
||||
/** Creates a class type from its underlying pointer value. */
|
||||
declare function classof<T>(ptr: usize): T;
|
||||
|
||||
// standard library
|
||||
|
||||
/** NaN (not a number) as a 32-bit or 64-bit float depending on context. */
|
||||
declare const NaN: number;
|
||||
/** Positive infinity as a 32-bit or 64-bit float depending on context. */
|
||||
declare const Infinity: number;
|
||||
|
||||
/** Tests if a 32-bit or 64-bit float is NaN. */
|
||||
declare function isNaN<T>(value: T): bool;
|
||||
/** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */
|
||||
declare function isFinite<T>(value: T): bool;
|
||||
|
||||
/** A decorator marking a function or class as global. */
|
||||
declare function global(name?: string): any;
|
||||
/** A decorator marking a function as ideally being inlined. */
|
||||
declare function inline(): any;
|
||||
/** A decorator marking a class that manually manages its memory. */
|
||||
declare function allocates(): any;
|
||||
declare function operator(token: string, fn: any): any;
|
||||
|
||||
/// <reference path="./std/array.d.ts" />
|
||||
/// <reference path="./std/map.d.ts" />
|
||||
/// <reference path="./std/math.d.ts" />
|
||||
/// <reference path="./std/memory.d.ts" />
|
||||
/// <reference path="./std/set.d.ts" />
|
||||
/// <reference path="./std/string.d.ts" />
|
178
src/ast.ts
178
src/ast.ts
@ -66,6 +66,7 @@
|
||||
|
||||
*/
|
||||
|
||||
import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER, PARENT_SUBST, STATIC_DELIMITER, INSTANCE_DELIMITER } from "./constants";
|
||||
import { Token, Tokenizer, operatorTokenToString, Range } from "./tokenizer";
|
||||
import { CharCode, I64, normalizePath, resolvePath } from "./util";
|
||||
|
||||
@ -139,59 +140,6 @@ export enum NodeKind {
|
||||
WHILE
|
||||
}
|
||||
|
||||
export function nodeKindToString(kind: NodeKind): string {
|
||||
switch (kind) {
|
||||
case NodeKind.SOURCE: return "SOURCE";
|
||||
case NodeKind.TYPE: return "TYPE";
|
||||
case NodeKind.TYPEPARAMETER: return "TYPEPARAMETER";
|
||||
case NodeKind.IDENTIFIER: return "IDENTIFIER";
|
||||
case NodeKind.ASSERTION: return "ASSERTION";
|
||||
case NodeKind.BINARY: return "BINARY";
|
||||
case NodeKind.CALL: return "CALL";
|
||||
case NodeKind.ELEMENTACCESS: return "ELEMENTACCESS";
|
||||
case NodeKind.LITERAL: return "LITERAL";
|
||||
case NodeKind.NEW: return "NEW";
|
||||
case NodeKind.PARENTHESIZED: return "PARENTHESIZED";
|
||||
case NodeKind.PROPERTYACCESS: return "PROPERTYACCESS";
|
||||
case NodeKind.SELECT: return "SELECT";
|
||||
case NodeKind.UNARYPOSTFIX: return "UNARYPOSTFIX";
|
||||
case NodeKind.UNARYPREFIX: return "UNARYPREFIX";
|
||||
case NodeKind.BLOCK: return "BLOCK";
|
||||
case NodeKind.BREAK: return "BREAK";
|
||||
case NodeKind.CLASS: return "CLASS";
|
||||
case NodeKind.CONTINUE: return "CONTINUE";
|
||||
case NodeKind.DO: return "DO";
|
||||
case NodeKind.EMPTY: return "EMPTY";
|
||||
case NodeKind.ENUM: return "ENUM";
|
||||
case NodeKind.ENUMVALUE: return "ENUMVALUE";
|
||||
case NodeKind.EXPORT: return "EXPORT";
|
||||
case NodeKind.EXPORTIMPORT: return "EXPORTIMPORT";
|
||||
case NodeKind.EXPRESSION: return "EXPRESSION";
|
||||
case NodeKind.INTERFACE: return "INTERFACE";
|
||||
case NodeKind.FALSE: return "FALSE";
|
||||
case NodeKind.FOR: return "FOR";
|
||||
case NodeKind.FUNCTION: return "FUNCTION";
|
||||
case NodeKind.IF: return "IF";
|
||||
case NodeKind.IMPORT: return "IMPORT";
|
||||
case NodeKind.IMPORTDECLARATION: return "IMPORTDECLARATION";
|
||||
case NodeKind.METHOD: return "METHOD";
|
||||
case NodeKind.NAMESPACE: return "NAMESPACE";
|
||||
case NodeKind.NULL: return "NULL";
|
||||
case NodeKind.FIELD: return "PROPERTY";
|
||||
case NodeKind.RETURN: return "RETURN";
|
||||
case NodeKind.SUPER: return "SUPER";
|
||||
case NodeKind.SWITCH: return "SWITCH";
|
||||
case NodeKind.THIS: return "THIS";
|
||||
case NodeKind.THROW: return "THROW";
|
||||
case NodeKind.TRUE: return "TRUE";
|
||||
case NodeKind.TRY: return "TRY";
|
||||
case NodeKind.VARIABLE: return "VARIABLE";
|
||||
case NodeKind.VARIABLEDECLARATION: return "VARIABLEDECLARATION";
|
||||
case NodeKind.WHILE: return "WHILE";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
// types
|
||||
|
||||
export class TypeNode extends Node {
|
||||
@ -416,18 +364,6 @@ export const enum LiteralKind {
|
||||
OBJECT
|
||||
}
|
||||
|
||||
export function literalKindToString(kind: LiteralKind): string {
|
||||
switch (kind) {
|
||||
case LiteralKind.FLOAT: return "FLOAT";
|
||||
case LiteralKind.INTEGER: return "INTEGER";
|
||||
case LiteralKind.STRING: return "STRING";
|
||||
case LiteralKind.REGEXP: return "REGEXP";
|
||||
case LiteralKind.ARRAY: return "ARRAY";
|
||||
case LiteralKind.OBJECT: return "OBJECT";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class LiteralExpression extends Expression {
|
||||
kind = NodeKind.LITERAL;
|
||||
literalKind: LiteralKind;
|
||||
@ -840,24 +776,6 @@ export enum ModifierKind {
|
||||
SET
|
||||
}
|
||||
|
||||
export function modifierKindToString(kind: ModifierKind): string {
|
||||
switch (kind) {
|
||||
case ModifierKind.ASYNC: return "ASYNC";
|
||||
case ModifierKind.CONST: return "CONST";
|
||||
case ModifierKind.DECLARE: return "DECLARE";
|
||||
case ModifierKind.EXPORT: return "EXPORT";
|
||||
case ModifierKind.IMPORT: return "IMPORT";
|
||||
case ModifierKind.STATIC: return "STATIC";
|
||||
case ModifierKind.ABSTRACT: return "ABSTRACT";
|
||||
case ModifierKind.PUBLIC: return "PUBLIC";
|
||||
case ModifierKind.PRIVATE: return "PRIVATE";
|
||||
case ModifierKind.PROTECTED: return "PROTECTED";
|
||||
case ModifierKind.GET: return "GET";
|
||||
case ModifierKind.SET: return "SET";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class Statement extends Node {
|
||||
|
||||
static createBlock(statements: Statement[], range: Range): BlockStatement {
|
||||
@ -943,6 +861,7 @@ export abstract class Statement extends Node {
|
||||
for (i = 0, k = (stmt.members = members).length; i < k; ++i) members[i].parent = stmt;
|
||||
stmt.path = path;
|
||||
stmt.normalizedPath = path ? resolvePath(normalizePath(path.value), range.source.normalizedPath) : null;
|
||||
stmt.internalPath = stmt.normalizedPath ? mangleInternalPath(stmt.normalizedPath) : null;
|
||||
return stmt;
|
||||
}
|
||||
|
||||
@ -984,6 +903,7 @@ export abstract class Statement extends Node {
|
||||
for (let i: i32 = 0, k: i32 = (stmt.declarations = declarations).length; i < k; ++i) declarations[i].parent = stmt;
|
||||
stmt.path = path;
|
||||
stmt.normalizedPath = resolvePath(normalizePath(path.value), range.source.normalizedPath);
|
||||
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
|
||||
return stmt;
|
||||
}
|
||||
|
||||
@ -995,7 +915,7 @@ export abstract class Statement extends Node {
|
||||
return elem;
|
||||
}
|
||||
|
||||
static createInterface(modifiers: Modifier[], extendsType: TypeNode | null, members: Statement[], range: Range): InterfaceDeclaration {
|
||||
static createInterface(modifiers: Modifier[], extendsType: TypeNode | null, members: DeclarationStatement[], range: Range): InterfaceDeclaration {
|
||||
const stmt: InterfaceDeclaration = new InterfaceDeclaration();
|
||||
stmt.range = range;
|
||||
let i: i32, k: i32;
|
||||
@ -1164,6 +1084,7 @@ export class Source extends Node {
|
||||
parent = null;
|
||||
path: string;
|
||||
normalizedPath: string;
|
||||
internalPath: string;
|
||||
statements: Statement[];
|
||||
|
||||
text: string;
|
||||
@ -1174,6 +1095,7 @@ export class Source extends Node {
|
||||
super();
|
||||
this.path = path;
|
||||
this.normalizedPath = normalizePath(path, true);
|
||||
this.internalPath = mangleInternalPath(this.normalizedPath);
|
||||
this.statements = new Array();
|
||||
this.range = new Range(this, 0, text.length);
|
||||
this.text = text;
|
||||
@ -1199,14 +1121,15 @@ export abstract class DeclarationStatement extends Statement {
|
||||
|
||||
identifier: IdentifierExpression;
|
||||
modifiers: Modifier[] | null;
|
||||
private _cachedInternalName: string | null = null;
|
||||
globalExportName: string | null = null;
|
||||
|
||||
get internalName(): string {
|
||||
if (this._cachedInternalName == null)
|
||||
this._cachedInternalName = mangleInternalName(this);
|
||||
return this._cachedInternalName;
|
||||
}
|
||||
protected _cachedInternalName: string | null = null;
|
||||
|
||||
get internalName(): string { return this._cachedInternalName === null ? this._cachedInternalName = mangleInternalName(this) : this._cachedInternalName; }
|
||||
}
|
||||
|
||||
export abstract class VariableLikeDeclarationStatement extends DeclarationStatement {
|
||||
type: TypeNode | null;
|
||||
initializer: Expression | null;
|
||||
}
|
||||
|
||||
export class BlockStatement extends Statement {
|
||||
@ -1250,6 +1173,16 @@ export class ClassDeclaration extends DeclarationStatement {
|
||||
members: DeclarationStatement[];
|
||||
decorators: DecoratorStatement[];
|
||||
|
||||
get internalName(): string {
|
||||
if (this._cachedInternalName !== null)
|
||||
return this._cachedInternalName;
|
||||
const globalDecorator: DecoratorStatement | null = getDecoratorByName("global", this.decorators);
|
||||
if (globalDecorator && globalDecorator.expression.kind == NodeKind.IDENTIFIER && (<IdentifierExpression>globalDecorator.expression).name == "global")
|
||||
return this._cachedInternalName = this.identifier.name;
|
||||
else
|
||||
return this._cachedInternalName = mangleInternalName(this);
|
||||
}
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
let i: i32, k: i32;
|
||||
for (i = 0, k = this.decorators.length; i < k; ++i) {
|
||||
@ -1429,6 +1362,7 @@ export class ExportStatement extends Statement {
|
||||
members: ExportMember[];
|
||||
path: StringLiteralExpression | null;
|
||||
normalizedPath: string | null;
|
||||
internalPath: string | null;
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
let i: i32, k: i32;
|
||||
@ -1461,11 +1395,9 @@ export class ExpressionStatement extends Statement {
|
||||
}
|
||||
}
|
||||
|
||||
export class FieldDeclaration extends DeclarationStatement {
|
||||
export class FieldDeclaration extends VariableLikeDeclarationStatement {
|
||||
|
||||
kind = NodeKind.FIELD;
|
||||
type: TypeNode | null;
|
||||
initializer: Expression | null;
|
||||
decorators: DecoratorStatement[];
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
@ -1527,6 +1459,16 @@ export class FunctionDeclaration extends DeclarationStatement {
|
||||
statements: Statement[] | null;
|
||||
decorators: DecoratorStatement[];
|
||||
|
||||
get internalName(): string {
|
||||
if (this._cachedInternalName !== null)
|
||||
return this._cachedInternalName;
|
||||
const globalDecorator: DecoratorStatement | null = getDecoratorByName("global", this.decorators);
|
||||
if (globalDecorator && globalDecorator.expression.kind == NodeKind.IDENTIFIER && (<IdentifierExpression>globalDecorator.expression).name == "global")
|
||||
return this._cachedInternalName = this.identifier.name;
|
||||
else
|
||||
return this._cachedInternalName = mangleInternalName(this);
|
||||
}
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
let i: i32, k: i32;
|
||||
for (i = 0, k = this.decorators.length; i < k; ++i) {
|
||||
@ -1625,6 +1567,7 @@ export class ImportStatement extends Statement {
|
||||
declarations: ImportDeclaration[];
|
||||
path: StringLiteralExpression;
|
||||
normalizedPath: string;
|
||||
internalPath: string;
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
sb.push("import {\n");
|
||||
@ -1638,12 +1581,9 @@ export class ImportStatement extends Statement {
|
||||
}
|
||||
}
|
||||
|
||||
export class InterfaceDeclaration extends DeclarationStatement {
|
||||
export class InterfaceDeclaration extends ClassDeclaration {
|
||||
|
||||
kind = NodeKind.INTERFACE;
|
||||
typeParameters: TypeParameter[];
|
||||
extendsType: TypeNode | null;
|
||||
members: Statement[];
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
let i: i32, k: i32;
|
||||
@ -1872,12 +1812,10 @@ export class TryStatement extends Statement {
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableDeclaration extends DeclarationStatement {
|
||||
export class VariableDeclaration extends VariableLikeDeclarationStatement {
|
||||
|
||||
kind = NodeKind.VARIABLEDECLARATION;
|
||||
modifiers = null;
|
||||
type: TypeNode | null;
|
||||
initializer: Expression | null;
|
||||
|
||||
serialize(sb: string[]): void {
|
||||
this.identifier.serialize(sb);
|
||||
@ -1940,21 +1878,53 @@ export function hasModifier(kind: ModifierKind, modifiers: Modifier[] | null): b
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getDecoratorByName(name: string, decorators: DecoratorStatement[]): DecoratorStatement | null {
|
||||
for (let i: i32 = 0, k: i32 = decorators.length; i < k; ++i) {
|
||||
const decorator: DecoratorStatement = decorators[i];
|
||||
const expression: Expression = decorator.expression;
|
||||
if (expression.kind == NodeKind.IDENTIFIER && (<IdentifierExpression>expression).name == name)
|
||||
return decorator;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function serialize(node: Node, indent: i32 = 0): string {
|
||||
const sb: string[] = new Array(); // shared builder could grow too much
|
||||
node.serialize(sb);
|
||||
return sb.join("");
|
||||
}
|
||||
|
||||
export function mangleInternalPath(path: string): string {
|
||||
if (PATH_DELIMITER.charCodeAt(0) != CharCode.SLASH)
|
||||
path = path.replace("/", PATH_DELIMITER);
|
||||
if (PARENT_SUBST != "..")
|
||||
path = path.replace("..", PARENT_SUBST);
|
||||
return path;
|
||||
}
|
||||
|
||||
export function mangleInternalName(declaration: DeclarationStatement): string {
|
||||
let name: string = declaration.identifier.name;
|
||||
let modifiers: Modifier[] | null;
|
||||
if (declaration.kind == NodeKind.METHOD && (modifiers = declaration.modifiers)) {
|
||||
for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i) {
|
||||
const modifier: Modifier = modifiers[i];
|
||||
if (modifier.modifierKind == ModifierKind.GET) {
|
||||
name = GETTER_PREFIX + name;
|
||||
break;
|
||||
}
|
||||
else if (modifier.modifierKind == ModifierKind.SET) {
|
||||
name = SETTER_PREFIX + name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!declaration.parent)
|
||||
return name;
|
||||
if (declaration.parent.kind == NodeKind.CLASS)
|
||||
return (<ClassDeclaration>declaration.parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? "." : "#") + name;
|
||||
return (<ClassDeclaration>declaration.parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
|
||||
if (declaration.parent.kind == NodeKind.NAMESPACE || declaration.parent.kind == NodeKind.ENUM)
|
||||
return (<DeclarationStatement>declaration.parent).internalName + "." + name;
|
||||
return declaration.range.source.normalizedPath + "/" + name;
|
||||
return (<DeclarationStatement>declaration.parent).internalName + STATIC_DELIMITER + name;
|
||||
return declaration.range.source.internalPath + PATH_DELIMITER + name;
|
||||
}
|
||||
|
||||
function builderEndsWith(sb: string[], code: CharCode): bool {
|
||||
|
742
src/compiler.ts
742
src/compiler.ts
File diff suppressed because it is too large
Load Diff
8
src/constants.ts
Normal file
8
src/constants.ts
Normal file
@ -0,0 +1,8 @@
|
||||
// internal naming scheme
|
||||
|
||||
export const PATH_DELIMITER: string = "/";
|
||||
export const PARENT_SUBST: string = "..";
|
||||
export const GETTER_PREFIX: string = "get:";
|
||||
export const SETTER_PREFIX: string = "set:";
|
||||
export const INSTANCE_DELIMITER: string = "#";
|
||||
export const STATIC_DELIMITER: string = ".";
|
@ -3,6 +3,7 @@
|
||||
export enum DiagnosticCode {
|
||||
Conversion_from_type_0_to_1_requires_an_explicit_cast = 100,
|
||||
Basic_type_0_cannot_be_nullable = 101,
|
||||
Operation_not_supported = 102,
|
||||
Unterminated_string_literal = 1002,
|
||||
Identifier_expected = 1003,
|
||||
_0_expected = 1005,
|
||||
@ -32,6 +33,7 @@ export enum DiagnosticCode {
|
||||
Unexpected_end_of_text = 1126,
|
||||
Invalid_character = 1127,
|
||||
_case_or_default_expected = 1130,
|
||||
Type_argument_expected = 1140,
|
||||
String_literal_expected = 1141,
|
||||
Line_break_not_permitted_here = 1142,
|
||||
Declaration_expected = 1146,
|
||||
@ -50,8 +52,10 @@ export enum DiagnosticCode {
|
||||
Type_0_is_not_assignable_to_type_1 = 2322,
|
||||
_this_cannot_be_referenced_in_current_location = 2332,
|
||||
The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access = 2357,
|
||||
The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access = 2364,
|
||||
Function_implementation_is_missing_or_not_immediately_following_the_declaration = 2391,
|
||||
Duplicate_function_implementation = 2393,
|
||||
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
|
||||
Expected_0_type_arguments_but_got_1 = 2558,
|
||||
File_0_not_found = 6054
|
||||
}
|
||||
@ -60,6 +64,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
switch (code) {
|
||||
case 100: return "Conversion from type '{0}' to '{1}' requires an explicit cast.";
|
||||
case 101: return "Basic type '{0}' cannot be nullable.";
|
||||
case 102: return "Operation not supported.";
|
||||
case 1002: return "Unterminated string literal.";
|
||||
case 1003: return "Identifier expected.";
|
||||
case 1005: return "'{0}' expected.";
|
||||
@ -89,6 +94,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 1126: return "Unexpected end of text.";
|
||||
case 1127: return "Invalid character.";
|
||||
case 1130: return "'case' or 'default' expected.";
|
||||
case 1140: return "Type argument expected.";
|
||||
case 1141: return "String literal expected.";
|
||||
case 1142: return "Line break not permitted here.";
|
||||
case 1146: return "Declaration expected.";
|
||||
@ -107,8 +113,10 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 2322: return "Type '{0}' is not assignable to type '{1}'.";
|
||||
case 2332: return "'this' cannot be referenced in current location.";
|
||||
case 2357: return "The operand of an increment or decrement operator must be a variable or a property access.";
|
||||
case 2364: return "The left-hand side of an assignment expression must be a variable or a property access.";
|
||||
case 2391: return "Function implementation is missing or not immediately following the declaration.";
|
||||
case 2393: return "Duplicate function implementation.";
|
||||
case 2541: return "The target of an assignment must be a variable or a property access.";
|
||||
case 2558: return "Expected {0} type arguments, but got {1}.";
|
||||
case 6054: return "File '{0}' not found.";
|
||||
default: return "";
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"Conversion from type '{0}' to '{1}' requires an explicit cast.": 100,
|
||||
"Basic type '{0}' cannot be nullable.": 101,
|
||||
"Operation not supported.": 102,
|
||||
|
||||
"Unterminated string literal.": 1002,
|
||||
"Identifier expected.": 1003,
|
||||
@ -31,6 +32,7 @@
|
||||
"Unexpected end of text.": 1126,
|
||||
"Invalid character.": 1127,
|
||||
"'case' or 'default' expected.": 1130,
|
||||
"Type argument expected.": 1140,
|
||||
"String literal expected.": 1141,
|
||||
"Line break not permitted here.": 1142,
|
||||
"Declaration expected.": 1146,
|
||||
@ -50,8 +52,10 @@
|
||||
"Type '{0}' is not assignable to type '{1}'.": 2322,
|
||||
"'this' cannot be referenced in current location.": 2332,
|
||||
"The operand of an increment or decrement operator must be a variable or a property access.": 2357,
|
||||
"The left-hand side of an assignment expression must be a variable or a property access.": 2364,
|
||||
"Function implementation is missing or not immediately following the declaration.": 2391,
|
||||
"Duplicate function implementation.": 2393,
|
||||
"The target of an assignment must be a variable or a property access.": 2541,
|
||||
"Expected {0} type arguments, but got {1}.": 2558,
|
||||
|
||||
"File '{0}' not found.": 6054
|
||||
|
@ -462,13 +462,13 @@ export class Parser extends DiagnosticEmitter {
|
||||
// Identifier ('extends' Type)?
|
||||
if (tn.next() == Token.IDENTIFIER) {
|
||||
const identifier: IdentifierExpression = Expression.createIdentifier(tn.readIdentifier(), tn.range());
|
||||
let extendsName: TypeNode | null = null;
|
||||
let extendsType: TypeNode | null = null;
|
||||
if (tn.skip(Token.EXTENDS)) {
|
||||
extendsName = this.parseType(tn);
|
||||
if (!extendsName)
|
||||
extendsType = this.parseType(tn);
|
||||
if (!extendsType)
|
||||
return null;
|
||||
}
|
||||
return Statement.createTypeParameter(identifier, extendsName, Range.join(identifier.range, tn.range()));
|
||||
return Statement.createTypeParameter(identifier, extendsType, Range.join(identifier.range, tn.range()));
|
||||
} else
|
||||
this.error(DiagnosticCode.Identifier_expected, tn.range());
|
||||
return null;
|
||||
|
747
src/program.ts
747
src/program.ts
@ -1,15 +1,27 @@
|
||||
import { Target } from "./compiler";
|
||||
import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER } from "./constants";
|
||||
import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics";
|
||||
import { Type } from "./types";
|
||||
import { Type, typesToString } from "./types";
|
||||
import { I64 } from "./util";
|
||||
import {
|
||||
|
||||
ModifierKind,
|
||||
Node,
|
||||
NodeKind,
|
||||
Source,
|
||||
Range,
|
||||
|
||||
TypeNode,
|
||||
Expression,
|
||||
IdentifierExpression,
|
||||
LiteralExpression,
|
||||
LiteralKind,
|
||||
PropertyAccessExpression,
|
||||
StringLiteralExpression,
|
||||
|
||||
ClassDeclaration,
|
||||
DeclarationStatement,
|
||||
DecoratorStatement,
|
||||
EnumDeclaration,
|
||||
EnumValueDeclaration,
|
||||
ExportMember,
|
||||
@ -20,8 +32,11 @@ import {
|
||||
ImportStatement,
|
||||
InterfaceDeclaration,
|
||||
MethodDeclaration,
|
||||
Modifier,
|
||||
NamespaceDeclaration,
|
||||
Statement,
|
||||
TypeParameter,
|
||||
VariableLikeDeclarationStatement,
|
||||
VariableDeclaration,
|
||||
VariableStatement,
|
||||
|
||||
@ -41,29 +56,32 @@ class QueuedImport {
|
||||
declaration: ImportDeclaration;
|
||||
}
|
||||
|
||||
const typesStub: Map<string,Type> = new Map();
|
||||
const reusableTypesStub: Map<string,Type> = new Map();
|
||||
|
||||
export class Program extends DiagnosticEmitter {
|
||||
|
||||
/** Array of source files. */
|
||||
sources: Source[];
|
||||
/** Diagnostic offset used where sequentially obtaining the next diagnostic. */
|
||||
diagnosticsOffset: i32 = 0;
|
||||
target: Target = Target.WASM32;
|
||||
|
||||
/** Map of internal names to declarations. */
|
||||
names: Map<string,DeclarationStatement> = new Map();
|
||||
/** Separate map of internal type names to declarations. */
|
||||
types: Map<string,Type> = typesStub;
|
||||
/** Separate map of internal export names to declarations. */
|
||||
exports: Map<string,DeclarationStatement> = new Map(); // not global exports
|
||||
/** WASM target. */
|
||||
target: Target = Target.WASM32; // set on initialization
|
||||
/** Elements by internal name. */
|
||||
elements: Map<string,Element> = new Map();
|
||||
/** Types by internal name. */
|
||||
types: Map<string,Type> = reusableTypesStub;
|
||||
/** Exports of individual files by internal name. Not global exports. */
|
||||
exports: Map<string,Element> = new Map();
|
||||
|
||||
constructor(diagnostics: DiagnosticMessage[] | null = null) {
|
||||
super(diagnostics);
|
||||
this.sources = new Array();
|
||||
}
|
||||
|
||||
initialize(target: Target): void {
|
||||
/** Initializes the program and its elements prior to compilation. */
|
||||
initialize(target: Target = Target.WASM32): void {
|
||||
this.target = target;
|
||||
this.types = new Map([
|
||||
this.types = new Map([ // replaces typesStub
|
||||
["i8", Type.i8],
|
||||
["i16", Type.i16],
|
||||
["i32", Type.i32],
|
||||
@ -127,9 +145,7 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
// at this point queued exports should be resolvable
|
||||
// export { add }
|
||||
// export { sub } from "./other"
|
||||
for (let [exportName, queuedExport] of queuedExports) {
|
||||
for (let [exportName, queuedExport] of queuedExports) { // all file-level exports
|
||||
if (queuedExport.isForeign) {
|
||||
const seen: Set<QueuedExport> = new Set();
|
||||
while (queuedExports.has(queuedExport.referencedName)) {
|
||||
@ -139,18 +155,20 @@ export class Program extends DiagnosticEmitter {
|
||||
seen.add(queuedExport);
|
||||
}
|
||||
if (this.exports.has(queuedExport.referencedName)) {
|
||||
const declaration: DeclarationStatement = <DeclarationStatement>this.exports.get(queuedExport.referencedName);
|
||||
this.addExport(exportName, declaration);
|
||||
const element: Element = <Element>this.exports.get(queuedExport.referencedName);
|
||||
if (!this.exports.has(exportName))
|
||||
this.exports.set(exportName, element);
|
||||
if (queuedExport.member.range.source.isEntry)
|
||||
declaration.globalExportName = queuedExport.member.externalIdentifier.name;
|
||||
element.globalExportName = queuedExport.member.externalIdentifier.name;
|
||||
} else
|
||||
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.referencedName);
|
||||
} else /* local */ {
|
||||
if (this.names.has(queuedExport.referencedName)) {
|
||||
const declaration: DeclarationStatement = <DeclarationStatement>this.names.get(queuedExport.referencedName);
|
||||
this.addExport(exportName, declaration);
|
||||
if (this.elements.has(queuedExport.referencedName)) {
|
||||
const element: Element = <Element>this.elements.get(queuedExport.referencedName);
|
||||
if (!this.exports.has(exportName))
|
||||
this.exports.set(exportName, element);
|
||||
if (queuedExport.member.range.source.isEntry)
|
||||
declaration.globalExportName = queuedExport.member.externalIdentifier.name;
|
||||
element.globalExportName = queuedExport.member.externalIdentifier.name;
|
||||
} else
|
||||
this.error(DiagnosticCode.Cannot_find_name_0, queuedExport.member.externalIdentifier.range, queuedExport.referencedName);
|
||||
}
|
||||
@ -170,13 +188,11 @@ export class Program extends DiagnosticEmitter {
|
||||
seen.add(queuedExport);
|
||||
}
|
||||
if (this.exports.has(importName)) {
|
||||
if (this.names.has(internalName))
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, queuedImport.declaration.identifier.range, internalName);
|
||||
else {
|
||||
const declaration: DeclarationStatement = <DeclarationStatement>this.exports.get(importName);
|
||||
this.names.set(internalName, declaration);
|
||||
// TODO: also mirror (non-private) member names?
|
||||
// wouldn't it be better to look up members based on their parent?
|
||||
const element: Element = <Element>this.exports.get(importName);
|
||||
this.elements.set(internalName, element);
|
||||
}
|
||||
} else
|
||||
this.error(DiagnosticCode.Cannot_find_name_0, queuedImport.declaration.externalIdentifier.range, importName);
|
||||
@ -185,20 +201,28 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
private initializeClass(declaration: ClassDeclaration): void {
|
||||
const internalName: string = declaration.internalName;
|
||||
this.addName(internalName, declaration);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||
this.addExport(/* same as */internalName, declaration);
|
||||
const members: DeclarationStatement[] = declaration.members;
|
||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||
const statement: Statement = members[j];
|
||||
switch (statement.kind) {
|
||||
const template: ClassTemplate = new ClassTemplate(this, internalName, declaration);
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.elements.set(internalName, template);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
|
||||
if (this.exports.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.exports.set(internalName, template);
|
||||
}
|
||||
const memberDeclarations: DeclarationStatement[] = declaration.members;
|
||||
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
|
||||
const memberDeclaration: DeclarationStatement = memberDeclarations[j];
|
||||
switch (memberDeclaration.kind) {
|
||||
|
||||
case NodeKind.FIELD:
|
||||
this.initializeField(<FieldDeclaration>statement);
|
||||
this.initializeField(<FieldDeclaration>memberDeclaration, template);
|
||||
break;
|
||||
|
||||
case NodeKind.METHOD:
|
||||
this.initializeMethod(<MethodDeclaration>statement);
|
||||
this.initializeMethod(<MethodDeclaration>memberDeclaration, template);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -207,42 +231,103 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private initializeField(declaration: FieldDeclaration): void {
|
||||
this.addName(declaration.internalName, declaration);
|
||||
private initializeField(declaration: FieldDeclaration, template: ClassTemplate): void {
|
||||
const name: string = declaration.identifier.name;
|
||||
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
|
||||
const internalName: string = declaration.internalName;
|
||||
if (template.members.has(name))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else {
|
||||
const global: Global = new Global(this, internalName, declaration, null);
|
||||
template.members.set(name, global);
|
||||
}
|
||||
} else {
|
||||
if (template.fieldDeclarations.has(name))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
|
||||
else
|
||||
template.fieldDeclarations.set(name, declaration);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeMethod(declaration: MethodDeclaration, template: ClassTemplate): void {
|
||||
let name: string = declaration.identifier.name;
|
||||
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
|
||||
const internalName: string = declaration.internalName;
|
||||
if (template.members.has(name))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else {
|
||||
const func: FunctionTemplate = new FunctionTemplate(this, internalName, declaration);
|
||||
let modifiers: Modifier[] | null;
|
||||
if (modifiers = declaration.modifiers) {
|
||||
for (let i: i32 = 0, k: i32 = modifiers.length; i < k; ++i) {
|
||||
const modifier: Modifier = modifiers[i];
|
||||
if (modifier.modifierKind == ModifierKind.GET) {
|
||||
name = GETTER_PREFIX + name;
|
||||
break;
|
||||
} else if (modifier.modifierKind == ModifierKind.SET) {
|
||||
name = SETTER_PREFIX + name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template.members.set(name, func);
|
||||
}
|
||||
} else {
|
||||
if (template.methodDeclarations.has(name))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, name);
|
||||
else
|
||||
template.methodDeclarations.set(name, declaration);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeEnum(declaration: EnumDeclaration): void {
|
||||
const internalName: string = declaration.internalName;
|
||||
this.addName(internalName, declaration);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||
this.addExport(/* same as */internalName, declaration);
|
||||
const members: EnumValueDeclaration[] = declaration.members;
|
||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||
this.initializeEnumValue(members[i]);
|
||||
const enm: Enum = new Enum(this, internalName, declaration);
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else {
|
||||
this.elements.set(internalName, enm);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
|
||||
if (this.exports.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.exports.set(internalName, enm);
|
||||
}
|
||||
const members: EnumValueDeclaration[] = declaration.members;
|
||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||
this.initializeEnumValue(members[i], enm);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeEnumValue(declaration: EnumValueDeclaration): void {
|
||||
this.addName(declaration.internalName, declaration);
|
||||
private initializeEnumValue(declaration: EnumValueDeclaration, enm: Enum): void {
|
||||
const name: string = declaration.identifier.name;
|
||||
const internalName: string = declaration.internalName;
|
||||
if (enm.members.has(name))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else {
|
||||
const value: EnumValue = new EnumValue(enm, this, internalName, declaration);
|
||||
enm.members.set(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeExports(statement: ExportStatement, queuedExports: Map<string,QueuedExport>): void {
|
||||
const members: ExportMember[] = statement.members;
|
||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i)
|
||||
this.initializeExport(members[i], statement.normalizedPath, queuedExports);
|
||||
this.initializeExport(members[i], statement.internalPath, queuedExports);
|
||||
}
|
||||
|
||||
private initializeExport(member: ExportMember, normalizedPath: string | null, queuedExports: Map<string,QueuedExport>): void {
|
||||
const exportName: string = member.range.source.normalizedPath + "/" + member.externalIdentifier.name;
|
||||
private initializeExport(member: ExportMember, internalPath: string | null, queuedExports: Map<string,QueuedExport>): void {
|
||||
const exportName: string = member.range.source.internalPath + PATH_DELIMITER + member.externalIdentifier.name;
|
||||
if (queuedExports.has(exportName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, member.externalIdentifier.range, exportName);
|
||||
else {
|
||||
const queuedExport: QueuedExport = new QueuedExport();
|
||||
if (normalizedPath == null) {
|
||||
if (internalPath == null) {
|
||||
queuedExport.isForeign = false;
|
||||
queuedExport.referencedName = member.range.source.normalizedPath + "/" + member.identifier.name;
|
||||
queuedExport.referencedName = member.range.source.internalPath + PATH_DELIMITER + member.identifier.name;
|
||||
} else {
|
||||
queuedExport.isForeign = true;
|
||||
queuedExport.referencedName = (<string>normalizedPath) + "/" + member.identifier.name;
|
||||
queuedExport.referencedName = (<string>internalPath) + PATH_DELIMITER + member.identifier.name;
|
||||
}
|
||||
queuedExport.member = member;
|
||||
queuedExports.set(exportName, queuedExport);
|
||||
@ -251,21 +336,30 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
private initializeFunction(declaration: FunctionDeclaration): void {
|
||||
const internalName: string = declaration.internalName;
|
||||
this.addName(internalName, declaration);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||
this.addExport(/* same as */internalName, declaration);
|
||||
const template: FunctionTemplate = new FunctionTemplate(this, internalName, declaration);
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else {
|
||||
this.elements.set(internalName, template);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
|
||||
if (this.exports.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.exports.set(internalName, template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private initializeImports(statement: ImportStatement, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
|
||||
const members: ImportDeclaration[] = statement.declarations;
|
||||
for (let i: i32 = 0, k: i32 = members.length; i < k; ++i) {
|
||||
const declaration: ImportDeclaration = members[i];
|
||||
this.initializeImport(declaration, statement.normalizedPath, queuedExports, queuedImports);
|
||||
this.initializeImport(declaration, statement.internalPath, queuedExports, queuedImports);
|
||||
}
|
||||
}
|
||||
|
||||
private initializeImport(declaration: ImportDeclaration, normalizedPath: string, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
|
||||
const importName: string = normalizedPath + "/" + declaration.externalIdentifier.name;
|
||||
private initializeImport(declaration: ImportDeclaration, internalPath: string, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
|
||||
const importName: string = internalPath + PATH_DELIMITER + declaration.externalIdentifier.name;
|
||||
let resolvedImportName: string = importName;
|
||||
const seen: Set<QueuedExport> = new Set();
|
||||
while (queuedExports.has(resolvedImportName)) {
|
||||
@ -277,10 +371,10 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
const internalName: string = declaration.internalName;
|
||||
if (this.exports.has(resolvedImportName)) { // resolvable right away
|
||||
if (this.names.has(internalName))
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.names.set(internalName, <DeclarationStatement>this.exports.get(resolvedImportName));
|
||||
this.elements.set(internalName, <Element>this.exports.get(resolvedImportName));
|
||||
} else { // points to yet unresolved export
|
||||
const queuedImport: QueuedImport = new QueuedImport();
|
||||
queuedImport.internalName = internalName;
|
||||
@ -292,20 +386,28 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
private initializeInterface(declaration: InterfaceDeclaration): void {
|
||||
const internalName: string = declaration.internalName;
|
||||
this.addName(internalName, declaration);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||
this.addExport(/* same as */internalName, declaration);
|
||||
const members: Statement[] = declaration.members;
|
||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||
const statement: Statement = members[j];
|
||||
switch (statement.kind) {
|
||||
const template: InterfaceTemplate = new InterfaceTemplate(this, internalName, declaration);
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.elements.set(internalName, template);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
|
||||
if (this.exports.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.exports.set(internalName, template);
|
||||
}
|
||||
const memberDeclarations: DeclarationStatement[] = declaration.members;
|
||||
for (let j: i32 = 0, l: i32 = memberDeclarations.length; j < l; ++j) {
|
||||
const memberDeclaration: DeclarationStatement = memberDeclarations[j];
|
||||
switch (memberDeclaration.kind) {
|
||||
|
||||
case NodeKind.FIELD:
|
||||
this.initializeField(<FieldDeclaration>statement);
|
||||
this.initializeField(<FieldDeclaration>memberDeclaration, template);
|
||||
break;
|
||||
|
||||
case NodeKind.METHOD:
|
||||
this.initializeMethod(<MethodDeclaration>statement);
|
||||
this.initializeMethod(<MethodDeclaration>memberDeclaration, template);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -314,15 +416,20 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
private initializeMethod(declaration: MethodDeclaration): void {
|
||||
this.addName(declaration.internalName, declaration);
|
||||
}
|
||||
|
||||
private initializeNamespace(declaration: NamespaceDeclaration): void {
|
||||
const internalName: string = declaration.internalName;
|
||||
this.addName(internalName, declaration);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers))
|
||||
this.addExport(/* same as */internalName, declaration);
|
||||
const ns: Namespace = new Namespace(this, internalName, declaration);
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else {
|
||||
this.elements.set(internalName, ns);
|
||||
if (hasModifier(ModifierKind.EXPORT, declaration.modifiers)) {
|
||||
if (this.exports.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.exports.set(internalName, ns);
|
||||
}
|
||||
}
|
||||
const members: Statement[] = declaration.members;
|
||||
for (let j: i32 = 0, l: i32 = members.length; j < l; ++j) {
|
||||
const statement: Statement = members[j];
|
||||
@ -364,26 +471,484 @@ export class Program extends DiagnosticEmitter {
|
||||
for (let i: i32 = 0, k: i32 = declarations.length; i < k; ++i) {
|
||||
const declaration: VariableDeclaration = declarations[i];
|
||||
const internalName: string = declaration.internalName;
|
||||
this.addName(internalName, declaration);
|
||||
if (isExport)
|
||||
this.addExport(/* same as */internalName, declaration);
|
||||
const global: Global = new Global(this, internalName, declaration, null);
|
||||
if (this.elements.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else {
|
||||
this.elements.set(internalName, global);
|
||||
if (isExport) {
|
||||
if (this.exports.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName);
|
||||
else
|
||||
this.exports.set(internalName, global);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addName(internalName: string, declaration: DeclarationStatement): void {
|
||||
if (this.names.has(internalName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, internalName); // recoverable
|
||||
else
|
||||
this.names.set(internalName, declaration);
|
||||
resolveType(node: TypeNode, contextualTypeArguments: Map<string,Type> | null = null, reportNotFound: bool = true): Type | null {
|
||||
|
||||
// resolve parameters
|
||||
const k: i32 = node.parameters.length;
|
||||
const paramTypes: Type[] = new Array(k);
|
||||
for (let i: i32 = 0; i < k; ++i) {
|
||||
const paramType: Type | null = this.resolveType(node.parameters[i], contextualTypeArguments, reportNotFound);
|
||||
if (!paramType)
|
||||
return null;
|
||||
paramTypes[i] = <Type>paramType;
|
||||
}
|
||||
|
||||
let globalName: string = node.identifier.name;
|
||||
if (k) // can't be a placeholder if it has parameters
|
||||
globalName += typesToString(paramTypes);
|
||||
else if (contextualTypeArguments) {
|
||||
const placeholderType: Type | null = <Type | null>contextualTypeArguments.get(globalName);
|
||||
if (placeholderType)
|
||||
return placeholderType;
|
||||
}
|
||||
|
||||
let type: Type | null;
|
||||
|
||||
// check file-global type
|
||||
if (type = <Type | null>this.types.get(node.range.source.internalPath + PATH_DELIMITER + globalName))
|
||||
return type;
|
||||
|
||||
// check program-global type
|
||||
if (type = <Type | null>this.types.get(globalName))
|
||||
return type;
|
||||
|
||||
if (reportNotFound)
|
||||
this.error(DiagnosticCode.Cannot_find_name_0, node.identifier.range, globalName);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private addExport(exportName: string, declaration: DeclarationStatement): void {
|
||||
if (this.exports.has(exportName))
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.identifier.range, exportName); // recoverable
|
||||
else {
|
||||
this.exports.set(exportName, declaration);
|
||||
if (declaration.range.source.isEntry)
|
||||
declaration.globalExportName = declaration.identifier.name;
|
||||
resolveTypeArguments(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Type[] | null {
|
||||
const parameterCount: i32 = typeParameters.length;
|
||||
const argumentCount: i32 = typeArgumentNodes.length;
|
||||
if (parameterCount != argumentCount) {
|
||||
if (argumentCount)
|
||||
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, Range.join(typeArgumentNodes[0].range, typeArgumentNodes[argumentCount - 1].range), parameterCount.toString(10), argumentCount.toString(10));
|
||||
else if (alternativeReportNode)
|
||||
this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, alternativeReportNode.range.atEnd, parameterCount.toString(10), "0");
|
||||
return null;
|
||||
}
|
||||
const typeArguments: Type[] = new Array(parameterCount);
|
||||
for (let i: i32 = 0; i < parameterCount; ++i) {
|
||||
const type: Type | null = this.resolveType(typeArgumentNodes[i], contextualTypeArguments, true); // reports
|
||||
if (!type)
|
||||
return null;
|
||||
// TODO: check extendsType
|
||||
typeArguments[i] = type;
|
||||
}
|
||||
return typeArguments;
|
||||
}
|
||||
|
||||
resolveElement(expression: Expression, contextualFunction: FunctionInstance): Element | null {
|
||||
|
||||
// this
|
||||
if (expression.kind == NodeKind.THIS) {
|
||||
if (contextualFunction.instanceMethodOf)
|
||||
return contextualFunction.instanceMethodOf.template;
|
||||
this.error(DiagnosticCode._this_cannot_be_referenced_in_current_location, expression.range);
|
||||
return null;
|
||||
}
|
||||
|
||||
// local or global name
|
||||
if (expression.kind == NodeKind.IDENTIFIER) {
|
||||
const name: string = (<IdentifierExpression>expression).name;
|
||||
const local: Local | null = <Local | null>contextualFunction.locals.get(name);
|
||||
if (local)
|
||||
return local;
|
||||
const fileGlobalElement: Element | null = <Element | null>this.elements.get(expression.range.source.internalPath + PATH_DELIMITER + name);
|
||||
if (fileGlobalElement)
|
||||
return fileGlobalElement;
|
||||
const programGlobalElement: Element | null = <Element | null>this.elements.get(name);
|
||||
if (programGlobalElement)
|
||||
return programGlobalElement;
|
||||
return null;
|
||||
|
||||
// static or instance property (incl. enum values) or method
|
||||
} else if (expression.kind == NodeKind.PROPERTYACCESS) {
|
||||
const target: Element | null = this.resolveElement((<PropertyAccessExpression>expression).expression, contextualFunction);
|
||||
if (!target)
|
||||
return null;
|
||||
switch (target.kind) {
|
||||
case ElementKind.CLASS:
|
||||
case ElementKind.ENUM:
|
||||
case ElementKind.NAMESPACE:
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function checkGlobalDecorator(decorators: DecoratorStatement[]): string | null {
|
||||
for (let i: i32 = 0, k: i32 = decorators.length; i < k; ++i) {
|
||||
const decorator: DecoratorStatement = decorators[i];
|
||||
const expression: Expression = decorator.expression;
|
||||
const args: Expression[] = decorator.arguments;
|
||||
if (expression.kind == NodeKind.IDENTIFIER && args.length <= 1 && (<IdentifierExpression>expression).name == "global") {
|
||||
if (args.length) {
|
||||
const firstArg: Expression = args[0];
|
||||
if (firstArg.kind == NodeKind.LITERAL && (<LiteralExpression>firstArg).literalKind == LiteralKind.STRING)
|
||||
return (<StringLiteralExpression>firstArg).value;
|
||||
} else
|
||||
return ""; // instead inherits declaration identifier
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export enum ElementKind {
|
||||
CLASS,
|
||||
CLASSINSTANCE,
|
||||
ENUM,
|
||||
ENUMVALUE,
|
||||
FUNCTION,
|
||||
FUNCTIONINSTANCE,
|
||||
GLOBAL,
|
||||
INTERFACE,
|
||||
INTERFACEINSTANCE,
|
||||
LOCAL,
|
||||
NAMESPACE
|
||||
}
|
||||
|
||||
export abstract class Element {
|
||||
|
||||
kind: ElementKind;
|
||||
program: Program;
|
||||
internalName: string;
|
||||
globalExportName: string | null = null;
|
||||
|
||||
constructor(program: Program, internalName: string) {
|
||||
this.program = program;
|
||||
this.internalName = internalName;
|
||||
}
|
||||
}
|
||||
|
||||
export class Enum extends Element {
|
||||
|
||||
kind = ElementKind.ENUM;
|
||||
declaration: EnumDeclaration | null;
|
||||
members: Map<string,EnumValue> = new Map();
|
||||
compiled: bool = false;
|
||||
|
||||
constructor(program: Program, internalName: string, declaration: EnumDeclaration | null = null) {
|
||||
super(program, internalName);
|
||||
this.declaration = declaration;
|
||||
}
|
||||
|
||||
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
|
||||
get isGlobalExport(): bool { return this.globalExportName != null; }
|
||||
get isConstant(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are const */ true; }
|
||||
}
|
||||
|
||||
export class EnumValue extends Element {
|
||||
|
||||
kind = ElementKind.ENUMVALUE;
|
||||
declaration: EnumValueDeclaration | null;
|
||||
enum: Enum;
|
||||
hasConstantValue: bool = false;
|
||||
constantValue: i32 = 0;
|
||||
|
||||
constructor(enm: Enum, program: Program, internalName: string, declaration: EnumValueDeclaration | null = null) {
|
||||
super(program, internalName);
|
||||
this.enum = enm;
|
||||
if (!(this.declaration = declaration)) this.hasConstantValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class Global extends Element {
|
||||
|
||||
kind = ElementKind.GLOBAL;
|
||||
declaration: VariableLikeDeclarationStatement | null;
|
||||
type: Type | null;
|
||||
hasConstantValue: bool = false;
|
||||
constantIntegerValue: I64 = new I64(0, 0);
|
||||
constantFloatValue: f64 = 0;
|
||||
compiled: bool = false;
|
||||
|
||||
constructor(program: Program, internalName: string, declaration: VariableLikeDeclarationStatement | null, type: Type | null) {
|
||||
super(program, internalName);
|
||||
if (!(this.declaration = declaration)) this.hasConstantValue = true;
|
||||
this.type = type; // resolved later if `null`
|
||||
}
|
||||
|
||||
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't exports */ false; }
|
||||
get isGlobalExport(): bool { return this.globalExportName != null; }
|
||||
get isMutable(): bool { return this.declaration ? hasModifier(ModifierKind.CONST, this.declaration.modifiers) : /* internals are immutable */ false; }
|
||||
}
|
||||
|
||||
export class Namespace extends Element {
|
||||
|
||||
kind = ElementKind.NAMESPACE;
|
||||
declaration: NamespaceDeclaration | null;
|
||||
members: Map<string,Element> = new Map();
|
||||
|
||||
constructor(program: Program, internalName: string, declaration: NamespaceDeclaration | null) {
|
||||
super(program, internalName);
|
||||
this.declaration = declaration;
|
||||
}
|
||||
|
||||
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : false; }
|
||||
get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't exports */ false; }
|
||||
}
|
||||
|
||||
export class Parameter {
|
||||
|
||||
name: string;
|
||||
type: Type;
|
||||
|
||||
constructor(name: string, type: Type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class Local extends Element {
|
||||
|
||||
kind = ElementKind.LOCAL;
|
||||
index: i32;
|
||||
type: Type;
|
||||
|
||||
constructor(program: Program, internalName: string, index: i32, type: Type) {
|
||||
super(program, internalName);
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class FunctionTemplate extends Element {
|
||||
|
||||
kind = ElementKind.FUNCTION;
|
||||
declaration: FunctionDeclaration | null;
|
||||
instances: Map<string,FunctionInstance>;
|
||||
isGeneric: bool;
|
||||
|
||||
constructor(program: Program, internalName: string, declaration: FunctionDeclaration | null) {
|
||||
super(program, internalName);
|
||||
this.declaration = declaration;
|
||||
this.instances = new Map();
|
||||
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins set this
|
||||
}
|
||||
|
||||
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; }
|
||||
get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; }
|
||||
get isGetter(): bool { return this.declaration ? hasModifier(ModifierKind.GET, this.declaration.modifiers) : /* internals aren't getters */ false; }
|
||||
get isSetter(): bool { return this.declaration ? hasModifier(ModifierKind.SET, this.declaration.modifiers) : /* internals aren't setters */ false; }
|
||||
|
||||
instantiate(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): FunctionInstance | null {
|
||||
const instanceKey: string = typesToString(typeArguments, "", "");
|
||||
let instance: FunctionInstance | null = <FunctionInstance | null>this.instances.get(instanceKey);
|
||||
if (instance)
|
||||
return instance;
|
||||
const declaration: FunctionDeclaration | null = this.declaration;
|
||||
if (!declaration)
|
||||
throw new Error("unexpected instantiation of internal function");
|
||||
|
||||
// override call specific contextual type arguments
|
||||
let i: i32, k: i32 = typeArguments.length;
|
||||
if (k) {
|
||||
const inheritedTypeArguments: Map<string,Type> | null = contextualTypeArguments;
|
||||
contextualTypeArguments = new Map();
|
||||
if (inheritedTypeArguments)
|
||||
for (let [name, type] of inheritedTypeArguments)
|
||||
contextualTypeArguments.set(name, type);
|
||||
for (i = 0; i < k; ++i)
|
||||
contextualTypeArguments.set(declaration.typeParameters[i].identifier.name, typeArguments[i]);
|
||||
}
|
||||
|
||||
// resolve parameters
|
||||
k = declaration.parameters.length;
|
||||
const parameters: Parameter[] = new Array(k);
|
||||
const parameterTypes: Type[] = new Array(k);
|
||||
for (let i = 0; i < k; ++i) {
|
||||
const typeNode: TypeNode | null = declaration.parameters[i].type;
|
||||
if (typeNode) {
|
||||
const type: Type | null = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
|
||||
if (type) {
|
||||
parameters[i] = new Parameter(declaration.parameters[i].identifier.name, type);
|
||||
parameterTypes[i] = <Type>type;
|
||||
} else
|
||||
return null;
|
||||
} else
|
||||
return null; // TODO: infer type? (currently reported by parser)
|
||||
}
|
||||
|
||||
// resolve return type
|
||||
const typeNode: TypeNode | null = declaration.returnType;
|
||||
let returnType: Type;
|
||||
if (typeNode) {
|
||||
const type: Type | null = this.program.resolveType(<TypeNode>typeNode, contextualTypeArguments, true); // reports
|
||||
if (type)
|
||||
returnType = <Type>type;
|
||||
else
|
||||
return null;
|
||||
} else
|
||||
return null; // TODO: infer type? (currently reported by parser)
|
||||
|
||||
let internalName: string = this.internalName;
|
||||
if (instanceKey.length)
|
||||
internalName += "<" + instanceKey + ">";
|
||||
instance = new FunctionInstance(this, internalName, typeArguments, parameters, returnType, null); // TODO: class
|
||||
this.instances.set(instanceKey, instance);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
export class FunctionInstance extends Element {
|
||||
|
||||
kind = ElementKind.FUNCTIONINSTANCE;
|
||||
template: FunctionTemplate;
|
||||
typeArguments: Type[];
|
||||
parameters: Parameter[];
|
||||
returnType: Type;
|
||||
instanceMethodOf: ClassInstance | null;
|
||||
locals: Map<string,Local> = new Map();
|
||||
additionalLocals: Type[] = [];
|
||||
breakContext: string | null = null;
|
||||
compiled: bool = false;
|
||||
|
||||
contextualTypeArguments: Map<string,Type> = new Map();
|
||||
|
||||
private breakMajor: i32 = 0;
|
||||
private breakMinor: i32 = 0;
|
||||
|
||||
constructor(template: FunctionTemplate, internalName: string, typeArguments: Type[], parameters: Parameter[], returnType: Type, instanceMethodOf: ClassInstance | null) {
|
||||
super(template.program, internalName);
|
||||
this.template = template;
|
||||
this.typeArguments = typeArguments;
|
||||
this.parameters = parameters;
|
||||
this.returnType = returnType;
|
||||
this.instanceMethodOf = instanceMethodOf;
|
||||
let localIndex: i32 = 0;
|
||||
if (instanceMethodOf) {
|
||||
this.locals.set("this", new Local(template.program, "this", localIndex++, instanceMethodOf.type));
|
||||
for (let [name, type] of instanceMethodOf.contextualTypeArguments)
|
||||
this.contextualTypeArguments.set(name, type);
|
||||
}
|
||||
for (let i: i32 = 0, k: i32 = parameters.length; i < k; ++i) {
|
||||
const parameter: Parameter = parameters[i];
|
||||
this.locals.set(parameter.name, new Local(template.program, parameter.name, localIndex++, parameter.type));
|
||||
}
|
||||
}
|
||||
|
||||
get isInstance(): bool { return this.instanceMethodOf != null; }
|
||||
|
||||
addLocal(type: Type, name: string | null = null): Local {
|
||||
// if it has a name, check previously as this method will throw otherwise
|
||||
let localIndex = this.parameters.length + this.additionalLocals.length;
|
||||
if (this.instanceMethodOf) localIndex++; // plus 'this'
|
||||
const local: Local = new Local(this.template.program, name ? name : "anonymous$" + localIndex.toString(10), localIndex, type);
|
||||
if (name) {
|
||||
if (this.locals.has(<string>name))
|
||||
throw new Error("unexpected duplicate local name");
|
||||
this.locals.set(<string>name, local);
|
||||
}
|
||||
this.additionalLocals.push(type);
|
||||
return local;
|
||||
}
|
||||
|
||||
enterBreakContext(): string {
|
||||
if (!this.breakMinor)
|
||||
this.breakMajor++;
|
||||
return this.breakContext = this.breakMajor.toString(10) + "." + (++this.breakMinor).toString(10);
|
||||
}
|
||||
|
||||
leaveBreakContext(): void {
|
||||
if (--this.breakMinor < 0)
|
||||
throw new Error("unexpected unbalanced break context");
|
||||
if (this.breakMinor == 0 && !--this.breakMajor)
|
||||
this.breakContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClassTemplate extends Namespace {
|
||||
|
||||
kind = ElementKind.CLASS;
|
||||
declaration: ClassDeclaration | null;
|
||||
fieldDeclarations: Map<string,FieldDeclaration>;
|
||||
methodDeclarations: Map<string,MethodDeclaration>;
|
||||
instances: Map<string,ClassInstance>;
|
||||
isGeneric: bool;
|
||||
|
||||
constructor(program: Program, internalName: string, declaration: ClassDeclaration | null = null) {
|
||||
super(program, internalName, null);
|
||||
this.declaration = declaration;
|
||||
this.fieldDeclarations = new Map();
|
||||
this.methodDeclarations = new Map();
|
||||
this.instances = new Map();
|
||||
this.isGeneric = declaration ? declaration.typeParameters.length > 0 : false; // builtins can set this
|
||||
}
|
||||
|
||||
get isExport(): bool { return this.declaration ? hasModifier(ModifierKind.EXPORT, this.declaration.modifiers) : /* internals aren't file-level exports */ false; }
|
||||
get isGlobalExport(): bool { return this.declaration ? this.globalExportName != null : /* internals aren't global exports */ false; }
|
||||
|
||||
instantiate(typeArguments: Type[], contextualTypeArguments: Map<string,Type> | null): ClassInstance {
|
||||
const key: string = typesToString(typeArguments, "", "");
|
||||
let instance: ClassInstance | null = <ClassInstance | null>this.instances.get(key);
|
||||
if (instance)
|
||||
return instance;
|
||||
if (!this.declaration)
|
||||
throw new Error("unexpected instantiation of internal class");
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
export class ClassInstance extends Element {
|
||||
|
||||
kind = ElementKind.CLASSINSTANCE;
|
||||
template: ClassTemplate;
|
||||
typeArguments: Type[];
|
||||
base: ClassInstance | null;
|
||||
type: Type;
|
||||
compiled: bool = false;
|
||||
|
||||
contextualTypeArguments: Map<string,Type> = new Map();
|
||||
|
||||
constructor(template: ClassTemplate, internalName: string, typeArguments: Type[], base: ClassInstance | null) {
|
||||
super(template.program, internalName);
|
||||
this.template = template;
|
||||
this.typeArguments = typeArguments;
|
||||
this.base = base;
|
||||
this.type = (template.program.target == Target.WASM64 ? Type.usize64 : Type.usize32).asClass(this);
|
||||
|
||||
// inherit base class contextual type arguments
|
||||
if (base)
|
||||
for (let [name, type] of base.contextualTypeArguments)
|
||||
this.contextualTypeArguments.set(name, type);
|
||||
|
||||
// apply instance-specific contextual type arguments
|
||||
const declaration: ClassDeclaration | null = this.template.declaration;
|
||||
if (declaration) { // irrelevant for builtins
|
||||
const typeParameters: TypeParameter[] = declaration.typeParameters;
|
||||
if (typeParameters.length != typeArguments.length)
|
||||
throw new Error("unexpected type argument count mismatch");
|
||||
for (let i: i32 = 0, k: i32 = typeArguments.length; i < k; ++i)
|
||||
this.contextualTypeArguments.set(typeParameters[i].identifier.name, typeArguments[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class InterfaceTemplate extends ClassTemplate {
|
||||
|
||||
kind = ElementKind.INTERFACE;
|
||||
declaration: InterfaceDeclaration | null;
|
||||
|
||||
constructor(program: Program, internalName: string, declaration: InterfaceDeclaration | null) {
|
||||
super(program, internalName, declaration);
|
||||
}
|
||||
}
|
||||
|
||||
export class InterfaceInstance extends ClassInstance {
|
||||
|
||||
kind = ElementKind.INTERFACEINSTANCE;
|
||||
base: InterfaceInstance | null;
|
||||
|
||||
constructor(template: InterfaceTemplate, internalName: string, typeArguments: Type[], base: InterfaceInstance | null) {
|
||||
super(template, internalName, typeArguments, base);
|
||||
}
|
||||
}
|
||||
|
@ -280,6 +280,7 @@ export function operatorTokenToString(token: Token): string {
|
||||
|
||||
const possibleIdentifiers: Set<string> = new Set([
|
||||
"from",
|
||||
"global",
|
||||
"module",
|
||||
"namespace",
|
||||
"type"
|
||||
@ -302,6 +303,9 @@ export class Range {
|
||||
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);
|
||||
}
|
||||
|
||||
get atStart(): Range { return new Range(this.source, this.start, this.start); }
|
||||
get atEnd(): Range { return new Range(this.source, this.end, this.end); }
|
||||
}
|
||||
|
||||
export class Tokenizer extends DiagnosticEmitter {
|
||||
@ -1107,7 +1111,7 @@ function isOctalDigit(c: i32): bool {
|
||||
function isIdentifierStart(c: i32): bool {
|
||||
return c >= CharCode.A && c <= CharCode.Z
|
||||
|| c >= CharCode.a && c <= CharCode.z
|
||||
|| c == CharCode.$
|
||||
// || c == CharCode.DOLLAR // reserved for internal in case we have to change the naming scheme
|
||||
|| c == CharCode._
|
||||
|| c > 0x7f && isUnicodeIdentifierStart(c);
|
||||
}
|
||||
@ -1120,7 +1124,7 @@ function isIdentifierPart(c: i32): bool {
|
||||
return c >= CharCode.A && c <= CharCode.Z
|
||||
|| c >= CharCode.a && c <= CharCode.z
|
||||
|| c >= CharCode._0 && c <= CharCode._9
|
||||
|| c == CharCode.$
|
||||
// || c == CharCode.DOLLAR // reserved for internal use, see above
|
||||
|| c == CharCode._
|
||||
|| c > 0x7f && isUnicodeIdentifierPart(c);
|
||||
}
|
||||
|
@ -36,5 +36,14 @@
|
||||
"util/charcode.ts",
|
||||
"util/i64.ts",
|
||||
"util/path.ts"
|
||||
]
|
||||
}
|
||||
],
|
||||
"assembly": {
|
||||
"exclude": [
|
||||
"glue/js.t.ts",
|
||||
"glue/js.ts"
|
||||
],
|
||||
"include": [
|
||||
"glue/wasm.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
104
src/types.ts
104
src/types.ts
@ -1,3 +1,4 @@
|
||||
import { ClassInstance } from "./program";
|
||||
import { sb } from "./util";
|
||||
|
||||
export const enum TypeKind {
|
||||
@ -28,7 +29,7 @@ export class Type {
|
||||
|
||||
kind: TypeKind;
|
||||
size: i32;
|
||||
classType: ClassType | null;
|
||||
classType: ClassInstance | null;
|
||||
nullable: bool = false;
|
||||
nullableType: Type | null = null; // cached, of this type
|
||||
|
||||
@ -49,7 +50,7 @@ export class Type {
|
||||
get isAnySize(): bool { return this.kind == TypeKind.ISIZE || this.kind == TypeKind.USIZE; }
|
||||
get isAnyFloat(): bool { return this.kind == TypeKind.F32 || this.kind == TypeKind.F64; }
|
||||
|
||||
asClass(classType: ClassType): Type {
|
||||
asClass(classType: ClassInstance): Type {
|
||||
const ret: Type = new Type(this.kind, this.size);
|
||||
ret.classType = classType;
|
||||
return ret;
|
||||
@ -73,7 +74,7 @@ export class Type {
|
||||
case TypeKind.U16: return "u16";
|
||||
case TypeKind.U32: return "u32";
|
||||
case TypeKind.U64: return "u64";
|
||||
case TypeKind.USIZE: return this.classType && !kindOnly ? this.classType.toString() : "usize";
|
||||
case TypeKind.USIZE: return this.classType != null && !kindOnly ? this.classType.toString() : "usize";
|
||||
case TypeKind.BOOL: return "bool";
|
||||
case TypeKind.F32: return "f32";
|
||||
case TypeKind.F64: return "f64";
|
||||
@ -101,101 +102,12 @@ export class Type {
|
||||
static readonly void: Type = new Type(TypeKind.VOID, 0);
|
||||
}
|
||||
|
||||
export class FunctionType {
|
||||
|
||||
typeArguments: Type[];
|
||||
parameterTypes: Type[];
|
||||
returnType: Type;
|
||||
|
||||
additionalLocals: Type[];
|
||||
typeArgumentsMap: Map<string, Type> | null = null;
|
||||
locals: Map<string, LocalType> = new Map();
|
||||
breakContext: string | null = null;
|
||||
|
||||
private breakMajor: i32 = 0;
|
||||
private breakMinor: i32 = 0;
|
||||
|
||||
constructor(typeArguments: Type[], parameterTypes: Type[], returnType: Type, parameterNames: string[] | null = null) {
|
||||
this.typeArguments = typeArguments;
|
||||
this.parameterTypes = parameterTypes;
|
||||
this.returnType = returnType;
|
||||
this.additionalLocals = new Array();
|
||||
if (parameterNames) {
|
||||
if (parameterTypes.length != (<string[]>parameterNames).length)
|
||||
throw new Error("unexpected parameter count mismatch");;
|
||||
for (let i: i32 = 0, k: i32 = parameterTypes.length; i < k; ++i) {
|
||||
const name: string = (<string[]>parameterNames)[i];
|
||||
if (this.locals.has(name))
|
||||
throw new Error("duplicate parameter name");
|
||||
this.locals.set(name, new LocalType(i, parameterTypes[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enterBreakContext(): string {
|
||||
if (!this.breakMinor)
|
||||
this.breakMajor++;
|
||||
return this.breakContext = this.breakMajor.toString(10) + "." + (++this.breakMinor).toString(10);
|
||||
}
|
||||
|
||||
leaveBreakContext(): void {
|
||||
if (--this.breakMinor < 0)
|
||||
throw new Error("unexpected unbalanced break context");
|
||||
if (this.breakMinor == 0 && !--this.breakMajor)
|
||||
this.breakContext = null;
|
||||
}
|
||||
|
||||
addLocal(type: Type, name: string | null = null): i32 {
|
||||
// internal locals don't necessarily need names if referenced by index only
|
||||
if (name && this.locals.has(<string>name))
|
||||
throw new Error("duplicate local name");
|
||||
const index: i32 = this.parameterTypes.length + this.additionalLocals.length;
|
||||
this.additionalLocals.push(type);
|
||||
if (name)
|
||||
this.locals.set(<string>name, new LocalType(index, type));
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
export class LocalType {
|
||||
|
||||
index: i32;
|
||||
type: Type;
|
||||
|
||||
constructor(index: i32, type: Type) {
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class ClassType {
|
||||
|
||||
name: string;
|
||||
typeArguments: Type[];
|
||||
base: ClassType | null;
|
||||
|
||||
type: Type;
|
||||
typeArgumentsMap: Map<string, Type> | null = null;
|
||||
|
||||
constructor(name: string, usizeType: Type, typeArguments: Type[], base: ClassType | null = null) {
|
||||
this.name = name;
|
||||
this.typeArguments = typeArguments;
|
||||
this.base = base;
|
||||
this.type = usizeType.asClass(this);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let str: string = this.typeArguments.length ? this.name + typeArgumentsToString(this.typeArguments) : this.name;
|
||||
return this.type.nullable ? str + " | null" : str;
|
||||
}
|
||||
}
|
||||
|
||||
export function typeArgumentsToString(typeArguments: Type[]): string {
|
||||
const k: i32 = typeArguments.length;
|
||||
export function typesToString(types: Type[], prefix: string = "<", postfix: string = ">"): string {
|
||||
const k: i32 = types.length;
|
||||
if (!k)
|
||||
return "";
|
||||
sb.length = 0;
|
||||
for (let i: i32 = 0; i < k; ++i)
|
||||
sb[i] = typeArguments[i].toString();
|
||||
return "<" + sb.join(",") + ">";
|
||||
sb[i] = types[i].toString();
|
||||
return prefix + sb.join(",") + postfix;
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ export const enum CharCode {
|
||||
OGHAM = 0x1680,
|
||||
|
||||
_ = 0x5F,
|
||||
$ = 0x24,
|
||||
|
||||
_0 = 0x30,
|
||||
_1 = 0x31,
|
||||
@ -106,6 +105,7 @@ export const enum CharCode {
|
||||
CLOSEPAREN = 0x29,
|
||||
COLON = 0x3A,
|
||||
COMMA = 0x2C,
|
||||
DOLLAR = 0x24,
|
||||
DOT = 0x2E,
|
||||
DOUBLEQUOTE = 0x22,
|
||||
EQUALS = 0x3D,
|
||||
|
@ -511,6 +511,10 @@ export class U64 extends I64 {
|
||||
return false;
|
||||
}
|
||||
|
||||
get fitsInU32(): bool {
|
||||
return this.hi == 0;
|
||||
}
|
||||
|
||||
comp32(lo: i32, hi: i32): i32 {
|
||||
// uses both a cast and a js-like shift for portability
|
||||
return ((hi as u32 >>> 0) > (this.hi as u32 >>> 0)) || (hi == this.hi && (lo as u32 >>> 0) > (this.lo as u32 >>> 0)) ? -1 : 1;
|
||||
|
17
std/array.d.ts
vendored
Normal file
17
std/array.d.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
declare class Array<T> {
|
||||
length: i32;
|
||||
readonly capacity: i32;
|
||||
readonly data: usize;
|
||||
constructor(capacity: i32);
|
||||
}
|
||||
|
||||
declare class Int8Array extends Array<i8> {}
|
||||
declare class Int16Array extends Array<i16> {}
|
||||
declare class Int32Array extends Array<i32> {}
|
||||
declare class Uint8Array extends Array<u8> {}
|
||||
declare class Uint16Array extends Array<u16> {}
|
||||
declare class Uint32Array extends Array<u32> {}
|
||||
declare class Float32Array extends Array<f32> {}
|
||||
declare class Float64Array extends Array<f64> {}
|
17
std/array.ts
Normal file
17
std/array.ts
Normal file
@ -0,0 +1,17 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
@global()
|
||||
class Array<T> {
|
||||
|
||||
length: i32;
|
||||
readonly capacity: i32;
|
||||
readonly data: usize;
|
||||
|
||||
constructor(capacity: i32) {
|
||||
if (capacity < 0)
|
||||
throw new RangeError("capacity out of bounds");
|
||||
this.length = capacity;
|
||||
this.capacity = capacity;
|
||||
this.data = Memory.allocate(sizeof<T>() * capacity);
|
||||
}
|
||||
}
|
10
std/error.d.ts
vendored
Normal file
10
std/error.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
declare class Error {
|
||||
message: string;
|
||||
constructor(message: string);
|
||||
}
|
||||
|
||||
declare class RangeError extends Error {}
|
||||
declare class ReferenceError extends Error {}
|
||||
declare class TypeError extends Error {}
|
20
std/error.ts
Normal file
20
std/error.ts
Normal file
@ -0,0 +1,20 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
@global()
|
||||
class Error {
|
||||
|
||||
message: string;
|
||||
|
||||
constructor(message: string) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
@global()
|
||||
class RangeError extends Error {}
|
||||
|
||||
@global()
|
||||
class ReferenceError extends Error {}
|
||||
|
||||
@global()
|
||||
class TypeError extends Error {}
|
1
std/map.d.ts
vendored
Normal file
1
std/map.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
22
std/map.ts
Normal file
22
std/map.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
@global()
|
||||
class Map<K,V> {
|
||||
private keys: K[];
|
||||
private values: V[];
|
||||
|
||||
constructor() {
|
||||
this.keys = [];
|
||||
this.values = [];
|
||||
}
|
||||
|
||||
has(key: K): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
set(key: K, value: V): void {
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
}
|
||||
}
|
4
std/math.d.ts
vendored
Normal file
4
std/math.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
declare class Math {
|
||||
}
|
5
std/math.ts
Normal file
5
std/math.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
@global()
|
||||
class Math {
|
||||
}
|
8
std/memory.d.ts
vendored
Normal file
8
std/memory.d.ts
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
declare class Memory {
|
||||
static allocate(size: usize): usize;
|
||||
static free(ptr: usize): void;
|
||||
static copy(src: usize, dst: usize, count: usize): void;
|
||||
static compare(src: usize, dst: usize, count: usize): i32;
|
||||
}
|
27
std/memory.ts
Normal file
27
std/memory.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
@global()
|
||||
class Memory {
|
||||
|
||||
static allocate(size: usize): usize {
|
||||
const ptr: usize = load<usize>(sizeof<usize>());
|
||||
store<usize>(sizeof<usize>(), ptr + size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static free(ptr: usize): void {
|
||||
}
|
||||
|
||||
static copy(src: usize, dst: usize, count: usize): void {
|
||||
for (let i: usize = 0; i < count; ++i)
|
||||
store<u8>(dst + i, load<u8>(src + i));
|
||||
}
|
||||
|
||||
static compare(src: usize, dst: usize, count: usize): i32 {
|
||||
for (let i: usize = 0; i < count; ++i) {
|
||||
const d: i32 = (load<u8>(src + i) as i32) - (load<u8>(dst + i) as i32);
|
||||
if (d) return d;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
4
std/set.d.ts
vendored
Normal file
4
std/set.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
declare class Set<T> {
|
||||
}
|
5
std/set.ts
Normal file
5
std/set.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
@global()
|
||||
class Set<T> {
|
||||
}
|
11
std/string.d.ts
vendored
Normal file
11
std/string.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
declare class String {
|
||||
readonly length: i32;
|
||||
constructor(length: i32);
|
||||
static fromCharCode(c1: i32, c2?: i32);
|
||||
static equals(a: string, b: string): bool;
|
||||
static concat(a: string, b: string): string;
|
||||
charCodeAt(index: i32): u16;
|
||||
concat(other: string): string;
|
||||
}
|
58
std/string.ts
Normal file
58
std/string.ts
Normal file
@ -0,0 +1,58 @@
|
||||
/// <reference path="../assembly.d.ts" />
|
||||
|
||||
@global()
|
||||
@allocates()
|
||||
@operator("==", String.equals)
|
||||
@operator("!=", String.notEquals)
|
||||
@operator("+", String.concat)
|
||||
class String {
|
||||
|
||||
readonly length: i32;
|
||||
|
||||
constructor(length: i32) {
|
||||
if (length < 0)
|
||||
throw new RangeError("invalid length");
|
||||
const data: usize = Memory.allocate(4 + length);
|
||||
store<i32>(data, length);
|
||||
return classof<String>(data);
|
||||
}
|
||||
|
||||
static fromCharCode(c1: i32 /* sic */, c2: i32 = -1): String {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
static equals(a: String, b: String): bool {
|
||||
const aLength: i32 = a.length;
|
||||
return aLength == b.length && !Memory.compare(pointerof(a) + 4, pointerof(b) + 4, aLength << 1);
|
||||
}
|
||||
|
||||
static notEquals(a: String, b: String): bool {
|
||||
const aLength: i32 = a.length;
|
||||
return aLength != b.length || Memory.compare(pointerof(a) + 4, pointerof(b) + 4, aLength << 1);
|
||||
}
|
||||
|
||||
static concat(a: String, b: String): String {
|
||||
const aLength: i32 = a.length;
|
||||
const bLength: i32 = b.length;
|
||||
const combinedLength: i32 = aLength + bLength;
|
||||
if (combinedLength < 0)
|
||||
throw new RangeError("invalid length");
|
||||
const aByteLength: i32 = aLength << 1;
|
||||
const bByteLength: i32 = bLength << 1;
|
||||
const data: usize = Memory.allocate(4 + combinedLength);
|
||||
store<i32>(data, combinedLength);
|
||||
Memory.copy(pointerof(a) + 4, data + 4, aByteLength);
|
||||
Memory.copy(pointerof(b) + 4, data + 4 + aByteLength, bByteLength);
|
||||
return classof<String>(data);
|
||||
}
|
||||
|
||||
charCodeAt(index: i32): u16 {
|
||||
if (index < 0 || index > this.length)
|
||||
throw new RangeError("index out of bounds");
|
||||
return load<u32>(pointerof(this) + 4 + index << 1);
|
||||
}
|
||||
|
||||
concat(other: String): String {
|
||||
return String.concat(this, other);
|
||||
}
|
||||
}
|
15
std/tsconfig.json
Normal file
15
std/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noLib": true,
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"files": [
|
||||
"array.ts",
|
||||
"error.ts",
|
||||
"map.ts",
|
||||
"math.ts",
|
||||
"memory.ts",
|
||||
"set.ts",
|
||||
"string.ts"
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user