Compare commits

...

3 Commits

Author SHA1 Message Date
Sam Nolan
b3e8253ed7 Add source id to error alert 2022-09-22 17:43:56 +10:00
Sam Nolan
eb86d12d63 Fix some build errors about error values 2022-09-22 17:29:02 +10:00
Sam Nolan
e2e30641b3 Add a source annotation to error objects 2022-09-22 17:07:26 +10:00
19 changed files with 85 additions and 66 deletions

View File

@ -172,15 +172,11 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
const signalListeners = { mousemove: handleHover, mouseout: handleOut };
//TODO: This custom error handling is a bit hacky and should be improved.
let mouseItem: result<SqValue, SqError> = !!mouseOverlay
? fn.call([mouseOverlay])
: {
tag: "Error",
value: SqError.createOtherError(
"Hover x-coordinate returned NaN. Expected a number."
),
};
let showChart =
let showChart: JSX.Element | null = null;
if (!isNaN(mouseOverlay)) {
let mouseItem = fn.call([mouseOverlay]);
showChart =
mouseItem.tag === "Ok" &&
mouseItem.value.tag === SqValueTag.Distribution ? (
<DistributionChart
@ -191,6 +187,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
{...distributionPlotSettings}
/>
) : null;
}
let getPercentilesMemoized = React.useMemo(
() => getPercentiles({ chartSettings, fn, environment }),

View File

@ -7,5 +7,5 @@ type Props = {
};
export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => {
return <ErrorAlert heading="Error">{error.toString()}</ErrorAlert>;
return <ErrorAlert heading="Error">{error.toString()} (Source: {error.getSource()})</ErrorAlert>;
};

View File

@ -27,7 +27,7 @@ x=1`,
let mainIncludes = Project.getIncludes(project, "main")
switch mainIncludes {
| Ok(includes) => expect(includes) == ["common"]
| Error(error) => fail(error->Reducer_ErrorValue.errorToString)
| Error(error) => fail(error.error->Reducer_ErrorValue.errorToString)
}
})
let internalProject = project->Project.T.Private.castToInternalProject
@ -63,7 +63,7 @@ x=1`,
let mainIncludes = Project.getIncludes(project, "main")
switch mainIncludes {
| Ok(includes) => expect(includes) == ["common", "myModule"]
| Error(error) => fail(error->Reducer_ErrorValue.errorToString)
| Error(error) => fail(error.error->Reducer_ErrorValue.errorToString)
}
})
@ -106,7 +106,7 @@ x=1`,
let mainIncludes = Project.getIncludes(project, "main")
switch mainIncludes {
| Ok(includes) => expect(includes) == ["common", "common2", "myModule"]
| Error(error) => fail(error->Reducer_ErrorValue.errorToString)
| Error(error) => fail(error.error->Reducer_ErrorValue.errorToString)
}
})
let internalProject = project->Project.T.Private.castToInternalProject

View File

@ -11,7 +11,7 @@ open Expect.Operators
let runFetchResult = (project, sourceId) => {
Project.run(project, sourceId)
Project.getResult(project, sourceId)->InternalExpressionValue.toStringResult
Project.getResult(project, sourceId)->InternalExpressionValue.toStringWithSourceResult
}
let runFetchFlatBindings = (project, sourceId) => {

View File

@ -50,7 +50,7 @@ Case "Running a single source".
/* Let's display the result and bindings */
(
result->InternalExpressionValue.toStringResult,
result->InternalExpressionValue.toStringWithSourceResult,
bindings->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(3)", "@{}")
/* You've got 3 with empty bindings. */
@ -64,7 +64,7 @@ Case "Running a single source".
let bindings = Project.getBindings(project, "main")->Bindings.removeResult
/* Now you have external bindings and external result. */
(
result->InternalExpressionValue.toStringResult,
result->InternalExpressionValue.toStringWithSourceResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}")
})
@ -80,7 +80,7 @@ Case "Running a single source".
Project.runAll(project)
let result = Project.getResult(project, "main")
let _bindings = Project.getBindings(project, "main")
result->InternalExpressionValue.toStringResult->expect == "Ok(3)"
result->InternalExpressionValue.toStringWithSourceResult->expect == "Ok(3)"
})
test("shortcut", () => {
@ -88,7 +88,7 @@ Case "Running a single source".
/* Examples above was to prepare you for the multi source tutorial. */
let (result, bindings) = Project.evaluate("1+2")
(
result->InternalExpressionValue.toStringResult,
result->InternalExpressionValue.toStringWithSourceResult,
bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(3)", "@{}")
})

View File

@ -32,7 +32,7 @@ describe("ReducerProject Tutorial", () => {
let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult
(
result3->InternalExpressionValue.toStringResult,
result3->InternalExpressionValue.toStringWithSourceResult,
bindings3->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
})
@ -58,7 +58,7 @@ describe("ReducerProject Tutorial", () => {
let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult
(
result3->InternalExpressionValue.toStringResult,
result3->InternalExpressionValue.toStringWithSourceResult,
bindings3->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
})
@ -94,7 +94,7 @@ describe("ReducerProject Tutorial", () => {
let bindings3 = Project.getBindings(project, "source3")->Bindings.removeResult
(
result3->InternalExpressionValue.toStringResult,
result3->InternalExpressionValue.toStringWithSourceResult,
bindings3->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(())", "@{x: 1,y: 2,z: 3}")
/*

View File

@ -38,7 +38,7 @@ Here we will finally proceed to a real life scenario. */
/* Parse includes has set the includes */
switch Project.getIncludes(project, "main") {
| Ok(includes) => includes->expect == ["common"]
| Error(err) => err->Reducer_ErrorValue.errorToString->fail
| Error(err) => err.error->Reducer_ErrorValue.errorToString->fail
}
/* If the includes cannot be parsed then you get a syntax error.
Otherwise you get the includes.
@ -87,7 +87,7 @@ Here we will finally proceed to a real life scenario. */
let rIncludes = Project.getIncludes(project, sourceName)
switch rIncludes {
/* Maybe there is an include syntax error */
| Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError
| Error(err) => err.error->Reducer_ErrorValue.errorToString->Js.Exn.raiseError
| Ok(includes) =>
Belt.Array.forEach(includes, newIncludeName => {
@ -146,7 +146,7 @@ Here we will finally proceed to a real life scenario. */
/* And see the result and bindings.. */
test("recursive includes", () => {
(
result->InternalExpressionValue.toStringResult,
result->InternalExpressionValue.toStringWithSourceResult,
bindings->Bindings.removeResult->InternalExpressionValue.toStringBindings,
)->expect == ("Ok(6)", "@{doubleX: 2,x: 1,y: 2,z: 3}")
/* Everything as expected */
@ -172,7 +172,7 @@ Here we will finally proceed to a real life scenario. */
test("getIncludes", () => {
switch Project.getIncludes(project, "main") {
| Ok(includes) => includes->expect == ["common"]
| Error(err) => err->Reducer_ErrorValue.errorToString->fail
| Error(err) => err.error->Reducer_ErrorValue.errorToString->fail
}
})
})

View File

@ -30,7 +30,7 @@ describe("ReducerProject Tutorial", () => {
/* We can now run the project */
Project.runAll(project)
let result = Project.getResult(project, "main")
result->InternalExpressionValue.toStringResult->expect == "Ok(6)"
result->InternalExpressionValue.toStringWithSourceResult->expect == "Ok(6)"
})
})

View File

@ -32,7 +32,7 @@ describe("ReducerProject Tutorial", () => {
test("userResults", () => {
let userResultsAsString = Belt.Array.map(userResults, aResult =>
aResult->InternalExpressionValue.toStringResult
aResult->InternalExpressionValue.toStringWithSourceResult
)
userResultsAsString->expect == ["Ok(2)", "Ok(4)", "Ok(6)", "Ok(8)", "Ok(10)"]
})

View File

@ -1,17 +1,13 @@
import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
export class SqError {
constructor(private _value: RSErrorValue.reducerErrorValue) {}
constructor(private _value: RSErrorValue.reducerErrorValueWithSource) {}
toString() {
return RSErrorValue.toString(this._value);
}
static createTodoError(v: string) {
return new SqError(RSErrorValue.createTodoError(v));
}
static createOtherError(v: string) {
return new SqError(RSErrorValue.createOtherError(v));
getSource() {
return RSErrorValue.getSource(this._value);
}
}

View File

@ -1,5 +1,5 @@
import * as RSProject from "../rescript/ForTS/ForTS_ReducerProject.gen";
import { reducerErrorValue } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
import { reducerErrorValueWithSource } from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen";
import { environment } from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution_Environment.gen";
import { SqError } from "./SqError";
import { SqModule } from "./SqModule";
@ -50,7 +50,7 @@ export class SqProject {
return resultMap2(
RSProject.getIncludes(this._value, sourceId),
(a) => a,
(v: reducerErrorValue) => new SqError(v)
(v: reducerErrorValueWithSource) => new SqError(v)
);
}
@ -104,7 +104,7 @@ export class SqProject {
items: [],
})
),
(v: reducerErrorValue) => new SqError(v)
(v: reducerErrorValueWithSource) => new SqError(v)
);
}

View File

@ -1,6 +1,7 @@
@genType type reducerProject = ReducerProject_T.t //re-export
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
type reducerErrorValueWithSource = ForTS_Reducer_ErrorValue.reducerErrorValueWithSource //use
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
type squiggleValue_Module = ForTS_SquiggleValue_Module.squiggleValue_Module //use
@ -102,7 +103,7 @@ To set the includes one first has to call "parseIncludes". The parsed includes o
@genType
let getIncludes = (project: reducerProject, sourceId: string): result<
array<string>,
reducerErrorValue,
reducerErrorValueWithSource,
> => project->T.Private.castToInternalProject->Private.getIncludes(sourceId)
/* Other sources contributing to the global namespace of this source. */
@ -200,7 +201,7 @@ Get the result after running this source file or the project
@genType
let getResult = (project: reducerProject, sourceId: string): result<
squiggleValue,
reducerErrorValue,
reducerErrorValueWithSource,
> => project->T.Private.castToInternalProject->Private.getResult(sourceId)
/*
@ -210,7 +211,7 @@ The source has to be include free
*/
@genType
let evaluate = (sourceCode: string): (
result<squiggleValue, reducerErrorValue>,
result<squiggleValue, reducerErrorValueWithSource>,
squiggleValue_Module,
) => Private.evaluate(sourceCode)

View File

@ -1,12 +1,13 @@
@genType type reducerErrorValue = Reducer_ErrorValue.errorValue //alias
@genType type reducerErrorValueWithSource = Reducer_ErrorValue.errorValueWithSource //alias
@genType type syntaxErrorLocation = Reducer_ErrorValue.syntaxErrorLocation //alias
@genType
let toString = (e: reducerErrorValue): string => Reducer_ErrorValue.errorToString(e)
let toString = (e: reducerErrorValueWithSource): string => Reducer_ErrorValue.errorToString(e.error)
@genType
let getLocation = (e: reducerErrorValue): option<syntaxErrorLocation> =>
switch e {
let getLocation = (e: reducerErrorValueWithSource): option<syntaxErrorLocation> =>
switch e.error {
| RESyntaxError(_, optionalLocation) => optionalLocation
| _ => None
}
@ -16,3 +17,6 @@ let createTodoError = (v: string) => Reducer_ErrorValue.RETodo(v)
@genType
let createOtherError = (v: string) => Reducer_ErrorValue.REOther(v)
@genType
let getSource = (err: reducerErrorValueWithSource) => err.sourceId

View File

@ -81,3 +81,11 @@ let fromException = exn =>
}
let toException = (errorValue: t) => raise(ErrorException(errorValue))
@genType.opaque
type errorValueWithSource = {error: errorValue, sourceId: string}
let errorAddSource = (r: result<'a, errorValue>, sourceId: string): result<
'a,
errorValueWithSource,
> => r->E.R2.errMap(err => {error: err, sourceId: sourceId})

View File

@ -131,6 +131,12 @@ let toStringResult = x =>
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
}
let toStringWithSourceResult = (x: result<t, ErrorValue.errorValueWithSource>) =>
switch x {
| Ok(a) => `Ok(${toString(a)})`
| Error(m) => `Error(${ErrorValue.errorToString(m.error)})`
}
let toStringOptionResult = x =>
switch x {
| Some(a) => toStringResult(a)

View File

@ -129,7 +129,7 @@ module Private = {
let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType =>
switch getResultOption(project, sourceId) {
| None => RENeedToRun->Error
| None => RENeedToRun->Error->ErrorValue.errorAddSource(sourceId)
| Some(result) => result
}

View File

@ -6,12 +6,14 @@ module ExpressionT = Reducer_Expression_T
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ReducerFnT = ReducerProject_ReducerFn_T
module ErrorValue = Reducer_ErrorValue
module T = ReducerProject_ProjectItem_T
type projectItem = T.projectItem
type t = T.t
let emptyItem = T.ProjectItem({
let emptyItem = (id: string) => T.ProjectItem({
id: id,
source: "",
rawParse: None,
expression: None,
@ -25,6 +27,7 @@ let emptyItem = T.ProjectItem({
// source -> rawParse -> includes -> expression -> continuation -> result
let getSource = (T.ProjectItem(r)): T.sourceType => r.source
let getId = (T.ProjectItem(r)): T.sourceType => r.id
let getRawParse = (T.ProjectItem(r)): T.rawParseType => r.rawParse
let getExpression = (T.ProjectItem(r)): T.expressionType => r.expression
let getContinuation = (T.ProjectItem(r)): T.continuationArgumentType => r.continuation
@ -36,7 +39,7 @@ let getDirectIncludes = (T.ProjectItem(r)): array<string> => r.directIncludes
let getIncludesAsVariables = (T.ProjectItem(r)): T.importAsVariablesType => r.includeAsVariables
let touchSource = (this: t): t => {
let T.ProjectItem(r) = emptyItem
let T.ProjectItem(r) = emptyItem(getId(this))
T.ProjectItem({
...r,
source: getSource(this),
@ -48,7 +51,7 @@ let touchSource = (this: t): t => {
}
let touchRawParse = (this: t): t => {
let T.ProjectItem(r) = emptyItem
let T.ProjectItem(r) = emptyItem(getId(this))
T.ProjectItem({
...r,
source: getSource(this),
@ -61,7 +64,7 @@ let touchRawParse = (this: t): t => {
}
let touchExpression = (this: t): t => {
let T.ProjectItem(r) = emptyItem
let T.ProjectItem(r) = emptyItem(getId(this))
T.ProjectItem({
...r,
source: getSource(this),
@ -104,7 +107,7 @@ let setResult = (T.ProjectItem(r): t, result: T.resultArgumentType): t => T.Proj
let cleanResults = touchExpression
let clean = (this: t): t => {
let T.ProjectItem(r) = emptyItem
let T.ProjectItem(r) = emptyItem(getId(this))
T.ProjectItem({
...r,
source: getSource(this),
@ -143,7 +146,7 @@ let parseIncludes = (this: t): t => {
let T.ProjectItem(r) = this
let rRawImportAsVariables = getSource(this)->ReducerProject_ParseIncludes.parseIncludes
switch rRawImportAsVariables {
| Error(e) => resetIncludes(this)->setIncludes(Error(e))
| Error(e) => resetIncludes(this)->setIncludes(Error(e)->ErrorValue.errorAddSource(getId(this)))
| Ok(rawImportAsVariables) => {
let includes = rawImportAsVariables->Belt.Array.map(((_variable, file)) => file)->Ok
let includeAsVariables =
@ -190,14 +193,16 @@ let doBuildResult = (
): T.resultType =>
this
->getExpression
->Belt.Option.map(
Belt.Result.flatMap(_, expression =>
->Belt.Option.map(expressionResult =>
expressionResult
->Belt.Result.flatMap(_, expression =>
try {
Reducer_Expression.reduceExpressionInProject(expression, aContinuation, accessors)->Ok
} catch {
| exn => Reducer_ErrorValue.fromException(exn)->Error
}
),
)
->ErrorValue.errorAddSource(getId(this))
)
let buildResult = (this: t, aContinuation: T.continuation, accessors: ProjectAccessorsT.t): t => {

View File

@ -4,6 +4,7 @@ module InternalExpressionValue = ReducerInterface_InternalExpressionValue
open Reducer_ErrorValue
type sourceArgumentType = string
type idArgumentType = string
type sourceType = string
type rawParseArgumentType = result<Parse.node, errorValue>
type rawParseType = option<rawParseArgumentType>
@ -15,16 +16,17 @@ type continuationType = option<continuationArgumentType>
type continuationResultType = option<result<continuationArgumentType, errorValue>>
type bindingsArgumentType = InternalExpressionValue.nameSpace
type bindingsType = option<bindingsArgumentType>
type resultArgumentType = result<InternalExpressionValue.t, errorValue>
type resultArgumentType = result<InternalExpressionValue.t, errorValueWithSource>
type resultType = option<resultArgumentType>
type continuesArgumentType = array<string>
type continuesType = array<string>
type includesArgumentType = string
type includesType = result<array<string>, errorValue>
type includesType = result<array<string>, errorValueWithSource>
type importAsVariablesType = array<(string, string)>
type projectItem =
| ProjectItem({
id: string,
source: sourceType,
rawParse: rawParseType,
expression: expressionType,

View File

@ -33,5 +33,5 @@ module Private = {
let getSourceIds = (this: t): array<string> => Belt.Map.String.keysToArray(this["items"])
let getItem = (this: t, sourceId: string) =>
Belt.Map.String.getWithDefault(this["items"], sourceId, ProjectItem.emptyItem)
Belt.Map.String.getWithDefault(this["items"], sourceId, ProjectItem.emptyItem(sourceId))
}