From 25fb4b0c640ba8b39aa36e3c03a2220ffaf7037c Mon Sep 17 00:00:00 2001 From: Sheyne Anderson Date: Sun, 26 Feb 2017 17:20:38 -0700 Subject: [PATCH] feat(Value): return Errors as CoreValues BREAKING CHANGE --- sinap-includes/plugin-program.ts | 12 ++++++++---- sinap-includes/plugin-stub.ts | 10 ++++++---- src/program.ts | 25 +++++++++++++++++++------ test/test-interpreters.ts | 31 ++++++++++++++++++++++++------- test/test-stub.ts | 4 +++- 5 files changed, 60 insertions(+), 22 deletions(-) diff --git a/sinap-includes/plugin-program.ts b/sinap-includes/plugin-program.ts index 5360d34..7b965b0 100644 --- a/sinap-includes/plugin-program.ts +++ b/sinap-includes/plugin-program.ts @@ -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"; } \ No newline at end of file diff --git a/sinap-includes/plugin-stub.ts b/sinap-includes/plugin-stub.ts index 11ceaef..96223d5 100644 --- a/sinap-includes/plugin-stub.ts +++ b/sinap-includes/plugin-stub.ts @@ -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); @@ -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) { } } diff --git a/src/program.ts b/src/program.ts index 2f5a989..64d758d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -47,15 +47,28 @@ export class Program { runArguments: Type[][]; run(a: CoreValue[]): { states: CoreValue[], result: CoreValue } { const output = this.program.run(a.map(v => v.value)); - if (isError(output)) { - throw output.error; - } 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, this.plugin.typeEnvironment), - output.result), + result: result, }; } } diff --git a/test/test-interpreters.ts b/test/test-interpreters.ts index aa49122..2bf451b 100644 --- a/test/test-interpreters.ts +++ b/test/test-interpreters.ts @@ -259,8 +259,10 @@ describe("various interpreters", () => { const pluginProg = new context.global["plugin-stub"].Program(JSON.parse(serialGraph)); const prog = new Program(pluginProg, dfa); const stringType = dfa.typeEnvironment.getStringType(); + const errorType = dfa.typeEnvironment.lookupGlobalType("Error"); - assert.throws(() => prog.run([new CoreValue(stringType, "11")]), "allows multiple start states"); + assert.equal(errorType, prog.run([new CoreValue(stringType, "11")]).result.type); + assert.equal("Only one start state allowed", prog.run([new CoreValue(stringType, "11")]).result.value.message); }); it("checks for 0 start states", () => { const model = new CoreModel(dfa, { @@ -363,7 +365,10 @@ describe("various interpreters", () => { const prog = new Program(pluginProg, dfa); const stringType = dfa.typeEnvironment.getStringType(); - assert.throws(() => prog.run([new CoreValue(stringType, "11")]), "allows zero start states"); + const errorType = dfa.typeEnvironment.lookupGlobalType("Error"); + + assert.equal(errorType, prog.run([new CoreValue(stringType, "11")]).result.type); + assert.equal("Must have one start state", prog.run([new CoreValue(stringType, "11")]).result.value.message); }); it("checks for empty transitions", () => { const model = new CoreModel(dfa, { @@ -466,7 +471,10 @@ describe("various interpreters", () => { const prog = new Program(pluginProg, dfa); const stringType = dfa.typeEnvironment.getStringType(); - assert.throws(() => prog.run([new CoreValue(stringType, "11")]), "allows empty transitions"); + const errorType = dfa.typeEnvironment.lookupGlobalType("Error"); + + assert.equal(errorType, prog.run([new CoreValue(stringType, "11")]).result.type); + assert.equal("Lambda transition from q0 to q0 is not allowed", prog.run([new CoreValue(stringType, "11")]).result.value.message); }); it("checks for two character transitions", () => { const model = new CoreModel(dfa, { @@ -569,7 +577,10 @@ describe("various interpreters", () => { const prog = new Program(pluginProg, dfa); const stringType = dfa.typeEnvironment.getStringType(); - assert.throws(() => prog.run([new CoreValue(stringType, "11")]), "allows two character transitions"); + const errorType = dfa.typeEnvironment.lookupGlobalType("Error"); + + assert.equal(errorType, prog.run([new CoreValue(stringType, "11")]).result.type); + assert.equal("Edge 23 must be one symbol", prog.run([new CoreValue(stringType, "11")]).result.value.message); }); }); describe("nfa", () => { @@ -809,8 +820,10 @@ describe("various interpreters", () => { const pluginProg = new context.global["plugin-stub"].Program(JSON.parse(serialGraph)); const prog = new Program(pluginProg, nfa); const stringType = nfa.typeEnvironment.getStringType(); + const errorType = nfa.typeEnvironment.lookupGlobalType("Error"); - assert.throws(() => prog.run([new CoreValue(stringType, "11")]), "allows multiple start states"); + assert.equal(errorType, prog.run([new CoreValue(stringType, "11")]).result.type, "allows multiple start states"); + assert.equal("Only one start state allowed", prog.run([new CoreValue(stringType, "11")]).result.value.message, "allows multiple start states"); }); it("checks for 0 start states", () => { const model = new CoreModel(nfa, { @@ -912,8 +925,10 @@ describe("various interpreters", () => { const pluginProg = new context.global["plugin-stub"].Program(JSON.parse(serialGraph)); const prog = new Program(pluginProg, nfa); const stringType = nfa.typeEnvironment.getStringType(); + const errorType = nfa.typeEnvironment.lookupGlobalType("Error"); - assert.throws(() => prog.run([new CoreValue(stringType, "11")]), "allows zero start states"); + assert.equal(errorType, prog.run([new CoreValue(stringType, "11")]).result.type, "allows zero start states"); + assert.equal("Must have one start state", prog.run([new CoreValue(stringType, "11")]).result.value.message, "allows zero start states"); }); it("allows empty transitions", () => { const model = new CoreModel(nfa, { @@ -1118,8 +1133,10 @@ describe("various interpreters", () => { const pluginProg = new context.global["plugin-stub"].Program(JSON.parse(serialGraph)); const prog = new Program(pluginProg, nfa); const stringType = nfa.typeEnvironment.getStringType(); + const errorType = nfa.typeEnvironment.lookupGlobalType("Error"); - assert.throws(() => prog.run([new CoreValue(stringType, "11")]), "allows two character transitions"); + assert.equal(errorType, prog.run([new CoreValue(stringType, "11")]).result.type); + assert.equal("Edge 23 must be one symbol", prog.run([new CoreValue(stringType, "11")]).result.value.message); }); it("supports non-determinism", () => { const model = new CoreModel(nfa, { diff --git a/test/test-stub.ts b/test/test-stub.ts index e2cae08..ec76524 100644 --- a/test/test-stub.ts +++ b/test/test-stub.ts @@ -196,8 +196,10 @@ describe("plugin stub", () => { const pluginProg = new (context as any).global["plugin-stub"].Program({ elements: [] }); const prog = new Program(pluginProg, plugin); const numberType = plugin.typeEnvironment.getStringType(); + const errorType = plugin.typeEnvironment.lookupGlobalType("Error"); - assert.throws(() => prog.run([new CoreValue(numberType, 456)])); + assert.equal(errorType, prog.run([new CoreValue(numberType, 456)]).result.type); + assert.equal("Cannot read property 'parents' of undefined", prog.run([new CoreValue(numberType, 456)]).result.value.message); }); it("has sinap types", () => {