error stacktraces and locations (initial take, WIP)

This commit is contained in:
Vyacheslav Matyukhin 2022-09-25 03:16:14 +04:00
parent d3bc08ab9d
commit 41574e08c9
No known key found for this signature in database
GPG Key ID: 3D2A774C5489F96C
27 changed files with 480 additions and 340 deletions

View File

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

View File

@ -298,7 +298,9 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
{() => ( {() => (
<div> <div>
<span>No display for type: </span>{" "} <span>No display for type: </span>{" "}
<span className="font-semibold text-slate-600">{value.tag}</span> <span className="font-semibold text-slate-600">
{(value as any).tag}
</span>
</div> </div>
)} )}
</VariableList> </VariableList>

View File

@ -9,12 +9,12 @@ open Jest
open Expect open Expect
let expectParseToBe = (expr, answer) => let expectParseToBe = (expr, answer) =>
Parse.parse(expr)->Parse.toStringResult->expect->toBe(answer) Parse.parse(expr, "test")->Parse.toStringResult->expect->toBe(answer)
let testParse = (expr, answer) => test(expr, () => expectParseToBe(expr, answer)) let testParse = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
let expectToExpressionToBe = (expr, answer, ~v="_", ()) => { let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
let rExpr = Parse.parse(expr)->Result.map(ToExpression.fromNode) let rExpr = Parse.parse(expr, "test")->Result.map(ToExpression.fromNode)
let a1 = rExpr->ExpressionT.toStringResultOkless let a1 = rExpr->ExpressionT.toStringResultOkless
if v == "_" { if v == "_" {
@ -22,6 +22,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
} else { } else {
let a2 = let a2 =
rExpr rExpr
->E.R2.errMap(e => e->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue)
->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr)) ->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr))
->Reducer_Value.toStringResultOkless ->Reducer_Value.toStringResultOkless
(a1, a2)->expect->toEqual((answer, v)) (a1, a2)->expect->toEqual((answer, v))

View File

