CallStack, location -> frame, WIP
This commit is contained in:
parent
4c56b2fd07
commit
184584c9f3
|
@ -1,6 +1,8 @@
|
|||
import * as RSError from "../rescript/SqError.gen";
|
||||
|
||||
export type SqLocation = RSError.location;
|
||||
import * as RSCallStack from "../rescript/Reducer/Reducer_CallStack.gen";
|
||||
|
||||
export type SqFrame = RSCallStack.frame;
|
||||
|
||||
export class SqError {
|
||||
constructor(private _value: RSError.t) {}
|
||||
|
@ -17,13 +19,17 @@ export class SqError {
|
|||
return new SqError(RSError.createOtherError(v));
|
||||
}
|
||||
|
||||
toLocationArray() {
|
||||
const stackTrace = RSError.getStackTrace(this._value);
|
||||
|
||||
return stackTrace ? RSError.StackTrace.toLocationArray(stackTrace) : [];
|
||||
getTopFrame(): SqCallFrame | undefined {
|
||||
const frame = RSCallStack.getTopFrame(RSError.getStackTrace(this._value));
|
||||
return frame ? new SqCallFrame(frame) : undefined;
|
||||
}
|
||||
|
||||
toLocation() {
|
||||
return RSError.getLocation(this._value);
|
||||
getFrameArray(): SqCallFrame[] {
|
||||
const frames = RSError.getFrameArray(this._value);
|
||||
return frames.map((frame) => new SqCallFrame(frame));
|
||||
}
|
||||
}
|
||||
|
||||
export class SqCallFrame {
|
||||
constructor(private _value: SqFrame) {}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ export {
|
|||
environment,
|
||||
defaultEnvironment,
|
||||
} from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
|
||||
export { SqError, SqLocation } from "./SqError";
|
||||
export { SqError } from "./SqError";
|
||||
export { SqShape } from "./SqPointSetDist";
|
||||
|
||||
export { resultMap } from "./types";
|
||||
|
|
|
@ -67,7 +67,7 @@ module Integration = {
|
|||
let applyFunctionAtFloatToFloatOption = (point: float) => {
|
||||
// Defined here so that it has access to environment, reducer
|
||||
let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point)
|
||||
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
|
||||
let resultAsInternalExpression = Reducer_Lambda.doLambdaCall(
|
||||
aLambda,
|
||||
[pointAsInternalExpression],
|
||||
environment,
|
||||
|
@ -308,7 +308,7 @@ module DiminishingReturns = {
|
|||
let applyFunctionAtPoint = (lambda, point: float) => {
|
||||
// Defined here so that it has access to environment, reducer
|
||||
let pointAsInternalExpression = FunctionRegistry_Helpers.Wrappers.evNumber(point)
|
||||
let resultAsInternalExpression = Reducer_Expression_Lambda.doLambdaCall(
|
||||
let resultAsInternalExpression = Reducer_Lambda.doLambdaCall(
|
||||
lambda,
|
||||
[pointAsInternalExpression],
|
||||
environment,
|
||||
|
|
|
@ -76,7 +76,7 @@ let library = [
|
|||
->Belt.Array.map(dictValue =>
|
||||
switch dictValue {
|
||||
| IEvRecord(dict) => dict
|
||||
| _ => impossibleError->SqError.Message.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
)
|
||||
->Internals.mergeMany
|
||||
|
|
|
@ -22,7 +22,8 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber, FRTypeDistOrNumber],
|
||||
~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env),
|
||||
~run=(inputs, context, _) =>
|
||||
inputs->Prepare.ToValueTuple.twoDistOrNumber->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -31,8 +32,10 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeRecord([("p5", FRTypeDistOrNumber), ("p95", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, env, _) =>
|
||||
inputs->Prepare.ToValueTuple.Record.twoDistOrNumber(("p5", "p95"))->process(~fn, ~env),
|
||||
~run=(inputs, context, _) =>
|
||||
inputs
|
||||
->Prepare.ToValueTuple.Record.twoDistOrNumber(("p5", "p95"))
|
||||
->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -41,10 +44,10 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeRecord([("mean", FRTypeDistOrNumber), ("stdev", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, env, _) =>
|
||||
~run=(inputs, context, _) =>
|
||||
inputs
|
||||
->Prepare.ToValueTuple.Record.twoDistOrNumber(("mean", "stdev"))
|
||||
->process(~fn, ~env),
|
||||
->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
@ -61,7 +64,8 @@ module DistributionCreation = {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs=[FRTypeDistOrNumber],
|
||||
~run=(inputs, env, _) => inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env),
|
||||
~run=(inputs, context, _) =>
|
||||
inputs->Prepare.ToValueTuple.oneDistOrNumber->process(~fn, ~env=context.environment),
|
||||
(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -313,7 +313,7 @@ module Old = {
|
|||
| None =>
|
||||
SqError.Message.REOther(
|
||||
"Internal error in FR_GenericDist implementation",
|
||||
)->SqError.Message.toException
|
||||
)->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +326,7 @@ let makeProxyFn = (name: string, inputs: array<frType>) => {
|
|||
FnDefinition.make(
|
||||
~name,
|
||||
~inputs,
|
||||
~run=(inputs, env, _) => Old.dispatch((name, inputs), env),
|
||||
~run=(inputs, context, _) => Old.dispatch((name, inputs), context.environment),
|
||||
(),
|
||||
),
|
||||
],
|
||||
|
@ -402,9 +402,9 @@ let library = E.A.concatMany([
|
|||
])
|
||||
|
||||
// FIXME - impossible to implement with FR due to arbitrary parameters length;
|
||||
let mxLambda = Reducer_Expression_Lambda.makeFFILambda((inputs, env, _) => {
|
||||
switch Old.dispatch(("mx", inputs), env) {
|
||||
let mxLambda = Reducer_Lambda.makeFFILambda("mx", (inputs, context, _) => {
|
||||
switch Old.dispatch(("mx", inputs), context.environment) {
|
||||
| Ok(value) => value
|
||||
| Error(e) => e->SqError.Message.toException
|
||||
| Error(e) => e->SqError.Message.throw
|
||||
}
|
||||
})
|
||||
|
|
|
@ -26,11 +26,11 @@ module Internals = {
|
|||
let map = (
|
||||
array: array<Reducer_T.value>,
|
||||
eLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
): Reducer_T.value => {
|
||||
Belt.Array.map(array, elem =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(eLambdaValue, [elem], env, reducer)
|
||||
Reducer_Lambda.doLambdaCall(eLambdaValue, [elem], context, reducer)
|
||||
)->Wrappers.evArray
|
||||
}
|
||||
|
||||
|
@ -38,11 +38,11 @@ module Internals = {
|
|||
aValueArray,
|
||||
initialValue,
|
||||
aLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
aValueArray->E.A.reduce(initialValue, (acc, elem) =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer)
|
||||
Reducer_Lambda.doLambdaCall(aLambdaValue, [acc, elem], context, reducer)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -50,22 +50,22 @@ module Internals = {
|
|||
aValueArray,
|
||||
initialValue,
|
||||
aLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
aValueArray->Belt.Array.reduceReverse(initialValue, (acc, elem) =>
|
||||
Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [acc, elem], env, reducer)
|
||||
Reducer_Lambda.doLambdaCall(aLambdaValue, [acc, elem], context, reducer)
|
||||
)
|
||||
}
|
||||
|
||||
let filter = (
|
||||
aValueArray,
|
||||
aLambdaValue,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
Js.Array2.filter(aValueArray, elem => {
|
||||
let result = Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, [elem], env, reducer)
|
||||
let result = Reducer_Lambda.doLambdaCall(aLambdaValue, [elem], context, reducer)
|
||||
switch result {
|
||||
| IEvBool(true) => true
|
||||
| _ => false
|
||||
|
|
|
@ -16,16 +16,16 @@ let inputsToDist = (inputs: array<Reducer_T.value>, xyShapeToPointSetDist) => {
|
|||
let yValue = map->Belt.Map.String.get("y")
|
||||
switch (xValue, yValue) {
|
||||
| (Some(IEvNumber(x)), Some(IEvNumber(y))) => (x, y)
|
||||
| _ => impossibleError->SqError.Message.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
| _ => impossibleError->SqError.Message.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
)
|
||||
->Ok
|
||||
->E.R.bind(r => r->XYShape.T.makeFromZipped->E.R2.errMap(XYShape.Error.toString))
|
||||
->E.R2.fmap(r => Reducer_T.IEvDistribution(PointSet(r->xyShapeToPointSetDist)))
|
||||
| _ => impossibleError->SqError.Message.toException
|
||||
| _ => impossibleError->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ module Internal = {
|
|||
}
|
||||
|
||||
let doLambdaCall = (aLambdaValue, list, env, reducer) =>
|
||||
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
|
||||
switch Reducer_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
|
||||
| Reducer_T.IEvNumber(f) => Ok(f)
|
||||
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="fromDist",
|
||||
~inputs=[FRTypeDist],
|
||||
~run=(inputs, env, _) =>
|
||||
~run=(inputs, context, _) =>
|
||||
switch inputs {
|
||||
| [IEvDistribution(dist)] =>
|
||||
GenericDist.toPointSet(
|
||||
dist,
|
||||
~xyPointLength=env.xyPointLength,
|
||||
~sampleCount=env.sampleCount,
|
||||
~xyPointLength=context.environment.xyPointLength,
|
||||
~sampleCount=context.environment.sampleCount,
|
||||
(),
|
||||
)
|
||||
->E.R2.fmap(Wrappers.pointSet)
|
||||
|
|
|
@ -10,10 +10,10 @@ module Internal = {
|
|||
let doLambdaCall = (
|
||||
aLambdaValue,
|
||||
list,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) =>
|
||||
switch Reducer_Expression_Lambda.doLambdaCall(aLambdaValue, list, env, reducer) {
|
||||
switch Reducer_Lambda.doLambdaCall(aLambdaValue, list, context, reducer) {
|
||||
| IEvNumber(f) => Ok(f)
|
||||
| _ => Error(Operation.SampleMapNeedsNtoNFunction)
|
||||
}
|
||||
|
@ -25,26 +25,25 @@ module Internal = {
|
|||
}
|
||||
|
||||
//TODO: I don't know why this seems to need at least one input
|
||||
let fromFn = (aLambdaValue, environment: Reducer_T.environment, reducer: Reducer_T.reducerFn) => {
|
||||
let sampleCount = environment.sampleCount
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer)
|
||||
let fromFn = (aLambdaValue, context: Reducer_T.context, reducer: Reducer_T.reducerFn) => {
|
||||
let sampleCount = context.environment.sampleCount
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], context, reducer)
|
||||
Belt_Array.makeBy(sampleCount, r => fn(r->Js.Int.toFloat))->E.A.R.firstErrorOrOpen
|
||||
}
|
||||
|
||||
let map1 = (sampleSetDist: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], environment, reducer)
|
||||
let map1 = (sampleSetDist: t, aLambdaValue, context: Reducer_T.context, reducer) => {
|
||||
let fn = r => doLambdaCall(aLambdaValue, [IEvNumber(r)], context, reducer)
|
||||
SampleSetDist.samplesMap(~fn, sampleSetDist)->toType
|
||||
}
|
||||
|
||||
let map2 = (t1: t, t2: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
|
||||
let fn = (a, b) =>
|
||||
doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b)], environment, reducer)
|
||||
let map2 = (t1: t, t2: t, aLambdaValue, context: Reducer_T.context, reducer) => {
|
||||
let fn = (a, b) => doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b)], context, reducer)
|
||||
SampleSetDist.map2(~fn, ~t1, ~t2)->toType
|
||||
}
|
||||
|
||||
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, environment: Reducer_T.environment, reducer) => {
|
||||
let map3 = (t1: t, t2: t, t3: t, aLambdaValue, context: Reducer_T.context, reducer) => {
|
||||
let fn = (a, b, c) =>
|
||||
doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b), IEvNumber(c)], environment, reducer)
|
||||
doLambdaCall(aLambdaValue, [IEvNumber(a), IEvNumber(b), IEvNumber(c)], context, reducer)
|
||||
SampleSetDist.map3(~fn, ~t1, ~t2, ~t3)->toType
|
||||
}
|
||||
|
||||
|
@ -60,7 +59,7 @@ module Internal = {
|
|||
let mapN = (
|
||||
aValueArray: array<Reducer_T.value>,
|
||||
aLambdaValue,
|
||||
environment: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer,
|
||||
) => {
|
||||
switch parseSampleSetArray(aValueArray) {
|
||||
|
@ -69,7 +68,7 @@ module Internal = {
|
|||
doLambdaCall(
|
||||
aLambdaValue,
|
||||
[IEvArray(E.A.fmap(x => Wrappers.evNumber(x), a))],
|
||||
environment,
|
||||
context,
|
||||
reducer,
|
||||
)
|
||||
SampleSetDist.mapN(~fn, ~t1)->toType
|
||||
|
@ -89,10 +88,10 @@ let libaryBase = [
|
|||
FnDefinition.make(
|
||||
~name="fromDist",
|
||||
~inputs=[FRTypeDist],
|
||||
~run=(inputs, environment, _) =>
|
||||
~run=(inputs, context, _) =>
|
||||
switch inputs {
|
||||
| [IEvDistribution(dist)] =>
|
||||
GenericDist.toSampleSetDist(dist, environment.sampleCount)
|
||||
GenericDist.toSampleSetDist(dist, context.environment.sampleCount)
|
||||
->E.R2.fmap(Wrappers.sampleSet)
|
||||
->E.R2.fmap(Wrappers.evDistribution)
|
||||
->E.R2.errMap(e => SqError.Message.REDistributionError(e))
|
||||
|
|
|
@ -30,15 +30,15 @@ let library = [
|
|||
("prior", FRTypeDist),
|
||||
]),
|
||||
],
|
||||
~run=(inputs, environment, _) => {
|
||||
~run=(inputs, context, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.threeArgs(
|
||||
inputs,
|
||||
("estimate", "answer", "prior"),
|
||||
) {
|
||||
| Ok([IEvDistribution(estimate), IEvDistribution(d), IEvDistribution(prior)]) =>
|
||||
runScoring(estimate, Score_Dist(d), Some(prior), environment)
|
||||
runScoring(estimate, Score_Dist(d), Some(prior), context.environment)
|
||||
| Ok([IEvDistribution(estimate), IEvNumber(d), IEvDistribution(prior)]) =>
|
||||
runScoring(estimate, Score_Scalar(d), Some(prior), environment)
|
||||
runScoring(estimate, Score_Scalar(d), Some(prior), context.environment)
|
||||
| Error(e) => Error(e->FunctionRegistry_Helpers.wrapError)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
|
@ -48,15 +48,15 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="logScore",
|
||||
~inputs=[FRTypeRecord([("estimate", FRTypeDist), ("answer", FRTypeDistOrNumber)])],
|
||||
~run=(inputs, environment, _) => {
|
||||
~run=(inputs, context, _) => {
|
||||
switch FunctionRegistry_Helpers.Prepare.ToValueArray.Record.twoArgs(
|
||||
inputs,
|
||||
("estimate", "answer"),
|
||||
) {
|
||||
| Ok([IEvDistribution(estimate), IEvDistribution(d)]) =>
|
||||
runScoring(estimate, Score_Dist(d), None, environment)
|
||||
runScoring(estimate, Score_Dist(d), None, context.environment)
|
||||
| Ok([IEvDistribution(estimate), IEvNumber(d)]) =>
|
||||
runScoring(estimate, Score_Scalar(d), None, environment)
|
||||
runScoring(estimate, Score_Scalar(d), None, context.environment)
|
||||
| Error(e) => Error(e->FunctionRegistry_Helpers.wrapError)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
|
@ -76,10 +76,10 @@ let library = [
|
|||
FnDefinition.make(
|
||||
~name="klDivergence",
|
||||
~inputs=[FRTypeDist, FRTypeDist],
|
||||
~run=(inputs, environment, _) => {
|
||||
~run=(inputs, context, _) => {
|
||||
switch inputs {
|
||||
| [IEvDistribution(estimate), IEvDistribution(d)] =>
|
||||
runScoring(estimate, Score_Dist(d), None, environment)
|
||||
runScoring(estimate, Score_Dist(d), None, context.environment)
|
||||
| _ => Error(FunctionRegistry_Helpers.impossibleError)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
@genType type squiggleValue_Lambda = Reducer_T.lambdaValue //re-export
|
||||
|
||||
@genType
|
||||
let toString = (v: squiggleValue_Lambda): string => Reducer_Value.toStringFunction(v)
|
||||
let toString = (v: squiggleValue_Lambda): string => Reducer_Value.toStringLambda(v)
|
||||
|
||||
@genType
|
||||
let parameters = (v: squiggleValue_Lambda): array<string> => {
|
||||
v.parameters
|
||||
}
|
||||
let parameters = (v: squiggleValue_Lambda): array<string> => Reducer_Lambda.parameters(v)
|
||||
|
|
|
@ -30,7 +30,7 @@ type fnDefinition = {
|
|||
inputs: array<frType>,
|
||||
run: (
|
||||
array<Reducer_T.value>,
|
||||
Reducer_T.environment,
|
||||
Reducer_T.context,
|
||||
Reducer_T.reducerFn,
|
||||
) => result<Reducer_T.value, errorMessage>,
|
||||
}
|
||||
|
@ -122,11 +122,11 @@ module FnDefinition = {
|
|||
let run = (
|
||||
t: t,
|
||||
args: array<Reducer_T.value>,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
switch t->isMatch(args) {
|
||||
| true => t.run(args, env, reducer)
|
||||
| true => t.run(args, context, reducer)
|
||||
| false => REOther("Incorrect Types")->Error
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ module Function = {
|
|||
nameSpace: nameSpace,
|
||||
definitions: definitions,
|
||||
output: output,
|
||||
examples: examples |> E.O.default([]),
|
||||
examples: examples->E.O2.default([]),
|
||||
isExperimental: isExperimental,
|
||||
requiresNamespace: requiresNamespace,
|
||||
description: description,
|
||||
|
@ -225,7 +225,7 @@ module Registry = {
|
|||
registry,
|
||||
fnName: string,
|
||||
args: array<Reducer_T.value>,
|
||||
env: Reducer_T.environment,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
): result<Reducer_T.value, errorMessage> => {
|
||||
switch Belt.Map.String.get(registry.fnNameDict, fnName) {
|
||||
|
@ -241,7 +241,7 @@ module Registry = {
|
|||
|
||||
let match = definitions->Js.Array2.find(def => def->FnDefinition.isMatch(args))
|
||||
switch match {
|
||||
| Some(def) => def->FnDefinition.run(args, env, reducer)
|
||||
| Some(def) => def->FnDefinition.run(args, context, reducer)
|
||||
| None => REOther(showNameMatchDefinitions())->Error
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
@genType.opaque
|
||||
type rec lambdaFrame = {location: Reducer_Peggy_Parse.location, name: string}
|
||||
@genType.opaque and ffiFrame = {name: string}
|
||||
@genType
|
||||
and frame =
|
||||
| InLambda(lambdaFrame)
|
||||
| InFFI(ffiFrame)
|
||||
|
||||
let toStringFrame = (t: frame) =>
|
||||
switch t {
|
||||
| InLambda({location}) =>
|
||||
`Line ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}, source ${location.source}`
|
||||
| InFFI({name}) => `Builtin ${name}`
|
||||
}
|
||||
|
||||
@genType.opaque
|
||||
type rec t = list<frame>
|
||||
|
||||
let make = (): t => list{}
|
||||
|
||||
let extend = (t: t, frame: frame) => t->Belt.List.add(frame)
|
||||
|
||||
let toString = (t: t) =>
|
||||
t->Belt.List.map(s => " " ++ s->toStringFrame ++ "\n")->Belt.List.toArray->Js.Array2.joinWith("")
|
||||
|
||||
@genType
|
||||
let toFrameArray = (t: t): array<frame> => t->Belt.List.toArray
|
||||
|
||||
@genType
|
||||
let getTopFrame = (t: t): option<frame> => t->Belt.List.head
|
||||
|
||||
let isEmpty = (t: t): bool =>
|
||||
switch t->Belt.List.head {
|
||||
| Some(_) => true
|
||||
| None => false
|
||||
}
|
|
@ -4,9 +4,8 @@ let defaultEnvironment: Reducer_T.environment = DistributionOperation.defaultEnv
|
|||
|
||||
let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => {
|
||||
{
|
||||
callStack: list{},
|
||||
bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend,
|
||||
environment: environment,
|
||||
}
|
||||
}
|
||||
|
||||
let createDefaultContext = (): t => createContext(SquiggleLibrary_StdLib.stdLib, defaultEnvironment)
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
module Bindings = Reducer_Bindings
|
||||
module Lambda = Reducer_Expression_Lambda
|
||||
module Result = Belt.Result
|
||||
module T = Reducer_T
|
||||
|
||||
|
@ -7,8 +6,8 @@ let toLocation = (expression: T.expression): SqError.location => {
|
|||
expression.ast.location
|
||||
}
|
||||
|
||||
let throwFrom = (error: SqError.Message.t, expression: T.expression) =>
|
||||
error->SqError.fromMessageWithLocation(expression->toLocation)->SqError.throw
|
||||
let throwFrom = (error: SqError.Message.t, context: T.context) =>
|
||||
error->SqError.throwMessage(context)
|
||||
|
||||
/*
|
||||
Recursively evaluate the expression
|
||||
|
@ -54,7 +53,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")->throwFrom(expression)
|
||||
| _ => REOther("Record keys must be strings")->throwFrom(context)
|
||||
}
|
||||
let (value, _) = eValue->evaluate(context)
|
||||
(keyString, value)
|
||||
|
@ -78,7 +77,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 => RESymbolNotFound(name)->throwFrom(expression)
|
||||
| None => RESymbolNotFound(name)->throwFrom(context)
|
||||
}
|
||||
|
||||
| T.EValue(value) => (value, context)
|
||||
|
@ -87,12 +86,18 @@ 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", "")->throwFrom(expression)
|
||||
| _ => REExpectedType("Boolean", "")->throwFrom(context)
|
||||
}
|
||||
}
|
||||
|
||||
| T.ELambda(parameters, body) => (
|
||||
Lambda.makeLambda(parameters, context.bindings, body)->T.IEvLambda,
|
||||
Reducer_Lambda.makeLambda(
|
||||
None, // TODO - pass function name from parser
|
||||
parameters,
|
||||
context.bindings,
|
||||
body,
|
||||
expression->toLocation,
|
||||
)->T.IEvLambda,
|
||||
context,
|
||||
)
|
||||
|
||||
|
@ -103,14 +108,11 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
|||
argValue
|
||||
})
|
||||
switch lambda {
|
||||
| T.IEvLambda(lambda) =>
|
||||
try {
|
||||
let result = Lambda.doLambdaCall(lambda, argValues, context.environment, evaluate)
|
||||
| T.IEvLambda(lambda) => {
|
||||
let result = Reducer_Lambda.doLambdaCall(lambda, argValues, context, evaluate)
|
||||
(result, context)
|
||||
} catch {
|
||||
| exn => exn->SqError.fromException->SqError.extend(expression->toLocation)->SqError.throw
|
||||
}
|
||||
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression)
|
||||
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,8 +124,11 @@ module BackCompatible = {
|
|||
let parse = (peggyCode: string): result<T.expression, Reducer_Peggy_Parse.parseError> =>
|
||||
peggyCode->Reducer_Peggy_Parse.parse("main")->Result.map(Reducer_Peggy_ToExpression.fromNode)
|
||||
|
||||
let createDefaultContext = () =>
|
||||
Reducer_Context.createContext(SquiggleLibrary_StdLib.stdLib, Reducer_Context.defaultEnvironment)
|
||||
|
||||
let evaluate = (expression: T.expression): result<T.value, SqError.t> => {
|
||||
let context = Reducer_Context.createDefaultContext()
|
||||
let context = createDefaultContext()
|
||||
try {
|
||||
let (value, _) = expression->evaluate(context)
|
||||
value->Ok
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
let doLambdaCall = (
|
||||
lambdaValue: Reducer_T.lambdaValue,
|
||||
args,
|
||||
environment: Reducer_T.environment,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
): Reducer_T.value => {
|
||||
lambdaValue.body(args, environment, reducer)
|
||||
}
|
||||
|
||||
let makeLambda = (
|
||||
parameters: array<string>,
|
||||
bindings: Reducer_T.bindings,
|
||||
body: Reducer_T.expression,
|
||||
): Reducer_T.lambdaValue => {
|
||||
// TODO - clone bindings to avoid later redefinitions affecting lambdas?
|
||||
|
||||
// Note: with this implementation, FFI lambdas (created by other methods than calling `makeLambda`) are allowed to violate the rules, pollute the bindings, etc.
|
||||
// Not sure yet if that's a bug or a feature.
|
||||
// FunctionRegistry functions are unaffected by this, their API is too limited.
|
||||
|
||||
let lambda = (
|
||||
arguments: array<Reducer_T.value>,
|
||||
environment: Reducer_T.environment,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
let argsLength = arguments->Js.Array2.length
|
||||
let parametersLength = parameters->Js.Array2.length
|
||||
if argsLength !== parametersLength {
|
||||
SqError.Message.REArityError(None, parametersLength, argsLength)->SqError.Message.toException
|
||||
}
|
||||
|
||||
let localBindings = bindings->Reducer_Bindings.extend
|
||||
let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(localBindings, (
|
||||
currentBindings,
|
||||
parameter,
|
||||
index,
|
||||
) => {
|
||||
currentBindings->Reducer_Bindings.set(parameter, arguments[index])
|
||||
})
|
||||
|
||||
let (value, _) = reducer(
|
||||
body,
|
||||
{bindings: localBindingsWithParameters, environment: environment},
|
||||
)
|
||||
value
|
||||
}
|
||||
|
||||
{
|
||||
// context: bindings,
|
||||
body: lambda,
|
||||
parameters: parameters,
|
||||
}
|
||||
}
|
||||
|
||||
let makeFFILambda = (body: Reducer_T.lambdaBody): Reducer_T.lambdaValue => {
|
||||
body: body,
|
||||
parameters: ["..."],
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
type t = Reducer_T.lambdaValue
|
||||
|
||||
// user-defined functions, i.e. `add2 = {|x, y| x + y}`, are built by this method
|
||||
let makeLambda = (
|
||||
name: option<string>,
|
||||
parameters: array<string>,
|
||||
bindings: Reducer_T.bindings,
|
||||
body: Reducer_T.expression,
|
||||
location: Reducer_Peggy_Parse.location,
|
||||
): t => {
|
||||
let lambda = (
|
||||
arguments: array<Reducer_T.value>,
|
||||
context: Reducer_T.context,
|
||||
reducer: Reducer_T.reducerFn,
|
||||
) => {
|
||||
let argsLength = arguments->E.A.length
|
||||
let parametersLength = parameters->E.A.length
|
||||
if argsLength !== parametersLength {
|
||||
SqError.Message.REArityError(None, parametersLength, argsLength)->SqError.Message.throw
|
||||
}
|
||||
|
||||
// create new bindings scope - technically not necessary, since bindings are immutable, but might help with debugging/new features in the future
|
||||
let localBindings = bindings->Reducer_Bindings.extend
|
||||
|
||||
let localBindingsWithParameters = parameters->Belt.Array.reduceWithIndex(localBindings, (
|
||||
currentBindings,
|
||||
parameter,
|
||||
index,
|
||||
) => {
|
||||
currentBindings->Reducer_Bindings.set(parameter, arguments[index])
|
||||
})
|
||||
|
||||
let lambdaContext: Reducer_T.context = {
|
||||
bindings: localBindingsWithParameters, // based on bindings at the moment of lambda creation
|
||||
environment: context.environment, // environment at the moment when lambda is called
|
||||
callStack: context.callStack, // extended by main `evaluate` function
|
||||
}
|
||||
|
||||
let (value, _) = reducer(body, lambdaContext)
|
||||
value
|
||||
}
|
||||
|
||||
FnLambda({
|
||||
// context: bindings,
|
||||
name: name,
|
||||
body: lambda,
|
||||
parameters: parameters,
|
||||
location: location,
|
||||
})
|
||||
}
|
||||
|
||||
// stdlib lambdas (everything in FunctionRegistry) is built by this method. Body is generated in SquiggleLibrary_StdLib.res
|
||||
let makeFFILambda = (name: string, body: Reducer_T.lambdaBody): t => FnBuiltin({
|
||||
// Note: current bindings could be accidentally exposed here through context (compare with native lambda implementation above, where we override them with local bindings).
|
||||
// But FunctionRegistry API is too limited for that to matter. Please take care not to violate that in the future by accident.
|
||||
body: body,
|
||||
name: name,
|
||||
})
|
||||
|
||||
let extendCallStack = (t: t, callStack: Reducer_CallStack.t): Reducer_CallStack.t => {
|
||||
switch t {
|
||||
| FnLambda({location}) =>
|
||||
callStack->Reducer_CallStack.extend(InLambda({location: location, name: "TODO"})) // FIXME
|
||||
| FnBuiltin({name}) => callStack->Reducer_CallStack.extend(InFFI({name: name}))
|
||||
}
|
||||
}
|
||||
|
||||
// this function doesn't scale to FunctionRegistry's polymorphic functions
|
||||
let parameters = (t: t): array<string> => {
|
||||
switch t {
|
||||
| FnLambda({parameters}) => parameters
|
||||
| FnBuiltin(_) => ["..."]
|
||||
}
|
||||
}
|
||||
|
||||
let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
|
||||
let newContext = {
|
||||
...context,
|
||||
callStack: t->extendCallStack(context.callStack),
|
||||
}
|
||||
|
||||
SqError.contextualizeAndRethrow(() => {
|
||||
switch t {
|
||||
| FnLambda({body}) => body(args, newContext, reducer)
|
||||
| FnBuiltin({body}) => body(args, newContext, reducer)
|
||||
}
|
||||
}, newContext)
|
||||
}
|
|
@ -15,12 +15,16 @@ type rec value =
|
|||
| IEvVoid
|
||||
@genType.opaque and arrayValue = array<value>
|
||||
@genType.opaque and map = Belt.Map.String.t<value>
|
||||
and lambdaBody = (array<value>, environment, reducerFn) => value
|
||||
and lambdaBody = (array<value>, context, reducerFn) => value
|
||||
@genType.opaque
|
||||
and lambdaValue = {
|
||||
and lambdaValue =
|
||||
| FnLambda({
|
||||
parameters: array<string>,
|
||||
body: lambdaBody,
|
||||
}
|
||||
location: Reducer_Peggy_Parse.location,
|
||||
name: option<string>,
|
||||
})
|
||||
| FnBuiltin({body: lambdaBody, name: string})
|
||||
@genType.opaque and lambdaDeclaration = Declaration.declaration<lambdaValue>
|
||||
and expressionContent =
|
||||
| EBlock(array<expression>)
|
||||
|
@ -49,6 +53,7 @@ and bindings = {
|
|||
and context = {
|
||||
bindings: bindings,
|
||||
environment: environment,
|
||||
callStack: Reducer_CallStack.t,
|
||||
}
|
||||
|
||||
and reducerFn = (expression, context) => (value, context)
|
||||
|
|
|
@ -28,10 +28,12 @@ and toStringCall = fName => `:${fName}`
|
|||
and toStringDate = date => DateTime.Date.toString(date)
|
||||
and toStringDeclaration = d => Declaration.toString(d, r => toString(IEvLambda(r)))
|
||||
and toStringDistribution = dist => GenericDist.toString(dist)
|
||||
and toStringLambda = (lambdaValue: T.lambdaValue) =>
|
||||
`lambda(${Js.Array2.toString(lambdaValue.parameters)}=>internal code)`
|
||||
and toStringFunction = (lambdaValue: T.lambdaValue) =>
|
||||
`function(${Js.Array2.toString(lambdaValue.parameters)})`
|
||||
and toStringLambda = (lambdaValue: T.lambdaValue) => {
|
||||
switch lambdaValue {
|
||||
| FnLambda({parameters}) => `lambda(${Js.Array2.toString(parameters)}=>internal code)`
|
||||
| FnBuiltin(_) => "Builtin function"
|
||||
}
|
||||
}
|
||||
and toStringNumber = aNumber => Js.String.make(aNumber)
|
||||
and toStringRecord = aMap => aMap->toStringMap
|
||||
and toStringString = aString => `'${aString}'`
|
||||
|
@ -143,7 +145,7 @@ let arrayToValueArray = (arr: array<t>): array<t> => arr
|
|||
let resultToValue = (rExpression: result<t, SqError.Message.t>): t =>
|
||||
switch rExpression {
|
||||
| Ok(expression) => expression
|
||||
| Error(errorValue) => SqError.Message.toException(errorValue)
|
||||
| Error(errorValue) => SqError.Message.throw(errorValue)
|
||||
}
|
||||
|
||||
let recordToKeyValuePairs = (record: T.map): array<(string, t)> => record->Belt.Map.String.toArray
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
type location = Reducer_Peggy_Parse.location
|
||||
|
||||
// Messages don't contain any stack trace information.
|
||||
// FunctionRegistry functions are allowed to throw MessageExceptions, though, because they will be caught and rewrapped by Reducer_Lambda code
|
||||
module Message = {
|
||||
@genType.opaque
|
||||
type t =
|
||||
|
@ -82,81 +84,79 @@ module Message = {
|
|||
| _e => REOther("Unknown error")
|
||||
}
|
||||
|
||||
let toException = (errorValue: t) => errorValue->MessageException->raise
|
||||
}
|
||||
|
||||
module StackTrace = {
|
||||
@genType.opaque
|
||||
type rec t = {
|
||||
location: location,
|
||||
parent: option<t>,
|
||||
}
|
||||
|
||||
let rec toString = ({location, parent}: t) => {
|
||||
` Line ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}, source ${location.source}\n` ++
|
||||
switch parent {
|
||||
| Some(parent) => toString(parent)
|
||||
| None => ""
|
||||
}
|
||||
}
|
||||
|
||||
let rec toLocationList = (t: t): list<location> => {
|
||||
switch t.parent {
|
||||
| Some(parent) => Belt.List.add(toLocationList(parent), t.location)
|
||||
| None => list{t.location}
|
||||
}
|
||||
}
|
||||
|
||||
@genType
|
||||
let toLocationArray = (t: t): array<location> => t->toLocationList->Belt.List.toArray
|
||||
let throw = (errorValue: t) => errorValue->MessageException->raise
|
||||
}
|
||||
|
||||
@genType.opaque
|
||||
type t = {
|
||||
message: Message.t,
|
||||
stackTrace: option<StackTrace.t>,
|
||||
/*
|
||||
Errors raised from internal functions can have empty location.
|
||||
|
||||
Also, location is not the same as the top of the stackTrace.
|
||||
Consider this:
|
||||
```
|
||||
f() = {
|
||||
x = 5
|
||||
y = z // no such var
|
||||
x + y
|
||||
}
|
||||
```
|
||||
This code should report the location of assignment issue, but there's no function call there.
|
||||
*/
|
||||
location: option<location>,
|
||||
stackTrace: Reducer_CallStack.t,
|
||||
}
|
||||
|
||||
exception SqException(t)
|
||||
|
||||
@genType
|
||||
let fromMessage = (errorMessage: Message.t): t => {
|
||||
message: errorMessage,
|
||||
stackTrace: None,
|
||||
}
|
||||
|
||||
let fromMessageWithLocation = (errorMessage: Message.t, location: location): t => {
|
||||
message: errorMessage,
|
||||
stackTrace: Some({location: location, parent: None}),
|
||||
}
|
||||
|
||||
let extend = ({message, stackTrace}: t, location: location) => {
|
||||
// `context` should be specified for runtime errors, but can be left empty for errors from Reducer_Project and so on.
|
||||
// `location` can be empty for errors raised from FunctionRegistry.
|
||||
let fromMessage = (
|
||||
message: Message.t,
|
||||
context: option<Reducer_T.context>,
|
||||
location: option<location>,
|
||||
): t => {
|
||||
message: message,
|
||||
stackTrace: Some({location: location, parent: stackTrace}),
|
||||
location: location,
|
||||
stackTrace: switch context {
|
||||
| Some(context) => context.callStack
|
||||
| None => Reducer_CallStack.make()
|
||||
},
|
||||
}
|
||||
|
||||
@genType
|
||||
let getLocation = (t: t): option<location> => t.stackTrace->E.O2.fmap(stack => stack.location)
|
||||
let getTopFrame = (t: t): option<Reducer_CallStack.frame> =>
|
||||
t.stackTrace->Reducer_CallStack.getTopFrame
|
||||
|
||||
@genType
|
||||
let getStackTrace = (t: t): option<StackTrace.t> => t.stackTrace
|
||||
let getStackTrace = (t: t): Reducer_CallStack.t => t.stackTrace
|
||||
|
||||
@genType
|
||||
let toString = (t: t): string => t.message->Message.toString
|
||||
|
||||
@genType
|
||||
let createOtherError = (v: string): t => Message.REOther(v)->fromMessage
|
||||
let createOtherError = (v: string): t => Message.REOther(v)->fromMessage()
|
||||
|
||||
@genType
|
||||
let getFrameArray = (t: t): array<Reducer_CallStack.frame> =>
|
||||
t.stackTrace->Reducer_CallStack.toFrameArray
|
||||
|
||||
@genType
|
||||
let toStringWithStackTrace = (t: t) =>
|
||||
switch t.stackTrace {
|
||||
| Some(stack) => "Traceback:\n" ++ stack->StackTrace.toString
|
||||
| None => ""
|
||||
if t.stackTrace->Reducer_CallStack.isEmpty {
|
||||
"Traceback:\n" ++ t.stackTrace->Reducer_CallStack.toString
|
||||
} else {
|
||||
""
|
||||
} ++
|
||||
t->toString
|
||||
|
||||
let throw = (t: t) => t->SqException->raise
|
||||
|
||||
let throwMessage = (message: Message.t, context: Reducer_T.context, location: location) =>
|
||||
fromMessage(message, context, location)->throw
|
||||
|
||||
// this shouldn't be used for most runtime errors - the resulting error would have an empty stacktrace
|
||||
let fromException = exn =>
|
||||
switch exn {
|
||||
| SqException(e) => e
|
||||
|
@ -164,3 +164,17 @@ let fromException = exn =>
|
|||
| Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage
|
||||
| _ => REOther("Unknown exception")->fromMessage
|
||||
}
|
||||
|
||||
// converts raw exceptions into exceptions with stacktrace attached
|
||||
// already converted exceptions won't be affected
|
||||
let contextualizeAndRethrow = (fn: unit => 'a, context: Reducer_T.context) => {
|
||||
try {
|
||||
fn()
|
||||
} catch {
|
||||
| SqException(e) => e->throw
|
||||
| Message.MessageException(e) => e->throwMessage(context)
|
||||
| Js.Exn.Error(obj) =>
|
||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessage(context)
|
||||
| _ => REOther("Unknown exception")->throwMessage(context)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
let throwMessage = SqError.Message.toException
|
||||
|
||||
let stdLib: Reducer_T.namespace = {
|
||||
// constants
|
||||
let res =
|
||||
|
@ -10,22 +8,23 @@ let stdLib: Reducer_T.namespace = {
|
|||
// array and record lookups
|
||||
let res = res->Reducer_Namespace.set(
|
||||
"$_atIndex_$",
|
||||
Reducer_Expression_Lambda.makeFFILambda((inputs, _, _) => {
|
||||
Reducer_Lambda.makeFFILambda("$_atIndex_$", (inputs, _, _) => {
|
||||
switch inputs {
|
||||
| [IEvArray(aValueArray), IEvNumber(fIndex)] => {
|
||||
let index = Belt.Int.fromFloat(fIndex) // TODO - fail on non-integer indices?
|
||||
|
||||
switch Belt.Array.get(aValueArray, index) {
|
||||
| Some(value) => value
|
||||
| None => REArrayIndexNotFound("Array index not found", index)->throwMessage
|
||||
| None => REArrayIndexNotFound("Array index not found", index)->SqError.Message.throw
|
||||
}
|
||||
}
|
||||
| [IEvRecord(dict), IEvString(sIndex)] =>
|
||||
switch Belt.Map.String.get(dict, sIndex) {
|
||||
| Some(value) => value
|
||||
| None => RERecordPropertyNotFound("Record property not found", sIndex)->throwMessage
|
||||
| None =>
|
||||
RERecordPropertyNotFound("Record property not found", sIndex)->SqError.Message.throw
|
||||
}
|
||||
| _ => REOther("Trying to access key on wrong value")->throwMessage
|
||||
| _ => REOther("Trying to access key on wrong value")->SqError.Message.throw
|
||||
}
|
||||
})->Reducer_T.IEvLambda,
|
||||
)
|
||||
|
@ -45,10 +44,10 @@ let stdLib: Reducer_T.namespace = {
|
|||
->Belt.Array.reduce(res, (cur, name) => {
|
||||
cur->Reducer_Namespace.set(
|
||||
name,
|
||||
Reducer_Expression_Lambda.makeFFILambda((arguments, environment, reducer) => {
|
||||
switch FunctionRegistry_Library.call(name, arguments, environment, reducer) {
|
||||
Reducer_Lambda.makeFFILambda(name, (arguments, context, reducer) => {
|
||||
switch FunctionRegistry_Library.call(name, arguments, context, reducer) {
|
||||
| Ok(value) => value
|
||||
| Error(error) => error->SqError.Message.toException
|
||||
| Error(error) => error->SqError.Message.throw
|
||||
}
|
||||
})->Reducer_T.IEvLambda,
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user