error stacktraces and locations (initial take, WIP)
This commit is contained in:
		
							parent
							
								
									d3bc08ab9d
								
							
						
					
					
						commit
						41574e08c9
					
				|  | @ -7,5 +7,9 @@ type Props = { | |||
| }; | ||||
| 
 | ||||
| export const SquiggleErrorAlert: React.FC<Props> = ({ error }) => { | ||||
|   return <ErrorAlert heading="Error">{error.toString()}</ErrorAlert>; | ||||
|   return ( | ||||
|     <ErrorAlert heading="Error"> | ||||
|       <pre>{error.toString()}</pre> | ||||
|     </ErrorAlert> | ||||
|   ); | ||||
| }; | ||||
|  |  | |||
|  | @ -298,7 +298,9 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => { | |||
|           {() => ( | ||||
|             <div> | ||||
|               <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> | ||||
|           )} | ||||
|         </VariableList> | ||||
|  |  | |||
|  | @ -9,12 +9,12 @@ open Jest | |||
| open Expect | ||||
| 
 | ||||
| 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 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 | ||||
| 
 | ||||
|   if v == "_" { | ||||
|  | @ -22,6 +22,7 @@ let expectToExpressionToBe = (expr, answer, ~v="_", ()) => { | |||
|   } else { | ||||
|     let a2 = | ||||
|       rExpr | ||||
|       ->E.R2.errMap(e => e->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue) | ||||
|       ->Result.flatMap(expr => Expression.BackCompatible.evaluate(expr)) | ||||
|       ->Reducer_Value.toStringResultOkless | ||||
|     (a1, a2)->expect->toEqual((answer, v)) | ||||
|  |  | |||
|  | @ -25,7 +25,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->Reducer_ErrorValue.errorValueToString) | ||||
|     } | ||||
|   }) | ||||
|   test("past chain", () => { | ||||
|  | @ -60,7 +60,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->Reducer_ErrorValue.errorValueToString) | ||||
|     } | ||||
|   }) | ||||
| 
 | ||||