@ -25,7 +25,7 @@ x=1`,
let mainIncludes = Project.getIncludes(project, "main") let mainIncludes = Project.getIncludes(project, "main")
switch mainIncludes { switch mainIncludes {
| Ok(includes) => expect(includes) == ["common"] | Ok(includes) => expect(includes) == ["common"]
| Error(error) => fail(error->Reducer_ErrorValue.errorToString) | Error(error) => fail(error->Reducer_ErrorValue.errorValueToString)
} }
}) })
test("past chain", () => { test("past chain", () => {
@ -60,7 +60,7 @@ x=1`,
let mainIncludes = Project.getIncludes(project, "main") let mainIncludes = Project.getIncludes(project, "main")
switch mainIncludes { switch mainIncludes {
| Ok(includes) => expect(includes) == ["common", "myModule"] | Ok(includes) => expect(includes) == ["common", "myModule"]
| Error(error) => fail(error->Reducer_ErrorValue.errorToString) | Error(error) => fail(error->Reducer_ErrorValue.errorValueToString)
} }
}) })
@ -99,7 +99,7 @@ x=1`,
let mainIncludes = Project.getIncludes(project, "main") let mainIncludes = Project.getIncludes(project, "main")
switch mainIncludes { switch mainIncludes {
| Ok(includes) => expect(includes) == ["common", "common2", "myModule"] | Ok(includes) => expect(includes) == ["common", "common2", "myModule"]
| Error(error) => fail(error->Reducer_ErrorValue.errorToString) | Error(error) => fail(error->Reducer_ErrorValue.errorValueToString)
} }
}) })
test("direct past chain", () => { test("direct past chain", () => {

View File

@ -36,7 +36,7 @@ Here we will finally proceed to a real life scenario. */
/* Parse includes has set the includes */ /* Parse includes has set the includes */
switch project->Project.getIncludes("main") { switch project->Project.getIncludes("main") {
| Ok(includes) => includes->expect == ["common"] | Ok(includes) => includes->expect == ["common"]
| Error(err) => err->Reducer_ErrorValue.errorToString->fail | Error(err) => err->Reducer_ErrorValue.errorValueToString->fail
} }
/* If the includes cannot be parsed then you get a syntax error. /* If the includes cannot be parsed then you get a syntax error.
Otherwise you get the includes. Otherwise you get the includes.
@ -85,7 +85,7 @@ Here we will finally proceed to a real life scenario. */
let rIncludes = project->Project.getIncludes(sourceName) let rIncludes = project->Project.getIncludes(sourceName)
switch rIncludes { switch rIncludes {
/* Maybe there is an include syntax error */ /* Maybe there is an include syntax error */
| Error(err) => err->Reducer_ErrorValue.errorToString->Js.Exn.raiseError | Error(err) => err->Reducer_ErrorValue.errorValueToString->Js.Exn.raiseError
| Ok(includes) => | Ok(includes) =>
includes->Belt.Array.forEach(newIncludeName => { includes->Belt.Array.forEach(newIncludeName => {
@ -169,7 +169,7 @@ Here we will finally proceed to a real life scenario. */
test("getIncludes", () => { test("getIncludes", () => {
switch Project.getIncludes(project, "main") { switch Project.getIncludes(project, "main") {
| Ok(includes) => includes->expect == ["common"] | Ok(includes) => includes->expect == ["common"]
| Error(err) => err->Reducer_ErrorValue.errorToString->fail | Error(err) => err->Reducer_ErrorValue.errorValueToString->fail
} }
}) })
}) })

View File

@ -1,11 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env node
import { run } from "./lib.mjs"; import { run } from "./lib.mjs";
const src = process.argv[2]; import { Command } from "commander";
const program = new Command();
program.arguments("<string>");
const options = program.parse(process.argv);
const src = program.args[0];
if (!src) { if (!src) {
throw new Error("Expected src"); throw new Error("Expected src");
} }
console.log(`Running ${src}`);
const sampleCount = process.env.SAMPLE_COUNT; const sampleCount = process.env.SAMPLE_COUNT;

View File

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

View File

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

View File

@ -143,7 +143,7 @@ module Integration = {
| Error(b) => | Error(b) =>
("Integration error 2 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead." ++ ("Integration error 2 in Danger.integrate. It's possible that your function doesn't return a number, try definining auxiliaryFunction(x) = mean(yourFunction(x)) and integrate auxiliaryFunction instead." ++
"Original error: " ++ "Original error: " ++
b->Reducer_ErrorValue.errorToString) b->Reducer_ErrorValue.errorValueToString)
->Reducer_ErrorValue.REOther ->Reducer_ErrorValue.REOther
->Error ->Error
} }
@ -225,7 +225,7 @@ module Integration = {
reducer, reducer,
)->E.R2.errMap(b => )->E.R2.errMap(b =>
("Integration error 7 in Danger.integrate. Something went wrong along the way: " ++ ("Integration error 7 in Danger.integrate. Something went wrong along the way: " ++
b->Reducer_ErrorValue.errorToString)->Reducer_ErrorValue.REOther b->Reducer_ErrorValue.errorValueToString)->Reducer_ErrorValue.REOther
) )
| _ => | _ =>
"Integration error 8 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))" "Integration error 8 in Danger.integrate. Remember that inputs are (function, number (min), number (max), number(increment))"

View File

@ -1,5 +1,6 @@
@genType type reducerProject = ReducerProject_T.project //re-export @genType type reducerProject = ReducerProject_T.project //re-export
type reducerError = ForTS_Reducer_ErrorValue.reducerError //use
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use
type squiggleValue = ForTS_SquiggleValue.squiggleValue //use type squiggleValue = ForTS_SquiggleValue.squiggleValue //use
@ -191,10 +192,8 @@ let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Rec
Get the result after running this source file or the project Get the result after running this source file or the project
*/ */
@genType @genType
let getResult = (project: reducerProject, sourceId: string): result< let getResult = (project: reducerProject, sourceId: string): result<squiggleValue, reducerError> =>
squiggleValue, project->Private.getResult(sourceId)
reducerErrorValue,
> => project->Private.getResult(sourceId)
/* /*
This is a convenience function to get the result of a single source without creating a project. This is a convenience function to get the result of a single source without creating a project.
@ -202,10 +201,8 @@ However, without a project, you cannot handle include directives.
The source has to be include free The source has to be include free
*/ */
@genType @genType
let evaluate = (sourceCode: string): ( let evaluate = (sourceCode: string): (result<squiggleValue, reducerError>, squiggleValue_Record) =>
result<squiggleValue, reducerErrorValue>, Private.evaluate(sourceCode)
squiggleValue_Record,
) => Private.evaluate(sourceCode)
@genType @genType
let setEnvironment = (project: reducerProject, environment: environment): unit => let setEnvironment = (project: reducerProject, environment: environment): unit =>
@ -213,24 +210,3 @@ let setEnvironment = (project: reducerProject, environment: environment): unit =
@genType @genType
let getEnvironment = (project: reducerProject): environment => project->Private.getEnvironment let getEnvironment = (project: reducerProject): environment => project->Private.getEnvironment
/*
Foreign function interface is intentionally demolished.
There is another way to do that: Umur.
Also there is no more conversion from javascript to squiggle values currently.
If the conversion to the new project is too difficult, I can add it later.
*/
// let foreignFunctionInterface = (
// lambdaValue: squiggleValue_Lambda,
// argArray: array<squiggleValue>,
// environment: environment,
// ): result<squiggleValue, reducerErrorValue> => {
// let accessors = ReducerProject_ProjectAccessors_T.identityAccessorsWithEnvironment(environment)
// Reducer_Expression_Lambda.foreignFunctionInterface(
// lambdaValue,
// argArray,
// accessors,
// Reducer_Expression.reduceExpressionInProject,
// )
// }

View File

@ -1,18 +1,21 @@
@genType type reducerError = Reducer_ErrorValue.error //alias
@genType type reducerErrorValue = Reducer_ErrorValue.errorValue //alias @genType type reducerErrorValue = Reducer_ErrorValue.errorValue //alias
@genType type syntaxErrorLocation = Reducer_ErrorValue.syntaxErrorLocation //alias @genType type location = Reducer_ErrorValue.location //alias
@genType @genType
let toString = (e: reducerErrorValue): string => Reducer_ErrorValue.errorToString(e) let toString = (e: reducerError): string => Reducer_ErrorValue.errorToString(e)
@genType @genType
let getLocation = (e: reducerErrorValue): option<syntaxErrorLocation> => let getLocation = (e: reducerError): option<location> =>
switch e { switch e.stackTrace {
| RESyntaxError(_, optionalLocation) => optionalLocation | Some(stack) => Some(stack.location)
| _ => None | None => None
} }
@genType @genType
let createTodoError = (v: string) => Reducer_ErrorValue.RETodo(v) let createOtherError = (v: string): reducerError =>
Reducer_ErrorValue.REOther(v)->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue
@genType @genType
let createOtherError = (v: string) => Reducer_ErrorValue.REOther(v) let attachEmptyStackTraceToErrorValue = (v: reducerErrorValue): reducerError =>
Reducer_ErrorValue.attachEmptyStackTraceToErrorValue(v)

View File

@ -1,5 +1,5 @@
@genType type squiggleValue = Reducer_T.value //re-export @genType type squiggleValue = Reducer_T.value //re-export
type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //use type reducerError = ForTS_Reducer_ErrorValue.reducerError //use
@genType type squiggleValue_Array = Reducer_T.arrayValue //re-export recursive type @genType type squiggleValue_Array = Reducer_T.arrayValue //re-export recursive type
@genType type squiggleValue_Record = Reducer_T.map //re-export recursive type @genType type squiggleValue_Record = Reducer_T.map //re-export recursive type
@ -69,7 +69,7 @@ let toString = (variant: squiggleValue) => Reducer_Value.toString(variant)
// This is a useful method for unit tests. // This is a useful method for unit tests.
// Convert the result along with the error message to a string. // Convert the result along with the error message to a string.
@genType @genType
let toStringResult = (variantResult: result<squiggleValue, reducerErrorValue>) => let toStringResult = (variantResult: result<squiggleValue, reducerError>) =>
Reducer_Value.toStringResult(variantResult) Reducer_Value.toStringResult(variantResult)
@genType @genType

View File

@ -1,5 +1,5 @@
@genType type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //re-export @genType type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //re-export
@genType type syntaxErrorLocation = ForTS_Reducer_ErrorValue.syntaxErrorLocation //re-export @genType type location = ForTS_Reducer_ErrorValue.location //re-export
@genType type reducerProject = ForTS_ReducerProject.reducerProject //re-export @genType type reducerProject = ForTS_ReducerProject.reducerProject //re-export
@genType type squiggleValue = ForTS_SquiggleValue.squiggleValue //re-export @genType type squiggleValue = ForTS_SquiggleValue.squiggleValue //re-export

View File

@ -1,6 +1,16 @@
//TODO: Do not export here but in ForTS__Types // Do not gentype this, use LocationRange from peggy types instead
@gentype.import("peggy") @genType.as("LocationRange") // TODO - rename locationPoint -> location, location -> locationRange to match peggy
type syntaxErrorLocation @genType
type locationPoint = {
line: int,
column: int,
}
@genType
type location = {
source: string,
start: locationPoint,
end: locationPoint,
}
@genType.opaque @genType.opaque
type errorValue = type errorValue =
@ -18,7 +28,7 @@ type errorValue =
| REOperationError(Operation.operationError) | REOperationError(Operation.operationError)
| RERecordPropertyNotFound(string, string) | RERecordPropertyNotFound(string, string)
| RESymbolNotFound(string) | RESymbolNotFound(string)
| RESyntaxError(string, option<syntaxErrorLocation>) | RESyntaxError(string, option<location>)
| RETodo(string) // To do | RETodo(string) // To do
| REUnitNotFound(string) | REUnitNotFound(string)
| RENeedToRun | RENeedToRun
@ -28,7 +38,20 @@ type t = errorValue
exception ErrorException(errorValue) exception ErrorException(errorValue)
let errorToString = err => type rec stackTrace = {
location: location,
parent: option<stackTrace>,
}
@genType.opaque
type error = {
error: t,
stackTrace: option<stackTrace>,
}
exception ExceptionWithStackTrace(error)
let errorValueToString = (err: errorValue) =>
switch err { switch err {
| REArityError(_oFnName, arity, usedArity) => | REArityError(_oFnName, arity, usedArity) =>
`${Js.String.make(arity)} arguments expected. Instead ${Js.String.make( `${Js.String.make(arity)} arguments expected. Instead ${Js.String.make(
@ -80,4 +103,37 @@ let fromException = exn =>
| _e => REOther("Unknown error") | _e => REOther("Unknown error")
} }
let toException = (errorValue: t) => raise(ErrorException(errorValue)) let rec stackTraceToString = ({location, parent}: stackTrace) => {
` Line ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}, source ${location.source}\n` ++
switch parent {
| Some(parent) => stackTraceToString(parent)
| None => ""
}
}
let errorToString = (err: error) =>
switch err.stackTrace {
| Some(stack) => "Traceback:\n" ++ stack->stackTraceToString
| None => ""
} ++
err.error->errorValueToString
let toException = (errorValue: t) => errorValue->ErrorException->raise
let attachEmptyStackTraceToErrorValue = (errorValue: t) => {
error: errorValue,
stackTrace: None,
}
let attachLocationToErrorValue = (errorValue: t, location: location): error => {
error: errorValue,
stackTrace: Some({location: location, parent: None}),
}
let raiseNewExceptionWithStackTrace = (errorValue: t, location: location) =>
errorValue->attachLocationToErrorValue(location)->ExceptionWithStackTrace->raise
let raiseExtendedExceptionWithStackTrace = ({error, stackTrace}: error, location: location) =>
{error: error, stackTrace: Some({location: location, parent: stackTrace})}
->ExceptionWithStackTrace
->raise

View File

@ -5,12 +5,19 @@ module T = Reducer_T
type errorValue = Reducer_ErrorValue.errorValue type errorValue = Reducer_ErrorValue.errorValue
let toLocation = (expression: T.expression): Reducer_ErrorValue.location => {
expression.ast.location
}
let throwFrom = (error: errorValue, expression: T.expression) =>
error->Reducer_ErrorValue.raiseNewExceptionWithStackTrace(expression->toLocation)
/* /*
Recursively evaluate the expression Recursively evaluate the expression
*/ */
let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
// Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`) // Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`)
switch expression { switch expression.content {
| T.EBlock(statements) => { | T.EBlock(statements) => {
let innerContext = {...context, bindings: context.bindings->Bindings.extend} let innerContext = {...context, bindings: context.bindings->Bindings.extend}
let (value, _) = let (value, _) =
@ -49,7 +56,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
let (key, _) = eKey->evaluate(context) let (key, _) = eKey->evaluate(context)
let keyString = switch key { let keyString = switch key {
| IEvString(s) => s | IEvString(s) => s
| _ => REOther("Record keys must be strings")->Reducer_ErrorValue.ErrorException->raise | _ => REOther("Record keys must be strings")->throwFrom(expression)
} }
let (value, _) = eValue->evaluate(context) let (value, _) = eValue->evaluate(context)
(keyString, value) (keyString, value)
@ -73,7 +80,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
| T.ESymbol(name) => | T.ESymbol(name) =>
switch context.bindings->Bindings.get(name) { switch context.bindings->Bindings.get(name) {
| Some(v) => (v, context) | Some(v) => (v, context)
| None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise | None => RESymbolNotFound(name)->throwFrom(expression)
} }
| T.EValue(value) => (value, context) | T.EValue(value) => (value, context)
@ -82,7 +89,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
let (predicateResult, _) = predicate->evaluate(context) let (predicateResult, _) = predicate->evaluate(context)
switch predicateResult { switch predicateResult {
| T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context) | T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context)
| _ => REExpectedType("Boolean", "")->Reducer_ErrorValue.ErrorException->raise | _ => REExpectedType("Boolean", "")->throwFrom(expression)
} }
} }
@ -98,12 +105,18 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
argValue argValue
}) })
switch lambda { switch lambda {
| T.IEvLambda(lambda) => ( | T.IEvLambda(lambda) =>
Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate), try {
context, let result = Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate)
) (result, context)
| _ => } catch {
RENotAFunction(lambda->Reducer_Value.toString)->Reducer_ErrorValue.ErrorException->raise | Reducer_ErrorValue.ErrorException(e) => e->throwFrom(expression) // function implementation returned an error without location
| Reducer_ErrorValue.ExceptionWithStackTrace(e) =>
Reducer_ErrorValue.raiseExtendedExceptionWithStackTrace(e, expression->toLocation) // function implementation probably called a lambda that threw an exception
| Js.Exn.Error(obj) =>
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwFrom(expression)
}
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression)
} }
} }
} }
@ -113,18 +126,24 @@ module BackCompatible = {
// Those methods are used to support the existing tests // Those methods are used to support the existing tests
// If they are used outside limited testing context, error location reporting will fail // If they are used outside limited testing context, error location reporting will fail
let parse = (peggyCode: string): result<T.expression, errorValue> => let parse = (peggyCode: string): result<T.expression, errorValue> =>
peggyCode->Reducer_Peggy_Parse.parse->Result.map(Reducer_Peggy_ToExpression.fromNode) peggyCode->Reducer_Peggy_Parse.parse("main")->Result.map(Reducer_Peggy_ToExpression.fromNode)
let evaluate = (expression: T.expression): result<T.value, errorValue> => { let evaluate = (expression: T.expression): result<T.value, Reducer_ErrorValue.error> => {
let context = Reducer_Context.createDefaultContext() let context = Reducer_Context.createDefaultContext()
try { try {
let (value, _) = expression->evaluate(context) let (value, _) = expression->evaluate(context)
value->Ok value->Ok
} catch { } catch {
| exn => Reducer_ErrorValue.fromException(exn)->Error | exn =>
exn
->Reducer_ErrorValue.fromException
->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue
->Error // TODO - this could be done better (see ReducerProject)
} }
} }
let evaluateString = (peggyCode: string): result<T.value, errorValue> => let evaluateString = (peggyCode: string): result<T.value, Reducer_ErrorValue.error> =>
parse(peggyCode)->Result.flatMap(evaluate) parse(peggyCode)
->E.R2.errMap(e => e->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue)
->Result.flatMap(evaluate)
} }

