framestack reimplemented
This commit is contained in:
parent
a764f3075c
commit
26dbd29ec8
|
@ -1,4 +1,4 @@
|
||||||
import { SqError, SqLocation } from "@quri/squiggle-lang";
|
import { SqError, SqFrame } from "@quri/squiggle-lang";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ErrorAlert } from "./Alert";
|
import { ErrorAlert } from "./Alert";
|
||||||
|
|
||||||
|
@ -6,24 +6,26 @@ type Props = {
|
||||||
error: SqError;
|
error: SqError;
|
||||||
};
|
};
|
||||||
|
|
||||||
const StackTraceLocation: React.FC<{ location: SqLocation }> = ({
|
const StackTraceFrame: React.FC<{ frame: SqFrame }> = ({ frame }) => {
|
||||||
location,
|
const location = frame.location();
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
Line {location.start.line}, column {location.start.column}
|
{frame.name()}
|
||||||
|
{location
|
||||||
|
? ` at line ${location.start.line}, column ${location.start.column}`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const StackTrace: React.FC<Props> = ({ error }) => {
|
const StackTrace: React.FC<Props> = ({ error }) => {
|
||||||
const locations = error.toLocationArray();
|
const frames = error.getFrameArray();
|
||||||
return locations.length ? (
|
return frames.length ? (
|
||||||
<div>
|
<div>
|
||||||
<div>Traceback:</div>
|
<div>Traceback:</div>
|
||||||
<div className="ml-4">
|
<div className="ml-4">
|
||||||
{locations.map((location, i) => (
|
{frames.map((frame, i) => (
|
||||||
<StackTraceLocation location={location} key={i} />
|
<StackTraceFrame frame={frame} key={i} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -45,7 +45,7 @@ export function getValueToRender({ result, bindings }: ResultAndBindings) {
|
||||||
|
|
||||||
export function getErrorLocations(result: ResultAndBindings["result"]) {
|
export function getErrorLocations(result: ResultAndBindings["result"]) {
|
||||||
if (result.tag === "Error") {
|
if (result.tag === "Error") {
|
||||||
const location = result.value.toLocation();
|
const location = result.value.location();
|
||||||
return location ? [location] : [];
|
return location ? [location] : [];
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import * as RSError from "../rescript/SqError.gen";
|
import * as RSError from "../rescript/SqError.gen";
|
||||||
|
import * as RSReducerT from "../rescript/Reducer/Reducer_T.gen";
|
||||||
|
|
||||||
import * as RSCallStack from "../rescript/Reducer/Reducer_CallStack.gen";
|
import * as RSFrameStack from "../rescript/Reducer/Reducer_FrameStack.gen";
|
||||||
|
|
||||||
export type SqFrame = RSCallStack.frame;
|
export { location as SqLocation } from "../rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.gen";
|
||||||
|
|
||||||
export class SqError {
|
export class SqError {
|
||||||
constructor(private _value: RSError.t) {}
|
constructor(private _value: RSError.t) {}
|
||||||
|
@ -19,17 +20,31 @@ export class SqError {
|
||||||
return new SqError(RSError.createOtherError(v));
|
return new SqError(RSError.createOtherError(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
getTopFrame(): SqCallFrame | undefined {
|
getTopFrame(): SqFrame | undefined {
|
||||||
const frame = RSCallStack.getTopFrame(RSError.getStackTrace(this._value));
|
const frame = RSFrameStack.getTopFrame(RSError.getFrameStack(this._value));
|
||||||
return frame ? new SqCallFrame(frame) : undefined;
|
return frame ? new SqFrame(frame) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFrameArray(): SqCallFrame[] {
|
getFrameArray(): SqFrame[] {
|
||||||
const frames = RSError.getFrameArray(this._value);
|
const frames = RSError.getFrameArray(this._value);
|
||||||
return frames.map((frame) => new SqCallFrame(frame));
|
return frames.map((frame) => new SqFrame(frame));
|
||||||
|
}
|
||||||
|
|
||||||
|
location() {
|
||||||
|
return this.getTopFrame()?.location();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SqCallFrame {
|
export class SqFrame {
|
||||||
constructor(private _value: SqFrame) {}
|
constructor(private _value: RSReducerT.frame) {}
|
||||||
|
|
||||||
|
name(): string {
|
||||||
|
return RSFrameStack.Frame.toName(this._value);
|
||||||
|
}
|
||||||
|
|
||||||
|
location() {
|
||||||
|
console.log(RSFrameStack);
|
||||||
|
console.log(RSFrameStack.Frame);
|
||||||
|
return RSFrameStack.Frame.toLocation(this._value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { isParenthesisNode } from "mathjs";
|
|
||||||
import { SqProject } from "./SqProject";
|
import { SqProject } from "./SqProject";
|
||||||
|
|
||||||
type PathItem = string | number;
|
type PathItem = string | number;
|
||||||
|
|
|
@ -13,7 +13,7 @@ export {
|
||||||
environment,
|
environment,
|
||||||
defaultEnvironment,
|
defaultEnvironment,
|
||||||
} from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
|
} from "../rescript/ForTS/ForTS_Distribution/ForTS_Distribution.gen";
|
||||||
export { SqError } from "./SqError";
|
export { SqError, SqFrame, SqLocation } from "./SqError";
|
||||||
export { SqShape } from "./SqPointSetDist";
|
export { SqShape } from "./SqPointSetDist";
|
||||||
|
|
||||||
export { resultMap } from "./types";
|
export { resultMap } from "./types";
|
||||||
|
|
|
@ -4,8 +4,13 @@ let defaultEnvironment: Reducer_T.environment = DistributionOperation.defaultEnv
|
||||||
|
|
||||||
let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => {
|
let createContext = (stdLib: Reducer_Namespace.t, environment: Reducer_T.environment): t => {
|
||||||
{
|
{
|
||||||
callStack: list{},
|
frameStack: list{},
|
||||||
bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend,
|
bindings: stdLib->Reducer_Bindings.fromNamespace->Reducer_Bindings.extend,
|
||||||
environment: environment,
|
environment: environment,
|
||||||
|
inFunction: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentFunctionName = (t: t): string => {
|
||||||
|
t.inFunction->E.O2.fmap(Reducer_Lambda_T.name)->E.O2.default("<top>")
|
||||||
|
}
|
||||||
|
|
|
@ -2,14 +2,16 @@ module Bindings = Reducer_Bindings
|
||||||
module Result = Belt.Result
|
module Result = Belt.Result
|
||||||
module T = Reducer_T
|
module T = Reducer_T
|
||||||
|
|
||||||
let toLocation = (expression: T.expression): SqError.location => {
|
let toLocation = (expression: T.expression): Reducer_Peggy_Parse.location => {
|
||||||
expression.ast.location
|
expression.ast.location
|
||||||
}
|
}
|
||||||
|
|
||||||
let throwFrom = (error: SqError.Message.t, expression: T.expression, context: T.context) =>
|
let throwFrom = (error: SqError.Message.t, expression: T.expression, context: T.context) =>
|
||||||
error->SqError.throwMessage(
|
error->SqError.throwMessageWithFrameStack(
|
||||||
context.callStack,
|
context.frameStack->Reducer_FrameStack.extend(
|
||||||
location: Some(expression->toLocation)
|
context->Reducer_Context.currentFunctionName,
|
||||||
|
Some(expression->toLocation),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -93,9 +95,9 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
| T.ELambda(parameters, body) => (
|
| T.ELambda(parameters, body, name) => (
|
||||||
Reducer_Lambda.makeLambda(
|
Reducer_Lambda.makeLambda(
|
||||||
None, // TODO - pass function name from parser
|
name,
|
||||||
parameters,
|
parameters,
|
||||||
context.bindings,
|
context.bindings,
|
||||||
body,
|
body,
|
||||||
|
@ -112,7 +114,13 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
||||||
})
|
})
|
||||||
switch lambda {
|
switch lambda {
|
||||||
| T.IEvLambda(lambda) => {
|
| T.IEvLambda(lambda) => {
|
||||||
let result = Reducer_Lambda.doLambdaCall(lambda, argValues, context, evaluate)
|
let result = Reducer_Lambda.doLambdaCallFrom(
|
||||||
|
lambda,
|
||||||
|
argValues,
|
||||||
|
context,
|
||||||
|
evaluate,
|
||||||
|
Some(expression->toLocation), // we have to pass the location of a current expression here, to put it on frameStack
|
||||||
|
)
|
||||||
(result, context)
|
(result, context)
|
||||||
}
|
}
|
||||||
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression, context)
|
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression, context)
|
||||||
|
|
|
@ -9,10 +9,11 @@ let eBool = aBool => aBool->T.IEvBool->T.EValue
|
||||||
|
|
||||||
let eCall = (fn: expression, args: array<expression>): expressionContent => T.ECall(fn, args)
|
let eCall = (fn: expression, args: array<expression>): expressionContent => T.ECall(fn, args)
|
||||||
|
|
||||||
let eLambda = (parameters: array<string>, expr: expression): expressionContent => T.ELambda(
|
let eLambda = (
|
||||||
parameters,
|
parameters: array<string>,
|
||||||
expr,
|
expr: expression,
|
||||||
)
|
name: option<string>,
|
||||||
|
): expressionContent => T.ELambda(parameters, expr, name)
|
||||||
|
|
||||||
let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue
|
let eNumber = aNumber => aNumber->T.IEvNumber->T.EValue
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ let rec toString = (expression: t) =>
|
||||||
`${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})`
|
`${predicate->toString} ? (${trueCase->toString}) : (${falseCase->toString})`
|
||||||
| EAssign(name, value) => `${name} = ${value->toString}`
|
| EAssign(name, value) => `${name} = ${value->toString}`
|
||||||
| ECall(fn, args) => `(${fn->toString})(${args->Js.Array2.map(toString)->commaJoin})`
|
| ECall(fn, args) => `(${fn->toString})(${args->Js.Array2.map(toString)->commaJoin})`
|
||||||
| ELambda(parameters, body) => `{|${parameters->commaJoin}| ${body->toString}}`
|
| ELambda(parameters, body, _) => `{|${parameters->commaJoin}| ${body->toString}}`
|
||||||
| EValue(aValue) => Reducer_Value.toString(aValue)
|
| EValue(aValue) => Reducer_Value.toString(aValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,43 @@
|
||||||
type t = Reducer_T.frameStack
|
type t = Reducer_T.frameStack
|
||||||
|
|
||||||
module Frame = {
|
module Frame = {
|
||||||
let toString = ({lambda, location}: Reducer_T.frame) =>
|
let toString = ({name, location}: Reducer_T.frame) =>
|
||||||
`${fromFrame} at ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}`
|
name ++
|
||||||
|
switch location {
|
||||||
|
| Some(location) =>
|
||||||
|
` at line ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}`
|
||||||
|
| None => ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let toLocation = (t: Reducer_T.frame): option<Reducer_Peggy_Parse.location> => t.location
|
||||||
|
|
||||||
|
@genType
|
||||||
|
let toName = (t: Reducer_T.frame): string => t.name
|
||||||
}
|
}
|
||||||
|
|
||||||
let make = (): t => list{}
|
let make = (): t => list{}
|
||||||
|
|
||||||
let topFrameName = (t: t) =>
|
@genType
|
||||||
switch t->getTopFrame {
|
let getTopFrame = (t: t): option<Reducer_T.frame> => Belt.List.head(t)
|
||||||
| Some({lambda}) =>
|
|
||||||
switch lambda {
|
|
||||||
| FnLambda({name}) => name
|
|
||||||
| FnBuiltin({name}) => name
|
|
||||||
}
|
|
||||||
| None => "<main>"
|
|
||||||
}
|
|
||||||
|
|
||||||
let extend = (t: t, lambda: Reducer_T.lambdaValue, location: option<location>) =>
|
let extend = (t: t, name: string, location: option<Reducer_Peggy_Parse.location>) =>
|
||||||
t->Belt.List.add({
|
t->Belt.List.add({
|
||||||
lambda: lambda,
|
name: name,
|
||||||
fromLocation: location,
|
location: location,
|
||||||
fromFrame: t->topFrameName,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
let toString = (t: t) =>
|
let toString = (t: t) =>
|
||||||
t->Belt.List.map(s => " " ++ s->toStringFrame ++ "\n")->Belt.List.toArray->Js.Array2.joinWith("")
|
t
|
||||||
|
->Belt.List.map(s => " " ++ s->Frame.toString ++ "\n")
|
||||||
|
->Belt.List.toArray
|
||||||
|
->Js.Array2.joinWith("")
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let toFrameArray = (t: t): array<frame> => t->Belt.List.toArray
|
let toFrameArray = (t: t): array<Reducer_T.frame> => t->Belt.List.toArray
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let getTopFrame = (t: t): option<frame> => t->Belt.List.head
|
let getTopFrame = (t: t): option<Reducer_T.frame> => t->Belt.List.head
|
||||||
|
|
||||||
let isEmpty = (t: t): bool =>
|
let isEmpty = (t: t): bool =>
|
||||||
switch t->Belt.List.head {
|
switch t->Belt.List.head {
|
||||||
|
|
|
@ -33,7 +33,8 @@ let makeLambda = (
|
||||||
let lambdaContext: Reducer_T.context = {
|
let lambdaContext: Reducer_T.context = {
|
||||||
bindings: localBindingsWithParameters, // based on bindings at the moment of lambda creation
|
bindings: localBindingsWithParameters, // based on bindings at the moment of lambda creation
|
||||||
environment: context.environment, // environment at the moment when lambda is called
|
environment: context.environment, // environment at the moment when lambda is called
|
||||||
callStack: context.callStack, // extended by main `evaluate` function
|
frameStack: context.frameStack, // already extended in `doLambdaCall`
|
||||||
|
inFunction: context.inFunction, // already updated in `doLambdaCall`
|
||||||
}
|
}
|
||||||
|
|
||||||
let (value, _) = reducer(body, lambdaContext)
|
let (value, _) = reducer(body, lambdaContext)
|
||||||
|
@ -49,7 +50,7 @@ let makeLambda = (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// stdlib lambdas (everything in FunctionRegistry) is built by this method. Body is generated in SquiggleLibrary_StdLib.res
|
// stdlib functions (everything in FunctionRegistry) are built by this method. Body is generated in SquiggleLibrary_StdLib.res
|
||||||
let makeFFILambda = (name: string, body: Reducer_T.lambdaBody): t => FnBuiltin({
|
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).
|
// 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.
|
// But FunctionRegistry API is too limited for that to matter. Please take care not to violate that in the future by accident.
|
||||||
|
@ -65,10 +66,20 @@ let parameters = (t: t): array<string> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
|
let doLambdaCallFrom = (
|
||||||
|
t: t,
|
||||||
|
args: array<Reducer_T.value>,
|
||||||
|
context: Reducer_T.context,
|
||||||
|
reducer,
|
||||||
|
location: option<Reducer_Peggy_Parse.location>,
|
||||||
|
) => {
|
||||||
let newContext = {
|
let newContext = {
|
||||||
...context,
|
...context,
|
||||||
callStack: t->Reducer_CallStack.extend(t),
|
frameStack: context.frameStack->Reducer_FrameStack.extend(
|
||||||
|
context->Reducer_Context.currentFunctionName,
|
||||||
|
location,
|
||||||
|
),
|
||||||
|
inFunction: Some(t),
|
||||||
}
|
}
|
||||||
|
|
||||||
SqError.rethrowWithStacktrace(() => {
|
SqError.rethrowWithStacktrace(() => {
|
||||||
|
@ -76,5 +87,9 @@ let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
|
||||||
| FnLambda({body}) => body(args, newContext, reducer)
|
| FnLambda({body}) => body(args, newContext, reducer)
|
||||||
| FnBuiltin({body}) => body(args, newContext, reducer)
|
| FnBuiltin({body}) => body(args, newContext, reducer)
|
||||||
}
|
}
|
||||||
}, newContext.callStack)
|
}, newContext.frameStack)
|
||||||
|
}
|
||||||
|
|
||||||
|
let doLambdaCall = (t: t, args, context, reducer) => {
|
||||||
|
doLambdaCallFrom(t, args, context, reducer, None)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
type t = Reducer_T.lambdaValue
|
||||||
|
|
||||||
|
let name = (t: t): string => {
|
||||||
|
switch t {
|
||||||
|
| FnLambda({name}) => name->E.O2.default("<anonymous>")
|
||||||
|
| FnBuiltin({name}) => name
|
||||||
|
}
|
||||||
|
}
|
|
@ -50,7 +50,7 @@ letStatement
|
||||||
|
|
||||||
defunStatement
|
defunStatement
|
||||||
= variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression
|
= variable:variable '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression
|
||||||
{ var value = h.nodeLambda(args, body, location())
|
{ var value = h.nodeLambda(args, body, location(), variable)
|
||||||
return h.nodeLetStatement(variable, value, location()) }
|
return h.nodeLetStatement(variable, value, location()) }
|
||||||
|
|
||||||
assignmentOp "assignment" = '='
|
assignmentOp "assignment" = '='
|
||||||
|
@ -261,9 +261,9 @@ valueConstructor
|
||||||
lambda
|
lambda
|
||||||
= '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
|
= '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}'
|
||||||
{ statements.push(finalExpression)
|
{ statements.push(finalExpression)
|
||||||
return h.nodeLambda(args, h.nodeBlock(statements, location()), location()) }
|
return h.nodeLambda(args, h.nodeBlock(statements, location()), location(), undefined) }
|
||||||
/ '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}'
|
/ '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}'
|
||||||
{ return h.nodeLambda(args, finalExpression, location()) }
|
{ return h.nodeLambda(args, finalExpression, location(), undefined) }
|
||||||
|
|
||||||
arrayConstructor 'array'
|
arrayConstructor 'array'
|
||||||
= '[' _nl ']'
|
= '[' _nl ']'
|
||||||
|
|
|
@ -44,7 +44,7 @@ type nodeIdentifier = {...node, "value": string}
|
||||||
type nodeInteger = {...node, "value": int}
|
type nodeInteger = {...node, "value": int}
|
||||||
type nodeKeyValue = {...node, "key": node, "value": node}
|
type nodeKeyValue = {...node, "key": node, "value": node}
|
||||||
type nodeRecord = {...node, "elements": array<nodeKeyValue>}
|
type nodeRecord = {...node, "elements": array<nodeKeyValue>}
|
||||||
type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": node}
|
type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": node, "name": option<string>}
|
||||||
type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
|
type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
|
||||||
type nodeModuleIdentifier = {...node, "value": string}
|
type nodeModuleIdentifier = {...node, "value": string}
|
||||||
type nodeString = {...node, "value": string}
|
type nodeString = {...node, "value": string}
|
||||||
|
|
|
@ -20,7 +20,7 @@ let rec fromNode = (node: Parse.node): expression => {
|
||||||
nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"])
|
nodeLambda["args"]->Js.Array2.map((argNode: Parse.nodeIdentifier) => argNode["value"])
|
||||||
let body = nodeLambda["body"]->fromNode
|
let body = nodeLambda["body"]->fromNode
|
||||||
|
|
||||||
ExpressionBuilder.eLambda(args, body)
|
ExpressionBuilder.eLambda(args, body, nodeLambda["name"])
|
||||||
}
|
}
|
||||||
|
|
||||||
let caseRecord = (nodeRecord): expressionContent => {
|
let caseRecord = (nodeRecord): expressionContent => {
|
||||||
|
|
|
@ -89,6 +89,7 @@ type NodeLambda = Node & {
|
||||||
type: "Lambda";
|
type: "Lambda";
|
||||||
args: AnyPeggyNode[];
|
args: AnyPeggyNode[];
|
||||||
body: AnyPeggyNode;
|
body: AnyPeggyNode;
|
||||||
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type NodeTernary = Node & {
|
type NodeTernary = Node & {
|
||||||
|
@ -217,9 +218,10 @@ export function nodeKeyValue(
|
||||||
export function nodeLambda(
|
export function nodeLambda(
|
||||||
args: AnyPeggyNode[],
|
args: AnyPeggyNode[],
|
||||||
body: AnyPeggyNode,
|
body: AnyPeggyNode,
|
||||||
location: LocationRange
|
location: LocationRange,
|
||||||
|
name?: NodeIdentifier
|
||||||
): NodeLambda {
|
): NodeLambda {
|
||||||
return { type: "Lambda", args, body, location };
|
return { type: "Lambda", args, body, location, name: name?.value };
|
||||||
}
|
}
|
||||||
export function nodeLetStatement(
|
export function nodeLetStatement(
|
||||||
variable: NodeIdentifier,
|
variable: NodeIdentifier,
|
||||||
|
|
|
@ -36,7 +36,7 @@ and expressionContent =
|
||||||
| ETernary(expression, expression, expression)
|
| ETernary(expression, expression, expression)
|
||||||
| EAssign(string, expression)
|
| EAssign(string, expression)
|
||||||
| ECall(expression, array<expression>)
|
| ECall(expression, array<expression>)
|
||||||
| ELambda(array<string>, expression)
|
| ELambda(array<string>, expression, option<string>)
|
||||||
| EValue(value)
|
| EValue(value)
|
||||||
|
|
||||||
and expression = {
|
and expression = {
|
||||||
|
@ -50,18 +50,18 @@ and bindings = {
|
||||||
parent: option<bindings>,
|
parent: option<bindings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@genType.opaque
|
||||||
and frame = {
|
and frame = {
|
||||||
name: string,
|
name: string,
|
||||||
location: option<Reducer_Peggy_Parse.location>, // can be empty for calls from builtin functions
|
location: option<Reducer_Peggy_Parse.location>, // can be empty for calls from builtin functions
|
||||||
}
|
}
|
||||||
|
@genType.opaque and frameStack = list<frame>
|
||||||
and frameStack = list<frame>
|
|
||||||
|
|
||||||
and context = {
|
and context = {
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
environment: environment,
|
environment: environment,
|
||||||
inFunction: option<string>, // used to build the next frame in frameStack
|
frameStack: frameStack,
|
||||||
callStack: frameStack,
|
inFunction: option<lambdaValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
and reducerFn = (expression, context) => (value, context)
|
and reducerFn = (expression, context) => (value, context)
|
||||||
|
|
|
@ -95,19 +95,19 @@ type t = {
|
||||||
|
|
||||||
exception SqException(t)
|
exception SqException(t)
|
||||||
|
|
||||||
// `context` should be specified for runtime errors, but can be left empty for errors from Reducer_Project and so on.
|
let fromMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t): t => {
|
||||||
// `location` can be empty for errors raised from FunctionRegistry.
|
|
||||||
let fromMessage = (message: Message.t, frameStack: Reducer_FrameStack.t): t => {
|
|
||||||
message: message,
|
message: message,
|
||||||
stackTrace: stackTrace,
|
frameStack: frameStack,
|
||||||
}
|
}
|
||||||
|
|
||||||
let fromMessageWithoutFrameStack = (message: Message.t) =>
|
// this shouldn't be used much, since frame stack will be empty
|
||||||
fromMessage(message, Reducer_FrameStack.make())
|
// but it's useful for global errors, e.g. in ReducerProject or somethere in the frontend
|
||||||
|
@genType
|
||||||
|
let fromMessage = (message: Message.t) =>
|
||||||
|
fromMessageWithFrameStack(message, Reducer_FrameStack.make())
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let getTopFrame = (t: t): option<Reducer_CallStack.frame> =>
|
let getTopFrame = (t: t): option<Reducer_T.frame> => t.frameStack->Reducer_FrameStack.getTopFrame
|
||||||
t.stackTrace->Reducer_FrameStack.getTopFrame
|
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack
|
let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack
|
||||||
|
@ -116,42 +116,42 @@ let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack
|
||||||
let toString = (t: t): string => t.message->Message.toString
|
let toString = (t: t): string => t.message->Message.toString
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let createOtherError = (v: string): t => Message.REOther(v)->fromMessageWithoutFrameStack
|
let createOtherError = (v: string): t => Message.REOther(v)->fromMessage
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let getFrameArray = (t: t): array<Reducer_CallStack.frame> =>
|
let getFrameArray = (t: t): array<Reducer_T.frame> => t.frameStack->Reducer_FrameStack.toFrameArray
|
||||||
t.stackTrace->Reducer_CallStack.toFrameArray
|
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let toStringWithStacktrace = (t: t) =>
|
let toStringWithStackTrace = (t: t) =>
|
||||||
t->toString ++ if t.frameStack->Reducer_FrameStack.isEmpty {
|
t->toString ++ if t.frameStack->Reducer_FrameStack.isEmpty {
|
||||||
"Traceback:\n" ++ t.frameStack->Reducer_FrameStack.toString
|
"\nTraceback:\n" ++ t.frameStack->Reducer_FrameStack.toString
|
||||||
|
} else {
|
||||||
|
""
|
||||||
}
|
}
|
||||||
let throw = (t: t) => t->SqException->raise
|
let throw = (t: t) => t->SqException->raise
|
||||||
|
|
||||||
let throwMessage = (message: Message.t, frameStack: Reducer_FrameStack.t) =>
|
let throwMessageWithFrameStack = (message: Message.t, frameStack: Reducer_FrameStack.t) =>
|
||||||
fromMessage(message, frameStack)->throw
|
fromMessageWithFrameStack(message, frameStack)->throw
|
||||||
|
|
||||||
// this shouldn't be used for most runtime errors - the resulting error would have an empty stacktrace
|
// this shouldn't be used for most runtime errors - the resulting error would have an empty stacktrace
|
||||||
let fromException = exn =>
|
let fromException = exn =>
|
||||||
switch exn {
|
switch exn {
|
||||||
| SqException(e) => e
|
| SqException(e) => e
|
||||||
| Message.MessageException(e) => e->fromMessage(Reducer_CallStack.make())
|
| Message.MessageException(e) => e->fromMessage
|
||||||
| Js.Exn.Error(obj) =>
|
| Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage
|
||||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessageWithoutStacktrace
|
| _ => REOther("Unknown exception")->fromMessage
|
||||||
| _ => REOther("Unknown exception")->fromMessageWithoutStacktrace
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// converts raw exceptions into exceptions with stacktrace attached
|
// converts raw exceptions into exceptions with stacktrace attached
|
||||||
// already converted exceptions won't be affected
|
// already converted exceptions won't be affected
|
||||||
let rethrowWithStacktrace = (fn: unit => 'a, stackTrace: Reducer_CallStack.t) => {
|
let rethrowWithStacktrace = (fn: unit => 'a, frameStack: Reducer_FrameStack.t) => {
|
||||||
try {
|
try {
|
||||||
fn()
|
fn()
|
||||||
} catch {
|
} catch {
|
||||||
| SqException(e) => e->throw // exception already has a stacktrace
|
| SqException(e) => e->throw // exception already has a stacktrace
|
||||||
| Message.MessageException(e) => e->throwMessage(stackTrace) // probably comes from FunctionRegistry, adding stacktrace
|
| Message.MessageException(e) => e->throwMessageWithFrameStack(frameStack) // probably comes from FunctionRegistry, adding stacktrace
|
||||||
| Js.Exn.Error(obj) =>
|
| Js.Exn.Error(obj) =>
|
||||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessage(stackTrace)
|
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessageWithFrameStack(frameStack)
|
||||||
| _ => REOther("Unknown exception")->throwMessage(stackTrace)
|
| _ => REOther("Unknown exception")->throwMessageWithFrameStack(frameStack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user