Friday, May 24, 2013

Building a Lisp Interpreter from Scratch -- Part 9: Serialization

(This is Part 9 of a series of posts on pLisp)

(Note: This post is quite out of sync with the code base; please see Part 13)

Serialization in pLisp is an all-or-nothing thing, i.e. not at the level of individual objects, but at the image level. You can invoke the CREATE-IMAGE special form at any point (at the top level, in the middle of execution, or while in debug mode) to dump the image to the hard disk to a file of your choosing. Loading the image is done by invoking pLisp with the file name as the argument.


$ ./plisp

Welcome to pLISP. Type 'quit' to exit.

USER> (defun fact (n)

        (apply * (range 1 n 1)))

FACT
USER> (fact 10)
3628800
USER> (create-image "test.image")
NIL
USER> q
Bye.

$ ./plisp test.image
Welcome to pLISP. Type 'quit' to exit.
USER> (fact 10)
3628800
USER> quit
Bye.

Nifty. An even more powerful aspect is the ability to do this in the middle of a debugging session (Hi, Smalltalk. It's been a long time, we should catch up sometime soon.):

$ ./plisp
Welcome to pLISP. Type 'quit' to exit.
USER> (let ((x 10))
        (break)
        (* 2 x))
BREAK
DEBUG> x
10
DEBUG> (create-image "debug.image")
NIL
DEBUG> (resume)
20
USER> q
Bye.

$ ./plisp debug.image
Welcome to pLISP. Type 'quit' to exit.
DEBUG> x
10
DEBUG> (resume)
20
USER> q
Bye.

Astute readers will observe that starting pLisp with the image takes us directly into the debugging session that was active when the image was created. The real utility of this feature is in allowing us to share the image file with somebody and enlist them in our dirty debugging efforts.

All these good things are possible courtesy of the TPL serialization library (thanks Troy, especially for help with the tricky bit involving packing arrays inside structs). The following variables capture the complete state of pLisp, and serializing/deserializing them is all it takes:

strings
top_level_env
free_list
last_segment
heap
current_package
gen_sym_count
packages
debug_mode
debug_continuation
debug_env
execution_stack
debug_execution_stack
reg_accumulator
reg_next_expression
reg_current_env
reg_current_value_rib
reg_current_stack

images.c is the place where all this action is.

Update: One feature not yet implemented is the serialization of references to foreign function libraries: if we load a bunch of .so's and then create an image, the handles to these libraries will be lost when we invoke pLisp with the image file. The way out is to serialize an array of strings containing the shared library names, and then do a dlopen() on these libraries afresh.