View File

@ -3,14 +3,18 @@ module T = Reducer_T
type errorValue = BErrorValue.errorValue type errorValue = BErrorValue.errorValue
type expression = Reducer_T.expression type expression = Reducer_T.expression
type expressionContent = Reducer_T.expressionContent
let eArray = (anArray: array<T.expression>) => anArray->T.EArray let eArray = (anArray: array<T.expression>): expressionContent => anArray->T.EArray
let eBool = aBool => aBool->T.IEvBool->T.EValue let eBool = aBool => aBool->T.IEvBool->T.EValue
let eCall = (fn: expression, args: array<expression>): expression => T.ECall(fn, args) let eCall = (fn: expression, args: array<expression>): expressionContent => T.ECall(fn, args)
let eLambda = (parameters: array<string>, expr: expression) => T.ELambda(parameters, expr) let eLambda = (parameters: array<string>, expr: expression): expressionContent => T.ELambda(
parameters,
expr,
)
let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue
@ -18,13 +22,13 @@ let eRecord = (aMap: array<(T.expression, T.expression)>) => aMap->T.ERecord
let eString = aString => aString->T.IEvString->T.EValue let eString = aString => aString->T.IEvString->T.EValue
let eSymbol = (name: string): expression => T.ESymbol(name) let eSymbol = (name: string): expressionContent => T.ESymbol(name)
let eBlock = (exprs: array<expression>): expression => T.EBlock(exprs) let eBlock = (exprs: array<expression>): expressionContent => T.EBlock(exprs)
let eProgram = (exprs: array<expression>): expression => T.EProgram(exprs) let eProgram = (exprs: array<expression>): expressionContent => T.EProgram(exprs)
let eLetStatement = (symbol: string, valueExpression: expression): expression => T.EAssign( let eLetStatement = (symbol: string, valueExpression: expression): expressionContent => T.EAssign(
symbol, symbol,
valueExpression, valueExpression,
) )
@ -33,11 +37,8 @@ let eTernary = (
predicate: expression, predicate: expression,
trueCase: expression, trueCase: expression,
falseCase: expression, falseCase: expression,
): expression => T.ETernary(predicate, trueCase, falseCase) ): expressionContent => T.ETernary(predicate, trueCase, falseCase)
let eIdentifier = (name: string): expression => name->T.ESymbol let eIdentifier = (name: string): expressionContent => name->T.ESymbol
// let eTypeIdentifier = (name: string): expression => let eVoid: expressionContent = T.IEvVoid->T.EValue
// name->T.IEvTypeIdentifier->T.EValue
let eVoid: expression = T.IEvVoid->T.EValue

View File

