45 lines
1.8 KiB
Markdown
45 lines
1.8 KiB
Markdown
X-Date: 2024-09-15T03:18:50Z
|
|
X-Note-Id: f53b7c27-df57-4ede-8950-2b55cbb8031f
|
|
Subject: Backtraces in Valeri
|
|
X-Slug: backtraces_in_valeri
|
|
|
|
At the time of writing this post, I've finished implementing the first version of
|
|
backtraces in [Valeri](https://git.knazarov.com/knazarov/valeri). In case of
|
|
runtime errors, you'll now get the call stack printed to stdout, pretty much
|
|
as you would expect in other programming languages. Let's look at an example:
|
|
|
|
```
|
|
(fn get-foo (obj)
|
|
(get obj :foo)
|
|
)
|
|
|
|
;; This will fail becaue :foo is not present
|
|
;; in the dictionary
|
|
(get-foo {:bar 42})
|
|
```
|
|
|
|
When executed, this code will result in:
|
|
|
|
```
|
|
Error KeyError at /home/knazarov/dev/lisp/src/common.cpp:735
|
|
Function get
|
|
Function get-foo
|
|
```
|
|
|
|
The backtrace should look familiar, with the only possible problem being lack
|
|
of source location information except in the last frame. This is because I don't
|
|
yet produce debug information with line number / filename mappings.
|
|
|
|
What may not be immediately obvious is that `get` is a "native" function that
|
|
is implemented in C++. The reason it appears in the backtrace at all is due
|
|
to how native functions are called. This is not just an FFI call, but such functions
|
|
actually use the normal VM stack, as regular byte-compiled functions do.
|
|
|
|
This means that if I implement something like `map` or `fold` in C++ and put it into
|
|
the standard library, debugging code that uses them is going to be quite easy, even
|
|
if the C++ routines are nested in the middle of the callstack.
|
|
|
|
Overall, backtraces and the transition to tree-structured stack were the hardest
|
|
parts of the language so far. Not in terms of the implementation, but just design-wise.
|
|
The new stack structure is going to be used in lots of places starting from exceptions,
|
|
and ending with the coroutines, effect system and the debugger.
|