diff --git a/packages/components/src/components/DynamicSquiggleViewer.tsx b/packages/components/src/components/DynamicSquiggleViewer.tsx index 61fc3c5585..d470f34faa 100644 --- a/packages/components/src/components/DynamicSquiggleViewer.tsx +++ b/packages/components/src/components/DynamicSquiggleViewer.tsx @@ -2,7 +2,11 @@ import { forwardRef } from "react"; import { SquiggleViewer } from "../index.js"; import { SquiggleOutput } from "../lib/hooks/useSquiggle.js"; -import { getResultVariables, getResultValue } from "../lib/utility.js"; +import { + getResultVariables, + getResultValue, + getResultError, +} from "../lib/utility.js"; import { CodeEditorHandle } from "./CodeEditor/index.js"; import { PartialPlaygroundSettings } from "./PlaygroundSettings.js"; import { SquiggleViewerHandle } from "./SquiggleViewer/index.js"; @@ -42,6 +46,7 @@ export const DynamicSquiggleViewer = forwardRef( ref={viewerRef} resultVariables={getResultVariables(squiggleOutput)} resultItem={getResultValue(squiggleOutput)} + resultError={getResultError(squiggleOutput)} editor={editor} rootPathOverride={rootPathOverride} /> diff --git a/packages/components/src/components/SquiggleViewer/index.tsx b/packages/components/src/components/SquiggleViewer/index.tsx index 02d0c4efcf..ca37dcface 100644 --- a/packages/components/src/components/SquiggleViewer/index.tsx +++ b/packages/components/src/components/SquiggleViewer/index.tsx @@ -31,6 +31,7 @@ export type SquiggleViewerProps = { /** The output of squiggle's run */ resultVariables: result; resultItem: result | undefined; + resultError: SqError | undefined; editor?: CodeEditorHandle; rootPathOverride?: SqValuePath; } & PartialPlaygroundSettings; @@ -39,9 +40,10 @@ const SquiggleViewerOuter = forwardRef< SquiggleViewerHandle, SquiggleViewerProps >(function SquiggleViewerOuter( - { resultVariables, resultItem, rootPathOverride }, + { resultVariables, resultItem, resultError, rootPathOverride }, ref ) { + console.log("ERROR", resultError); const { focused, dispatch, getCalculator } = useViewerContext(); const unfocus = useUnfocus(); const focus = useFocus(); @@ -137,6 +139,7 @@ const SquiggleViewerOuter = forwardRef< return (
{focusedNavigation} + {resultError && } {body()}
); @@ -147,6 +150,7 @@ const innerComponent = forwardRef( { resultVariables, resultItem, + resultError, editor, rootPathOverride, ...partialPlaygroundSettings @@ -171,6 +175,7 @@ const innerComponent = forwardRef( diff --git a/packages/components/src/lib/hooks/useSquiggle.ts b/packages/components/src/lib/hooks/useSquiggle.ts index 503a48bb02..97be1e34bf 100644 --- a/packages/components/src/lib/hooks/useSquiggle.ts +++ b/packages/components/src/lib/hooks/useSquiggle.ts @@ -7,6 +7,7 @@ import { SqDict, SqValue, result, + SqRuntimeError, } from "@quri/squiggle-lang"; import { WINDOW_VARIABLE_NAME } from "../constants.js"; @@ -38,6 +39,7 @@ export type SquiggleOutput = { exports: SqDict; result: SqValue; bindings: SqDict; + error: SqRuntimeError | undefined; }, SqError >; @@ -101,6 +103,7 @@ export function useSquiggle(args: SquiggleArgs): UseSquiggleOutput { project.setContinues(sourceId, continues); await project.run(sourceId); const output = project.getOutput(sourceId); + console.log("output", output); const executionTime = Date.now() - startTime; setSquiggleOutput({ diff --git a/packages/components/src/lib/utility.ts b/packages/components/src/lib/utility.ts index ef6046ca88..74d2efeb01 100644 --- a/packages/components/src/lib/utility.ts +++ b/packages/components/src/lib/utility.ts @@ -4,6 +4,7 @@ import { SqDictValue, result, resultMap, + SqRuntimeError, } from "@quri/squiggle-lang"; import { SquiggleOutput } from "./hooks/useSquiggle.js"; @@ -59,6 +60,16 @@ export function getResultValue({ } } +export function getResultError({ + output, +}: SquiggleOutput): SqRuntimeError | undefined { + if (output.ok) { + return output.value.error; + } else { + return undefined; + } +} + export function getErrors(result: SquiggleOutput["output"]) { if (!result.ok) { return [result.value]; diff --git a/packages/squiggle-lang/src/public/SqProject/ProjectItem.ts b/packages/squiggle-lang/src/public/SqProject/ProjectItem.ts index 3472339814..430bcc19c3 100644 --- a/packages/squiggle-lang/src/public/SqProject/ProjectItem.ts +++ b/packages/squiggle-lang/src/public/SqProject/ProjectItem.ts @@ -17,6 +17,7 @@ export type RunOutput = { result: Value; bindings: Bindings; exports: Bindings; + error: SqError | undefined; }; export type Import = @@ -220,10 +221,13 @@ export class ProjectItem { throw new Error("Expected Program expression"); } - const [result, contextAfterEvaluation] = evaluate(expression.value, { - ...context, - evaluate: asyncEvaluate, - }); + const [result, contextAfterEvaluation, error] = evaluate( + expression.value, + { + ...context, + evaluate: asyncEvaluate, + } + ); const bindings = contextAfterEvaluation.stack.asBindings(); const exportNames = new Set(expression.value.value.exports); @@ -232,6 +236,7 @@ export class ProjectItem { result, bindings, exports, + error, }); } catch (e: unknown) { this.failRun(new SqRuntimeError(IRuntimeError.fromException(e))); diff --git a/packages/squiggle-lang/src/public/SqProject/index.ts b/packages/squiggle-lang/src/public/SqProject/index.ts index f3b6b64761..dae83b51b5 100644 --- a/packages/squiggle-lang/src/public/SqProject/index.ts +++ b/packages/squiggle-lang/src/public/SqProject/index.ts @@ -7,7 +7,7 @@ import { ImmutableMap } from "../../utility/immutableMap.js"; import * as Result from "../../utility/result.js"; import { Value, vDict } from "../../value/index.js"; -import { SqError, SqOtherError } from "../SqError.js"; +import { SqError, SqOtherError, SqRuntimeError } from "../SqError.js"; import { SqDict } from "../SqValue/SqDict.js"; import { SqValue, wrapValue } from "../SqValue/index.js"; import { SqValueContext } from "../SqValueContext.js"; @@ -16,6 +16,7 @@ import { SqValuePath } from "../SqValuePath.js"; import { SqLinker } from "../SqLinker.js"; import { SqOutputResult } from "../types.js"; import { Import, ProjectItem, RunOutput } from "./ProjectItem.js"; +import { IRuntimeError } from "../../errors/IError.js"; function getNeedToRunError() { return new SqOtherError("Need to run"); @@ -267,7 +268,15 @@ export class SqProject { ) ); - return Result.Ok({ result, bindings, exports }); + const error = internalOutputR.value.error; + return Result.Ok({ + result, + bindings, + exports, + error: error + ? new SqRuntimeError(IRuntimeError.fromException(error)) + : undefined, + }); } getResult(sourceId: string): Result.result { diff --git a/packages/squiggle-lang/src/public/types.ts b/packages/squiggle-lang/src/public/types.ts index 0a933d2d5f..5516ab354e 100644 --- a/packages/squiggle-lang/src/public/types.ts +++ b/packages/squiggle-lang/src/public/types.ts @@ -1,5 +1,5 @@ import { result } from "../utility/result.js"; -import { SqError } from "./SqError.js"; +import { SqError, SqRuntimeError } from "./SqError.js"; import { SqValue } from "./SqValue/index.js"; import { SqDict } from "./SqValue/SqDict.js"; @@ -7,6 +7,7 @@ export type SqOutput = { result: SqValue; bindings: SqDict; exports: SqDict; + error: SqRuntimeError | undefined; }; export type SqOutputResult = result; diff --git a/packages/squiggle-lang/src/reducer/index.ts b/packages/squiggle-lang/src/reducer/index.ts index 24d5f2a8d3..fd8413fb6f 100644 --- a/packages/squiggle-lang/src/reducer/index.ts +++ b/packages/squiggle-lang/src/reducer/index.ts @@ -13,6 +13,7 @@ import { } from "../errors/messages.js"; import { compileAst } from "../expression/compile.js"; import { Expression } from "../expression/index.js"; +import { SqError, SqRuntimeError } from "../index.js"; import { getStdLib } from "../library/index.js"; import { ImmutableMap } from "../utility/immutableMap.js"; import * as Result from "../utility/result.js"; @@ -33,13 +34,13 @@ import { UserDefinedLambdaParameter, UserDefinedLambda } from "./lambda.js"; export type ReducerFn = ( expression: Expression, context: Context.ReducerContext -) => [Value, Context.ReducerContext]; +) => [Value, Context.ReducerContext, SqError | undefined]; type SubReducerFn = ( expressionValue: Extract["value"], context: Context.ReducerContext, ast: ASTNode -) => [Value, Context.ReducerContext]; +) => [Value, Context.ReducerContext, SqError | undefined]; function throwFrom( error: ErrorMessage, @@ -104,7 +105,7 @@ const evaluateBlock: SubReducerFn<"Block"> = (statements, context) => { currentContext ); } - return [currentValue, context]; // throw away block's context + return [currentValue, context, undefined]; // throw away block's context }; const evaluateProgram: SubReducerFn<"Program"> = (expressionValue, context) => { @@ -113,12 +114,20 @@ const evaluateProgram: SubReducerFn<"Program"> = (expressionValue, context) => { let currentValue: Value = vVoid(); for (const statement of expressionValue.statements) { - [currentValue, currentContext] = context.evaluate( - statement, - currentContext - ); + try { + [currentValue, currentContext] = context.evaluate( + statement, + currentContext + ); + } catch (e: unknown) { + return [ + currentValue, + currentContext, + new SqRuntimeError(IRuntimeError.fromException(e)), + ]; + } } - return [currentValue, currentContext]; + return [currentValue, currentContext, undefined]; }; const evaluateArray: SubReducerFn<"Array"> = (expressionValue, context) => { @@ -127,7 +136,7 @@ const evaluateArray: SubReducerFn<"Array"> = (expressionValue, context) => { return value; }); const value = vArray(values); - return [value, context]; + return [value, context, undefined]; }; const evaluateDict: SubReducerFn<"Dict"> = (expressionValue, context, ast) => { @@ -148,7 +157,7 @@ const evaluateDict: SubReducerFn<"Dict"> = (expressionValue, context, ast) => { }) ) ); - return [value, context]; + return [value, context, undefined]; }; const evaluateAssign: SubReducerFn<"Assign"> = (expressionValue, context) => { @@ -163,6 +172,7 @@ const evaluateAssign: SubReducerFn<"Assign"> = (expressionValue, context) => { evaluate: context.evaluate, inFunction: context.inFunction, }, + undefined, ]; }; @@ -171,11 +181,11 @@ const evaluateResolvedSymbol: SubReducerFn<"ResolvedSymbol"> = ( context ) => { const value = context.stack.get(expressionValue.offset); - return [value, context]; + return [value, context, undefined]; }; const evaluateValue: SubReducerFn<"Value"> = (expressionValue, context) => { - return [expressionValue, context]; + return [expressionValue, context, undefined]; }; const evaluateTernary: SubReducerFn<"Ternary"> = ( @@ -199,7 +209,7 @@ const evaluateTernary: SubReducerFn<"Ternary"> = ( predicateResult.value ? expressionValue.ifTrue : expressionValue.ifFalse, context ); - return [value, context]; + return [value, context, undefined]; }; const evaluateLambda: SubReducerFn<"Lambda"> = ( @@ -246,7 +256,7 @@ const evaluateLambda: SubReducerFn<"Lambda"> = ( ast.location ) ); - return [value, context]; + return [value, context, undefined]; }; const evaluateCall: SubReducerFn<"Call"> = (expressionValue, context, ast) => { @@ -262,7 +272,7 @@ const evaluateCall: SubReducerFn<"Call"> = (expressionValue, context, ast) => { context, ast // we pass the ast of a current expression here, to put it on frameStack and in the resulting value ); - return [result, context]; + return [result, context, undefined]; } default: return throwFrom(new RENotAFunction(lambda.toString()), context, ast);