|  | @ -99,7 +99,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->Reducer_ErrorValue.errorValueToString) | ||||
|     } | ||||
|   }) | ||||
|   test("direct past chain", () => { | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ Here we will finally proceed to a real life scenario. */ | |||
|       /* Parse includes has set the includes */ | ||||
|       switch project->Project.getIncludes("main") { | ||||
|       | 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. | ||||
|       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) | ||||
|           switch rIncludes { | ||||
|           /* 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) => | ||||
|             includes->Belt.Array.forEach(newIncludeName => { | ||||
|  | @ -169,7 +169,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->Reducer_ErrorValue.errorValueToString->fail | ||||
|       } | ||||
|     }) | ||||
|   }) | ||||
|  |  | |||
|  | @ -1,11 +1,18 @@ | |||
| #!/usr/bin/env node
 | ||||
| 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) { | ||||
|   throw new Error("Expected src"); | ||||
| } | ||||
| console.log(`Running ${src}`); | ||||
| 
 | ||||
| const sampleCount = process.env.SAMPLE_COUNT; | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,16 +1,12 @@ | |||
| import * as RSErrorValue from "../rescript/ForTS/ForTS_Reducer_ErrorValue.gen"; | ||||
| 
 | ||||
| export class SqError { | ||||
|   constructor(private _value: RSErrorValue.reducerErrorValue) {} | ||||
|   constructor(private _value: RSErrorValue.reducerError) {} | ||||
| 
 | ||||
|   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)); | ||||
|   } | ||||
|  |  | |||
|  | @ -1,5 +1,9 @@ | |||
| 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 { SqError } from "./SqError"; | ||||
| import { SqRecord } from "./SqRecord"; | ||||
|  | @ -50,7 +54,8 @@ export class SqProject { | |||
|     return resultMap2( | ||||
|       RSProject.getIncludes(this._value, sourceId), | ||||
|       (a) => a, | ||||
|       (v: reducerErrorValue) => new SqError(v) | ||||
|       (v: reducerErrorValue) => | ||||
|         new SqError(attachEmptyStackTraceToErrorValue(v)) | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|  | @ -104,7 +109,7 @@ export class SqProject { | |||
|             items: [], | ||||
|           }) | ||||
|         ), | ||||
|       (v: reducerErrorValue) => new SqError(v) | ||||
|       (v: reducerError) => new SqError(v) | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -143,7 +143,7 @@ module Integration = { | |||
|       | 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." ++ | ||||
|         "Original error: " ++ | ||||
|         b->Reducer_ErrorValue.errorToString) | ||||
|         b->Reducer_ErrorValue.errorValueToString) | ||||
|         ->Reducer_ErrorValue.REOther | ||||
|         ->Error | ||||
|       } | ||||
|  | @ -225,7 +225,7 @@ module Integration = { | |||
|                 reducer, | ||||
|               )->E.R2.errMap(b => | ||||
|                 ("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))" | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| @genType type reducerProject = ReducerProject_T.project //re-export | ||||
| 
 | ||||
| type reducerError = ForTS_Reducer_ErrorValue.reducerError //use | ||||
| type reducerErrorValue = ForTS_Reducer_ErrorValue.reducerErrorValue //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 | ||||
| */ | ||||
| @genType | ||||
| let getResult = (project: reducerProject, sourceId: string): result< | ||||
|   squiggleValue, | ||||
|   reducerErrorValue, | ||||
| > => project->Private.getResult(sourceId) | ||||
| let getResult = (project: reducerProject, sourceId: string): result<squiggleValue, reducerError> => | ||||
|   project->Private.getResult(sourceId) | ||||
| 
 | ||||
| /* | ||||
| 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 | ||||
| */ | ||||
| @genType | ||||
| let evaluate = (sourceCode: string): ( | ||||
|   result<squiggleValue, reducerErrorValue>, | ||||
|   squiggleValue_Record, | ||||
| ) => Private.evaluate(sourceCode) | ||||
| let evaluate = (sourceCode: string): (result<squiggleValue, reducerError>, squiggleValue_Record) => | ||||
|   Private.evaluate(sourceCode) | ||||
| 
 | ||||
| @genType | ||||
| let setEnvironment = (project: reducerProject, environment: environment): unit => | ||||
|  | @ -213,24 +210,3 @@ let setEnvironment = (project: reducerProject, environment: environment): unit = | |||
| 
 | ||||
| @genType | ||||
| 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, | ||||
| //   ) | ||||
| // } | ||||
|  |  | |||
|  | @ -1,18 +1,21 @@ | |||
| @genType type reducerError = Reducer_ErrorValue.error //alias | ||||
| @genType type reducerErrorValue = Reducer_ErrorValue.errorValue //alias | ||||
| @genType type syntaxErrorLocation = Reducer_ErrorValue.syntaxErrorLocation //alias | ||||
| @genType type location = Reducer_ErrorValue.location //alias | ||||
| 
 | ||||
| @genType | ||||
| let toString = (e: reducerErrorValue): string => Reducer_ErrorValue.errorToString(e) | ||||
| let toString = (e: reducerError): string => Reducer_ErrorValue.errorToString(e) | ||||
| 
 | ||||
| @genType | ||||
| let getLocation = (e: reducerErrorValue): option<syntaxErrorLocation> => | ||||
|   switch e { | ||||
|   | RESyntaxError(_, optionalLocation) => optionalLocation | ||||
|   | _ => None | ||||
| let getLocation = (e: reducerError): option<location> => | ||||
|   switch e.stackTrace { | ||||
|   | Some(stack) => Some(stack.location) | ||||
|   | None => None | ||||
|   } | ||||
| 
 | ||||
| @genType | ||||
| let createTodoError = (v: string) => Reducer_ErrorValue.RETodo(v) | ||||
| let createOtherError = (v: string): reducerError => | ||||
|   Reducer_ErrorValue.REOther(v)->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue | ||||
| 
 | ||||
| @genType | ||||
| let createOtherError = (v: string) => Reducer_ErrorValue.REOther(v) | ||||
| let attachEmptyStackTraceToErrorValue = (v: reducerErrorValue): reducerError => | ||||
|   Reducer_ErrorValue.attachEmptyStackTraceToErrorValue(v) | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| @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_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. | ||||
| // Convert the result along with the error message to a string. | ||||
| @genType | ||||
| let toStringResult = (variantResult: result<squiggleValue, reducerErrorValue>) => | ||||
| let toStringResult = (variantResult: result<squiggleValue, reducerError>) => | ||||
|   Reducer_Value.toStringResult(variantResult) | ||||
| 
 | ||||
| @genType | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| @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 squiggleValue = ForTS_SquiggleValue.squiggleValue //re-export | ||||
|  |  | |||
|  | @ -1,6 +1,16 @@ | |||
| //TODO: Do not export here but in ForTS__Types | ||||
| @gentype.import("peggy") @genType.as("LocationRange") | ||||
| type syntaxErrorLocation | ||||
| // Do not gentype this, use LocationRange from peggy types instead | ||||
| // TODO - rename locationPoint -> location, location -> locationRange to match peggy | ||||
| @genType | ||||
| type locationPoint = { | ||||
|   line: int, | ||||
|   column: int, | ||||
| } | ||||
| @genType | ||||
| type location = { | ||||
|   source: string, | ||||
|   start: locationPoint, | ||||
|   end: locationPoint, | ||||
| } | ||||
| 
 | ||||
| @genType.opaque | ||||
| type errorValue = | ||||
|  | @ -18,7 +28,7 @@ type errorValue = | |||
|   | REOperationError(Operation.operationError) | ||||
|   | RERecordPropertyNotFound(string, string) | ||||
|   | RESymbolNotFound(string) | ||||
|   | RESyntaxError(string, option<syntaxErrorLocation>) | ||||
|   | RESyntaxError(string, option<location>) | ||||
|   | RETodo(string) // To do | ||||
|   | REUnitNotFound(string) | ||||
|   | RENeedToRun | ||||
|  | @ -28,7 +38,20 @@ type t = 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 { | ||||
|   | REArityError(_oFnName, arity, usedArity) => | ||||
|     `${Js.String.make(arity)} arguments expected. Instead ${Js.String.make( | ||||
|  | @ -80,4 +103,37 @@ let fromException = exn => | |||
|   | _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 | ||||
|  |  | |||
|  | @ -5,12 +5,19 @@ module T = Reducer_T | |||
| 
 | ||||
| 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 | ||||
| */ | ||||
| let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { | ||||
|   // Js.log(`reduce: ${expression->Reducer_Expression_T.toString}`) | ||||
|   switch expression { | ||||
|   switch expression.content { | ||||
|   | T.EBlock(statements) => { | ||||
|       let innerContext = {...context, bindings: context.bindings->Bindings.extend} | ||||
|       let (value, _) = | ||||
|  | @ -49,7 +56,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { | |||
|           let (key, _) = eKey->evaluate(context) | ||||
|           let keyString = switch key { | ||||
|           | 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) | ||||
|           (keyString, value) | ||||
|  | @ -73,7 +80,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => { | |||
|   | T.ESymbol(name) => | ||||
|     switch context.bindings->Bindings.get(name) { | ||||
|     | Some(v) => (v, context) | ||||
|     | None => Reducer_ErrorValue.RESymbolNotFound(name)->Reducer_ErrorValue.ErrorException->raise | ||||
|     | None => RESymbolNotFound(name)->throwFrom(expression) | ||||
|     } | ||||
| 
 | ||||
|   | 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) | ||||
|       switch predicateResult { | ||||
|       | 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 | ||||
|       }) | ||||
|       switch lambda { | ||||
|       | T.IEvLambda(lambda) => ( | ||||
|           Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate), | ||||
|           context, | ||||
|         ) | ||||
|       | _ => | ||||
|         RENotAFunction(lambda->Reducer_Value.toString)->Reducer_ErrorValue.ErrorException->raise | ||||
|       | T.IEvLambda(lambda) => | ||||
|         try { | ||||
|           let result = Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate) | ||||
|           (result, context) | ||||
|         } catch { | ||||
|         | 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 | ||||
|   // If they are used outside limited testing context, error location reporting will fail | ||||
|   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() | ||||
|     try { | ||||
|       let (value, _) = expression->evaluate(context) | ||||
|       value->Ok | ||||
|     } 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> => | ||||
|     parse(peggyCode)->Result.flatMap(evaluate) | ||||
|   let evaluateString = (peggyCode: string): result<T.value, Reducer_ErrorValue.error> => | ||||
|     parse(peggyCode) | ||||
|     ->E.R2.errMap(e => e->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue) | ||||
|     ->Result.flatMap(evaluate) | ||||
| } | ||||
|  |  | |||
|  | @ -3,14 +3,18 @@ module T = Reducer_T | |||
| 
 | ||||
| type errorValue = BErrorValue.errorValue | ||||
| 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 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 | ||||
| 
 | ||||
|  | @ -18,13 +22,13 @@ let eRecord = (aMap: array<(T.expression, T.expression)>) => aMap->T.ERecord | |||
| 
 | ||||
| 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, | ||||
|   valueExpression, | ||||
| ) | ||||
|  | @ -33,11 +37,8 @@ let eTernary = ( | |||
|   predicate: expression, | ||||
|   trueCase: 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 => | ||||
| //   name->T.IEvTypeIdentifier->T.EValue | ||||
| 
 | ||||
| let eVoid: expression = T.IEvVoid->T.EValue | ||||
| let eVoid: expressionContent = T.IEvVoid->T.EValue | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ let semicolonJoin = values => | |||
|   Converts the expression to String | ||||
| */ | ||||
| let rec toString = (expression: t) => | ||||
|   switch expression { | ||||
|   switch expression.content { | ||||
|   | EBlock(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 => | ||||
|   switch codeResult { | ||||
|   | Ok(a) => `Ok(${toString(a)})` | ||||
|   | Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` | ||||
|   | Error(m) => `Error(${Reducer_ErrorValue.errorValueToString(m)})` | ||||
|   } | ||||
| 
 | ||||
| let toStringResultOkless = codeResult => | ||||
|   switch codeResult { | ||||
|   | Ok(a) => toString(a) | ||||
|   | Error(m) => `Error(${Reducer_ErrorValue.errorToString(m)})` | ||||
|   | Error(m) => `Error(${Reducer_ErrorValue.errorValueToString(m)})` | ||||
|   } | ||||
| 
 | ||||
| let inspect = (expr: t): t => { | ||||
|  |  | |||
|  | @ -12,21 +12,21 @@ zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda | |||
| outerBlock  | ||||
|   = statements:array_statements  finalExpression: (statementSeparator @expression)? | ||||
|     { if (finalExpression) statements.push(finalExpression) | ||||
|       return h.nodeProgram(statements) } | ||||
|       return h.nodeProgram(statements, location()) } | ||||
|   / finalExpression: expression | ||||
|     { return h.nodeProgram([finalExpression]) } | ||||
|     { return h.nodeProgram([finalExpression], location()) } | ||||
|      | ||||
| innerBlockOrExpression   | ||||
|   = quotedInnerBlock | ||||
|   / finalExpression: expression | ||||
|     { return h.nodeBlock([finalExpression])} | ||||
|     { return h.nodeBlock([finalExpression], location())} | ||||
| 
 | ||||
| quotedInnerBlock   | ||||
|   = '{' _nl statements:array_statements  finalExpression: (statementSeparator @expression)  _nl '}' | ||||
|       { if (finalExpression) statements.push(finalExpression) | ||||
|         return h.nodeBlock(statements) } | ||||
|         return h.nodeBlock(statements, location()) } | ||||
|   / '{' _nl finalExpression: expression  _nl '}' | ||||
|       { return h.nodeBlock([finalExpression]) } | ||||
|       { return h.nodeBlock([finalExpression], location()) } | ||||
| 
 | ||||
| array_statements | ||||
|   = head:statement tail:(statementSeparator @array_statements ) | ||||
|  | @ -42,16 +42,16 @@ statement | |||
| voidStatement  | ||||
|   = "call" _nl value:zeroOMoreArgumentsBlockOrExpression | ||||
|     { var variable = h.nodeIdentifier("_", location()); | ||||
|       return h.nodeLetStatement(variable, value); } | ||||
|       return h.nodeLetStatement(variable, value, location()); } | ||||
| 
 | ||||
| letStatement  | ||||
|   = variable:variable _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression | ||||
|     { return h.nodeLetStatement(variable, value) } | ||||
|     { return h.nodeLetStatement(variable, value, location()) } | ||||
| 
 | ||||
| defunStatement | ||||
|   = variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression | ||||
|     { var value = h.nodeLambda(args, body) | ||||
|       return h.nodeLetStatement(variable, value) } | ||||
|     { var value = h.nodeLambda(args, body, location()) | ||||
|       return h.nodeLetStatement(variable, value, location()) } | ||||
| 
 | ||||
|   assignmentOp "assignment" = '=' | ||||
| 
 | ||||
|  | @ -67,16 +67,16 @@ ifthenelse | |||
|   = 'if' __nl condition:logicalAdditive  | ||||
|       __nl 'then' __nl trueExpression:innerBlockOrExpression  | ||||
|     __nl 'else' __nl falseExpression:(ifthenelse/innerBlockOrExpression) | ||||
|     { return h.nodeTernary(condition, trueExpression, falseExpression) } | ||||
|     { return h.nodeTernary(condition, trueExpression, falseExpression, location()) } | ||||
|    | ||||
| ternary  | ||||
|   = condition:logicalAdditive _ '?' _nl trueExpression:logicalAdditive _ ':' _nl falseExpression:(ternary/logicalAdditive) | ||||
|     { return h.nodeTernary(condition, trueExpression, falseExpression) } | ||||
|     { return h.nodeTernary(condition, trueExpression, falseExpression, location()) } | ||||
| 
 | ||||
| logicalAdditive | ||||
|   = head:logicalMultiplicative tail:(_ operator:logicalAdditiveOp _nl arg:logicalMultiplicative {return {operator: operator, right: arg}})*  | ||||
|   { 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)} | ||||
| 
 | ||||
|   logicalAdditiveOp "operator" = '||' | ||||
|  | @ -85,21 +85,21 @@ logicalAdditive | |||
| logicalMultiplicative | ||||
|   = head:equality tail:(_ operator:logicalMultiplicativeOp _nl arg:equality {return {operator: operator, right: arg}})*  | ||||
|   { 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)} | ||||
| 
 | ||||
