Program elements and resolve infrastructure; Stdlib ideas; Restructuring

This commit is contained in:
dcodeIO 2017-10-19 18:55:27 +02:00
parent 6e98c52f76
commit d1c1178f25
28 changed files with 1474 additions and 683 deletions

107
assembly.d.ts vendored Normal file
View 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" />

View File

@ -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 {

File diff suppressed because it is too large Load Diff

8
src/constants.ts Normal file
View 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 = ".";

View File

@ -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 "";

View File

@ -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

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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"
]
}
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
/// <reference path="../assembly.d.ts" />

22
std/map.ts Normal file
View 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
View File

@ -0,0 +1,4 @@
/// <reference path="../assembly.d.ts" />
declare class Math {
}

5
std/math.ts Normal file
View File

@ -0,0 +1,5 @@
/// <reference path="../assembly.d.ts" />
@global()
class Math {
}

8
std/memory.d.ts vendored Normal file
View 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
View 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
View File

@ -0,0 +1,4 @@
/// <reference path="../assembly.d.ts" />
declare class Set<T> {
}

5
std/set.ts Normal file
View File

@ -0,0 +1,5 @@
/// <reference path="../assembly.d.ts" />
@global()
class Set<T> {
}

11
std/string.d.ts vendored Normal file
View 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
View 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
View 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"
]
}