Skip to content

Commit

Permalink
Merge pull request #39 from 2graphic/types-to-interfaces
Browse files Browse the repository at this point in the history
Types to interfaces
  • Loading branch information
Sheyne authored Feb 27, 2017
2 parents d54c5e8 + aae2464 commit ee56464
Show file tree
Hide file tree
Showing 22 changed files with 903 additions and 188 deletions.
12 changes: 8 additions & 4 deletions sinap-includes/plugin-program.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
export type Error = { error: string };
export type Error = {
kind: "sinap-error",
message: string,
stack?: string,
};
export type Result = { states: any[], result: any };

export interface PluginProgram {
run(a: any): Result | Error;
run(a: any): Result;
validate(): string[];
}

export function isError(e: { states: any[], result: any } | { error: any } | null): e is Error {
return e != null && (e as any).error !== undefined;
export function isError(e: any): e is Error {
return e != null && typeof (e.message) === "string" && (e.stack === undefined || typeof (e.stack) === "string") && e.kind === "sinap-error";
}
10 changes: 6 additions & 4 deletions sinap-includes/plugin-stub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,15 @@ export class Program implements PluginProgram {
// TODO: improve if plugin defines a validate function
const res = this.run([]);
if (isError(res)) {
return [res.error];
return [res.message];
}
return [];
}

run(input: any[]) {
const states: plugin.State[] = [];
try {
let current = (plugin as any).start(this.graph, ...input);
const states: plugin.State[] = [];
while (current instanceof plugin.State) {
states.push(current);
current = plugin.step(current);
Expand All @@ -117,14 +117,16 @@ export class Program implements PluginProgram {
result: current,
};
} catch (e) {
const message = e instanceof Error ? e.message : e;
const stack = e instanceof Error ? e.stack : undefined;
return {
error: e
result: { message: e.message, stack: stack, kind: "sinap-error" },
states: states,
};
}
}
}


export class File {
constructor(public name: string) { }
}
Expand Down
73 changes: 65 additions & 8 deletions sinap-includes/types-interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,77 @@
export interface Type {
readonly name: string;
export interface TypeEnvironment {
kind: "SinapTypeEnvironment";

getAnyType(): Type;
getStringType(): Type;
getNumberType(): Type;
getBooleanType(): Type;
getVoidType(): Type;
getUndefinedType(): Type;
getNullType(): Type;
getESSymbolType(): Type;
getNeverType(): Type;
getUnknownType(): Type;
getStringLiteralType(text: string): Type;
getNumberLiteralType(text: string): Type;
getFalseType(): Type;
getTrueType(): Type;
lookupGlobalType(name: string): Type;
}

export interface TypeComparisons {
/**
* Return true if this type is assignable to that type
*/
isAssignableTo(that: Type, alreadyFlipped?: boolean): boolean;

/**
* Return if this type is assignable to that type
* Return true if this type is assignable to that type
*/
isAssignableTo(that: Type): boolean;
isAssignableFrom(that: Type, alreadyFlipped?: boolean): boolean;

/**
* Return true if this type is identical to that type
*/
isIdenticalTo(that: Type): boolean;
}

export interface UnionType {
types: Type[];
export interface Type extends TypeComparisons {
readonly name: string;
readonly env: TypeEnvironment;
}

export interface IntersectionType {
export interface UnionType extends Type {
types: Set<Type>;
kind: "SinapUnionType";
}

export interface IntersectionType extends Type {
types: Type[];
kind: "SinapIntersectionType";
}

export interface ObjectType {
export interface ObjectType extends Type {
readonly members: Map<string, Type>;
kind: "SinapObjectType";
isArray(): this is ArrayType;
}

export interface ArrayType extends ObjectType {
typeArguments: Type[];
}

export function isObjectType(t: Type): t is ObjectType {
return t && (t as any).kind === "SinapObjectType";
}

export function isUnionType(t: Type): t is UnionType {
return t && (t as any).kind === "SinapUnionType";
}

export function isIntersectionType(t: Type): t is IntersectionType {
return t && (t as any).kind === "SinapIntersectionType";
}

export function isTypeEnvironment(t: any): t is TypeEnvironment {
return t && t.kind === "SinapTypeEnvironment";
}
53 changes: 47 additions & 6 deletions src/element.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,61 @@
import { Plugin, ObjectType, IType } from ".";
import { Plugin, ObjectType, CoreObjectValue, CoreValue, makeValue } from ".";
import * as assert from "assert";

export enum CoreElementKind { Node, Edge, Graph };

export class CoreValue {
constructor(readonly type: IType, public data: any) {

}
function makeDataProxy(data: { [a: string]: any }, type: ObjectType) {
return new Proxy(data, {
get: (b, k: string) => {
const bv = b[k];
if (bv instanceof CoreElement) {
return bv;
}
return makeValue(bv, type.members.get(k) || type.env);
},
set: () => {
throw new Error("setting values is unimplemented");
}
});
}

/**
* Represents nodes, edges, and graphs.
*/
export class CoreElement extends CoreValue {
export class CoreElement extends CoreObjectValue {
private _data: { [a: string]: any };
private _value: { [a: string]: CoreValue };

/**
* Distinction between data and value:
* data = {
* [a: string]: CoreElement (if this.members.get(a) == CoreElement
* [b: string]: raw_data (Unwrapped CoreValue if this.members.get(b) != CoreElement)
* }
*/
get data() {
return this._data;
}

set data(d) {
this._value = makeDataProxy(d, this.type);
this._data = d;
}

get value() {
return this._value;
}

set value(obj) {
// TODO: this needs test cases
for (const key of Object.getOwnPropertyNames(obj)) {
this._value[key] = obj[key];
}
}

constructor(readonly type: ObjectType, readonly kind: CoreElementKind) {
super(type, {});

this.data = {};
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export * from "./types";
export * from "./plugin-loader";
export * from "./value";
export * from "./element";
export * from "./plugin";
export * from "./files";
export * from "./program"
export * from "./program";
export * from "../sinap-includes/types-interfaces";
export * from "./typecheck-json";
32 changes: 16 additions & 16 deletions src/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as ts from "typescript";
import { CoreElementKind, CoreElement, TypeEnvironment, Type, UnionType, ObjectType, printDiagnostics } from ".";
import { CoreElementKind, CoreElement, ScriptTypeEnvironment, WrappedScriptType, WrappedScriptUnionType, WrappedScriptObjectType, printDiagnostics } from ".";

function unionToList(type: Type): [string, ObjectType][] {
if (type instanceof UnionType) {
return type.types.map(unionToList).reduce((p, c) => p.concat(c));
} else if (type instanceof ObjectType) {
function unionToList(type: WrappedScriptType): [string, WrappedScriptObjectType][] {
if (type instanceof WrappedScriptUnionType) {
return [...type.types.values()].map(unionToList).reduce((p, c) => p.concat(c));
} else if (type instanceof WrappedScriptObjectType) {
return [[type.name, type]];
}
throw `type must be a union type or an object type.`;
Expand All @@ -23,14 +23,14 @@ function kindToKey(kind: CoreElementKind): string {
}


export class PluginTypeEnvironment extends TypeEnvironment {
private pluginTypes: Map<string, Map<string, ObjectType>>;
export class PluginTypeEnvironment extends ScriptTypeEnvironment {
private pluginTypes: Map<string, Map<string, WrappedScriptObjectType>>;
private pluginSourceFile: ts.SourceFile;
private sinapSourceFile: ts.SourceFile;

public drawableTypes: Map<CoreElementKind, ObjectType>;
public drawableTypes: Map<CoreElementKind, WrappedScriptObjectType>;

public startTypes: [Type[], Type][];
public startTypes: [WrappedScriptType[], WrappedScriptType][];

lookupPluginType(n: string) {
return this.getType(this.checker.lookupTypeAt(n, this.pluginSourceFile));
Expand All @@ -52,23 +52,23 @@ export class PluginTypeEnvironment extends TypeEnvironment {
[
s.getParameters().map(p => this.getType(this.checker.getTypeOfSymbol(p))),
this.getType(s.getReturnType())
] as [Type[], Type]);
] as [WrappedScriptType[], WrappedScriptType]);
}

constructor(program: ts.Program) {
super(program.getTypeChecker());
this.pluginSourceFile = program.getSourceFile("plugin.ts");
this.sinapSourceFile = program.getSourceFile("plugin-stub.ts");
this.drawableTypes = new Map();
this.drawableTypes.set(CoreElementKind.Node, this.lookupSinapType("DrawableNode") as ObjectType);
this.drawableTypes.set(CoreElementKind.Edge, this.lookupSinapType("DrawableEdge") as ObjectType);
this.drawableTypes.set(CoreElementKind.Graph, this.lookupSinapType("DrawableGraph") as ObjectType);
this.drawableTypes.set(CoreElementKind.Node, this.lookupSinapType("DrawableNode") as WrappedScriptObjectType);
this.drawableTypes.set(CoreElementKind.Edge, this.lookupSinapType("DrawableEdge") as WrappedScriptObjectType);
this.drawableTypes.set(CoreElementKind.Graph, this.lookupSinapType("DrawableGraph") as WrappedScriptObjectType);

this.startTypes = this.getFunctionSignatures("start", program.getSourceFile("plugin.ts"));

this.pluginTypes = new Map(["Nodes", "Edges", "Graph"]
.map(k => [k, this.lookupPluginType(k)] as [string, Type])
.map(([n, v]) => [n, new Map(unionToList(v))] as [string, Map<string, ObjectType>]));
.map(k => [k, this.lookupPluginType(k)] as [string, WrappedScriptType])
.map(([n, v]) => [n, new Map(unionToList(v))] as [string, Map<string, WrappedScriptObjectType>]));
}

elementTypes(kind: CoreElementKind) {
Expand All @@ -79,7 +79,7 @@ export class PluginTypeEnvironment extends TypeEnvironment {
return type.keys();
}

getElementType(kind: CoreElementKind, type: string): ObjectType {
getElementType(kind: CoreElementKind, type: string): WrappedScriptObjectType {
const t = this.pluginTypes.get(kindToKey(kind));
if (t === undefined) {
throw Error("kind not found");
Expand Down
45 changes: 30 additions & 15 deletions src/program.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { PluginProgram, isError } from "../sinap-includes/plugin-program";
import { CoreValue, Plugin, IType, Type, UnionType, FakeUnionType } from ".";
import { CoreValue, Plugin, Type, WrappedScriptUnionType, FakeUnionType, TypeEnvironment } from ".";

function signatureAssignable(t1: IType[], t2: IType[]) {
function signatureAssignable(t1: Type[], t2: Type[]) {
return t1.reduce((a, v, i) => a && v.isAssignableTo(t2[i]), true);
}

function pickReturnType(argTypes: IType[], signatures: [Type[], Type][], stateType: Type): IType {
const checker = signatures[0][1].env.checker;
function pickReturnType(argTypes: Type[], signatures: [Type[], Type][], stateType: Type, env: TypeEnvironment): Type {
// find all the signatures that argTypes is assignable to
const viableSignatures = signatures.filter(sig =>
signatureAssignable(argTypes, sig[0].slice(1))
Expand All @@ -16,7 +15,7 @@ function pickReturnType(argTypes: IType[], signatures: [Type[], Type][], stateTy
throw new Error("no matching function signatures found");
}

const nonAnySigs = viableSignatures.filter(t => !checker.isIdenticalTo(t.type, checker.getAnyType()));
const nonAnySigs = viableSignatures.filter(t => !t.isIdenticalTo(env.getAnyType()));

let bestSignature = nonAnySigs.pop();
if (bestSignature === undefined) {
Expand All @@ -30,33 +29,49 @@ function pickReturnType(argTypes: IType[], signatures: [Type[], Type][], stateTy
}
}

if (bestSignature instanceof UnionType) {
return new FakeUnionType(bestSignature.types.filter(t => !checker.isIdenticalTo(t.type, stateType.type)));
if (bestSignature instanceof WrappedScriptUnionType) {
return new FakeUnionType(env, new Set([...bestSignature.types.values()].filter(t => !t.isIdenticalTo(stateType))));
}

return bestSignature;
}

export class Program {
constructor(private program: PluginProgram, private plugin: Plugin) {
this.runArguments = this.plugin.typeEnvironment.startTypes.map(
t => t[0].slice(1)
);
};

validate(): string[] {
return this.program.validate();
}

runArguments: IType[][];
runArguments: Type[][];
run(a: CoreValue[]): { states: CoreValue[], result: CoreValue } {
const output = this.program.run(a.map(v => v.data));
if (isError(output)) {
throw output.error;
}
const output = this.program.run(a.map(v => v.value));
const stateType = this.plugin.typeEnvironment.lookupPluginType("State");
const errorType = this.plugin.typeEnvironment.lookupGlobalType("Error");

let result: CoreValue;

if (isError(output.result)) {
const err = new Error(output.result.message);
err.stack = output.result.stack;
result = new CoreValue(
errorType,
err,
);
} else {
result = new CoreValue(
pickReturnType(a.map(v => v.type), this.plugin.typeEnvironment.startTypes, stateType, this.plugin.typeEnvironment),
output.result
);
}

return {
states: output.states.map(s => new CoreValue(stateType, s)),
result: new CoreValue(
pickReturnType(a.map(v => v.type), this.plugin.typeEnvironment.startTypes, stateType),
output.result),
result: result,
};
}
}
Loading

0 comments on commit ee56464

Please sign in to comment.