|   logicalMultiplicativeOp "operator" = '&&' | ||||
| 
 | ||||
| equality | ||||
|   = 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   | ||||
|    | ||||
|   equalityOp "operator" = '=='/'!=' | ||||
| 
 | ||||
| relational | ||||
|   = 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 | ||||
| 
 | ||||
|   relationalOp "operator" = '<='/'<'/'>='/'>' | ||||
|  | @ -107,7 +107,7 @@ relational | |||
| additive | ||||
|   = head:multiplicative tail:(_ operator:additiveOp _nl arg:multiplicative {return {operator: operator, right: arg}})*  | ||||
|   { 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)} | ||||
| 
 | ||||
|   additiveOp "operator" = '+' / '-' / '.+' / '.-' | ||||
|  | @ -115,7 +115,7 @@ additive | |||
| multiplicative | ||||
|   = head:power tail:(_ operator:multiplicativeOp _nl arg:power {return {operator: operator, right: arg}})*  | ||||
|   { 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)} | ||||
| 
 | ||||
|   multiplicativeOp "operator" = '*' / '/' / '.*' / './' | ||||
|  | @ -123,7 +123,7 @@ multiplicative | |||
| power | ||||
|   = head:credibleInterval tail:(_ operator:powerOp _nl arg:credibleInterval {return {operator: operator, right: arg}})*  | ||||
|   { 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)} | ||||
| 
 | ||||
|   powerOp "operator" = '^' / '.^' | ||||
|  | @ -131,7 +131,7 @@ power | |||
| credibleInterval | ||||
|   = head:chainFunctionCall tail:(__ operator:credibleIntervalOp __nl arg:chainFunctionCall {return {operator: operator, right: arg}})*  | ||||
|   { 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)} | ||||
| 
 | ||||
|     credibleIntervalOp "operator" = 'to' | ||||
|  | @ -139,7 +139,7 @@ credibleInterval | |||
| chainFunctionCall | ||||
|   = head:unary tail:(_ ('->'/'|>') _nl chained:chainedFunction {return chained})*  | ||||
|   { return tail.reduce(function(result, element) { | ||||
|       return h.makeFunctionCall(element.fnName, [result, ...element.args]) | ||||
|       return h.makeFunctionCall(element.fnName, [result, ...element.args], location()) | ||||
|     }, head)} | ||||
| 
 | ||||
