diff --git a/engine/builtin_test.go b/engine/builtin_test.go index 6546f49..d982ac4 100644 --- a/engine/builtin_test.go +++ b/engine/builtin_test.go @@ -33,6 +33,9 @@ func TestCall(t *testing.T) { panic("told you") }) }) + vm.Register0(NewAtom("do_not_call_wrapped"), func(*VM, Cont, *Env) *Promise { + panic(errors.New("told you")) + }) assert.NoError(t, vm.Compile(context.Background(), ` foo. foo(_, _). @@ -60,8 +63,9 @@ f(g([a, [b, c|X]])). {title: `cover all`, goal: atomComma.Apply(atomCut, NewAtom("f").Apply(NewAtom("g").Apply(List(NewAtom("a"), PartialList(NewVariable(), NewAtom("b"), NewAtom("c")))))), ok: true}, {title: `out of memory`, goal: NewAtom("foo").Apply(NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable(), NewVariable()), err: resourceError(resourceMemory, nil), mem: 1}, - {title: `panic`, goal: NewAtom("do_not_call"), err: errors.New("panic: told you")}, - {title: `panic (lazy)`, goal: NewAtom("lazy_do_not_call"), err: errors.New("panic: told you")}, + {title: `panic`, goal: NewAtom("do_not_call"), err: PanicError{errors.New("told you")}}, + {title: `panic (lazy)`, goal: NewAtom("lazy_do_not_call"), err: PanicError{errors.New("told you")}}, + {title: `panic (wrapped)`, goal: NewAtom("do_not_call_wrapped"), err: PanicError{errors.New("told you")}}, } for _, tt := range tests { diff --git a/engine/promise.go b/engine/promise.go index 0b043c0..f3a94b5 100644 --- a/engine/promise.go +++ b/engine/promise.go @@ -129,7 +129,12 @@ func ensurePromise(p **Promise) { } func panicError(r interface{}) error { - return fmt.Errorf("panic: %v", r) + switch r := r.(type) { + case error: + return PanicError{r} + default: + return PanicError{fmt.Errorf("%v", r)} + } } type promiseStack []*Promise @@ -164,3 +169,12 @@ func (s *promiseStack) recover(err error) error { // went through all the ancestor promises and still got the unhandled error. return err } + +// PanicError is an error thrown once panic occurs during the execution of a promise. +type PanicError struct { + OriginErr error +} + +func (p PanicError) Error() string { + return fmt.Sprintf("panic: %v", p.OriginErr) +} diff --git a/engine/variable.go b/engine/variable.go index 89142a7..4154005 100644 --- a/engine/variable.go +++ b/engine/variable.go @@ -7,7 +7,7 @@ import ( "sync" ) -var errMaxVariables = errors.New("maximum number of variables reached") +var ErrMaxVariables = errors.New("maximum number of variables reached") var maxVariables uint64 var varCounter = struct { @@ -31,7 +31,7 @@ func NewVariable() Variable { defer varCounter.Unlock() varCounter.Lock() if maxVariables != 0 && varCounter.count >= maxVariables { - panic(errMaxVariables) + panic(ErrMaxVariables) } varCounter.count++ return Variable(varCounter.count)