1
0
mirror of https://github.com/fluencelabs/assemblyscript synced 2025-06-19 01:41:30 +00:00

Support 'import * as' directives, see

This commit is contained in:
dcodeIO
2018-06-12 00:45:19 +02:00
parent f2eb64c0fd
commit 25b433dca9
9 changed files with 213 additions and 51 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -602,7 +602,7 @@ export abstract class Node {
} }
static createExportStatement( static createExportStatement(
members: ExportMember[], members: ExportMember[] | null,
path: StringLiteralExpression | null, path: StringLiteralExpression | null,
flags: CommonFlags, flags: CommonFlags,
range: Range range: Range
@ -610,7 +610,7 @@ export abstract class Node {
var stmt = new ExportStatement(); var stmt = new ExportStatement();
stmt.range = range; stmt.range = range;
stmt.flags = flags; stmt.flags = flags;
stmt.members = members; setParent(members, stmt); stmt.members = members; if (members) setParent(members, stmt);
stmt.path = path; stmt.path = path;
if (path) { if (path) {
let normalizedPath = normalizePath(path.value); let normalizedPath = normalizePath(path.value);
@ -1627,8 +1627,8 @@ export class ExportMember extends Node {
export class ExportStatement extends Statement { export class ExportStatement extends Statement {
kind = NodeKind.EXPORT; kind = NodeKind.EXPORT;
/** Array of members. */ /** Array of members if a set of named exports, or `null` if a filespace export. */
members: ExportMember[]; members: ExportMember[] | null;
/** Path being exported from, if applicable. */ /** Path being exported from, if applicable. */
path: StringLiteralExpression | null; path: StringLiteralExpression | null;
/** Normalized path, if `path` is set. */ /** Normalized path, if `path` is set. */

@ -1097,8 +1097,9 @@ export class Compiler extends DiagnosticEmitter {
compileExportStatement(statement: ExportStatement): void { compileExportStatement(statement: ExportStatement): void {
var module = this.module; var module = this.module;
var exports = this.program.fileLevelExports; var fileLevelExports = this.program.fileLevelExports;
var members = statement.members; var members = statement.members;
if (!members) return; // filespace
for (let i = 0, k = members.length; i < k; ++i) { for (let i = 0, k = members.length; i < k; ++i) {
let member = members[i]; let member = members[i];
let internalExportName = ( let internalExportName = (
@ -1106,7 +1107,7 @@ export class Compiler extends DiagnosticEmitter {
PATH_DELIMITER + PATH_DELIMITER +
member.externalName.text member.externalName.text
); );
let element = exports.get(internalExportName); let element = fileLevelExports.get(internalExportName);
if (!element) continue; // reported in Program#initialize if (!element) continue; // reported in Program#initialize
switch (element.kind) { switch (element.kind) {
case ElementKind.CLASS_PROTOTYPE: { case ElementKind.CLASS_PROTOTYPE: {

@ -1915,6 +1915,7 @@ export class Parser extends DiagnosticEmitter {
// at 'export': '{' ExportMember (',' ExportMember)* }' ('from' StringLiteral)? ';'? // at 'export': '{' ExportMember (',' ExportMember)* }' ('from' StringLiteral)? ';'?
var path: StringLiteralExpression | null = null;
if (tn.skip(Token.OPENBRACE)) { if (tn.skip(Token.OPENBRACE)) {
let members = new Array<ExportMember>(); let members = new Array<ExportMember>();
while (!tn.skip(Token.CLOSEBRACE)) { while (!tn.skip(Token.CLOSEBRACE)) {
@ -1933,7 +1934,6 @@ export class Parser extends DiagnosticEmitter {
} }
} }
} }
let path: StringLiteralExpression | null = null;
if (tn.skip(Token.FROM)) { if (tn.skip(Token.FROM)) {
if (tn.skip(Token.STRINGLITERAL)) { if (tn.skip(Token.STRINGLITERAL)) {
path = Node.createStringLiteralExpression(tn.readString(), tn.range()); path = Node.createStringLiteralExpression(tn.readString(), tn.range());
@ -1953,6 +1953,30 @@ export class Parser extends DiagnosticEmitter {
} }
tn.skip(Token.SEMICOLON); tn.skip(Token.SEMICOLON);
return ret; return ret;
} else if (tn.skip(Token.ASTERISK)) {
if (tn.skip(Token.FROM)) {
if (tn.skip(Token.STRINGLITERAL)) {
path = Node.createStringLiteralExpression(tn.readString(), tn.range());
let ret = Node.createExportStatement(null, path, flags, tn.range(startPos, tn.pos));
let internalPath = ret.internalPath;
if (internalPath !== null && !this.seenlog.has(internalPath)) {
this.backlog.push(internalPath);
this.seenlog.add(internalPath);
}
tn.skip(Token.SEMICOLON);
return ret;
} else {
this.error(
DiagnosticCode.String_literal_expected,
tn.range()
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "from"
);
}
} else { } else {
this.error( this.error(
DiagnosticCode._0_expected, DiagnosticCode._0_expected,
@ -2805,8 +2829,8 @@ export class Parser extends DiagnosticEmitter {
return Node.createFalseExpression(tn.range()); return Node.createFalseExpression(tn.range());
} }
var p = determinePrecedenceStart(token); var precedence = determinePrecedenceStart(token);
if (p != Precedence.INVALID) { if (precedence != Precedence.INVALID) {
let operand: Expression | null; let operand: Expression | null;
// TODO: SpreadExpression, YieldExpression (currently become unsupported UnaryPrefixExpressions) // TODO: SpreadExpression, YieldExpression (currently become unsupported UnaryPrefixExpressions)
@ -2830,7 +2854,7 @@ export class Parser extends DiagnosticEmitter {
} }
return null; return null;
} else { } else {
operand = this.parseExpression(tn, p); operand = this.parseExpression(tn, precedence);
if (!operand) return null; if (!operand) return null;
} }
@ -3328,7 +3352,7 @@ export const enum Precedence {
} }
/** Determines the precedence of a starting token. */ /** Determines the precedence of a starting token. */
function determinePrecedenceStart(kind: Token): i32 { function determinePrecedenceStart(kind: Token): Precedence {
switch (kind) { switch (kind) {
case Token.DOT_DOT_DOT: return Precedence.SPREAD; case Token.DOT_DOT_DOT: return Precedence.SPREAD;
case Token.YIELD: return Precedence.YIELD; case Token.YIELD: return Precedence.YIELD;
@ -3347,7 +3371,7 @@ function determinePrecedenceStart(kind: Token): i32 {
} }
/** Determines the precende of a non-starting token. */ /** Determines the precende of a non-starting token. */
function determinePrecedence(kind: Token): i32 { function determinePrecedence(kind: Token): Precedence {
switch (kind) { switch (kind) {
case Token.COMMA: return Precedence.COMMA; case Token.COMMA: return Precedence.COMMA;
case Token.EQUALS: case Token.EQUALS:

@ -120,6 +120,8 @@ export const INNER_DELIMITER = "~";
export const LIBRARY_SUBST = "~lib"; export const LIBRARY_SUBST = "~lib";
/** Library directory prefix. */ /** Library directory prefix. */
export const LIBRARY_PREFIX = LIBRARY_SUBST + PATH_DELIMITER; export const LIBRARY_PREFIX = LIBRARY_SUBST + PATH_DELIMITER;
/** Prefix used to indicate a filespace element. */
export const FILESPACE_PREFIX = "file:";
/** Represents a yet unresolved export. */ /** Represents a yet unresolved export. */
class QueuedExport { class QueuedExport {
@ -133,7 +135,7 @@ class QueuedImport {
internalName: string; internalName: string;
referencedName: string; referencedName: string;
referencedNameAlt: string; referencedNameAlt: string;
declaration: ImportDeclaration; declaration: ImportDeclaration | null; // not set if a filespace
} }
/** Represents a type alias. */ /** Represents a type alias. */
@ -338,6 +340,8 @@ export class Program extends DiagnosticEmitter {
resolvedThisExpression: Expression | null = null; resolvedThisExpression: Expression | null = null;
/** Element expression of the previously resolved element access. */ /** Element expression of the previously resolved element access. */
resolvedElementExpression : Expression | null = null; resolvedElementExpression : Expression | null = null;
/** Currently processing filespace. */
currentFilespace: Filespace;
/** Constructs a new program, optionally inheriting parser diagnostics. */ /** Constructs a new program, optionally inheriting parser diagnostics. */
constructor(diagnostics: DiagnosticMessage[] | null = null) { constructor(diagnostics: DiagnosticMessage[] | null = null) {
@ -357,11 +361,12 @@ export class Program extends DiagnosticEmitter {
/** Looks up the source for the specified possibly ambiguous path. */ /** Looks up the source for the specified possibly ambiguous path. */
lookupSourceByPath(normalizedPathWithoutExtension: string): Source | null { lookupSourceByPath(normalizedPathWithoutExtension: string): Source | null {
var tmp: string;
return ( return (
this.getSource(normalizedPathWithoutExtension + ".ts") || this.getSource(normalizedPathWithoutExtension + ".ts") ||
this.getSource(normalizedPathWithoutExtension + "/index.ts") || this.getSource(normalizedPathWithoutExtension + "/index.ts") ||
this.getSource(LIBRARY_PREFIX + normalizedPathWithoutExtension + ".ts") || this.getSource((tmp = LIBRARY_PREFIX + normalizedPathWithoutExtension) + ".ts") ||
this.getSource(LIBRARY_PREFIX + normalizedPathWithoutExtension + "/index.ts") this.getSource( tmp + "/index.ts")
); );
} }
@ -395,6 +400,13 @@ export class Program extends DiagnosticEmitter {
// build initial lookup maps of internal names to declarations // build initial lookup maps of internal names to declarations
for (let i = 0, k = this.sources.length; i < k; ++i) { for (let i = 0, k = this.sources.length; i < k; ++i) {
let source = this.sources[i]; let source = this.sources[i];
// create one filespace per source
let filespace = new Filespace(this, source);
this.elementsLookup.set(filespace.internalName, filespace);
this.currentFilespace = filespace;
// process this source's statements
let statements = source.statements; let statements = source.statements;
for (let j = 0, l = statements.length; j < l; ++j) { for (let j = 0, l = statements.length; j < l; ++j) {
let statement = statements[j]; let statement = statements[j];
@ -442,22 +454,39 @@ export class Program extends DiagnosticEmitter {
// queued imports should be resolvable now through traversing exports and queued exports // queued imports should be resolvable now through traversing exports and queued exports
for (let i = 0; i < queuedImports.length;) { for (let i = 0; i < queuedImports.length;) {
let queuedImport = queuedImports[i]; let queuedImport = queuedImports[i];
let element = this.tryResolveImport(queuedImport.referencedName, queuedExports); let declaration = queuedImport.declaration;
if (element) { if (declaration) { // named
this.elementsLookup.set(queuedImport.internalName, element); let element = this.tryResolveImport(queuedImport.referencedName, queuedExports);
queuedImports.splice(i, 1); if (element) {
} else {
if (element = this.tryResolveImport(queuedImport.referencedNameAlt, queuedExports)) {
this.elementsLookup.set(queuedImport.internalName, element); this.elementsLookup.set(queuedImport.internalName, element);
queuedImports.splice(i, 1); queuedImports.splice(i, 1);
} else { } else {
this.error( if (element = this.tryResolveImport(queuedImport.referencedNameAlt, queuedExports)) {
DiagnosticCode.Module_0_has_no_exported_member_1, this.elementsLookup.set(queuedImport.internalName, element);
queuedImport.declaration.range, queuedImports.splice(i, 1);
(<ImportStatement>queuedImport.declaration.parent).path.value, } else {
queuedImport.declaration.externalName.text this.error(
); DiagnosticCode.Module_0_has_no_exported_member_1,
++i; declaration.range,
(<ImportStatement>declaration.parent).path.value,
declaration.externalName.text
);
++i;
}
}
} else { // filespace
let element = this.elementsLookup.get(queuedImport.referencedName);
if (element) {
this.elementsLookup.set(queuedImport.internalName, element);
queuedImports.splice(i, 1);
} else {
if (element = this.elementsLookup.get(queuedImport.referencedNameAlt)) {
this.elementsLookup.set(queuedImport.internalName, element);
queuedImports.splice(i, 1);
} else {
assert(false); // already reported by the parser not finding the file
++i;
}
} }
} }
} }
@ -732,6 +761,7 @@ export class Program extends DiagnosticEmitter {
return; return;
} }
this.fileLevelExports.set(internalName, prototype); this.fileLevelExports.set(internalName, prototype);
this.currentFilespace.members.set(simpleName, prototype);
if (prototype.is(CommonFlags.EXPORT) && declaration.range.source.isEntry) { if (prototype.is(CommonFlags.EXPORT) && declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(internalName)) { if (this.moduleLevelExports.has(internalName)) {
this.error( this.error(
@ -1165,6 +1195,7 @@ export class Program extends DiagnosticEmitter {
return; return;
} }
this.fileLevelExports.set(internalName, element); this.fileLevelExports.set(internalName, element);
this.currentFilespace.members.set(simpleName, element);
if (declaration.range.source.isEntry) { if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(internalName)) { if (this.moduleLevelExports.has(internalName)) {
this.error( this.error(
@ -1215,26 +1246,45 @@ export class Program extends DiagnosticEmitter {
queuedExports: Map<string,QueuedExport> queuedExports: Map<string,QueuedExport>
): void { ): void {
var members = statement.members; var members = statement.members;
for (let i = 0, k = members.length; i < k; ++i) { if (members) { // named
this.initializeExport(members[i], statement.internalPath, queuedExports); for (let i = 0, k = members.length; i < k; ++i) {
this.initializeExport(members[i], statement.internalPath, queuedExports);
}
} else { // TODO: filespace
this.error(
DiagnosticCode.Operation_not_supported,
statement.range
);
} }
} }
private setExportAndCheckLibrary( private setExportAndCheckLibrary(
name: string, internalName: string,
element: Element, element: Element,
identifier: IdentifierExpression identifier: IdentifierExpression
): void { ): void {
this.fileLevelExports.set(name, element); // add to file-level exports
if (identifier.range.source.isLibrary) { // add global alias this.fileLevelExports.set(internalName, element);
if (this.elementsLookup.has(identifier.text)) {
// add to filespace
var internalPath = identifier.range.source.internalPath;
var prefix = FILESPACE_PREFIX + internalPath;
var filespace = this.elementsLookup.get(prefix);
if (!filespace) filespace = assert(this.elementsLookup.get(prefix + PATH_DELIMITER + "index"));
assert(filespace.kind == ElementKind.FILESPACE);
var simpleName = identifier.text;
(<Filespace>filespace).members.set(simpleName, element);
// add global alias if from a library file
if (identifier.range.source.isLibrary) {
if (this.elementsLookup.has(simpleName)) {
this.error( this.error(
DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0, DiagnosticCode.Export_declaration_conflicts_with_exported_declaration_of_0,
identifier.range, identifier.text identifier.range, simpleName
); );
} else { } else {
element.internalName = identifier.text; element.internalName = simpleName;
this.elementsLookup.set(identifier.text, element); this.elementsLookup.set(simpleName, element);
} }
} }
} }
@ -1401,6 +1451,7 @@ export class Program extends DiagnosticEmitter {
return; return;
} }
this.fileLevelExports.set(internalName, prototype); this.fileLevelExports.set(internalName, prototype);
this.currentFilespace.members.set(simpleName, prototype);
if (declaration.range.source.isEntry) { if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(internalName)) { if (this.moduleLevelExports.has(internalName)) {
this.error( this.error(
@ -1446,10 +1497,22 @@ export class Program extends DiagnosticEmitter {
); );
return; return;
} }
this.error( // TODO
DiagnosticCode.Operation_not_supported, // resolve right away if the exact filespace exists
statement.range let filespace = this.elementsLookup.get(statement.internalPath);
); if (filespace) {
this.elementsLookup.set(internalName, filespace);
return;
}
// otherwise queue it
let queuedImport = new QueuedImport();
queuedImport.internalName = internalName;
let prefix = FILESPACE_PREFIX + statement.internalPath;
queuedImport.referencedName = prefix;
queuedImport.referencedNameAlt = prefix + PATH_DELIMITER + "index";
queuedImport.declaration = null;
queuedImports.push(queuedImport);
} }
} }
@ -1511,9 +1574,10 @@ export class Program extends DiagnosticEmitter {
} }
var decorators = declaration.decorators; var decorators = declaration.decorators;
var simpleName = declaration.name.text;
var prototype = new InterfacePrototype( var prototype = new InterfacePrototype(
this, this,
declaration.name.text, simpleName,
internalName, internalName,
declaration, declaration,
decorators decorators
@ -1548,6 +1612,7 @@ export class Program extends DiagnosticEmitter {
return; return;
} }
this.fileLevelExports.set(internalName, prototype); this.fileLevelExports.set(internalName, prototype);
this.currentFilespace.members.set(simpleName, prototype);
if (declaration.range.source.isEntry) { if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(internalName)) { if (this.moduleLevelExports.has(internalName)) {
this.error( this.error(
@ -1632,6 +1697,7 @@ export class Program extends DiagnosticEmitter {
} else { } else {
this.fileLevelExports.set(internalName, namespace); this.fileLevelExports.set(internalName, namespace);
} }
this.currentFilespace.members.set(simpleName, namespace);
if (declaration.range.source.isEntry) { if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(internalName)) { if (this.moduleLevelExports.has(internalName)) {
this.error( this.error(
@ -1759,6 +1825,7 @@ export class Program extends DiagnosticEmitter {
} else { } else {
this.fileLevelExports.set(internalName, global); this.fileLevelExports.set(internalName, global);
} }
this.currentFilespace.members.set(simpleName, global);
if (declaration.range.source.isEntry) { if (declaration.range.source.isEntry) {
if (this.moduleLevelExports.has(internalName)) { if (this.moduleLevelExports.has(internalName)) {
this.error( this.error(
@ -2144,11 +2211,13 @@ export class Program extends DiagnosticEmitter {
} }
default: { // enums or other namespace-like elements default: { // enums or other namespace-like elements
let members = target.members; let members = target.members;
let member: Element | null; if (members) {
if (members && (member = members.get(propertyName))) { let member = members.get(propertyName);
this.resolvedThisExpression = targetExpression; if (member) {
this.resolvedElementExpression = null; this.resolvedThisExpression = targetExpression;
return member; // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE... this.resolvedElementExpression = null;
return member; // static ENUMVALUE, static GLOBAL, static FUNCTION_PROTOTYPE...
}
} }
break; break;
} }
@ -2373,7 +2442,9 @@ export enum ElementKind {
/** A {@link Property}. */ /** A {@link Property}. */
PROPERTY, PROPERTY,
/** A {@link Namespace}. */ /** A {@link Namespace}. */
NAMESPACE NAMESPACE,
/** A {@link Filespace}. */
FILESPACE,
} }
/** Indicates traits of a {@link Node} or {@link Element}. */ /** Indicates traits of a {@link Node} or {@link Element}. */
@ -2515,7 +2586,25 @@ export abstract class Element {
hasDecorator(flag: DecoratorFlags): bool { return (this.decoratorFlags & flag) == flag; } hasDecorator(flag: DecoratorFlags): bool { return (this.decoratorFlags & flag) == flag; }
} }
/** A namespace. */ /** A filespace representing the implicit top-level namespace of a source. */
export class Filespace extends Element {
kind = ElementKind.FILESPACE;
/** File members (externally visible only). */
members: Map<string,Element>; // more specific
/** Constructs a new filespace. */
constructor(
program: Program,
source: Source
) {
super(program, source.internalPath, FILESPACE_PREFIX + source.internalPath);
this.members = new Map();
}
}
/** A namespace that differs from a filespace in being user-declared with a name. */
export class Namespace extends Element { export class Namespace extends Element {
// All elements have namespace semantics. This is an explicitly declared one. // All elements have namespace semantics. This is an explicitly declared one.

@ -45,5 +45,24 @@
) )
) )
(call $export/ns.two) (call $export/ns.two)
(drop
(i32.add
(i32.add
(call $export/add
(i32.const 1)
(i32.const 2)
)
(call $export/sub
(i32.const 2)
(i32.const 3)
)
)
(call $export/mul
(i32.const 3)
(i32.const 1)
)
)
)
(call $export/ns.two)
) )
) )

@ -1,3 +1,5 @@
/* tslint:disable:no-duplicate-imports */
import { import {
add, add,
sub as sub, sub as sub,
@ -11,3 +13,11 @@ import {
add(a, b) + sub(b, c) + mul(c, a); add(a, b) + sub(b, c) + mul(c, a);
renamed_ns.two(); renamed_ns.two();
import * as other from "./export";
other.add(other.a, other.b) +
other.sub(other.b, other.renamed_c) +
other.renamed_mul(other.renamed_c, other.a);
other.ns.two();

@ -55,5 +55,24 @@
) )
) )
(call $export/ns.two) (call $export/ns.two)
(drop
(i32.add
(i32.add
(call $export/add
(i32.const 1)
(i32.const 2)
)
(call $export/sub
(i32.const 2)
(i32.const 3)
)
)
(call $export/mul
(i32.const 3)
(i32.const 1)
)
)
)
(call $export/ns.two)
) )
) )