diff --git a/packages/components/src/components/SquiggleErrorAlert.tsx b/packages/components/src/components/SquiggleErrorAlert.tsx
index d03e237f..e9dd4245 100644
--- a/packages/components/src/components/SquiggleErrorAlert.tsx
+++ b/packages/components/src/components/SquiggleErrorAlert.tsx
@@ -1,4 +1,4 @@
-import { SqError } from "@quri/squiggle-lang";
+import { SqError, SqLocation } from "@quri/squiggle-lang";
import React from "react";
import { ErrorAlert } from "./Alert";
@@ -6,10 +6,34 @@ type Props = {
error: SqError;
};
+const StackTraceLocation: React.FC<{ location: SqLocation }> = ({
+ location,
+}) => {
+ return (
+
+ Line {location.start.line}, column {location.start.column}
+
+ );
+};
+
+const StackTrace: React.FC = ({ error }) => {
+ return (
+
+ {error.toLocationArray().map((location, i) => (
+
+ ))}
+
+ );
+};
+
export const SquiggleErrorAlert: React.FC = ({ error }) => {
return (
- {error.toString()}
+ {error.toString()}
+ Traceback:
+
+
+
);
};
diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res
index e30d3274..3e5abceb 100644
--- a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res
+++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_TestHelpers.res
@@ -22,7 +22,7 @@ let expectExpressionToBe = (expr, answer, ~v="_", ()) => {
} else {
let a2 =
rExpr
- ->E.R2.errMap(e => e->SqError.Message.fromParseError->SqError.Error.fromMessage)
+ ->E.R2.errMap(e => e->SqError.Message.fromParseError->SqError.fromMessage)
->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
->Reducer_Value.toStringResultOkless
(a1, a2)->expect->toEqual((answer, v))
diff --git a/packages/squiggle-lang/scripts/lib.mjs b/packages/squiggle-lang/scripts/lib.mjs
index 6f778769..5fc91873 100644
--- a/packages/squiggle-lang/scripts/lib.mjs
+++ b/packages/squiggle-lang/scripts/lib.mjs
@@ -36,6 +36,6 @@ export const run = (src, { output, sampleCount } = {}) => {
"Time:",
String(time),
result.tag === "Error" ? red(result.tag) : green(result.tag),
- result.tag === "Error" ? result.value.toString() : ""
+ result.tag === "Error" ? result.value.toStringWithStackTrace() : ""
);
};
diff --git a/packages/squiggle-lang/src/js/SqError.ts b/packages/squiggle-lang/src/js/SqError.ts
index ea21dc11..5025e671 100644
--- a/packages/squiggle-lang/src/js/SqError.ts
+++ b/packages/squiggle-lang/src/js/SqError.ts
@@ -1,13 +1,25 @@
-import * as RSError from "../rescript/ForTS/ForTS_SqError.gen";
+import * as RSError from "../rescript/SqError.gen";
+
+export type SqLocation = RSError.location;
export class SqError {
- constructor(private _value: RSError.error) {}
+ constructor(private _value: RSError.t) {}
toString() {
return RSError.toString(this._value);
}
+ toStringWithStackTrace() {
+ return RSError.toStringWithStackTrace(this._value);
+ }
+
static createOtherError(v: string) {
return new SqError(RSError.createOtherError(v));
}
+
+ toLocationArray() {
+ const stackTrace = RSError.getStackTrace(this._value);
+
+ return stackTrace ? RSError.StackTrace.toLocationArray(stackTrace) : [];
+ }
}
diff --git a/packages/squiggle-lang/src/js/SqProject.ts b/packages/squiggle-lang/src/js/SqProject.ts
index 30d8b245..b00b8927 100644
--- a/packages/squiggle-lang/src/js/SqProject.ts
+++ b/packages/squiggle-lang/src/js/SqProject.ts
@@ -1,12 +1,11 @@
import * as RSProject from "../rescript/ForTS/ForTS_ReducerProject.gen";
-import * as RSError from "../rescript/ForTS/ForTS_SqError.gen";
+import * as RSError from "../rescript/SqError.gen";
import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen";
import { SqError } from "./SqError";
import { SqRecord } from "./SqRecord";
import { wrapValue } from "./SqValue";
import { resultMap2 } from "./types";
import { SqValueLocation } from "./SqValueLocation";
-import { errorFromMessage } from "../rescript/ForTS/ForTS_SqError.gen";
export class SqProject {
constructor(private _value: RSProject.reducerProject) {}
@@ -51,7 +50,7 @@ export class SqProject {
return resultMap2(
RSProject.getIncludes(this._value, sourceId),
(a) => a,
- (v: RSError.errorMessage) => new SqError(errorFromMessage(v))
+ (v: RSError.Message_t) => new SqError(RSError.fromMessage(v))
);
}
@@ -105,7 +104,7 @@ export class SqProject {
items: [],
})
),
- (v: RSError.error) => new SqError(v)
+ (v: RSError.t) => new SqError(v)
);
}
diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts
index aac26bd5..82e12103 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, SqLocation } from "./SqError";
export { SqShape } from "./SqPointSetDist";
export { resultMap } from "./types";
diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res
index 93581b15..ff2ea2ce 100644
--- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res
+++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_ReducerProject.res
@@ -1,7 +1,7 @@
@genType type reducerProject = ReducerProject_T.project //re-export
-type error = ForTS_SqError.error //use
-type errorMessage = ForTS_SqError.errorMessage //use
+type error = SqError.t //use
+type errorMessage = SqError.Message.t //use
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
type squiggleValue_Record = ForTS_SquiggleValue.squiggleValue_Record //use
diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SqError.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SqError.res
deleted file mode 100644
index c6a4b131..00000000
--- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SqError.res
+++ /dev/null
@@ -1,19 +0,0 @@
-@genType type error = SqError.Error.t //alias
-@genType type errorMessage = SqError.Message.t //alias
-@genType type location = Reducer_Peggy_Parse.location //alias
-
-@genType
-let toString = (e: error): string => SqError.Error.toString(e)
-
-@genType
-let getLocation = (e: error): option =>
- switch e.stackTrace {
- | Some(stack) => Some(stack.location)
- | None => None
- }
-
-@genType
-let createOtherError = (v: string): error => SqError.Message.REOther(v)->SqError.Error.fromMessage
-
-@genType
-let errorFromMessage = (v: errorMessage): error => v->SqError.Error.fromMessage
diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res
index 2a8e8e1c..d116b74b 100644
--- a/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res
+++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS_SquiggleValue/ForTS_SquiggleValue.res
@@ -1,5 +1,5 @@
@genType type squiggleValue = Reducer_T.value //re-export
-type error = ForTS_SqError.error //use
+type error = SqError.t //use
@genType type squiggleValue_Array = Reducer_T.arrayValue //re-export recursive type
@genType type squiggleValue_Record = Reducer_T.map //re-export recursive type
diff --git a/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res b/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res
index cd6562b9..b2bb9d98 100644
--- a/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res
+++ b/packages/squiggle-lang/src/rescript/ForTS/ForTS__Types.res
@@ -1,5 +1,3 @@
-@genType type location = ForTS_SqError.location //re-export
-
@genType type reducerProject = ForTS_ReducerProject.reducerProject //re-export
@genType type squiggleValue = ForTS_SquiggleValue.squiggleValue //re-export
@genType type squiggleValue_Array = ForTS_SquiggleValue_Array.squiggleValue_Array //re-export
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 d0060982..d33d7ba3 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
@@ -8,7 +8,7 @@ let toLocation = (expression: T.expression): SqError.location => {
}
let throwFrom = (error: SqError.Message.t, expression: T.expression) =>
- error->SqError.Error.fromMessageWithLocation(expression->toLocation)->SqError.Error.throw
+ error->SqError.fromMessageWithLocation(expression->toLocation)->SqError.throw
/*
Recursively evaluate the expression
@@ -108,11 +108,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
let result = Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate)
(result, context)
} catch {
- | exn =>
- exn
- ->SqError.Error.fromException
- ->SqError.Error.extend(expression->toLocation)
- ->SqError.Error.throw
+ | exn => exn->SqError.fromException->SqError.extend(expression->toLocation)->SqError.throw
}
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression)
}
@@ -126,18 +122,18 @@ module BackCompatible = {
let parse = (peggyCode: string): result =>
peggyCode->Reducer_Peggy_Parse.parse("main")->Result.map(Reducer_Peggy_ToExpression.fromNode)
- let evaluate = (expression: T.expression): result => {
+ let evaluate = (expression: T.expression): result => {
let context = Reducer_Context.createDefaultContext()
try {
let (value, _) = expression->evaluate(context)
value->Ok
} catch {
- | exn => exn->SqError.Error.fromException->Error
+ | exn => exn->SqError.fromException->Error
}
}
- let evaluateString = (peggyCode: string): result =>
+ let evaluateString = (peggyCode: string): result =>
parse(peggyCode)
- ->E.R2.errMap(e => e->SqError.Message.fromParseError->SqError.Error.fromMessage)
+ ->E.R2.errMap(e => e->SqError.Message.fromParseError->SqError.fromMessage)
->Result.flatMap(evaluate)
}
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 4a0a77da..b2b57bed 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
@@ -1,7 +1,5 @@
module Extra = Reducer_Extra
-// Do not gentype this, use LocationRange from peggy types instead
-// TODO - rename locationPoint -> location, location -> locationRange to match peggy
@genType
type locationPoint = {
line: int,
diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res
index ea0931f2..7abecb92 100644
--- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res
+++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Value.res
@@ -72,13 +72,13 @@ let toStringFunctionCall = ((fn, args)): string => `${fn}(${argsToString(args)})
let toStringResult = x =>
switch x {
| Ok(a) => `Ok(${toString(a)})`
- | Error(m) => `Error(${SqError.Error.toString(m)})`
+ | Error(m) => `Error(${SqError.toString(m)})`
}
-let toStringResultOkless = (codeResult: result): string =>
+let toStringResultOkless = (codeResult: result): string =>
switch codeResult {
| Ok(a) => toString(a)
- | Error(m) => `Error(${SqError.Error.toString(m)})`
+ | Error(m) => `Error(${SqError.toString(m)})`
}
type internalExpressionValueType =
diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res
index 8ead6e32..6655f062 100644
--- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res
+++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject.res
@@ -116,7 +116,7 @@ let getResultOption = (project: t, sourceId: string): ProjectItem.T.resultType =
let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType =>
switch getResultOption(project, sourceId) {
- | None => RENeedToRun->SqError.Error.fromMessage->Error
+ | None => RENeedToRun->SqError.fromMessage->Error
| Some(result) => result
}
@@ -170,7 +170,7 @@ let linkDependencies = (project: t, sourceId: string): Reducer_T.namespace => {
"__result__",
switch project->getResult(id) {
| Ok(result) => result
- | Error(error) => error->SqError.Error.throw
+ | Error(error) => error->SqError.throw
},
),
])
diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res
index 07ecac88..2211f004 100644
--- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res
+++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem.res
@@ -174,7 +174,7 @@ let buildExpression = (this: t): t => {
}
}
-let failRun = (this: t, e: SqError.Error.t): t =>
+let failRun = (this: t, e: SqError.t): t =>
this->setResult(e->Error)->setContinuation(Reducer_Namespace.make())
let doRun = (this: t, context: Reducer_T.context): t =>
@@ -188,11 +188,11 @@ let doRun = (this: t, context: Reducer_T.context): t =>
->setResult(result->Ok)
->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals)
} catch {
- | e => this->failRun(e->SqError.Error.fromException)
+ | e => this->failRun(e->SqError.fromException)
}
- | Error(e) => this->failRun(e->SqError.Error.fromMessage)
+ | Error(e) => this->failRun(e->SqError.fromMessage)
}
- | None => this->failRun(RETodo("attempt to run without expression")->SqError.Error.fromMessage)
+ | None => this->failRun(RETodo("attempt to run without expression")->SqError.fromMessage)
}
let run = (this: t, context: Reducer_T.context): t => {
diff --git a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res
index 8ea3a814..9ef83536 100644
--- a/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res
+++ b/packages/squiggle-lang/src/rescript/ReducerProject/ReducerProject_ProjectItem_T.res
@@ -10,7 +10,7 @@ type expressionType = option
type continuationArgumentType = Reducer_T.namespace
type continuationType = option
type continuationResultType = option>
-type resultArgumentType = result
+type resultArgumentType = result
type resultType = option
type continuesArgumentType = array
type continuesType = array
diff --git a/packages/squiggle-lang/src/rescript/SqError.res b/packages/squiggle-lang/src/rescript/SqError.res
index 15f72d54..2d04fcaf 100644
--- a/packages/squiggle-lang/src/rescript/SqError.res
+++ b/packages/squiggle-lang/src/rescript/SqError.res
@@ -86,6 +86,7 @@ module Message = {
}
module StackTrace = {
+ @genType.opaque
type rec t = {
location: location,
parent: option,
@@ -98,48 +99,68 @@ module StackTrace = {
| None => ""
}
}
-}
-module Error = {
- @genType.opaque
- type t = {
- message: Message.t,
- stackTrace: option,
- }
-
- exception SqException(t)
-
- let toString = (err: t) => err.message->Message.toString
-
- let toStringWithStackTrace = (err: t) =>
- switch err.stackTrace {
- | Some(stack) => "Traceback:\n" ++ stack->StackTrace.toString
- | None => ""
- } ++
- err->toString
-
- let fromMessage = (errorMessage: Message.t) => {
- message: errorMessage,
- stackTrace: None,
- }
-
- let fromMessageWithLocation = (errorMessage: Message.t, location: location): t => {
- message: errorMessage,
- stackTrace: Some({location: location, parent: None}),
- }
-
- let extend = ({message, stackTrace}: t, location: location) => {
- message: message,
- stackTrace: Some({location: location, parent: stackTrace}),
- }
-
- let throw = (t: t) => t->SqException->raise
-
- let fromException = exn =>
- switch exn {
- | SqException(e) => e
- | Message.MessageException(e) => e->fromMessage
- | Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage
- | _ => REOther("Unknown exception")->fromMessage
+ let rec toLocationList = (t: t): list => {
+ switch t.parent {
+ | Some(parent) => Belt.List.add(toLocationList(parent), t.location)
+ | None => list{t.location}
}
+ }
+
+ @genType
+ let toLocationArray = (t: t): array => t->toLocationList->Belt.List.toArray
}
+
+@genType.opaque
+type t = {
+ message: Message.t,
+ stackTrace: option,
+}
+
+exception SqException(t)
+
+@genType
+let fromMessage = (errorMessage: Message.t): t => {
+ message: errorMessage,
+ stackTrace: None,
+}
+
+let fromMessageWithLocation = (errorMessage: Message.t, location: location): t => {
+ message: errorMessage,
+ stackTrace: Some({location: location, parent: None}),
+}
+
+let extend = ({message, stackTrace}: t, location: location) => {
+ message: message,
+ stackTrace: Some({location: location, parent: stackTrace}),
+}
+
+@genType
+let getLocation = (t: t): option => t.stackTrace->E.O2.fmap(stack => stack.location)
+
+@genType
+let getStackTrace = (t: t): option => t.stackTrace
+
+@genType
+let toString = (t: t): string => t.message->Message.toString
+
+@genType
+let createOtherError = (v: string): t => Message.REOther(v)->fromMessage
+
+@genType
+let toStringWithStackTrace = (t: t) =>
+ switch t.stackTrace {
+ | Some(stack) => "Traceback:\n" ++ stack->StackTrace.toString
+ | None => ""
+ } ++
+ t->toString
+
+let throw = (t: t) => t->SqException->raise
+
+let fromException = exn =>
+ switch exn {
+ | SqException(e) => e
+ | Message.MessageException(e) => e->fromMessage
+ | Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage
+ | _ => REOther("Unknown exception")->fromMessage
+ }