@ -12,7 +12,7 @@ let semicolonJoin = values =>
Converts the expression to String Converts the expression to String
*/ */
let rec toString = (expression: t) => let rec toString = (expression: t) =>
switch expression { switch expression.content {
| EBlock(statements) => | EBlock(statements) =>
`{${Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin}}` `{${Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin}}`
| EProgram(statements) => Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin | EProgram(statements) => Js.Array2.map(statements, aValue => toString(aValue))->semicolonJoin
@ -31,13 +31,13 @@ let rec toString = (expression: t) =>
let toStringResult = codeResult => let toStringResult = codeResult =>
switch codeResult { switch codeResult {
| Ok(a) => `Ok(${toString(a)})` | Ok(a) => `Ok(${toString(a)})`
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` | Error(m) => `Error(${Reducer_ErrorValue.errorValueToString(m)})`
} }
let toStringResultOkless = codeResult => let toStringResultOkless = codeResult =>
switch codeResult { switch codeResult {
| Ok(a) => toString(a) | Ok(a) => toString(a)
| Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` | Error(m) => `Error(${Reducer_ErrorValue.errorValueToString(m)})`
} }
let inspect = (expr: t): t => { let inspect = (expr: t): t => {

View File

@ -12,21 +12,21 @@ zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda
outerBlock outerBlock
= statements:array_statements finalExpression: (statementSeparator @expression)? = statements:array_statements finalExpression: (statementSeparator @expression)?
{ if (finalExpression) statements.push(finalExpression) { if (finalExpression) statements.push(finalExpression)
return h.nodeProgram(statements) } return h.nodeProgram(statements, location()) }
/ finalExpression: expression / finalExpression: expression
{ return h.nodeProgram([finalExpression]) } { return h.nodeProgram([finalExpression], location()) }
innerBlockOrExpression innerBlockOrExpression
= quotedInnerBlock = quotedInnerBlock
/ finalExpression: expression / finalExpression: expression
{ return h.nodeBlock([finalExpression])} { return h.nodeBlock([finalExpression], location())}
quotedInnerBlock quotedInnerBlock
= '{' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}' = '{' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
{ if (finalExpression) statements.push(finalExpression) { if (finalExpression) statements.push(finalExpression)
return h.nodeBlock(statements) } return h.nodeBlock(statements, location()) }
/ '{' _nl finalExpression: expression _nl '}' / '{' _nl finalExpression: expression _nl '}'
{ return h.nodeBlock([finalExpression]) } { return h.nodeBlock([finalExpression], location()) }
array_statements array_statements
= head:statement tail:(statementSeparator @array_statements ) = head:statement tail:(statementSeparator @array_statements )
@ -42,16 +42,16 @@ statement
voidStatement voidStatement
= "call" _nl value:zeroOMoreArgumentsBlockOrExpression = "call" _nl value:zeroOMoreArgumentsBlockOrExpression
{ var variable = h.nodeIdentifier("_", location()); { var variable = h.nodeIdentifier("_", location());
return h.nodeLetStatement(variable, value); } return h.nodeLetStatement(variable, value, location()); }
letStatement letStatement
= variable:variable _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression = variable:variable _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression
{ return h.nodeLetStatement(variable, value) } { return h.nodeLetStatement(variable, value, location()) }
defunStatement defunStatement
= variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression = variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression
{ var value = h.nodeLambda(args, body) { var value = h.nodeLambda(args, body, location())
return h.nodeLetStatement(variable, value) } return h.nodeLetStatement(variable, value, location()) }
assignmentOp "assignment" = '=' assignmentOp "assignment" = '='
@ -67,16 +67,16 @@ ifthenelse
= 'if' __nl condition:logicalAdditive = 'if' __nl condition:logicalAdditive
__nl 'then' __nl trueExpression:innerBlockOrExpression __nl 'then' __nl trueExpression:innerBlockOrExpression
__nl 'else' __nl falseExpression:(ifthenelse/innerBlockOrExpression) __nl 'else' __nl falseExpression:(ifthenelse/innerBlockOrExpression)
{ return h.nodeTernary(condition, trueExpression, falseExpression) } { return h.nodeTernary(condition, trueExpression, falseExpression, location()) }
ternary ternary
= condition:logicalAdditive _ '?' _nl trueExpression:logicalAdditive _ ':' _nl falseExpression:(ternary/logicalAdditive) = condition:logicalAdditive _ '?' _nl trueExpression:logicalAdditive _ ':' _nl falseExpression:(ternary/logicalAdditive)
{ return h.nodeTernary(condition, trueExpression, falseExpression) } { return h.nodeTernary(condition, trueExpression, falseExpression, location()) }
logicalAdditive logicalAdditive
= head:logicalMultiplicative tail:(_ operator:logicalAdditiveOp _nl arg:logicalMultiplicative {return {operator: operator, right: arg}})* = head:logicalMultiplicative tail:(_ operator:logicalAdditiveOp _nl arg:logicalMultiplicative {return {operator: operator, right: arg}})*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right]) return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
}, head)} }, head)}
logicalAdditiveOp "operator" = '||' logicalAdditiveOp "operator" = '||'
@ -85,21 +85,21 @@ logicalAdditive
logicalMultiplicative logicalMultiplicative
= head:equality tail:(_ operator:logicalMultiplicativeOp _nl arg:equality {return {operator: operator, right: arg}})* = head:equality tail:(_ operator:logicalMultiplicativeOp _nl arg:equality {return {operator: operator, right: arg}})*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right]) return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
}, head)} }, head)}
logicalMultiplicativeOp "operator" = '&&' logicalMultiplicativeOp "operator" = '&&'
equality equality
= left:relational _ operator:equalityOp _nl right:relational = left:relational _ operator:equalityOp _nl right:relational
{ return h.makeFunctionCall(h.toFunction[operator], [left, right])} { return h.makeFunctionCall(h.toFunction[operator], [left, right], location())}
/ relational / relational
equalityOp "operator" = '=='/'!=' equalityOp "operator" = '=='/'!='
relational relational
= left:additive _ operator:relationalOp _nl right:additive = left:additive _ operator:relationalOp _nl right:additive
{ return h.makeFunctionCall(h.toFunction[operator], [left, right])} { return h.makeFunctionCall(h.toFunction[operator], [left, right], location())}
/ additive / additive
relationalOp "operator" = '<='/'<'/'>='/'>' relationalOp "operator" = '<='/'<'/'>='/'>'
@ -107,7 +107,7 @@ relational
additive additive
= head:multiplicative tail:(_ operator:additiveOp _nl arg:multiplicative {return {operator: operator, right: arg}})* = head:multiplicative tail:(_ operator:additiveOp _nl arg:multiplicative {return {operator: operator, right: arg}})*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right]) return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
}, head)} }, head)}
additiveOp "operator" = '+' / '-' / '.+' / '.-' additiveOp "operator" = '+' / '-' / '.+' / '.-'
@ -115,7 +115,7 @@ additive
multiplicative multiplicative
= head:power tail:(_ operator:multiplicativeOp _nl arg:power {return {operator: operator, right: arg}})* = head:power tail:(_ operator:multiplicativeOp _nl arg:power {return {operator: operator, right: arg}})*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right]) return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
}, head)} }, head)}
multiplicativeOp "operator" = '*' / '/' / '.*' / './' multiplicativeOp "operator" = '*' / '/' / '.*' / './'
@ -123,7 +123,7 @@ multiplicative
power power
= head:credibleInterval tail:(_ operator:powerOp _nl arg:credibleInterval {return {operator: operator, right: arg}})* = head:credibleInterval tail:(_ operator:powerOp _nl arg:credibleInterval {return {operator: operator, right: arg}})*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right]) return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
}, head)} }, head)}
powerOp "operator" = '^' / '.^' powerOp "operator" = '^' / '.^'
@ -131,7 +131,7 @@ power
credibleInterval credibleInterval
= head:chainFunctionCall tail:(__ operator:credibleIntervalOp __nl arg:chainFunctionCall {return {operator: operator, right: arg}})* = head:chainFunctionCall tail:(__ operator:credibleIntervalOp __nl arg:chainFunctionCall {return {operator: operator, right: arg}})*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right]) return h.makeFunctionCall(h.toFunction[element.operator], [result, element.right], location())
}, head)} }, head)}
credibleIntervalOp "operator" = 'to' credibleIntervalOp "operator" = 'to'
@ -139,7 +139,7 @@ credibleInterval
chainFunctionCall chainFunctionCall
= head:unary tail:(_ ('->'/'|>') _nl chained:chainedFunction {return chained})* = head:unary tail:(_ ('->'/'|>') _nl chained:chainedFunction {return chained})*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(element.fnName, [result, ...element.args]) return h.makeFunctionCall(element.fnName, [result, ...element.args], location())
}, head)} }, head)}
chainedFunction chainedFunction
@ -154,7 +154,7 @@ chainFunctionCall
unary unary
= unaryOperator:unaryOperator _nl right:(unary/postOperator) = unaryOperator:unaryOperator _nl right:(unary/postOperator)
{ return h.makeFunctionCall(h.unaryToFunction[unaryOperator], [right])} { return h.makeFunctionCall(h.unaryToFunction[unaryOperator], [right], location())}
/ postOperator / postOperator
unaryOperator "unary operator" unaryOperator "unary operator"
@ -169,17 +169,17 @@ collectionElement
tail:( tail:(
_ '[' _nl arg:expression _nl ']' {return {fn: h.postOperatorToFunction['[]'], args: [arg]}} _ '[' _nl arg:expression _nl ']' {return {fn: h.postOperatorToFunction['[]'], args: [arg]}}
/ _ '(' _nl args:array_functionArguments _nl ')' {return {fn: h.postOperatorToFunction['()'], args: args}} / _ '(' _nl args:array_functionArguments _nl ')' {return {fn: h.postOperatorToFunction['()'], args: args}}
/ '.' arg:$dollarIdentifier {return {fn: h.postOperatorToFunction['[]'], args: [h.nodeString(arg)]}} / '.' arg:$dollarIdentifier {return {fn: h.postOperatorToFunction['[]'], args: [h.nodeString(arg, location())]}}
)* )*
{ return tail.reduce(function(result, element) { { return tail.reduce(function(result, element) {
return h.makeFunctionCall(element.fn, [result, ...element.args]) return h.makeFunctionCall(element.fn, [result, ...element.args], location())
}, head)} }, head)}
array_functionArguments array_functionArguments
= head:expression tail:(_ ',' _nl @expression)* = head:expression tail:(_ ',' _nl @expression)*
{ return [head, ...tail]; } { return [head, ...tail]; }
/ "" / ""
{return [h.nodeVoid()];} {return [h.nodeVoid(location())];}
atom atom
= '(' _nl expression:expression _nl ')' {return expression} = '(' _nl expression:expression _nl ')' {return expression}
@ -195,7 +195,7 @@ basicLiteral
/ voidLiteral / voidLiteral
voidLiteral 'void' voidLiteral 'void'
= "()" {return h.nodeVoid();} = "()" {return h.nodeVoid(location());}
variable = dollarIdentifierWithModule / dollarIdentifier variable = dollarIdentifierWithModule / dollarIdentifier
@ -221,36 +221,36 @@ dollarIdentifier '$identifier'
= ([\$_a-z]+[\$_a-z0-9]i*) {return h.nodeIdentifier(text(), location())} = ([\$_a-z]+[\$_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}
moduleIdentifier 'identifier' moduleIdentifier 'identifier'
= ([A-Z]+[_a-z0-9]i*) {return h.nodeModuleIdentifier(text())} = ([A-Z]+[_a-z0-9]i*) {return h.nodeModuleIdentifier(text(), location())}
string 'string' string 'string'
= characters:("'" @([^'])* "'") {return h.nodeString(characters.join(''))} = characters:("'" @([^'])* "'") {return h.nodeString(characters.join(''), location())}
/ characters:('"' @([^"])* '"') {return h.nodeString(characters.join(''))} / characters:('"' @([^"])* '"') {return h.nodeString(characters.join(''), location())}
number = number:(float / integer) unit:unitIdentifier? number = number:(float / integer) unit:unitIdentifier?
{ {
if (unit === null) if (unit === null)
{ return number } { return number }
else else
{ return h.makeFunctionCall('fromUnit_'+unit.value, [number]) { return h.makeFunctionCall('fromUnit_'+unit.value, [number], location())
} }
} }
integer 'integer' integer 'integer'
= d+ !"\." ![e]i = d+ !"\." ![e]i
{ return h.nodeInteger(parseInt(text()))} { return h.nodeInteger(parseInt(text()), location())}
float 'float' float 'float'
= $(((d+ "\." d*) / ("\." d+)) floatExponent? / d+ floatExponent) = $(((d+ "\." d*) / ("\." d+)) floatExponent? / d+ floatExponent)
{ return h.nodeFloat(parseFloat(text()))} { return h.nodeFloat(parseFloat(text()), location())}
floatExponent = [e]i '-'? d+ floatExponent = [e]i '-'? d+
d = [0-9] d = [0-9]
boolean 'boolean' boolean 'boolean'
= ('true'/'false') ! [a-z]i ! [_$] = ('true'/'false') ! [a-z]i ! [_$]
{ return h.nodeBoolean(text() === 'true')} { return h.nodeBoolean(text() === 'true', location())}
valueConstructor valueConstructor
= recordConstructor = recordConstructor
@ -261,15 +261,15 @@ valueConstructor
lambda lambda
= '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}' = '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
{ statements.push(finalExpression) { statements.push(finalExpression)
return h.nodeLambda(args, h.nodeBlock(statements)) } return h.nodeLambda(args, h.nodeBlock(statements, location()), location()) }
/ '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}' / '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}'
{ return h.nodeLambda(args, finalExpression) } { return h.nodeLambda(args, finalExpression, location()) }
arrayConstructor 'array' arrayConstructor 'array'
= '[' _nl ']' = '[' _nl ']'
{ return h.constructArray([]); } { return h.constructArray([], location()); }
/ '[' _nl args:array_elements _nl ']' / '[' _nl args:array_elements _nl ']'
{ return h.constructArray(args); } { return h.constructArray(args, location()); }
array_elements array_elements
= head:expression tail:(_ ',' _nl @expression)* = head:expression tail:(_ ',' _nl @expression)*
@ -277,7 +277,7 @@ arrayConstructor 'array'
recordConstructor 'record' recordConstructor 'record'
= '{' _nl args:array_recordArguments _nl end_of_record = '{' _nl args:array_recordArguments _nl end_of_record
{ return h.constructRecord(args); } { return h.constructRecord(args, location()); }
end_of_record end_of_record
= '}' = '}'
@ -289,7 +289,7 @@ recordConstructor 'record'
keyValuePair keyValuePair
= key:expression _ ':' _nl value:expression = key:expression _ ':' _nl value:expression
{ return h.nodeKeyValue(key, value)} { return h.nodeKeyValue(key, value, location())}
// Separators // Separators

View File

@ -1,19 +1,20 @@
module Extra = Reducer_Extra module Extra = Reducer_Extra
open Reducer_ErrorValue open Reducer_ErrorValue
type node = {"type": string} type node = {"type": string, "location": Reducer_ErrorValue.location}
@module("./Reducer_Peggy_GeneratedParser.js") external parse__: string => node = "parse" @module("./Reducer_Peggy_GeneratedParser.js")
external parse__: (string, {"grammarSource": string}) => node = "parse"
type withLocation = {"location": Reducer_ErrorValue.syntaxErrorLocation} type withLocation = {"location": Reducer_ErrorValue.location}
external castWithLocation: Js.Exn.t => withLocation = "%identity" external castWithLocation: Js.Exn.t => withLocation = "%identity"
let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.syntaxErrorLocation => let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.location =>
castWithLocation(error)["location"] castWithLocation(error)["location"]
let parse = (expr: string): result<node, errorValue> => let parse = (expr: string, source: string): result<node, errorValue> =>
try { try {
Ok(parse__(expr)) Ok(parse__(expr, {"grammarSource": source}))
} catch { } catch {
| Js.Exn.Error(obj) => | Js.Exn.Error(obj) =>
RESyntaxError(Belt.Option.getExn(Js.Exn.message(obj)), syntaxErrorToLocation(obj)->Some)->Error RESyntaxError(Belt.Option.getExn(Js.Exn.message(obj)), syntaxErrorToLocation(obj)->Some)->Error
@ -34,27 +35,30 @@ type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
type nodeModuleIdentifier = {...node, "value": string} type nodeModuleIdentifier = {...node, "value": string}
type nodeString = {...node, "value": string} type nodeString = {...node, "value": string}
type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node} type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node}
// type nodeTypeIdentifier = {...node, "value": string}
type nodeVoid = node type nodeVoid = node
type peggyNode = type astContent =
| PgNodeBlock(nodeBlock) | ASTBlock(nodeBlock)
| PgNodeProgram(nodeProgram) | ASTProgram(nodeProgram)
| PgNodeArray(nodeArray) | ASTArray(nodeArray)
| PgNodeRecord(nodeRecord) | ASTRecord(nodeRecord)
| PgNodeBoolean(nodeBoolean) | ASTBoolean(nodeBoolean)
| PgNodeFloat(nodeFloat) | ASTFloat(nodeFloat)
| PgNodeCall(nodeCall) | ASTCall(nodeCall)
| PgNodeIdentifier(nodeIdentifier) | ASTIdentifier(nodeIdentifier)
| PgNodeInteger(nodeInteger) | ASTInteger(nodeInteger)
| PgNodeKeyValue(nodeKeyValue) | ASTKeyValue(nodeKeyValue)
| PgNodeLambda(nodeLambda) | ASTLambda(nodeLambda)
| PgNodeLetStatement(nodeLetStatement) | ASTLetStatement(nodeLetStatement)
| PgNodeModuleIdentifier(nodeModuleIdentifier) | ASTModuleIdentifier(nodeModuleIdentifier)
| PgNodeString(nodeString) | ASTString(nodeString)
| PgNodeTernary(nodeTernary) | ASTTernary(nodeTernary)
// | PgNodeTypeIdentifier(nodeTypeIdentifier) | ASTVoid(nodeVoid)
| PgNodeVoid(nodeVoid)
type ast = {
location: location,
content: astContent,
}
external castNodeBlock: node => nodeBlock = "%identity" external castNodeBlock: node => nodeBlock = "%identity"
external castNodeProgram: node => nodeProgram = "%identity" external castNodeProgram: node => nodeProgram = "%identity"
@ -71,80 +75,87 @@ external castNodeLetStatement: node => nodeLetStatement = "%identity"
external castNodeModuleIdentifier: node => nodeModuleIdentifier = "%identity" external castNodeModuleIdentifier: node => nodeModuleIdentifier = "%identity"
external castNodeString: node => nodeString = "%identity" external castNodeString: node => nodeString = "%identity"
external castNodeTernary: node => nodeTernary = "%identity" external castNodeTernary: node => nodeTernary = "%identity"
// external castNodeTypeIdentifier: node => nodeTypeIdentifier = "%identity"
external castNodeVoid: node => nodeVoid = "%identity" external castNodeVoid: node => nodeVoid = "%identity"
exception UnsupportedPeggyNodeType(string) // This should never happen; programming error exception UnsupportedPeggyNodeType(string) // This should never happen; programming error
let castNodeType = (node: node) => let nodeToAST = (node: node) => {
switch node["type"] { let content = switch node["type"] {
| "Block" => node->castNodeBlock->PgNodeBlock | "Block" => node->castNodeBlock->ASTBlock
| "Program" => node->castNodeBlock->PgNodeProgram | "Program" => node->castNodeBlock->ASTProgram
| "Array" => node->castNodeArray->PgNodeArray | "Array" => node->castNodeArray->ASTArray
| "Record" => node->castNodeRecord->PgNodeRecord | "Record" => node->castNodeRecord->ASTRecord
| "Boolean" => node->castNodeBoolean->PgNodeBoolean | "Boolean" => node->castNodeBoolean->ASTBoolean
| "Call" => node->castNodeCall->PgNodeCall | "Call" => node->castNodeCall->ASTCall
| "Float" => node->castNodeFloat->PgNodeFloat | "Float" => node->castNodeFloat->ASTFloat
| "Identifier" => node->castNodeIdentifier->PgNodeIdentifier | "Identifier" => node->castNodeIdentifier->ASTIdentifier
| "Integer" => node->castNodeInteger->PgNodeInteger | "Integer" => node->castNodeInteger->ASTInteger
| "KeyValue" => node->castNodeKeyValue->PgNodeKeyValue | "KeyValue" => node->castNodeKeyValue->ASTKeyValue
| "Lambda" => node->castNodeLambda->PgNodeLambda | "Lambda" => node->castNodeLambda->ASTLambda
| "LetStatement" => node->castNodeLetStatement->PgNodeLetStatement | "LetStatement" => node->castNodeLetStatement->ASTLetStatement
| "ModuleIdentifier" => node->castNodeModuleIdentifier->PgNodeModuleIdentifier | "ModuleIdentifier" => node->castNodeModuleIdentifier->ASTModuleIdentifier
| "String" => node->castNodeString->PgNodeString | "String" => node->castNodeString->ASTString
| "Ternary" => node->castNodeTernary->PgNodeTernary | "Ternary" => node->castNodeTernary->ASTTernary
// | "TypeIdentifier" => node->castNodeTypeIdentifier->PgNodeTypeIdentifier | "Void" => node->castNodeVoid->ASTVoid
| "Void" => node->castNodeVoid->PgNodeVoid
| _ => raise(UnsupportedPeggyNodeType(node["type"])) | _ => raise(UnsupportedPeggyNodeType(node["type"]))
} }
let rec pgToString = (peggyNode: peggyNode): string => { {location: node["location"], content: content}
}
let nodeIdentifierToAST = (node: nodeIdentifier) => {
{location: node["location"], content: node->ASTIdentifier}
}
let nodeKeyValueToAST = (node: nodeKeyValue) => {
{location: node["location"], content: node->ASTKeyValue}
}
let rec pgToString = (ast: ast): string => {
let argsToString = (args: array<nodeIdentifier>): string => let argsToString = (args: array<nodeIdentifier>): string =>
args->Js.Array2.map(arg => PgNodeIdentifier(arg)->pgToString)->Js.Array2.toString args->Belt.Array.map(arg => arg->nodeIdentifierToAST->pgToString)->Js.Array2.toString
let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string => let nodesToStringUsingSeparator = (nodes: array<node>, separator: string): string =>
nodes->Js.Array2.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") nodes->Belt.Array.map(toString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
let pgNodesToStringUsingSeparator = (nodes: array<peggyNode>, separator: string): string => let pgNodesToStringUsingSeparator = (nodes: array<ast>, separator: string): string =>
nodes->Js.Array2.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") nodes->Belt.Array.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("")
switch peggyNode { switch ast.content {
| PgNodeBlock(node) | ASTBlock(node)
| PgNodeProgram(node) => | ASTProgram(node) =>
"{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}" "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}"
| PgNodeArray(node) => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]" | ASTArray(node) => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]"
| PgNodeRecord(node) => | ASTRecord(node) =>
"{" ++ "{" ++
node["elements"] node["elements"]
->Js.Array2.map(element => PgNodeKeyValue(element)) ->Belt.Array.map(element => element->nodeKeyValueToAST)
->pgNodesToStringUsingSeparator(", ") ++ "}" ->pgNodesToStringUsingSeparator(", ") ++ "}"
| PgNodeBoolean(node) => node["value"]->Js.String.make | ASTBoolean(node) => node["value"]->Js.String.make
| PgNodeCall(node) => | ASTCall(node) =>
"(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")" "(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")"
| PgNodeFloat(node) => node["value"]->Js.String.make | ASTFloat(node) => node["value"]->Js.String.make
| PgNodeIdentifier(node) => `:${node["value"]}` | ASTIdentifier(node) => `:${node["value"]}`
| PgNodeInteger(node) => node["value"]->Js.String.make | ASTInteger(node) => node["value"]->Js.String.make
| PgNodeKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"]) | ASTKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"])
| PgNodeLambda(node) => | ASTLambda(node) => "{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}"
"{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}" | ASTLetStatement(node) =>
| PgNodeLetStatement(node) => pgToString(node["variable"]->nodeIdentifierToAST) ++ " = " ++ toString(node["value"])
pgToString(PgNodeIdentifier(node["variable"])) ++ " = " ++ toString(node["value"]) | ASTModuleIdentifier(node) => `@${node["value"]}`
| PgNodeModuleIdentifier(node) => `@${node["value"]}` | ASTString(node) => `'${node["value"]->Js.String.make}'`
| PgNodeString(node) => `'${node["value"]->Js.String.make}'` | ASTTernary(node) =>
| PgNodeTernary(node) =>
"(::$$_ternary_$$ " ++ "(::$$_ternary_$$ " ++
toString(node["condition"]) ++ toString(node["condition"]) ++
" " ++ " " ++
toString(node["trueExpression"]) ++ toString(node["trueExpression"]) ++
" " ++ " " ++
toString(node["falseExpression"]) ++ ")" toString(node["falseExpression"]) ++ ")"
// | PgNodeTypeIdentifier(node) => `#${node["value"]}` | ASTVoid(_node) => "()"
| PgNodeVoid(_node) => "()"
} }
} }
and toString = (node: node): string => node->castNodeType->pgToString and toString = (node: node): string => node->nodeToAST->pgToString
let toStringResult = (rNode: result<node, errorValue>): string => let toStringResult = (rNode: result<node, errorValue>): string =>
switch rNode { switch rNode {
| Ok(node) => toString(node) | Ok(node) => toString(node)
| Error(error) => `Error(${errorToString(error)})` | Error(error) => `Error(${errorValueToString(error)})`
} }

