Add basic type mapping

This change adds basic type mapping using bound node information for the
primitive types (string, number, bool, void, any).
This commit is contained in:
joeduffy 2017-01-13 08:23:22 -08:00
parent 048557ca79
commit 3c8990f720
14 changed files with 84 additions and 48 deletions

View file

@ -66,7 +66,7 @@ export type ClassMembers = { [token: string /*symbols.TypeToken*/]: ClassMember
// A variable is a typed storage location.
export interface Variable extends Definition {
type: symbols.TypeToken;
type?: symbols.TypeToken;
default?: any; // a trivially serializable default value.
readonly?: boolean;
}

View file

@ -109,7 +109,7 @@ type ModuleReference = string;
// between them. This facilitates code reuse in the translation passes.
interface VariableLikeDeclaration {
name: ast.Identifier;
type: symbols.TypeToken;
type?: symbols.TypeToken;
readonly?: boolean;
legacyVar?: boolean;
initializer?: ast.Expression;
@ -124,6 +124,11 @@ interface FunctionLikeDeclaration {
returnType?: symbols.TypeToken;
}
// TypeLike is any interface that has a possible TypeNode attached to it and can be queried for binding information.
interface TypeLike extends ts.Node {
type?: ts.TypeNode;
}
function ident(id: string): ast.Identifier {
return {
kind: ast.identifierKind,
@ -345,13 +350,39 @@ export class Transformer {
return exports;
}
private transformIdentifier(node: ts.Identifier): ast.Identifier {
return this.withLocation(node, ident(node.text));
// resolveTypeToken takes a TypeScript AST node that carries possible typing information and resolves it to a fully
// qualified MuIL type token name.
private resolveTypeToken(node: TypeLike): symbols.TypeToken | undefined {
if (node) {
let ty: ts.Type = this.checker().getTypeAtLocation(node);
contract.assert(!!ty);
if (ty.flags & ts.TypeFlags.Any) {
return symbols.anyType;
}
else if (ty.flags & ts.TypeFlags.String) {
return symbols.stringType;
}
else if (ty.flags & ts.TypeFlags.Number) {
return symbols.numberType;
}
else if (ty.flags & ts.TypeFlags.Boolean) {
return symbols.boolType;
}
else if (ty.flags & ts.TypeFlags.Void) {
return undefined;
}
// TODO[marapongo/mu#36]: detect more cases (including complex types).
}
// If none of those matched, simply default to the weakly typed "any" type.
return symbols.anyType;
}
private transformTypeNode(node: ts.TypeNode | undefined): symbols.TypeToken {
// TODO[marapongo/mu#46]: emit strong typing information.
return "any";
private transformIdentifier(node: ts.Identifier): ast.Identifier {
return this.withLocation(node, ident(node.text));
}
/** Modules **/
@ -407,7 +438,7 @@ export class Transformer {
if (statements.length > 0) {
let initializer: ast.ModuleMethod = {
kind: ast.moduleMethodKind,
name: ident(symbols.specialFunctionInitializer),
name: ident(symbols.initializerFunction),
access: symbols.publicAccessibility,
body: {
kind: ast.blockKind,
@ -796,12 +827,12 @@ export class Transformer {
if (propertyInitializers.length > 0) {
// Locate the constructor, possibly fabricating one if necessary.
let ctor: ast.ClassMethod | undefined =
<ast.ClassMethod>members[symbols.specialFunctionConstructor];
<ast.ClassMethod>members[symbols.constructorFunction];
if (!ctor) {
// TODO: once we support base classes, inject a call to super() at the front.
ctor = members[symbols.specialFunctionConstructor] = <ast.ClassMethod>{
ctor = members[symbols.constructorFunction] = <ast.ClassMethod>{
kind: ast.classMethodKind,
name: ident(symbols.specialFunctionConstructor),
name: ident(symbols.constructorFunction),
};
}
if (!ctor.body) {
@ -897,7 +928,7 @@ export class Transformer {
}
else if (node.kind === ts.SyntaxKind.Constructor) {
// Constructors have a special name.
name = ident(symbols.specialFunctionConstructor);
name = ident(symbols.constructorFunction);
}
else {
// All others are assumed to be default exports.
@ -920,7 +951,7 @@ export class Transformer {
name: name,
parameters: parameters.map((p: VariableDeclaration<ast.LocalVariable>) => p.variable),
body: body,
returnType: this.transformTypeNode(node.type),
returnType: this.resolveTypeToken(node),
};
}
@ -993,7 +1024,7 @@ export class Transformer {
variable: {
kind: ast.localVariableKind,
name: name,
type: this.transformTypeNode(node.type),
type: this.resolveTypeToken(node),
},
initializer: initializer,
};
@ -1006,7 +1037,7 @@ export class Transformer {
kind: ast.classKind,
name: this.transformIdentifier(node.name),
access: access,
extends: this.transformTypeNode(node.type),
extends: this.resolveTypeToken(node),
});
}
@ -1111,7 +1142,7 @@ export class Transformer {
}
return {
name: name,
type: this.transformTypeNode(node.type),
type: this.resolveTypeToken(node),
initializer: initializer,
};
}
@ -1199,7 +1230,6 @@ export class Transformer {
if (node.initializer) {
initializer = this.transformExpression(node.initializer);
}
let mods: ts.ModifierFlags = ts.getCombinedModifierFlags(node);
// TODO: primary properties.
return new VariableDeclaration<ast.ClassProperty>(
@ -1210,7 +1240,7 @@ export class Transformer {
access: this.getClassAccessibility(node),
readonly: !!(mods & ts.ModifierFlags.Readonly),
static: !!(mods & ts.ModifierFlags.Static),
type: this.transformTypeNode(node.type),
type: this.resolveTypeToken(node),
},
false,
initializer,
@ -1468,7 +1498,7 @@ export class Transformer {
private transformArrayLiteralExpression(node: ts.ArrayLiteralExpression): ast.ArrayLiteral {
return this.withLocation(node, <ast.ArrayLiteral>{
kind: ast.arrayLiteralKind,
type: this.transformTypeNode(undefined),
type: symbols.anyType, // TODO[marapongo/mu#46]: come up with a type.
elements: node.elements.map((expr: ts.Expression) => this.transformExpression(expr)),
});
}
@ -1620,7 +1650,7 @@ export class Transformer {
// always be encased in something that prepares it for dynamic cast in the consuming expression.
return this.withLocation(node, <ast.ObjectLiteral>{
kind: ast.objectLiteralKind,
type: this.transformTypeNode(undefined),
type: symbols.anyType, // TODO[marapongo/mu#46]: come up with a type.
properties: node.properties.map(
(prop: ts.ObjectLiteralElement) => this.transformObjectLiteralElement(prop)),
});
@ -1733,7 +1763,7 @@ export class Transformer {
private transformSuperExpression(node: ts.SuperExpression): ast.LoadLocationExpression {
return this.withLocation(node, <ast.LoadLocationExpression>{
kind: ast.loadLocationExpressionKind,
name: ident(symbols.specialVariableSuper),
name: ident(symbols.superVariable),
});
}
@ -1748,7 +1778,7 @@ export class Transformer {
private transformThisExpression(node: ts.ThisExpression): ast.LoadLocationExpression {
return this.withLocation(node, <ast.LoadLocationExpression>{
kind: ast.loadLocationExpressionKind,
name: ident(symbols.specialVariableThis),
name: ident(symbols.thisVariable),
});
}

View file

@ -20,11 +20,17 @@ export const privateAccessibility: Accessibility = "private";
export const protectedAccessibility: ClassMemberAccessibility = "protected";
// Special variable tokens.
export const specialVariableThis: VariableToken = ".this"; // the current object (for class methods).
export const specialVariableSuper: VariableToken = ".super"; // the parent class object (for class methods).
export const thisVariable: VariableToken = ".this"; // the current object (for class methods).
export const superVariable: VariableToken = ".super"; // the parent class object (for class methods).
// Special function tokens.
export const specialFunctionEntryPoint: FunctionToken = ".main"; // the special package entrypoint function.
export const specialFunctionInitializer: FunctionToken = ".init"; // the special module/class initialize function.
export const specialFunctionConstructor: FunctionToken = ".ctor"; // the special class instance constructor function.
export const entryPointFunction: FunctionToken = ".main"; // the special package entrypoint function.
export const initializerFunction: FunctionToken = ".init"; // the special module/class initialize function.
export const constructorFunction: FunctionToken = ".ctor"; // the special class instance constructor function.
// Special type tokens.
export const anyType: TypeToken = "any";
export const stringType: TypeToken = "string";
export const numberType: TypeToken = "number";
export const boolType: TypeToken = "bool";

View file

@ -97,11 +97,11 @@ describe("outputs", () => {
});
function compareLines(actuals: string[], expects: string[], label: string): void {
let mismatches: { num: number, actual: string, expect: string }[] = [];
let mismatches: { line: number, actual: string, expect: string }[] = [];
for (let i = 0; i < actuals.length && i < expects.length; i++) {
if (actuals[i] !== expects[i]) {
mismatches.push({
num: i,
line: i+1,
actual: actuals[i],
expect: expects[i],
});
@ -112,8 +112,8 @@ function compareLines(actuals: string[], expects: string[], label: string): void
let expect: string = "";
let actual: string = "";
for (let mismatch of mismatches) {
actual += `${mismatch.num}: ${mismatch.actual}${os.EOL}`;
expect += `${mismatch.num}: ${mismatch.expect}${os.EOL}`;
actual += `${mismatch.line}: ${mismatch.actual}${os.EOL}`;
expect += `${mismatch.line}: ${mismatch.expect}${os.EOL}`;
}
assert.strictEqual(actual, expect, `Expected ${label} to match; ${mismatches.length} did not`);
}

View file

@ -90,7 +90,7 @@
}
},
"access": "public",
"type": "any"
"type": "number"
},
".init": {
"kind": "ModuleMethod",
@ -275,7 +275,7 @@
}
},
"access": "public",
"type": "any"
"type": "number"
},
".init": {
"kind": "ModuleMethod",

View file

@ -44,7 +44,7 @@
}
}
},
"type": "any"
"type": "number"
},
{
"kind": "LocalVariable",
@ -63,7 +63,7 @@
}
}
},
"type": "any"
"type": "string"
}
],
"body": {

View file

@ -44,7 +44,7 @@
}
}
},
"type": "any"
"type": "number"
},
{
"kind": "LocalVariable",
@ -63,7 +63,7 @@
}
}
},
"type": "any"
"type": "string"
}
],
"body": {

View file

@ -33,7 +33,7 @@
}
}
},
"type": "any"
"type": "number"
},
{
"kind": "LocalVariable",
@ -52,7 +52,7 @@
}
}
},
"type": "any"
"type": "string"
}
],
"body": {

View file

@ -90,7 +90,7 @@
}
},
"access": "public",
"type": "any"
"type": "number"
},
".init": {
"kind": "ModuleMethod",

View file

@ -90,7 +90,7 @@
}
},
"access": "public",
"type": "any"
"type": "number"
},
".init": {
"kind": "ModuleMethod",

View file

@ -90,7 +90,7 @@
}
},
"access": "public",
"type": "any"
"type": "number"
},
".init": {
"kind": "ModuleMethod",

View file

@ -26,7 +26,7 @@
}
},
"access": "private",
"type": "any"
"type": "number"
},
".init": {
"kind": "ModuleMethod",

View file

@ -26,7 +26,7 @@
}
},
"access": "public",
"type": "any"
"type": "number"
},
".init": {
"kind": "ModuleMethod",

View file

@ -52,7 +52,7 @@
}
}
},
"type": "any"
"type": "number"
},
{
"kind": "LocalVariable",
@ -71,7 +71,7 @@
}
}
},
"type": "any"
"type": "number"
}
],
"body": {
@ -634,7 +634,7 @@
"access": "public",
"readonly": true,
"static": false,
"type": "any"
"type": "number"
},
"y": {
"kind": "ClassProperty",
@ -656,7 +656,7 @@
"access": "public",
"readonly": true,
"static": false,
"type": "any"
"type": "number"
}
},
"abstract": false,