From 26dbd29ec8eecf495f2576e6f6266713d2059279 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Wed, 5 Oct 2022 04:51:23 +0400 Subject: [PATCH] framestack reimplemented --- .../src/components/SquiggleErrorAlert.tsx | 20 ++++---- packages/components/src/lib/utility.ts | 2 +- packages/squiggle-lang/src/js/SqError.ts | 33 +++++++++---- .../squiggle-lang/src/js/SqValueLocation.ts | 1 - packages/squiggle-lang/src/js/index.ts | 2 +- .../src/rescript/Reducer/Reducer_Context.res | 7 ++- .../Reducer_Expression/Reducer_Expression.res | 22 ++++++--- .../Reducer_Expression_ExpressionBuilder.res | 9 ++-- .../Reducer_Expression_T.res | 2 +- .../rescript/Reducer/Reducer_FrameStack.res | 42 +++++++++-------- .../src/rescript/Reducer/Reducer_Lambda.res | 25 ++++++++-- .../src/rescript/Reducer/Reducer_Lambda_T.res | 8 ++++ .../Reducer_Peggy_GeneratedParser.peggy | 6 +-- .../Reducer_Peggy/Reducer_Peggy_Parse.res | 2 +- .../Reducer_Peggy_ToExpression.res | 2 +- .../rescript/Reducer/Reducer_Peggy/helpers.ts | 6 ++- .../src/rescript/Reducer/Reducer_T.res | 10 ++-- .../squiggle-lang/src/rescript/SqError.res | 46 +++++++++---------- 18 files changed, 153 insertions(+), 92 deletions(-) create mode 100644 packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda_T.res diff --git a/packages/components/src/components/SquiggleErrorAlert.tsx b/packages/components/src/components/SquiggleErrorAlert.tsx index fb4721eb..c4b14f17 100644 --- a/packages/components/src/components/SquiggleErrorAlert.tsx +++ b/packages/components/src/components/SquiggleErrorAlert.tsx @@ -1,4 +1,4 @@ -import { SqError, SqLocation } from "@quri/squiggle-lang"; +import { SqError, SqFrame } from "@quri/squiggle-lang"; import React from "react"; import { ErrorAlert } from "./Alert"; @@ -6,24 +6,26 @@ type Props = { error: SqError; }; -const StackTraceLocation: React.FC<{ location: SqLocation }> = ({ - location, -}) => { +const StackTraceFrame: React.FC<{ frame: SqFrame }> = ({ frame }) => { + const location = frame.location(); return (
- Line {location.start.line}, column {location.start.column} + {frame.name()} + {location + ? ` at line ${location.start.line}, column ${location.start.column}` + : ""}
); }; const StackTrace: React.FC = ({ error }) => { - const locations = error.toLocationArray(); - return locations.length ? ( + const frames = error.getFrameArray(); + return frames.length ? (
Traceback:
- {locations.map((location, i) => ( - + {frames.map((frame, i) => ( + ))}
diff --git a/packages/components/src/lib/utility.ts b/packages/components/src/lib/utility.ts index c3ce08a4..cb002954 100644 --- a/packages/components/src/lib/utility.ts +++ b/packages/components/src/lib/utility.ts @@ -45,7 +45,7 @@ export function getValueToRender({ result, bindings }: ResultAndBindings) { export function getErrorLocations(result: ResultAndBindings["result"]) { if (result.tag === "Error") { - const location = result.value.toLocation(); + const location = result.value.location(); return location ? [location] : []; } else { return []; diff --git a/packages/squiggle-lang/src/js/SqError.ts b/packages/squiggle-lang/src/js/SqError.ts index 61d09096..59036830 100644 --- a/packages/squiggle-lang/src/js/SqError.ts +++ b/packages/squiggle-lang/src/js/SqError.ts @@ -1,8 +1,9 @@ import * as RSError from "../rescript/SqError.gen"; +import * as RSReducerT from "../rescript/Reducer/Reducer_T.gen"; -import * as RSCallStack from "../rescript/Reducer/Reducer_CallStack.gen"; +import * as RSFrameStack from "../rescript/Reducer/Reducer_FrameStack.gen"; -export type SqFrame = RSCallStack.frame; +export { location as SqLocation } from "../rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.gen"; export class SqError { constructor(private _value: RSError.t) {} @@ -19,17 +20,31 @@ export class SqError { return new SqError(RSError.createOtherError(v)); } - getTopFrame(): SqCallFrame | undefined { - const frame = RSCallStack.getTopFrame(RSError.getStackTrace(this._value)); - return frame ? new SqCallFrame(frame) : undefined; + getTopFrame(): SqFrame | undefined { + const frame = RSFrameStack.getTopFrame(RSError.getFrameStack(this._value)); + return frame ? new SqFrame(frame) : undefined; } - getFrameArray(): SqCallFrame[] { + getFrameArray(): SqFrame[] { const frames = RSError.getFrameArray(this._value); - return frames.map((frame) => new SqCallFrame(frame)); + return frames.map((frame) => new SqFrame(frame)); + } + + location() { + return this.getTopFrame()?.location(); } } -export class SqCallFrame { - constructor(private _value: SqFrame) {} +export class SqFrame { + constructor(private _value: RSReducerT.frame) {} + + name(): string { + return RSFrameStack.Frame.toName(this._value); + } + + location() { + console.log(RSFrameStack); + console.log(RSFrameStack.Frame); + return RSFrameStack.Frame.toLocation(this._value); + } } diff --git a/packages/squiggle-lang/src/js/SqValueLocation.ts b/packages/squiggle-lang/src/js/SqValueLocation.ts index 33c7060b..ad2e7e02 100644 --- a/packages/squiggle-lang/src/js/SqValueLocation.ts +++ b/packages/squiggle-lang/src/js/SqValueLocation.ts @@ -1,4 +1,3 @@ -import { isParenthesisNode } from "mathjs"; import { SqProject } from "./SqProject"; type PathItem = string | number; diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index aac26bd5..886c830a 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -13,7 +13,7 @@ export { environment, defaultEnvironment, } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen"; -export { SqError } from "./SqError"; +export { SqError, SqFrame, SqLocation } from "./SqError"; export { SqShape } from "./SqPointSetDist"; export { resultMap } from "./types"; diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res index af596dc2..a2d98f92 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Context.res @@ -4,8 +4,13 @@ let defaultEnvironment: Reducer_T.environment = DistributionOperation.defaultEnv let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => { { - callStack: list{}, + frameStack: list{}, bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend, environment: environment, + inFunction: None, } } + +let currentFunctionName = (t: t): string => { + t.inFunction->E.O2.fmap(Reducer_Lambda_T.name)->E.O2.default("") +} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index f334335a..0036816b 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -2,14 +2,16 @@ module Bindings = Reducer_Bindings module Result = Belt.Result module T = Reducer_T -let toLocation = (expression: T.expression): SqError.location => { +let toLocation = (expression: T.expression): Reducer_Peggy_Parse.location => { expression.ast.location } let throwFrom = (error: SqError.Message.t, expression: T.expression, context: T.context) => - error->SqError.throwMessage( - context.callStack, - location: Some(expression->toLocation) + error->SqError.throwMessageWithFrameStack( + context.frameStack->Reducer_FrameStack.extend( + context->Reducer_Context.currentFunctionName, + Some(expression->toLocation), + ), ) /* @@ -93,9 +95,9 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { } } - | T.ELambda(parameters, body) => ( + | T.ELambda(parameters, body, name) => ( Reducer_Lambda.makeLambda( - None, // TODO - pass function name from parser + name, parameters, context.bindings, body, @@ -112,7 +114,13 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { }) switch lambda { | T.IEvLambda(lambda) => { - let result = Reducer_Lambda.doLambdaCall(lambda, argValues, context, evaluate) + let result = Reducer_Lambda.doLambdaCallFrom( + lambda, + argValues, + context, + evaluate, + Some(expression->toLocation), // we have to pass the location of a current expression here, to put it on frameStack + ) (result, context) } | _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression, context) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res index 965fc499..527331fb 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res @@ -9,10 +9,11 @@ let eBool = aBool => aBool->T.IEvBool->T.EValue let eCall = (fn: expression, args: array): expressionContent => T.ECall(fn, args) -let eLambda = (parameters: array, expr: expression): expressionContent => T.ELambda( - parameters, - expr, -) +let eLambda = ( + parameters: array, + expr: expression, + name: option, +): expressionContent => T.ELambda(parameters, expr, name) let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res index 6b2982f6..c7804c7a 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_T.res @@ -24,7 +24,7 @@ let rec toString = (expression: t) => `${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})` | EAssign(name, value) => `${name} = ${value->toString}` | ECall(fn, args) => `(${fn->toString})(${args->Js.Array2.map(toString)->commaJoin})` - | ELambda(parameters, body) => `{|${parameters->commaJoin}| ${body->toString}}` + | ELambda(parameters, body, _) => `{|${parameters->commaJoin}| ${body->toString}}` | EValue(aValue) => Reducer_Value.toString(aValue) } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_FrameStack.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_FrameStack.res index 217993f0..e61efd5d 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_FrameStack.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_FrameStack.res @@ -1,37 +1,43 @@ type t = Reducer_T.frameStack module Frame = { - let toString = ({lambda, location}: Reducer_T.frame) => - `${fromFrame} at ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}` + let toString = ({name, location}: Reducer_T.frame) => + name ++ + switch location { + | Some(location) => + ` at line ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}` + | None => "" + } + + @genType + let toLocation = (t: Reducer_T.frame): option => t.location + + @genType + let toName = (t: Reducer_T.frame): string => t.name } let make = (): t => list{} -let topFrameName = (t: t) => - switch t->getTopFrame { - | Some({lambda}) => - switch lambda { - | FnLambda({name}) => name - | FnBuiltin({name}) => name - } - | None => "
" - } +@genType +let getTopFrame = (t: t): option => Belt.List.head(t) -let extend = (t: t, lambda: Reducer_T.lambdaValue, location: option) => +let extend = (t: t, name: string, location: option) => t->Belt.List.add({ - lambda: lambda, - fromLocation: location, - fromFrame: t->topFrameName, + name: name, + location: location, }) let toString = (t: t) => - t->Belt.List.map(s => " " ++ s->toStringFrame ++ "\n")->Belt.List.toArray->Js.Array2.joinWith("") + t + ->Belt.List.map(s => " " ++ s->Frame.toString ++ "\n") + ->Belt.List.toArray + ->Js.Array2.joinWith("") @genType -let toFrameArray = (t: t): array => t->Belt.List.toArray +let toFrameArray = (t: t): array => t->Belt.List.toArray @genType -let getTopFrame = (t: t): option => t->Belt.List.head +let getTopFrame = (t: t): option => t->Belt.List.head let isEmpty = (t: t): bool => switch t->Belt.List.head { diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda.res index 04adeea0..98a272db 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda.res @@ -33,7 +33,8 @@ let makeLambda = ( let lambdaContext: Reducer_T.context = { bindings: localBindingsWithParameters, // based on bindings at the moment of lambda creation environment: context.environment, // environment at the moment when lambda is called - callStack: context.callStack, // extended by main `evaluate` function + frameStack: context.frameStack, // already extended in `doLambdaCall` + inFunction: context.inFunction, // already updated in `doLambdaCall` } let (value, _) = reducer(body, lambdaContext) @@ -49,7 +50,7 @@ let makeLambda = ( }) } -// stdlib lambdas (everything in FunctionRegistry) is built by this method. Body is generated in SquiggleLibrary_StdLib.res +// stdlib functions (everything in FunctionRegistry) are built by this method. Body is generated in SquiggleLibrary_StdLib.res let makeFFILambda = (name: string, body: Reducer_T.lambdaBody): t => FnBuiltin({ // Note: current bindings could be accidentally exposed here through context (compare with native lambda implementation above, where we override them with local bindings). // But FunctionRegistry API is too limited for that to matter. Please take care not to violate that in the future by accident. @@ -65,10 +66,20 @@ let parameters = (t: t): array => { } } -let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => { +let doLambdaCallFrom = ( + t: t, + args: array, + context: Reducer_T.context, + reducer, + location: option, +) => { let newContext = { ...context, - callStack: t->Reducer_CallStack.extend(t), + frameStack: context.frameStack->Reducer_FrameStack.extend( + context->Reducer_Context.currentFunctionName, + location, + ), + inFunction: Some(t), } SqError.rethrowWithStacktrace(() => { @@ -76,5 +87,9 @@ let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => { | FnLambda({body}) => body(args, newContext, reducer) | FnBuiltin({body}) => body(args, newContext, reducer) } - }, newContext.callStack) + }, newContext.frameStack) +} + +let doLambdaCall = (t: t, args, context, reducer) => { + doLambdaCallFrom(t, args, context, reducer, None) } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda_T.res new file mode 100644 index 00000000..ab0724b4 --- /dev/null +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Lambda_T.res @@ -0,0 +1,8 @@ +type t = Reducer_T.lambdaValue + +let name = (t: t): string => { + switch t { + | FnLambda({name}) => name->E.O2.default("") + | FnBuiltin({name}) => name + } +} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy index 0cd28a86..cbe1f9da 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy @@ -50,7 +50,7 @@ letStatement defunStatement = variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression - { var value = h.nodeLambda(args, body, location()) + { var value = h.nodeLambda(args, body, location(), variable) return h.nodeLetStatement(variable, value, location()) } assignmentOp "assignment" = '=' @@ -261,9 +261,9 @@ valueConstructor lambda = '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}' { statements.push(finalExpression) - return h.nodeLambda(args, h.nodeBlock(statements, location()), location()) } + return h.nodeLambda(args, h.nodeBlock(statements, location()), location(), undefined) } / '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}' - { return h.nodeLambda(args, finalExpression, location()) } + { return h.nodeLambda(args, finalExpression, location(), undefined) } arrayConstructor 'array' = '[' _nl ']' diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res index b2b57bed..001f3e5b 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res @@ -44,7 +44,7 @@ type nodeIdentifier = {...node, "value": string} type nodeInteger = {...node, "value": int} type nodeKeyValue = {...node, "key": node, "value": node} type nodeRecord = {...node, "elements": array} -type nodeLambda = {...node, "args": array, "body": node} +type nodeLambda = {...node, "args": array, "body": node, "name": option} type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node} type nodeModuleIdentifier = {...node, "value": string} type nodeString = {...node, "value": string} diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res index 6c187476..2e858c55 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res @@ -20,7 +20,7 @@ let rec fromNode = (node: Parse.node): expression => { nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"]) let body = nodeLambda["body"]->fromNode - ExpressionBuilder.eLambda(args, body) + ExpressionBuilder.eLambda(args, body, nodeLambda["name"]) } let caseRecord = (nodeRecord): expressionContent => { diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts index 6e9fe80c..6253b875 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts @@ -89,6 +89,7 @@ type NodeLambda = Node & { type: "Lambda"; args: AnyPeggyNode[]; body: AnyPeggyNode; + name?: string; }; type NodeTernary = Node & { @@ -217,9 +218,10 @@ export function nodeKeyValue( export function nodeLambda( args: AnyPeggyNode[], body: AnyPeggyNode, - location: LocationRange + location: LocationRange, + name?: NodeIdentifier ): NodeLambda { - return { type: "Lambda", args, body, location }; + return { type: "Lambda", args, body, location, name: name?.value }; } export function nodeLetStatement( variable: NodeIdentifier, diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res index eab7bd55..dcc9e4bf 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_T.res @@ -36,7 +36,7 @@ and expressionContent = | ETernary(expression, expression, expression) | EAssign(string, expression) | ECall(expression, array) - | ELambda(array, expression) + | ELambda(array, expression, option) | EValue(value) and expression = { @@ -50,18 +50,18 @@ and bindings = { parent: option, } +@genType.opaque and frame = { name: string, location: option, // can be empty for calls from builtin functions } - -and frameStack = list +@genType.opaque and frameStack = list and context = { bindings: bindings, environment: environment, - inFunction: option, // used to build the next frame in frameStack - callStack: frameStack, + frameStack: frameStack, + inFunction: option, } and reducerFn = (expression, context) => (value, context) diff --git a/packages/squiggle-lang/src/rescript/SqError.res b/packages/squiggle-lang/src/rescript/SqError.res index 461e1feb..29d98517 100644 --- a/packages/squiggle-lang/src/rescript/SqError.res +++ b/packages/squiggle-lang/src/rescript/SqError.res @@ -95,19 +95,19 @@ type t = { exception SqException(t) -// `context` should be specified for runtime errors, but can be left empty for errors from Reducer_Project and so on. -// `location` can be empty for errors raised from FunctionRegistry. -let fromMessage = (message: Message.t, frameStack: Reducer_FrameStack.t): t => { +let fromMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t): t => { message: message, - stackTrace: stackTrace, + frameStack: frameStack, } -let fromMessageWithoutFrameStack = (message: Message.t) => - fromMessage(message, Reducer_FrameStack.make()) +// this shouldn't be used much, since frame stack will be empty +// but it's useful for global errors, e.g. in ReducerProject or somethere in the frontend +@genType +let fromMessage = (message: Message.t) => + fromMessageWithFrameStack(message, Reducer_FrameStack.make()) @genType -let getTopFrame = (t: t): option => - t.stackTrace->Reducer_FrameStack.getTopFrame +let getTopFrame = (t: t): option => t.frameStack->Reducer_FrameStack.getTopFrame @genType let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack @@ -116,42 +116,42 @@ let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack let toString = (t: t): string => t.message->Message.toString @genType -let createOtherError = (v: string): t => Message.REOther(v)->fromMessageWithoutFrameStack +let createOtherError = (v: string): t => Message.REOther(v)->fromMessage @genType -let getFrameArray = (t: t): array => - t.stackTrace->Reducer_CallStack.toFrameArray +let getFrameArray = (t: t): array => t.frameStack->Reducer_FrameStack.toFrameArray @genType -let toStringWithStacktrace = (t: t) => +let toStringWithStackTrace = (t: t) => t->toString ++ if t.frameStack->Reducer_FrameStack.isEmpty { - "Traceback:\n" ++ t.frameStack->Reducer_FrameStack.toString + "\nTraceback:\n" ++ t.frameStack->Reducer_FrameStack.toString + } else { + "" } let throw = (t: t) => t->SqException->raise -let throwMessage = (message: Message.t, frameStack: Reducer_FrameStack.t) => - fromMessage(message, frameStack)->throw +let throwMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t) => + fromMessageWithFrameStack(message, frameStack)->throw // this shouldn't be used for most runtime errors - the resulting error would have an empty stacktrace let fromException = exn => switch exn { | SqException(e) => e - | Message.MessageException(e) => e->fromMessage(Reducer_CallStack.make()) - | Js.Exn.Error(obj) => - REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessageWithoutStacktrace - | _ => REOther("Unknown exception")->fromMessageWithoutStacktrace + | Message.MessageException(e) => e->fromMessage + | Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage + | _ => REOther("Unknown exception")->fromMessage } // converts raw exceptions into exceptions with stacktrace attached // already converted exceptions won't be affected -let rethrowWithStacktrace = (fn: unit => 'a, stackTrace: Reducer_CallStack.t) => { +let rethrowWithStacktrace = (fn: unit => 'a, frameStack: Reducer_FrameStack.t) => { try { fn() } catch { | SqException(e) => e->throw // exception already has a stacktrace - | Message.MessageException(e) => e->throwMessage(stackTrace) // probably comes from FunctionRegistry, adding stacktrace + | Message.MessageException(e) => e->throwMessageWithFrameStack(frameStack) // probably comes from FunctionRegistry, adding stacktrace | Js.Exn.Error(obj) => - REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessage(stackTrace) - | _ => REOther("Unknown exception")->throwMessage(stackTrace) + REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessageWithFrameStack(frameStack) + | _ => REOther("Unknown exception")->throwMessageWithFrameStack(frameStack) } }