View File

@ -3,62 +3,72 @@ module ExpressionT = Reducer_Expression_T
module Parse = Reducer_Peggy_Parse module Parse = Reducer_Peggy_Parse
type expression = Reducer_T.expression type expression = Reducer_T.expression
type expressionContent = Reducer_T.expressionContent
let rec fromNode = (node: Parse.node): expression => { let rec fromNode = (node: Parse.node): expression => {
let caseBlock = nodeBlock => let ast = Parse.nodeToAST(node)
ExpressionBuilder.eBlock(nodeBlock["statements"]->Js.Array2.map(fromNode))
let caseProgram = nodeProgram => let content: expressionContent = {
ExpressionBuilder.eProgram(nodeProgram["statements"]->Js.Array2.map(fromNode)) let caseBlock = nodeBlock =>
ExpressionBuilder.eBlock(nodeBlock["statements"]->Js.Array2.map(fromNode))
let caseLambda = (nodeLambda: Parse.nodeLambda): expression => { let caseProgram = nodeProgram =>
let args = ExpressionBuilder.eProgram(nodeProgram["statements"]->Js.Array2.map(fromNode))
nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"])
let body = nodeLambda["body"]->fromNode
ExpressionBuilder.eLambda(args, body) let caseLambda = (nodeLambda: Parse.nodeLambda): expressionContent => {
let args =
nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"])
let body = nodeLambda["body"]->fromNode
ExpressionBuilder.eLambda(args, body)
}
let caseRecord = (nodeRecord): expressionContent => {
nodeRecord["elements"]
->Js.Array2.map(keyValueNode => (
keyValueNode["key"]->fromNode,
keyValueNode["value"]->fromNode,
))
->ExpressionBuilder.eRecord
}
switch ast.content {
| ASTBlock(nodeBlock) => caseBlock(nodeBlock)
| ASTProgram(nodeProgram) => caseProgram(nodeProgram)
| ASTArray(nodeArray) =>
ExpressionBuilder.eArray(nodeArray["elements"]->Js.Array2.map(fromNode))
| ASTRecord(nodeRecord) => caseRecord(nodeRecord)
| ASTBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"])
| ASTCall(nodeCall) =>
ExpressionBuilder.eCall(fromNode(nodeCall["fn"]), nodeCall["args"]->Js.Array2.map(fromNode))
| ASTFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"])
| ASTIdentifier(nodeIdentifier) => ExpressionBuilder.eSymbol(nodeIdentifier["value"])
| ASTInteger(nodeInteger) => ExpressionBuilder.eNumber(Belt.Int.toFloat(nodeInteger["value"]))
| ASTKeyValue(nodeKeyValue) =>
ExpressionBuilder.eArray([fromNode(nodeKeyValue["key"]), fromNode(nodeKeyValue["value"])])
| ASTLambda(nodeLambda) => caseLambda(nodeLambda)
| ASTLetStatement(nodeLetStatement) =>
ExpressionBuilder.eLetStatement(
nodeLetStatement["variable"]["value"],
fromNode(nodeLetStatement["value"]),
)
| ASTModuleIdentifier(nodeModuleIdentifier) =>
ExpressionBuilder.eIdentifier(nodeModuleIdentifier["value"])
| ASTString(nodeString) => ExpressionBuilder.eString(nodeString["value"])
| ASTTernary(nodeTernary) =>
ExpressionBuilder.eTernary(
fromNode(nodeTernary["condition"]),
fromNode(nodeTernary["trueExpression"]),
fromNode(nodeTernary["falseExpression"]),
)
// | PgNodeTypeIdentifier(nodeTypeIdentifier) =>
// ExpressionBuilder.eTypeIdentifier(nodeTypeIdentifier["value"])
| ASTVoid(_) => ExpressionBuilder.eVoid
}
} }
let caseRecord = (nodeRecord): expression => { {
nodeRecord["elements"] ast: ast,
->Js.Array2.map(keyValueNode => ( content: content,
keyValueNode["key"]->fromNode,
keyValueNode["value"]->fromNode,
))
->ExpressionBuilder.eRecord
}
switch Parse.castNodeType(node) {
| PgNodeBlock(nodeBlock) => caseBlock(nodeBlock)
| PgNodeProgram(nodeProgram) => caseProgram(nodeProgram)
| PgNodeArray(nodeArray) =>
ExpressionBuilder.eArray(nodeArray["elements"]->Js.Array2.map(fromNode))
| PgNodeRecord(nodeRecord) => caseRecord(nodeRecord)
| PgNodeBoolean(nodeBoolean) => ExpressionBuilder.eBool(nodeBoolean["value"])
| PgNodeCall(nodeCall) =>
ExpressionBuilder.eCall(fromNode(nodeCall["fn"]), nodeCall["args"]->Js.Array2.map(fromNode))
| PgNodeFloat(nodeFloat) => ExpressionBuilder.eNumber(nodeFloat["value"])
| PgNodeIdentifier(nodeIdentifier) => ExpressionBuilder.eSymbol(nodeIdentifier["value"])
| PgNodeInteger(nodeInteger) => ExpressionBuilder.eNumber(Belt.Int.toFloat(nodeInteger["value"]))
| PgNodeKeyValue(nodeKeyValue) =>
ExpressionBuilder.eArray([fromNode(nodeKeyValue["key"]), fromNode(nodeKeyValue["value"])])
| PgNodeLambda(nodeLambda) => caseLambda(nodeLambda)
| PgNodeLetStatement(nodeLetStatement) =>
ExpressionBuilder.eLetStatement(
nodeLetStatement["variable"]["value"],
fromNode(nodeLetStatement["value"]),
)
| PgNodeModuleIdentifier(nodeModuleIdentifier) =>
ExpressionBuilder.eIdentifier(nodeModuleIdentifier["value"])
| PgNodeString(nodeString) => ExpressionBuilder.eString(nodeString["value"])
| PgNodeTernary(nodeTernary) =>
ExpressionBuilder.eTernary(
fromNode(nodeTernary["condition"]),
fromNode(nodeTernary["trueExpression"]),
fromNode(nodeTernary["falseExpression"]),
)
// | PgNodeTypeIdentifier(nodeTypeIdentifier) =>
// ExpressionBuilder.eTypeIdentifier(nodeTypeIdentifier["value"])
| PgNodeVoid(_) => ExpressionBuilder.eVoid
} }
} }

