Lower identifiers

This changes the way we represent identifiers slightly, and then lowers
the TypeScript representation to them.  Prior to this change, identifiers
could be naked strings; after this change, identifiers are always first class
AST nodes -- so they can carry location information -- and tokens assume the
role of naked strings.  This means some definition maps must now be keyed by
tokens and also means that some AST node properties that used to carry just
naked strings needed to be updated to carry first class AST nodes.
This commit is contained in:
joeduffy 2017-01-10 07:21:03 -08:00
parent 478d1c3173
commit 2669f5b860
4 changed files with 38 additions and 31 deletions

View file

@ -1,6 +1,6 @@
// Copyright 2016 Marapongo, Inc. All rights reserved.
import {Node} from "./nodes";
import {Node, Identifier} from "./nodes";
import * as statements from "./statements";
import * as symbols from "../symbols";
@ -10,8 +10,8 @@ import * as symbols from "../symbols";
// A definition is something that possibly exported for external usage.
export interface Definition extends Node {
name: symbols.Identifier; // a required name, unique amongst definitions with a common parent.
description?: string; // an optional informative description.
name: Identifier; // a required name, unique amongst definitions with a common parent.
description?: string; // an optional informative description.
}
export function isDefinition(node: Node): boolean {
@ -38,13 +38,13 @@ export interface Module extends Definition {
}
export const moduleKind = "Module";
export type ModuleKind = "Module";
export type Modules = Map<symbols.Identifier, Definition>;
export type Modules = Map<symbols.Token, Definition>;
// A module member is a definition that belongs to a module.
export interface ModuleMember extends Definition {
access?: symbols.Accessibility;
}
export type ModuleMembers = Map<symbols.Identifier, ModuleMember>;
export type ModuleMembers = Map<symbols.ModuleToken, ModuleMember>;
/* Classes */
@ -67,7 +67,7 @@ export interface ClassMember extends Definition {
access?: symbols.ClassMemberAccessibility;
static?: boolean;
}
export type ClassMembers = Map<symbols.Identifier, ClassMember>;
export type ClassMembers = Map<symbols.TypeToken, ClassMember>;
/* Variables */
@ -81,7 +81,6 @@ export interface Variable extends Definition {
// A variable that is lexically scoped within a function (either a parameter or local).
export interface LocalVariable extends Variable {
kind: LocalVariableKind;
name: symbols.Identifier;
}
export const localVariableKind = "LocalVariable";
export type LocalVariableKind = "LocalVariable";

View file

@ -65,7 +65,7 @@ export type NodeKind =
export interface Identifier extends Node {
kind: IdentifierKind;
ident: symbols.Identifier;
ident: symbols.Token; // a valid identifier: (letter|"_") (letter | digit | "_")*
}
export const identifierKind = "Identifier";
export type IdentifierKind = "Identifier";

View file

@ -91,7 +91,7 @@ type ModuleElement = ast.Definition | ast.Statement;
// definitions, while any loose code (including variable initializers) is bundled into module inits and entrypoints.
function transformSourceFile(node: ts.SourceFile): ast.Module {
// All definitions will go into a map keyed by their identifier.
let definitions = new Map<symbols.Identifier, ast.Definition>();
let definitions = new Map<symbols.Token, ast.Definition>();
// Any top-level non-definition statements will pile up into the module initializer.
let statements: ast.Statement[] = [];
@ -102,7 +102,7 @@ function transformSourceFile(node: ts.SourceFile): ast.Module {
for (let element of elements) {
if (ast.isDefinition(element)) {
let defn: ast.Definition = <ast.Definition>element;
definitions.set(defn.name, defn);
definitions.set(defn.name.ident, defn)
}
else {
statements.push(<ast.Statement>element);
@ -114,19 +114,25 @@ function transformSourceFile(node: ts.SourceFile): ast.Module {
if (statements.length > 0) {
let initializer: ast.ModuleMethod = {
kind: ast.moduleMethodKind,
name: symbols.specialFunctionInitializer,
name: {
kind: ast.identifierKind,
ident: symbols.specialFunctionInitializer,
},
access: symbols.publicAccessibility,
body: {
kind: ast.blockKind,
statements: statements,
},
};
definitions.set(initializer.name, initializer);
definitions.set(initializer.name.ident, initializer);
}
return copyLocation(node, {
kind: ast.moduleKind,
name: node.moduleName,
name: <ast.Identifier>{
kind: ast.identifierKind,
ident: node.moduleName,
},
members: definitions,
});
}
@ -172,7 +178,10 @@ function transformExportStatement(node: ts.Statement): ModuleElement[] {
if (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Default) {
contract.assert(elements.length === 1);
contract.assert(elements[0].kind === ast.moduleMethodKind || elements[0].kind === ast.classKind);
(<ast.Definition>elements[0]).name = defaultExport;
(<ast.Definition>elements[0]).name = {
kind: ast.identifierKind,
ident: defaultExport,
};
}
return elements;
@ -322,6 +331,15 @@ function transformDeclarationName(node: ts.DeclarationName): ast.Expression {
}
}
function transformDeclarationIdentifier(node: ts.DeclarationName): ast.Identifier {
switch (node.kind) {
case ts.SyntaxKind.Identifier:
return transformIdentifierExpression(node);
default:
return contract.failf(`Unrecognized declaration identifier: ${ts.SyntaxKind[node.kind]}`);
}
}
function transformFunctionDeclaration(node: ts.FunctionDeclaration, access: symbols.Accessibility): ast.Function {
return contract.failf("NYI");
}
@ -360,7 +378,7 @@ function makeVariableInitializer(variable: VariableDeclaration): ast.Statement {
kind: ast.binaryOperatorExpressionKind,
left: <ast.LoadVariableExpression>{
kind: ast.loadVariableExpressionKind,
variable: variable.local.name,
variable: variable.local.name.ident,
},
operator: "=",
right: variable.initializer,
@ -436,25 +454,15 @@ function transformModuleVariableStatement(node: ts.VariableStatement, access: sy
}
function transformVariableDeclaration(node: ts.VariableDeclaration): VariableDeclaration {
let name: string;
let nameExpression: ast.Expression = transformDeclarationName(node.name);
if (nameExpression.kind === ast.stringLiteralKind) {
name = (<ast.StringLiteral>nameExpression).value;
}
else {
return contract.failf("NYI");
}
let initializer: ast.Expression | undefined;
if (node.initializer) {
initializer = transformExpression(node.initializer);
}
return {
node: node,
local: {
kind: ast.localVariableKind,
name: name,
name: transformDeclarationIdentifier(node.name),
type: "TODO",
},
initializer: initializer,
@ -1034,8 +1042,11 @@ function transformComputedPropertyName(node: ts.ComputedPropertyName): ast.Expre
return contract.failf("NYI");
}
function transformIdentifierExpression(node: ts.Identifier): ast.Expression {
return contract.failf("NYI");
function transformIdentifierExpression(node: ts.Identifier): ast.Identifier {
return copyLocation(node, {
kind: ast.identifierKind,
ident: node.text,
});
}
function transformObjectBindingPattern(node: ts.ObjectBindingPattern): ast.Expression {

View file

@ -7,9 +7,6 @@ export type TypeToken = Token; // a symbol token that resolves to a type.
export type VariableToken = Token; // a symbol token that resolves to a variable.
export type FunctionToken = Token; // a symbol token that resolves to a function.
// Identifiers.
export type Identifier = string; // a valid identifier: (letter|"_") (letter | digit | "_")*
// Accessibility modifiers.
export type Accessibility = "public" | "private"; // accessibility modifiers common to all.
export type ClassMemberAccessibility = Accessibility | "protected"; // accessibility modifiers for class members.