|   chainedFunction | ||||
|  | @ -154,7 +154,7 @@ chainFunctionCall | |||
| 
 | ||||
| unary | ||||
|   = unaryOperator:unaryOperator _nl right:(unary/postOperator) | ||||
|   { return h.makeFunctionCall(h.unaryToFunction[unaryOperator], [right])} | ||||
|   { return h.makeFunctionCall(h.unaryToFunction[unaryOperator], [right], location())} | ||||
|   / postOperator | ||||
|    | ||||
|   unaryOperator "unary operator" | ||||
|  | @ -169,17 +169,17 @@ collectionElement | |||
|     tail:( | ||||
|       _ '[' _nl arg:expression  _nl ']' {return {fn: h.postOperatorToFunction['[]'], args: [arg]}} | ||||
|     / _ '(' _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 h.makeFunctionCall(element.fn, [result, ...element.args]) | ||||
|       return h.makeFunctionCall(element.fn, [result, ...element.args], location()) | ||||
|     }, head)} | ||||
| 
 | ||||
|     array_functionArguments  | ||||
|       = head:expression tail:(_ ',' _nl @expression)*  | ||||
|       { return [head, ...tail]; } | ||||
|   / "" | ||||
|       {return [h.nodeVoid()];} | ||||
|       {return [h.nodeVoid(location())];} | ||||
| 
 | ||||
| atom | ||||
|   = '(' _nl expression:expression _nl ')' {return expression} | ||||
|  | @ -195,7 +195,7 @@ basicLiteral | |||
|   / voidLiteral | ||||
| 
 | ||||
| voidLiteral 'void' | ||||
|   = "()" {return h.nodeVoid();} | ||||
|   = "()" {return h.nodeVoid(location());} | ||||
| 
 | ||||
| variable = dollarIdentifierWithModule / dollarIdentifier | ||||
| 
 | ||||
|  | @ -221,36 +221,36 @@ dollarIdentifier '$identifier' | |||
|   = ([\$_a-z]+[\$_a-z0-9]i*) {return h.nodeIdentifier(text(), location())}  | ||||
| 
 | ||||