View File

@ -34,83 +34,91 @@ export const postOperatorToFunction = {
"[]": "$_atIndex_$", "[]": "$_atIndex_$",
}; };
type NodeBlock = { type Node = {
location: LocationRange;
};
type NodeBlock = Node & {
type: "Block"; type: "Block";
statements: AnyPeggyNode[]; statements: AnyPeggyNode[];
}; };
type NodeProgram = { type NodeProgram = Node & {
type: "Program"; type: "Program";
statements: AnyPeggyNode[]; statements: AnyPeggyNode[];
}; };
type NodeArray = { type NodeArray = Node & {
type: "Array"; type: "Array";
elements: AnyPeggyNode[]; elements: AnyPeggyNode[];
}; };
type NodeRecord = { type NodeRecord = Node & {
type: "Record"; type: "Record";
elements: NodeKeyValue[]; elements: NodeKeyValue[];
}; };
type NodeCall = { type NodeCall = Node & {
type: "Call"; type: "Call";
fn: AnyPeggyNode; fn: AnyPeggyNode;
args: AnyPeggyNode[]; args: AnyPeggyNode[];
}; };
type NodeFloat = { type NodeFloat = Node & {
type: "Float"; type: "Float";
value: number; value: number;
}; };
type NodeInteger = { type NodeInteger = Node & {
type: "Integer"; type: "Integer";
value: number; value: number;
}; };
type NodeIdentifier = { type NodeIdentifier = Node & {
type: "Identifier"; type: "Identifier";
value: string; value: string;
}; };
type NodeLetStatement = { type NodeLetStatement = Node & {
type: "LetStatement"; type: "LetStatement";
variable: NodeIdentifier; variable: NodeIdentifier;
value: AnyPeggyNode; value: AnyPeggyNode;
}; };
type NodeLambda = { type NodeLambda = Node & {
type: "Lambda"; type: "Lambda";
args: AnyPeggyNode[]; args: AnyPeggyNode[];
body: AnyPeggyNode; body: AnyPeggyNode;
}; };
type NodeTernary = { type NodeTernary = Node & {
type: "Ternary"; type: "Ternary";
condition: AnyPeggyNode; condition: AnyPeggyNode;
trueExpression: AnyPeggyNode; trueExpression: AnyPeggyNode;
falseExpression: AnyPeggyNode; falseExpression: AnyPeggyNode;
}; };
type NodeKeyValue = { type NodeKeyValue = Node & {
type: "KeyValue"; type: "KeyValue";
key: AnyPeggyNode; key: AnyPeggyNode;
value: AnyPeggyNode; value: AnyPeggyNode;
}; };
type NodeString = { type NodeString = Node & {
type: "String"; type: "String";
value: string; value: string;
location?: LocationRange; location?: LocationRange;
}; };
type NodeBoolean = { type NodeBoolean = Node & {
type: "Boolean"; type: "Boolean";
value: boolean; value: boolean;
}; };
type NodeVoid = Node & {
type: "Void";
};
export type AnyPeggyNode = export type AnyPeggyNode =
| NodeArray | NodeArray
| NodeRecord | NodeRecord
@ -125,47 +133,78 @@ export type AnyPeggyNode =
| NodeTernary | NodeTernary
| NodeKeyValue | NodeKeyValue
| NodeString | NodeString
| NodeBoolean; | NodeBoolean
| NodeVoid;
export function makeFunctionCall(fn: string, args: AnyPeggyNode[]) { export function makeFunctionCall(
fn: string,
args: AnyPeggyNode[],
location: LocationRange
) {
if (fn === "$$_applyAll_$$") { if (fn === "$$_applyAll_$$") {
return nodeCall(args[0], args.splice(1)); return nodeCall(args[0], args.splice(1), location);
} else { } else {
return nodeCall(nodeIdentifier(fn), args); return nodeCall(nodeIdentifier(fn, location), args, location);
} }
} }
export function constructArray(elements: AnyPeggyNode[]) { export function constructArray(
return { type: "Array", elements }; elements: AnyPeggyNode[],
location: LocationRange
): NodeArray {
return { type: "Array", elements, location };
} }
export function constructRecord(elements: AnyPeggyNode[]) { export function constructRecord(
return { type: "Record", elements }; elements: NodeKeyValue[],
location: LocationRange
): NodeRecord {
return { type: "Record", elements, location };
} }
export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock { export function nodeBlock(
return { type: "Block", statements }; statements: AnyPeggyNode[],
location: LocationRange
): NodeBlock {
return { type: "Block", statements, location };
} }
export function nodeProgram(statements: AnyPeggyNode[]): NodeProgram { export function nodeProgram(
return { type: "Program", statements }; statements: AnyPeggyNode[],
location: LocationRange
): NodeProgram {
return { type: "Program", statements, location };
} }
export function nodeBoolean(value: boolean): NodeBoolean { export function nodeBoolean(
return { type: "Boolean", value }; value: boolean,
location: LocationRange
): NodeBoolean {
return { type: "Boolean", value, location };
} }
export function nodeCall(fn: AnyPeggyNode, args: AnyPeggyNode[]): NodeCall { export function nodeCall(
return { type: "Call", fn, args }; fn: AnyPeggyNode,
args: AnyPeggyNode[],
location: LocationRange
): NodeCall {
return { type: "Call", fn, args, location };
} }
export function nodeFloat(value: number): NodeFloat { export function nodeFloat(value: number, location: LocationRange): NodeFloat {
return { type: "Float", value }; return { type: "Float", value, location };
} }
export function nodeIdentifier(value: string): NodeIdentifier { export function nodeIdentifier(
return { type: "Identifier", value }; value: string,
location: LocationRange
): NodeIdentifier {
return { type: "Identifier", value, location };
} }
export function nodeInteger(value: number): NodeInteger { export function nodeInteger(
return { type: "Integer", value }; value: number,
location: LocationRange
): NodeInteger {
return { type: "Integer", value, location };
} }
export function nodeKeyValue( export function nodeKeyValue(
key: AnyPeggyNode, key: AnyPeggyNode,
value: AnyPeggyNode value: AnyPeggyNode,
location: LocationRange
): NodeKeyValue { ): NodeKeyValue {
if (key.type === "Identifier") { if (key.type === "Identifier") {
key = { key = {
@ -173,43 +212,43 @@ export function nodeKeyValue(
type: "String", type: "String",
}; };
} }
return { type: "KeyValue", key, value }; return { type: "KeyValue", key, value, location };
} }
export function nodeLambda( export function nodeLambda(
args: AnyPeggyNode[], args: AnyPeggyNode[],
body: AnyPeggyNode body: AnyPeggyNode,
location: LocationRange
): NodeLambda { ): NodeLambda {
return { type: "Lambda", args, body }; return { type: "Lambda", args, body, location };
} }
export function nodeLetStatement( export function nodeLetStatement(
variable: NodeIdentifier, variable: NodeIdentifier,
value: AnyPeggyNode value: AnyPeggyNode,
location: LocationRange
): NodeLetStatement { ): NodeLetStatement {
return { type: "LetStatement", variable, value }; return { type: "LetStatement", variable, value, location };
} }
export function nodeModuleIdentifier(value: string) { export function nodeModuleIdentifier(value: string, location: LocationRange) {
return { type: "ModuleIdentifier", value }; return { type: "ModuleIdentifier", value, location };
} }
export function nodeString(value: string): NodeString { export function nodeString(value: string, location: LocationRange): NodeString {
return { type: "String", value }; return { type: "String", value, location };
} }
export function nodeTernary( export function nodeTernary(
condition: AnyPeggyNode, condition: AnyPeggyNode,
trueExpression: AnyPeggyNode, trueExpression: AnyPeggyNode,
falseExpression: AnyPeggyNode falseExpression: AnyPeggyNode,
location: LocationRange
): NodeTernary { ): NodeTernary {
return { return {
type: "Ternary", type: "Ternary",
condition, condition,
trueExpression, trueExpression,
falseExpression, falseExpression,
location,
}; };
} }
export function nodeTypeIdentifier(typeValue: string) { export function nodeVoid(location: LocationRange): NodeVoid {
return { type: "TypeIdentifier", value: typeValue }; return { type: "Void", location };
}
export function nodeVoid() {
return { type: "Void" };
} }

View File

@ -22,7 +22,7 @@ and lambdaValue = {
body: lambdaBody, body: lambdaBody,
} }
@genType.opaque and lambdaDeclaration = Declaration.declaration<lambdaValue> @genType.opaque and lambdaDeclaration = Declaration.declaration<lambdaValue>
and expression = and expressionContent =
| EBlock(array<expression>) | EBlock(array<expression>)
// programs are similar to blocks, but don't create an inner scope. there can be only one program at the top level of the expression. // programs are similar to blocks, but don't create an inner scope. there can be only one program at the top level of the expression.
| EProgram(array<expression>) | EProgram(array<expression>)
@ -35,6 +35,11 @@ and expression =
| ELambda(array<string>, expression) | ELambda(array<string>, expression)
| EValue(value) | EValue(value)
and expression = {
ast: Reducer_Peggy_Parse.ast,
content: expressionContent,
}
and namespace = Belt.Map.String.t<value> and namespace = Belt.Map.String.t<value>
and bindings = { and bindings = {
namespace: namespace, namespace: namespace,

View File

@ -79,24 +79,12 @@ let toStringResult = x =>
| Error(m) => `Error(${ErrorValue.errorToString(m)})` | Error(m) => `Error(${ErrorValue.errorToString(m)})`
} }
let toStringOptionResult = x => let toStringResultOkless = (codeResult: result<t, ErrorValue.error>): string =>
switch x {
| Some(a) => toStringResult(a)
| None => "None"
}
let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): string =>
switch codeResult { switch codeResult {
| Ok(a) => toString(a) | Ok(a) => toString(a)
| Error(m) => `Error(${ErrorValue.errorToString(m)})` | Error(m) => `Error(${ErrorValue.errorToString(m)})`
} }
let toStringResultRecord = x =>
switch x {
| Ok(a) => `Ok(${toStringMap(a)})`
| Error(m) => `Error(${ErrorValue.errorToString(m)})`
}
type internalExpressionValueType = type internalExpressionValueType =
| EvtArray | EvtArray
| EvtBool | EvtBool

