WIP (broken)
This commit is contained in:
parent
184584c9f3
commit
a764f3075c
|
@ -1,36 +0,0 @@
|
||||||
@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
|
|
||||||
}
|
|
|
@ -6,8 +6,11 @@ let toLocation = (expression: T.expression): SqError.location => {
|
||||||
expression.ast.location
|
expression.ast.location
|
||||||
}
|
}
|
||||||
|
|
||||||
let throwFrom = (error: SqError.Message.t, context: T.context) =>
|
let throwFrom = (error: SqError.Message.t, expression: T.expression, context: T.context) =>
|
||||||
error->SqError.throwMessage(context)
|
error->SqError.throwMessage(
|
||||||
|
context.callStack,
|
||||||
|
location: Some(expression->toLocation)
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Recursively evaluate the expression
|
Recursively evaluate the expression
|
||||||
|
@ -53,7 +56,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
||||||
let (key, _) = eKey->evaluate(context)
|
let (key, _) = eKey->evaluate(context)
|
||||||
let keyString = switch key {
|
let keyString = switch key {
|
||||||
| IEvString(s) => s
|
| IEvString(s) => s
|
||||||
| _ => REOther("Record keys must be strings")->throwFrom(context)
|
| _ => REOther("Record keys must be strings")->throwFrom(expression, context)
|
||||||
}
|
}
|
||||||
let (value, _) = eValue->evaluate(context)
|
let (value, _) = eValue->evaluate(context)
|
||||||
(keyString, value)
|
(keyString, value)
|
||||||
|
@ -77,7 +80,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
||||||
| T.ESymbol(name) =>
|
| T.ESymbol(name) =>
|
||||||
switch context.bindings->Bindings.get(name) {
|
switch context.bindings->Bindings.get(name) {
|
||||||
| Some(v) => (v, context)
|
| Some(v) => (v, context)
|
||||||
| None => RESymbolNotFound(name)->throwFrom(context)
|
| None => RESymbolNotFound(name)->throwFrom(expression, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
| T.EValue(value) => (value, context)
|
| T.EValue(value) => (value, context)
|
||||||
|
@ -86,7 +89,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
||||||
let (predicateResult, _) = predicate->evaluate(context)
|
let (predicateResult, _) = predicate->evaluate(context)
|
||||||
switch predicateResult {
|
switch predicateResult {
|
||||||
| T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context)
|
| T.IEvBool(value) => (value ? trueCase : falseCase)->evaluate(context)
|
||||||
| _ => REExpectedType("Boolean", "")->throwFrom(context)
|
| _ => REExpectedType("Boolean", "")->throwFrom(expression, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +115,7 @@ let rec evaluate: T.reducerFn = (expression, context): (T.value, T.context) => {
|
||||||
let result = Reducer_Lambda.doLambdaCall(lambda, argValues, context, evaluate)
|
let result = Reducer_Lambda.doLambdaCall(lambda, argValues, context, evaluate)
|
||||||
(result, context)
|
(result, context)
|
||||||
}
|
}
|
||||||
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(context)
|
| _ => RENotAFunction(lambda->Reducer_Value.toString)->throwFrom(expression, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
type t = Reducer_T.frameStack
|
||||||
|
|
||||||
|
module Frame = {
|
||||||
|
let toString = ({lambda, location}: Reducer_T.frame) =>
|
||||||
|
`${fromFrame} at ${location.start.line->Js.Int.toString}, column ${location.start.column->Js.Int.toString}`
|
||||||
|
}
|
||||||
|
|
||||||
|
let make = (): t => list{}
|
||||||
|
|
||||||
|
let topFrameName = (t: t) =>
|
||||||
|
switch t->getTopFrame {
|
||||||
|
| Some({lambda}) =>
|
||||||
|
switch lambda {
|
||||||
|
| FnLambda({name}) => name
|
||||||
|
| FnBuiltin({name}) => name
|
||||||
|
}
|
||||||
|
| None => "<main>"
|
||||||
|
}
|
||||||
|
|
||||||
|
let extend = (t: t, lambda: Reducer_T.lambdaValue, location: option<location>) =>
|
||||||
|
t->Belt.List.add({
|
||||||
|
lambda: lambda,
|
||||||
|
fromLocation: location,
|
||||||
|
fromFrame: t->topFrameName,
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -57,14 +57,6 @@ let makeFFILambda = (name: string, body: Reducer_T.lambdaBody): t => FnBuiltin({
|
||||||
name: name,
|
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
|
// this function doesn't scale to FunctionRegistry's polymorphic functions
|
||||||
let parameters = (t: t): array<string> => {
|
let parameters = (t: t): array<string> => {
|
||||||
switch t {
|
switch t {
|
||||||
|
@ -76,13 +68,13 @@ let parameters = (t: t): array<string> => {
|
||||||
let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
|
let doLambdaCall = (t: t, args, context: Reducer_Context.t, reducer) => {
|
||||||
let newContext = {
|
let newContext = {
|
||||||
...context,
|
...context,
|
||||||
callStack: t->extendCallStack(context.callStack),
|
callStack: t->Reducer_CallStack.extend(t),
|
||||||
}
|
}
|
||||||
|
|
||||||
SqError.contextualizeAndRethrow(() => {
|
SqError.rethrowWithStacktrace(() => {
|
||||||
switch t {
|
switch t {
|
||||||
| 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)
|
}, newContext.callStack)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,18 @@ and bindings = {
|
||||||
parent: option<bindings>,
|
parent: option<bindings>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
and frame = {
|
||||||
|
name: string,
|
||||||
|
location: option<Reducer_Peggy_Parse.location>, // can be empty for calls from builtin functions
|
||||||
|
}
|
||||||
|
|
||||||
|
and frameStack = list<frame>
|
||||||
|
|
||||||
and context = {
|
and context = {
|
||||||
bindings: bindings,
|
bindings: bindings,
|
||||||
environment: environment,
|
environment: environment,
|
||||||
callStack: Reducer_CallStack.t,
|
inFunction: option<string>, // used to build the next frame in frameStack
|
||||||
|
callStack: frameStack,
|
||||||
}
|
}
|
||||||
|
|
||||||
and reducerFn = (expression, context) => (value, context)
|
and reducerFn = (expression, context) => (value, context)
|
||||||
|
|
|
@ -90,91 +90,68 @@ module Message = {
|
||||||
@genType.opaque
|
@genType.opaque
|
||||||
type t = {
|
type t = {
|
||||||
message: Message.t,
|
message: Message.t,
|
||||||
/*
|
frameStack: Reducer_FrameStack.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)
|
exception SqException(t)
|
||||||
|
|
||||||
// `context` should be specified for runtime errors, but can be left empty for errors from Reducer_Project and so on.
|
// `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.
|
// `location` can be empty for errors raised from FunctionRegistry.
|
||||||
let fromMessage = (
|
let fromMessage = (message: Message.t, frameStack: Reducer_FrameStack.t): t => {
|
||||||
message: Message.t,
|
|
||||||
context: option<Reducer_T.context>,
|
|
||||||
location: option<location>,
|
|
||||||
): t => {
|
|
||||||
message: message,
|
message: message,
|
||||||
location: location,
|
stackTrace: stackTrace,
|
||||||
stackTrace: switch context {
|
|
||||||
| Some(context) => context.callStack
|
|
||||||
| None => Reducer_CallStack.make()
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fromMessageWithoutFrameStack = (message: Message.t) =>
|
||||||
|
fromMessage(message, Reducer_FrameStack.make())
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let getTopFrame = (t: t): option<Reducer_CallStack.frame> =>
|
let getTopFrame = (t: t): option<Reducer_CallStack.frame> =>
|
||||||
t.stackTrace->Reducer_CallStack.getTopFrame
|
t.stackTrace->Reducer_FrameStack.getTopFrame
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let getStackTrace = (t: t): Reducer_CallStack.t => t.stackTrace
|
let getFrameStack = (t: t): Reducer_FrameStack.t => t.frameStack
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
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)->fromMessage()
|
let createOtherError = (v: string): t => Message.REOther(v)->fromMessageWithoutFrameStack
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let getFrameArray = (t: t): array<Reducer_CallStack.frame> =>
|
let getFrameArray = (t: t): array<Reducer_CallStack.frame> =>
|
||||||
t.stackTrace->Reducer_CallStack.toFrameArray
|
t.stackTrace->Reducer_CallStack.toFrameArray
|
||||||
|
|
||||||
@genType
|
@genType
|
||||||
let toStringWithStackTrace = (t: t) =>
|
let toStringWithStacktrace = (t: t) =>
|
||||||
if t.stackTrace->Reducer_CallStack.isEmpty {
|
t->toString ++ if t.frameStack->Reducer_FrameStack.isEmpty {
|
||||||
"Traceback:\n" ++ t.stackTrace->Reducer_CallStack.toString
|
"Traceback:\n" ++ t.frameStack->Reducer_FrameStack.toString
|
||||||
} else {
|
}
|
||||||
""
|
|
||||||
} ++
|
|
||||||
t->toString
|
|
||||||
|
|
||||||
let throw = (t: t) => t->SqException->raise
|
let throw = (t: t) => t->SqException->raise
|
||||||
|
|
||||||
let throwMessage = (message: Message.t, context: Reducer_T.context, location: location) =>
|
let throwMessage = (message: Message.t, frameStack: Reducer_FrameStack.t) =>
|
||||||
fromMessage(message, context, location)->throw
|
fromMessage(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
|
| Message.MessageException(e) => e->fromMessage(Reducer_CallStack.make())
|
||||||
| Js.Exn.Error(obj) => REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessage
|
| Js.Exn.Error(obj) =>
|
||||||
| _ => REOther("Unknown exception")->fromMessage
|
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->fromMessageWithoutStacktrace
|
||||||
|
| _ => 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 contextualizeAndRethrow = (fn: unit => 'a, context: Reducer_T.context) => {
|
let rethrowWithStacktrace = (fn: unit => 'a, stackTrace: Reducer_CallStack.t) => {
|
||||||
try {
|
try {
|
||||||
fn()
|
fn()
|
||||||
} catch {
|
} catch {
|
||||||
| SqException(e) => e->throw
|
| SqException(e) => e->throw // exception already has a stacktrace
|
||||||
| Message.MessageException(e) => e->throwMessage(context)
|
| Message.MessageException(e) => e->throwMessage(stackTrace) // probably comes from FunctionRegistry, adding stacktrace
|
||||||
| Js.Exn.Error(obj) =>
|
| Js.Exn.Error(obj) =>
|
||||||
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessage(context)
|
REJavaScriptExn(obj->Js.Exn.message, obj->Js.Exn.name)->throwMessage(stackTrace)
|
||||||
| _ => REOther("Unknown exception")->throwMessage(context)
|
| _ => REOther("Unknown exception")->throwMessage(stackTrace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user