% ./repl > .d float x > x = sin(3) > .p x p x $1 = 0.141120002 > *((char*)0) = 0 segfault detected; resuming. > .p x p x $1 = 0.141120002 > printf("my pid is %d\n", getpid()) my pid is 11934 > .help d "d type variable": define a new variable. g "g foobar": run an arbitrary command through gdb. help show help on commands. p "p variable": print out the current value of the variable. s cause a segfault to let crepl attempt to recover. t test if the repl is ok by running a printf through it.
Unlike the other C-interpretation systems mentioned when I posted about this before, this works with unmodified C libraries, runtimes, etc. and doesn't require source code.
Satoru was full of good ideas for how to make this work: for example, to run a line I had thought of writing my own parser and only allowing function calls of a specific form. He suggested building a shared object for each line and bringing those in at runtime. (This sounds slow but it's actually not even noticable; consider that your shell fork+execs something every time you type in a command.) So now we can use pretty much all of C (see the example line above with the pointers and the cast), which is much more useful.
Here are some random ideas for the future work:
.include "foo.h"to bring in headers,
.library "foo.so"to bring in a library,
.pkg "gtk+-2.0"to bring in a pkg-config package.
- Allow the driver program to hook in to a glib mainloop, so that bringing in gtk would be useful. (This actually won't be too hard.)
- Make the segfault-handling more robust. Right now it replays the initial part of the program, instead of using my original forking idea. Satoru pointed out that maybe the current behavior makes more sense, though, as it'll rewind all open files, etc. to their state before the crashing code.
- Figure out a better way to handle declarations; right now, you have to mark declarations with that
.dbit, but I'd prefer to allow natural-style C declarations (
int x = 3and function declarations). This may require some parsing, though.