| 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' | ||||
|   = characters:("'" @([^'])* "'") {return h.nodeString(characters.join(''))}  | ||||
|   / characters:('"' @([^"])* '"') {return h.nodeString(characters.join(''))} | ||||
|   = characters:("'" @([^'])* "'") {return h.nodeString(characters.join(''), location())}  | ||||
|   / characters:('"' @([^"])* '"') {return h.nodeString(characters.join(''), location())} | ||||
| 
 | ||||
| number = number:(float / integer) unit:unitIdentifier? | ||||
|   {  | ||||
|     if (unit === null) | ||||
|       { return number } | ||||
|     else | ||||
|       { return h.makeFunctionCall('fromUnit_'+unit.value, [number])  | ||||
|       { return h.makeFunctionCall('fromUnit_'+unit.value, [number], location())  | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
| integer 'integer' | ||||
|   = d+ !"\." ![e]i | ||||
|   { return h.nodeInteger(parseInt(text()))}  | ||||
|   { return h.nodeInteger(parseInt(text()), location())}  | ||||
|    | ||||
| float 'float' | ||||
|   = $(((d+ "\." d*) / ("\." d+)) floatExponent? / d+ floatExponent) | ||||
|   { return h.nodeFloat(parseFloat(text()))}  | ||||
|   { return h.nodeFloat(parseFloat(text()), location())}  | ||||
| 
 | ||||
|     floatExponent = [e]i '-'? d+ | ||||
|     d = [0-9] | ||||
|      | ||||
| boolean 'boolean' | ||||
|   = ('true'/'false')  ! [a-z]i ! [_$] | ||||
|   { return h.nodeBoolean(text() === 'true')} | ||||
|   { return h.nodeBoolean(text() === 'true', location())} | ||||
| 
 | ||||
| valueConstructor | ||||
|   = recordConstructor | ||||
|  | @ -261,15 +261,15 @@ valueConstructor | |||
| lambda   | ||||
|   = '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements  finalExpression: (statementSeparator @expression)  _nl '}' | ||||
|       { statements.push(finalExpression)  | ||||
|         return h.nodeLambda(args, h.nodeBlock(statements)) } | ||||
|         return h.nodeLambda(args, h.nodeBlock(statements, location()), location()) } | ||||
|   / '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression  _nl '}' | ||||
|       { return h.nodeLambda(args, finalExpression) } | ||||
|       { return h.nodeLambda(args, finalExpression, location()) } | ||||
| 
 | ||||
| arrayConstructor 'array' | ||||
|   = '[' _nl ']' | ||||
|     { return h.constructArray([]); } | ||||
|     { return h.constructArray([], location()); } | ||||
|   / '[' _nl args:array_elements _nl ']'  | ||||
|     { return h.constructArray(args); } | ||||
|     { return h.constructArray(args, location()); } | ||||
| 
 | ||||
|   array_elements  | ||||
|     = head:expression tail:(_ ',' _nl @expression)*  | ||||
|  | @ -277,7 +277,7 @@ arrayConstructor 'array' | |||
| 
 | ||||
| recordConstructor  'record' | ||||
|   = '{' _nl args:array_recordArguments _nl end_of_record | ||||
|   { return h.constructRecord(args); } | ||||
|   { return h.constructRecord(args, location()); } | ||||
| 
 | ||||
|   end_of_record  | ||||
|     = '}' | ||||
|  | @ -289,7 +289,7 @@ recordConstructor  'record' | |||
| 
 | ||||
|   keyValuePair  | ||||
|     = key:expression _ ':' _nl value:expression  | ||||
|     { return h.nodeKeyValue(key, value)} | ||||
|     { return h.nodeKeyValue(key, value, location())} | ||||
| 
 | ||||
| // Separators | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,19 +1,20 @@ | |||
| module Extra = Reducer_Extra | ||||
| 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" | ||||
| 
 | ||||
| let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.syntaxErrorLocation => | ||||
| let syntaxErrorToLocation = (error: Js.Exn.t): Reducer_ErrorValue.location => | ||||
|   castWithLocation(error)["location"] | ||||
| 
 | ||||
| let parse = (expr: string): result<node, errorValue> => | ||||
| let parse = (expr: string, source: string): result<node, errorValue> => | ||||
|   try { | ||||
|     Ok(parse__(expr)) | ||||
|     Ok(parse__(expr, {"grammarSource": source})) | ||||
|   } catch { | ||||
|   | Js.Exn.Error(obj) => | ||||
|     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 nodeString = {...node, "value": string} | ||||
| type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node} | ||||
| // type nodeTypeIdentifier = {...node, "value": string} | ||||
| type nodeVoid = node | ||||
| 
 | ||||
| type peggyNode = | ||||
|   | PgNodeBlock(nodeBlock) | ||||
|   | PgNodeProgram(nodeProgram) | ||||
|   | PgNodeArray(nodeArray) | ||||
|   | PgNodeRecord(nodeRecord) | ||||
|   | PgNodeBoolean(nodeBoolean) | ||||
|   | PgNodeFloat(nodeFloat) | ||||
|   | PgNodeCall(nodeCall) | ||||
|   | PgNodeIdentifier(nodeIdentifier) | ||||
|   | PgNodeInteger(nodeInteger) | ||||
|   | PgNodeKeyValue(nodeKeyValue) | ||||
|   | PgNodeLambda(nodeLambda) | ||||
|   | PgNodeLetStatement(nodeLetStatement) | ||||
|   | PgNodeModuleIdentifier(nodeModuleIdentifier) | ||||
|   | PgNodeString(nodeString) | ||||
|   | PgNodeTernary(nodeTernary) | ||||
|   // | PgNodeTypeIdentifier(nodeTypeIdentifier) | ||||
|   | PgNodeVoid(nodeVoid) | ||||
| type astContent = | ||||
|   | ASTBlock(nodeBlock) | ||||
|   | ASTProgram(nodeProgram) | ||||
|   | ASTArray(nodeArray) | ||||
|   | ASTRecord(nodeRecord) | ||||
|   | ASTBoolean(nodeBoolean) | ||||
|   | ASTFloat(nodeFloat) | ||||
|   | ASTCall(nodeCall) | ||||
|   | ASTIdentifier(nodeIdentifier) | ||||
|   | ASTInteger(nodeInteger) | ||||
|   | ASTKeyValue(nodeKeyValue) | ||||
|   | ASTLambda(nodeLambda) | ||||
|   | ASTLetStatement(nodeLetStatement) | ||||
|   | ASTModuleIdentifier(nodeModuleIdentifier) | ||||
|   | ASTString(nodeString) | ||||
|   | ASTTernary(nodeTernary) | ||||
|   | ASTVoid(nodeVoid) | ||||
| 
 | ||||
| type ast = { | ||||
|   location: location, | ||||
|   content: astContent, | ||||
| } | ||||
| 
 | ||||
| external castNodeBlock: node => nodeBlock = "%identity" | ||||
| external castNodeProgram: node => nodeProgram = "%identity" | ||||
|  | @ -71,80 +75,87 @@ external castNodeLetStatement: node => nodeLetStatement = "%identity" | |||
| external castNodeModuleIdentifier: node => nodeModuleIdentifier = "%identity" | ||||
| external castNodeString: node => nodeString = "%identity" | ||||
| external castNodeTernary: node => nodeTernary = "%identity" | ||||
| // external castNodeTypeIdentifier: node => nodeTypeIdentifier = "%identity" | ||||
| external castNodeVoid: node => nodeVoid = "%identity" | ||||
| 
 | ||||
| exception UnsupportedPeggyNodeType(string) // This should never happen; programming error | ||||
| let castNodeType = (node: node) => | ||||
|   switch node["type"] { | ||||
|   | "Block" => node->castNodeBlock->PgNodeBlock | ||||
|   | "Program" => node->castNodeBlock->PgNodeProgram | ||||
|   | "Array" => node->castNodeArray->PgNodeArray | ||||
|   | "Record" => node->castNodeRecord->PgNodeRecord | ||||
|   | "Boolean" => node->castNodeBoolean->PgNodeBoolean | ||||
|   | "Call" => node->castNodeCall->PgNodeCall | ||||
|   | "Float" => node->castNodeFloat->PgNodeFloat | ||||
|   | "Identifier" => node->castNodeIdentifier->PgNodeIdentifier | ||||
|   | "Integer" => node->castNodeInteger->PgNodeInteger | ||||
|   | "KeyValue" => node->castNodeKeyValue->PgNodeKeyValue | ||||
|   | "Lambda" => node->castNodeLambda->PgNodeLambda | ||||
|   | "LetStatement" => node->castNodeLetStatement->PgNodeLetStatement | ||||
|   | "ModuleIdentifier" => node->castNodeModuleIdentifier->PgNodeModuleIdentifier | ||||
|   | "String" => node->castNodeString->PgNodeString | ||||
|   | "Ternary" => node->castNodeTernary->PgNodeTernary | ||||
|   // | "TypeIdentifier" => node->castNodeTypeIdentifier->PgNodeTypeIdentifier | ||||
|   | "Void" => node->castNodeVoid->PgNodeVoid | ||||
| let nodeToAST = (node: node) => { | ||||
|   let content = switch node["type"] { | ||||
|   | "Block" => node->castNodeBlock->ASTBlock | ||||
|   | "Program" => node->castNodeBlock->ASTProgram | ||||
|   | "Array" => node->castNodeArray->ASTArray | ||||
|   | "Record" => node->castNodeRecord->ASTRecord | ||||
|   | "Boolean" => node->castNodeBoolean->ASTBoolean | ||||
|   | "Call" => node->castNodeCall->ASTCall | ||||
|   | "Float" => node->castNodeFloat->ASTFloat | ||||
|   | "Identifier" => node->castNodeIdentifier->ASTIdentifier | ||||
|   | "Integer" => node->castNodeInteger->ASTInteger | ||||
|   | "KeyValue" => node->castNodeKeyValue->ASTKeyValue | ||||
|   | "Lambda" => node->castNodeLambda->ASTLambda | ||||
|   | "LetStatement" => node->castNodeLetStatement->ASTLetStatement | ||||
|   | "ModuleIdentifier" => node->castNodeModuleIdentifier->ASTModuleIdentifier | ||||
|   | "String" => node->castNodeString->ASTString | ||||
|   | "Ternary" => node->castNodeTernary->ASTTernary | ||||
|   | "Void" => node->castNodeVoid->ASTVoid | ||||
|   | _ => 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 => | ||||
|     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 => | ||||
|     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 => | ||||
|     nodes->Js.Array2.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") | ||||
|   let pgNodesToStringUsingSeparator = (nodes: array<ast>, separator: string): string => | ||||
|     nodes->Belt.Array.map(pgToString)->Extra.Array.intersperse(separator)->Js.String.concatMany("") | ||||
| 
 | ||||
|   switch peggyNode { | ||||
|   | PgNodeBlock(node) | ||||
|   | PgNodeProgram(node) => | ||||
|   switch ast.content { | ||||
|   | ASTBlock(node) | ||||
|   | ASTProgram(node) => | ||||
|     "{" ++ node["statements"]->nodesToStringUsingSeparator("; ") ++ "}" | ||||
|   | PgNodeArray(node) => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]" | ||||
|   | PgNodeRecord(node) => | ||||
|   | ASTArray(node) => "[" ++ node["elements"]->nodesToStringUsingSeparator("; ") ++ "]" | ||||
|   | ASTRecord(node) => | ||||
|     "{" ++ | ||||
|     node["elements"] | ||||
|     ->Js.Array2.map(element => PgNodeKeyValue(element)) | ||||
|     ->Belt.Array.map(element => element->nodeKeyValueToAST) | ||||
|     ->pgNodesToStringUsingSeparator(", ") ++ "}" | ||||
|   | PgNodeBoolean(node) => node["value"]->Js.String.make | ||||
|   | PgNodeCall(node) => | ||||
|   | ASTBoolean(node) => node["value"]->Js.String.make | ||||
|   | ASTCall(node) => | ||||
|     "(" ++ node["fn"]->toString ++ " " ++ node["args"]->nodesToStringUsingSeparator(" ") ++ ")" | ||||
|   | PgNodeFloat(node) => node["value"]->Js.String.make | ||||
|   | PgNodeIdentifier(node) => `:${node["value"]}` | ||||
|   | PgNodeInteger(node) => node["value"]->Js.String.make | ||||
|   | PgNodeKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"]) | ||||
|   | PgNodeLambda(node) => | ||||
|     "{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}" | ||||
|   | PgNodeLetStatement(node) => | ||||
|     pgToString(PgNodeIdentifier(node["variable"])) ++ " = " ++ toString(node["value"]) | ||||
|   | PgNodeModuleIdentifier(node) => `@${node["value"]}` | ||||
|   | PgNodeString(node) => `'${node["value"]->Js.String.make}'` | ||||
|   | PgNodeTernary(node) => | ||||
|   | ASTFloat(node) => node["value"]->Js.String.make | ||||
|   | ASTIdentifier(node) => `:${node["value"]}` | ||||
|   | ASTInteger(node) => node["value"]->Js.String.make | ||||
|   | ASTKeyValue(node) => toString(node["key"]) ++ ": " ++ toString(node["value"]) | ||||
|   | ASTLambda(node) => "{|" ++ node["args"]->argsToString ++ "| " ++ node["body"]->toString ++ "}" | ||||
|   | ASTLetStatement(node) => | ||||
|     pgToString(node["variable"]->nodeIdentifierToAST) ++ " = " ++ toString(node["value"]) | ||||
|   | ASTModuleIdentifier(node) => `@${node["value"]}` | ||||
|   | ASTString(node) => `'${node["value"]->Js.String.make}'` | ||||
|   | ASTTernary(node) => | ||||
|     "(::$$_ternary_$$ " ++ | ||||
|     toString(node["condition"]) ++ | ||||
|     " " ++ | ||||
|     toString(node["trueExpression"]) ++ | ||||
|     " " ++ | ||||
|     toString(node["falseExpression"]) ++ ")" | ||||
|   // | PgNodeTypeIdentifier(node) => `#${node["value"]}` | ||||
|   | PgNodeVoid(_node) => "()" | ||||
|   | ASTVoid(_node) => "()" | ||||
|   } | ||||
| } | ||||
| and toString = (node: node): string => node->castNodeType->pgToString | ||||
| and toString = (node: node): string => node->nodeToAST->pgToString | ||||
| 
 | ||||
| let toStringResult = (rNode: result<node, errorValue>): string => | ||||
|   switch rNode { | ||||
|   | Ok(node) => toString(node) | ||||
|   | Error(error) => `Error(${errorToString(error)})` | ||||
|   | Error(error) => `Error(${errorValueToString(error)})` | ||||
|   } | ||||
|  |  | |||
|  | @ -3,62 +3,72 @@ module ExpressionT = Reducer_Expression_T | |||
| module Parse = Reducer_Peggy_Parse | ||||
| 
 | ||||
| type expression = Reducer_T.expression | ||||
| type expressionContent = Reducer_T.expressionContent | ||||
| 
 | ||||
| let rec fromNode = (node: Parse.node): expression => { | ||||
|   let caseBlock = nodeBlock => | ||||
|     ExpressionBuilder.eBlock(nodeBlock["statements"]->Js.Array2.map(fromNode)) | ||||
|   let ast = Parse.nodeToAST(node) | ||||
| 
 | ||||
|   let caseProgram = nodeProgram => | ||||
|     ExpressionBuilder.eProgram(nodeProgram["statements"]->Js.Array2.map(fromNode)) | ||||
|   let content: expressionContent = { | ||||
|     let caseBlock = nodeBlock => | ||||
|       ExpressionBuilder.eBlock(nodeBlock["statements"]->Js.Array2.map(fromNode)) | ||||
| 
 | ||||
|   let caseLambda = (nodeLambda: Parse.nodeLambda): expression => { | ||||
|     let args = | ||||
|       nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"]) | ||||
|     let body = nodeLambda["body"]->fromNode | ||||
|     let caseProgram = nodeProgram => | ||||
|       ExpressionBuilder.eProgram(nodeProgram["statements"]->Js.Array2.map(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"] | ||||
|     ->Js.Array2.map(keyValueNode => ( | ||||
|       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 | ||||
|   { | ||||
|     ast: ast, | ||||
|     content: content, | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -34,83 +34,91 @@ export const postOperatorToFunction = { | |||
|   "[]": "$_atIndex_$", | ||||
| }; | ||||
| 
 | ||||
| type NodeBlock = { | ||||
| type Node = { | ||||
|   location: LocationRange; | ||||
| }; | ||||
| 
 | ||||
| type NodeBlock = Node & { | ||||
|   type: "Block"; | ||||
|   statements: AnyPeggyNode[]; | ||||
| }; | ||||
| 
 | ||||
| type NodeProgram = { | ||||
| type NodeProgram = Node & { | ||||
|   type: "Program"; | ||||
|   statements: AnyPeggyNode[]; | ||||
| }; | ||||
| 
 | ||||
| type NodeArray = { | ||||
| type NodeArray = Node & { | ||||
|   type: "Array"; | ||||
|   elements: AnyPeggyNode[]; | ||||
| }; | ||||
| 
 | ||||
| type NodeRecord = { | ||||
| type NodeRecord = Node & { | ||||
|   type: "Record"; | ||||
|   elements: NodeKeyValue[]; | ||||
| }; | ||||
| 
 | ||||
| type NodeCall = { | ||||
| type NodeCall = Node & { | ||||
|   type: "Call"; | ||||
|   fn: AnyPeggyNode; | ||||
|   args: AnyPeggyNode[]; | ||||
| }; | ||||
| 
 | ||||
| type NodeFloat = { | ||||
| type NodeFloat = Node & { | ||||
|   type: "Float"; | ||||
|   value: number; | ||||
| }; | ||||
| 
 | ||||
| type NodeInteger = { | ||||
| type NodeInteger = Node & { | ||||
|   type: "Integer"; | ||||
|   value: number; | ||||
| }; | ||||
| 
 | ||||
| type NodeIdentifier = { | ||||
| type NodeIdentifier = Node & { | ||||
|   type: "Identifier"; | ||||
|   value: string; | ||||
| }; | ||||
| 
 | ||||
| type NodeLetStatement = { | ||||
| type NodeLetStatement = Node & { | ||||
|   type: "LetStatement"; | ||||
|   variable: NodeIdentifier; | ||||
|   value: AnyPeggyNode; | ||||
| }; | ||||
| 
 | ||||
| type NodeLambda = { | ||||
| type NodeLambda = Node & { | ||||
|   type: "Lambda"; | ||||
|   args: AnyPeggyNode[]; | ||||
|   body: AnyPeggyNode; | ||||
| }; | ||||
| 
 | ||||
| type NodeTernary = { | ||||
| type NodeTernary = Node & { | ||||
|   type: "Ternary"; | ||||
|   condition: AnyPeggyNode; | ||||
|   trueExpression: AnyPeggyNode; | ||||
|   falseExpression: AnyPeggyNode; | ||||
| }; | ||||
| 
 | ||||
| type NodeKeyValue = { | ||||
| type NodeKeyValue = Node & { | ||||
|   type: "KeyValue"; | ||||
|   key: AnyPeggyNode; | ||||
|   value: AnyPeggyNode; | ||||
| }; | ||||
| 
 | ||||
| type NodeString = { | ||||
| type NodeString = Node & { | ||||
|   type: "String"; | ||||
|   value: string; | ||||
|   location?: LocationRange; | ||||
| }; | ||||
| 
 | ||||
| type NodeBoolean = { | ||||
| type NodeBoolean = Node & { | ||||
|   type: "Boolean"; | ||||
|   value: boolean; | ||||
| }; | ||||
| 
 | ||||
| type NodeVoid = Node & { | ||||
|   type: "Void"; | ||||
| }; | ||||
| 
 | ||||
| export type AnyPeggyNode = | ||||
|   | NodeArray | ||||
|   | NodeRecord | ||||
|  | @ -125,47 +133,78 @@ export type AnyPeggyNode = | |||
|   | NodeTernary | ||||
|   | NodeKeyValue | ||||
|   | NodeString | ||||
|   | NodeBoolean; | ||||
|   | NodeBoolean | ||||
|   | NodeVoid; | ||||
| 
 | ||||
| export function makeFunctionCall(fn: string, args: AnyPeggyNode[]) { | ||||
| export function makeFunctionCall( | ||||
|   fn: string, | ||||
|   args: AnyPeggyNode[], | ||||
|   location: LocationRange | ||||
| ) { | ||||
|   if (fn === "$$_applyAll_$$") { | ||||
|     return nodeCall(args[0], args.splice(1)); | ||||
|     return nodeCall(args[0], args.splice(1), location); | ||||
|   } else { | ||||
|     return nodeCall(nodeIdentifier(fn), args); | ||||
|     return nodeCall(nodeIdentifier(fn, location), args, location); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function constructArray(elements: AnyPeggyNode[]) { | ||||
|   return { type: "Array", elements }; | ||||
| export function constructArray( | ||||
|   elements: AnyPeggyNode[], | ||||
|   location: LocationRange | ||||
| ): NodeArray { | ||||
|   return { type: "Array", elements, location }; | ||||
| } | ||||
| export function constructRecord(elements: AnyPeggyNode[]) { | ||||
|   return { type: "Record", elements }; | ||||
| export function constructRecord( | ||||
|   elements: NodeKeyValue[], | ||||
|   location: LocationRange | ||||
| ): NodeRecord { | ||||
|   return { type: "Record", elements, location }; | ||||
| } | ||||
| 
 | ||||
| export function nodeBlock(statements: AnyPeggyNode[]): NodeBlock { | ||||
|   return { type: "Block", statements }; | ||||
| export function nodeBlock( | ||||
|   statements: AnyPeggyNode[], | ||||
|   location: LocationRange | ||||
| ): NodeBlock { | ||||
|   return { type: "Block", statements, location }; | ||||
| } | ||||
| export function nodeProgram(statements: AnyPeggyNode[]): NodeProgram { | ||||
|   return { type: "Program", statements }; | ||||
| export function nodeProgram( | ||||
|   statements: AnyPeggyNode[], | ||||
|   location: LocationRange | ||||
| ): NodeProgram { | ||||
|   return { type: "Program", statements, location }; | ||||
| } | ||||
| export function nodeBoolean(value: boolean): NodeBoolean { | ||||
|   return { type: "Boolean", value }; | ||||
| export function nodeBoolean( | ||||
|   value: boolean, | ||||
|   location: LocationRange | ||||
| ): NodeBoolean { | ||||
|   return { type: "Boolean", value, location }; | ||||
| } | ||||
| export function nodeCall(fn: AnyPeggyNode, args: AnyPeggyNode[]): NodeCall { | ||||
|   return { type: "Call", fn, args }; | ||||
| export function nodeCall( | ||||
|   fn: AnyPeggyNode, | ||||
|   args: AnyPeggyNode[], | ||||
|   location: LocationRange | ||||
| ): NodeCall { | ||||
|   return { type: "Call", fn, args, location }; | ||||
| } | ||||
| export function nodeFloat(value: number): NodeFloat { | ||||
|   return { type: "Float", value }; | ||||
| export function nodeFloat(value: number, location: LocationRange): NodeFloat { | ||||
|   return { type: "Float", value, location }; | ||||
| } | ||||
| export function nodeIdentifier(value: string): NodeIdentifier { | ||||
|   return { type: "Identifier", value }; | ||||
| export function nodeIdentifier( | ||||
|   value: string, | ||||
|   location: LocationRange | ||||
| ): NodeIdentifier { | ||||
|   return { type: "Identifier", value, location }; | ||||
| } | ||||
| export function nodeInteger(value: number): NodeInteger { | ||||
|   return { type: "Integer", value }; | ||||
| export function nodeInteger( | ||||
|   value: number, | ||||
|   location: LocationRange | ||||
| ): NodeInteger { | ||||
|   return { type: "Integer", value, location }; | ||||
| } | ||||
| export function nodeKeyValue( | ||||
|   key: AnyPeggyNode, | ||||
|   value: AnyPeggyNode | ||||
|   value: AnyPeggyNode, | ||||
|   location: LocationRange | ||||
| ): NodeKeyValue { | ||||
|   if (key.type === "Identifier") { | ||||
|     key = { | ||||
|  | @ -173,43 +212,43 @@ export function nodeKeyValue( | |||
|       type: "String", | ||||
|     }; | ||||
|   } | ||||
|   return { type: "KeyValue", key, value }; | ||||
|   return { type: "KeyValue", key, value, location }; | ||||
| } | ||||
| export function nodeLambda( | ||||
|   args: AnyPeggyNode[], | ||||
|   body: AnyPeggyNode | ||||
|   body: AnyPeggyNode, | ||||
|   location: LocationRange | ||||
| ): NodeLambda { | ||||
|   return { type: "Lambda", args, body }; | ||||
|   return { type: "Lambda", args, body, location }; | ||||
| } | ||||
| export function nodeLetStatement( | ||||
|   variable: NodeIdentifier, | ||||
|   value: AnyPeggyNode | ||||
|   value: AnyPeggyNode, | ||||
|   location: LocationRange | ||||
| ): NodeLetStatement { | ||||
|   return { type: "LetStatement", variable, value }; | ||||
|   return { type: "LetStatement", variable, value, location }; | ||||
| } | ||||
| export function nodeModuleIdentifier(value: string) { | ||||
|   return { type: "ModuleIdentifier", value }; | ||||
| export function nodeModuleIdentifier(value: string, location: LocationRange) { | ||||
|   return { type: "ModuleIdentifier", value, location }; | ||||
| } | ||||
| export function nodeString(value: string): NodeString { | ||||
|   return { type: "String", value }; | ||||
| export function nodeString(value: string, location: LocationRange): NodeString { | ||||
|   return { type: "String", value, location }; | ||||
| } | ||||
| export function nodeTernary( | ||||
|   condition: AnyPeggyNode, | ||||
|   trueExpression: AnyPeggyNode, | ||||
|   falseExpression: AnyPeggyNode | ||||
|   falseExpression: AnyPeggyNode, | ||||
|   location: LocationRange | ||||
| ): NodeTernary { | ||||
|   return { | ||||
|     type: "Ternary", | ||||
|     condition, | ||||
|     trueExpression, | ||||
|     falseExpression, | ||||
|     location, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| export function nodeTypeIdentifier(typeValue: string) { | ||||
|   return { type: "TypeIdentifier", value: typeValue }; | ||||
| } | ||||
| 
 | ||||
| export function nodeVoid() { | ||||
|   return { type: "Void" }; | ||||
| export function nodeVoid(location: LocationRange): NodeVoid { | ||||
|   return { type: "Void", location }; | ||||
| } | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ and lambdaValue = { | |||
|   body: lambdaBody, | ||||
| } | ||||
| @genType.opaque and lambdaDeclaration = Declaration.declaration<lambdaValue> | ||||
| and expression = | ||||
| and expressionContent = | ||||
|   | 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. | ||||
|   | EProgram(array<expression>) | ||||
|  | @ -35,6 +35,11 @@ and expression = | |||
|   | ELambda(array<string>, expression) | ||||
|   | EValue(value) | ||||
| 
 | ||||
| and expression = { | ||||
|   ast: Reducer_Peggy_Parse.ast, | ||||
|   content: expressionContent, | ||||
| } | ||||
| 
 | ||||
| and namespace = Belt.Map.String.t<value> | ||||
| and bindings = { | ||||
|   namespace: namespace, | ||||
|  |  | |||
|  | @ -79,24 +79,12 @@ let toStringResult = x => | |||
|   | Error(m) => `Error(${ErrorValue.errorToString(m)})` | ||||
|   } | ||||
| 
 | ||||
| let toStringOptionResult = x => | ||||
|   switch x { | ||||
|   | Some(a) => toStringResult(a) | ||||
|   | None => "None" | ||||
|   } | ||||
| 
 | ||||
| let toStringResultOkless = (codeResult: result<t, ErrorValue.errorValue>): string => | ||||
| let toStringResultOkless = (codeResult: result<t, ErrorValue.error>): string => | ||||
|   switch codeResult { | ||||
|   | Ok(a) => toString(a) | ||||
|   | Error(m) => `Error(${ErrorValue.errorToString(m)})` | ||||
|   } | ||||
| 
 | ||||
| let toStringResultRecord = x => | ||||
|   switch x { | ||||
|   | Ok(a) => `Ok(${toStringMap(a)})` | ||||
|   | Error(m) => `Error(${ErrorValue.errorToString(m)})` | ||||
|   } | ||||
| 
 | ||||
| type internalExpressionValueType = | ||||
|   | EvtArray | ||||
|   | EvtBool | ||||
|  |  | |||
|  | @ -117,7 +117,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->Error | ||||
|   | None => RENeedToRun->Reducer_ErrorValue.attachEmptyStackTraceToErrorValue->Error | ||||
|   | Some(result) => result | ||||
|   } | ||||
| 
 | ||||
|  | @ -171,7 +171,7 @@ let linkDependencies = (project: t, sourceId: string): Reducer_T.namespace => { | |||
|             "__result__", | ||||
|             switch project->getResult(id) { | ||||
|             | Ok(result) => result | ||||
|             | Error(error) => error->Reducer_ErrorValue.ErrorException->raise | ||||
|             | Error(error) => error->Reducer_ErrorValue.ExceptionWithStackTrace->raise | ||||
|             }, | ||||
|           ), | ||||
|         ]) | ||||
|  |  | |||
|  | @ -4,8 +4,9 @@ module T = ReducerProject_ProjectItem_T | |||
| type projectItem = T.projectItem | ||||
| type t = T.t | ||||
| 
 | ||||
| let emptyItem: projectItem = { | ||||
| let emptyItem = (sourceId: string): projectItem => { | ||||
|   source: "", | ||||
|   sourceId: sourceId, | ||||
|   rawParse: None, | ||||
|   expression: None, | ||||
|   continuation: Reducer_Namespace.make(), | ||||
|  | @ -18,6 +19,7 @@ let emptyItem: projectItem = { | |||
| // source -> rawParse -> includes -> expression -> continuation -> result | ||||
| 
 | ||||
| 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 getExpression = (r: t): T.expressionType => r.expression | ||||
| 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 touchSource = (this: t): t => { | ||||
|   let r = emptyItem | ||||
|   let r = emptyItem(this->getSourceId) | ||||
|   { | ||||
|     ...r, | ||||
|     source: getSource(this), | ||||
|  | @ -41,8 +43,9 @@ let touchSource = (this: t): t => { | |||
| } | ||||
| 
 | ||||
| let touchRawParse = (this: t): t => { | ||||
|   let r = emptyItem(this->getSourceId) | ||||
|   { | ||||
|     ...emptyItem, | ||||
|     ...r, | ||||
|     source: getSource(this), | ||||
|     continues: getContinues(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 => | ||||
|   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()) | ||||
| 
 | ||||
| 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) | ||||
|         ->setContinuation(contextAfterEvaluation.bindings->Reducer_Bindings.locals) | ||||
|       } catch { | ||||
|       | Reducer_ErrorValue.ErrorException(e) => this->failRun(e) | ||||
|       | _ => this->failRun(RETodo("unhandled rescript exception")) | ||||
|       | Reducer_ErrorValue.ErrorException(e) => | ||||
|         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 => { | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ type expressionType = option<expressionArgumentType> | |||
| type continuationArgumentType = Reducer_T.namespace | ||||
| type continuationType = option<continuationArgumentType> | ||||
| 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 continuesArgumentType = array<string> | ||||
| type continuesType = array<string> | ||||
|  | @ -21,6 +21,7 @@ type importAsVariablesType = array<(string, string)> | |||
| 
 | ||||
| type projectItem = { | ||||
|   source: sourceType, | ||||
|   sourceId: string, | ||||
|   rawParse: rawParseType, | ||||
|   expression: expressionType, | ||||
|   continuation: continuationArgumentType, | ||||
|  |  | |||
|  | @ -13,4 +13,4 @@ type t = project | |||
| let getSourceIds = (project: t): array<string> => Belt.MutableMap.String.keysToArray(project.items) | ||||
| 
 | ||||
| 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)) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user