View File

@ -117,7 +117,7 @@ let getResultOption = (project: t, sourceId: string): ProjectItem.T.resultType =
let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType => let getResult = (project: t, sourceId: string): ProjectItem.T.resultArgumentType =>
switch getResultOption(project, sourceId) { switch getResultOption(project, sourceId) {
| None => RENeedToRun->Error | None => RENeedToRun->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue->Error
| Some(result) => result | Some(result) => result
} }
@ -171,7 +171,7 @@ let linkDependencies = (project: t, sourceId: string): Reducer_T.namespace => {
"__result__", "__result__",
switch project->getResult(id) { switch project->getResult(id) {
| Ok(result) => result | Ok(result) => result
| Error(error) => error->Reducer_ErrorValue.ErrorException->raise | Error(error) => error->Reducer_ErrorValue.ExceptionWithStackTrace->raise
}, },
), ),
]) ])

View File

@ -4,8 +4,9 @@ module T = ReducerProject_ProjectItem_T
type projectItem = T.projectItem type projectItem = T.projectItem
type t = T.t type t = T.t
let emptyItem: projectItem = { let emptyItem = (sourceId: string): projectItem => {
source: "", source: "",
sourceId: sourceId,
rawParse: None, rawParse: None,
expression: None, expression: None,
continuation: Reducer_Namespace.make(), continuation: Reducer_Namespace.make(),
@ -18,6 +19,7 @@ let emptyItem: projectItem = {
// source -> rawParse -> includes -> expression -> continuation -> result // source -> rawParse -> includes -> expression -> continuation -> result
let getSource = (r: t): T.sourceType => r.source let getSource = (r: t): T.sourceType => r.source
let getSourceId = (r: t): T.sourceType => r.sourceId
let getRawParse = (r: t): T.rawParseType => r.rawParse let getRawParse = (r: t): T.rawParseType => r.rawParse
let getExpression = (r: t): T.expressionType => r.expression let getExpression = (r: t): T.expressionType => r.expression
let getContinuation = (r: t): T.continuationArgumentType => r.continuation let getContinuation = (r: t): T.continuationArgumentType => r.continuation
@ -29,7 +31,7 @@ let getDirectIncludes = (r: t): array<string> => r.directIncludes
let getIncludesAsVariables = (r: t): T.importAsVariablesType => r.includeAsVariables let getIncludesAsVariables = (r: t): T.importAsVariablesType => r.includeAsVariables
let touchSource = (this: t): t => { let touchSource = (this: t): t => {
let r = emptyItem let r = emptyItem(this->getSourceId)
{ {
...r, ...r,
source: getSource(this), source: getSource(this),
@ -41,8 +43,9 @@ let touchSource = (this: t): t => {
} }
let touchRawParse = (this: t): t => { let touchRawParse = (this: t): t => {
let r = emptyItem(this->getSourceId)
{ {
...emptyItem, ...r,
source: getSource(this), source: getSource(this),
continues: getContinues(this), continues: getContinues(this),
includes: getIncludes(this), includes: getIncludes(this),
@ -148,7 +151,8 @@ let parseIncludes = (this: t): t => {
} }
} }
} }
let doRawParse = (this: t): T.rawParseArgumentType => this->getSource->Reducer_Peggy_Parse.parse let doRawParse = (this: t): T.rawParseArgumentType =>
this->getSource->Reducer_Peggy_Parse.parse(this.sourceId)
let rawParse = (this: t): t => let rawParse = (this: t): t =>
this->getRawParse->E.O2.defaultFn(() => doRawParse(this))->setRawParse(this, _) this->getRawParse->E.O2.defaultFn(() => doRawParse(this))->setRawParse(this, _)
@ -167,7 +171,7 @@ let buildExpression = (this: t): t => {
} }
} }
let failRun = (this: t, e: Reducer_ErrorValue.errorValue): t => let failRun = (this: t, e: Reducer_ErrorValue.error): t =>
this->setResult(e->Error)->setContinuation(Reducer_Namespace.make()) this->setResult(e->Error)->setContinuation(Reducer_Namespace.make())
let doRun = (this: t, context: Reducer_T.context): t => let doRun = (this: t, context: Reducer_T.context): t =>
@ -181,12 +185,24 @@ let doRun = (this: t, context: Reducer_T.context): t =>
->setResult(result->Ok) ->setResult(result->Ok)
->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals) ->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals)
} catch { } catch {
| Reducer_ErrorValue.ErrorException(e) => this->failRun(e) | Reducer_ErrorValue.ErrorException(e) =>
| _ => this->failRun(RETodo("unhandled rescript exception")) this->failRun(e->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue)
| Reducer_ErrorValue.ExceptionWithStackTrace(e) => this->failRun(e)
| _ =>
this->failRun(
RETodo(
"unhandled rescript exception",
)->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue,
)
} }
| Error(e) => this->failRun(e) | Error(e) => this->failRun(e->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue)
} }
| None => this->failRun(RETodo("attempt to run without expression")) | None =>
this->failRun(
RETodo(
"attempt to run without expression",
)->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue,
)
} }
let run = (this: t, context: Reducer_T.context): t => { let run = (this: t, context: Reducer_T.context): t => {

View File

@ -11,7 +11,7 @@ type expressionType = option<expressionArgumentType>
type continuationArgumentType = Reducer_T.namespace type continuationArgumentType = Reducer_T.namespace
type continuationType = option<continuationArgumentType> type continuationType = option<continuationArgumentType>
type continuationResultType = option<result<continuationArgumentType, errorValue>> type continuationResultType = option<result<continuationArgumentType, errorValue>>
type resultArgumentType = result<Reducer_T.value, errorValue> type resultArgumentType = result<Reducer_T.value, error>
type resultType = option<resultArgumentType> type resultType = option<resultArgumentType>
type continuesArgumentType = array<string> type continuesArgumentType = array<string>
type continuesType = array<string> type continuesType = array<string>
@ -21,6 +21,7 @@ type importAsVariablesType = array<(string, string)>
type projectItem = { type projectItem = {
source: sourceType, source: sourceType,
sourceId: string,
rawParse: rawParseType, rawParse: rawParseType,
expression: expressionType, expression: expressionType,
continuation: continuationArgumentType, continuation: continuationArgumentType,

View File

@ -13,4 +13,4 @@ type t = project
let getSourceIds = (project: t): array<string> => Belt.MutableMap.String.keysToArray(project.items) let getSourceIds = (project: t): array<string> => Belt.MutableMap.String.keysToArray(project.items)
let getItem = (project: t, sourceId: string) => let getItem = (project: t, sourceId: string) =>
Belt.MutableMap.String.getWithDefault(project.items, sourceId, ProjectItem.emptyItem) Belt.MutableMap.String.getWithDefault(project.items, sourceId, ProjectItem.emptyItem(sourceId))