Imagine a world where you write a piece of software, be it an desktop application or a server application, it doesn’t really matter, but imagine that during runtime you could reprogram certain aspects of your application, imagine allowing users to add functionality without rebuilding.
This is very common in sci-fi movies of course, it is also common in the world of editors, both Emacs and Vim have the ability to extend themselves with their own language, most Java based editors allow you to write plugins that add some form of capability like support for another language and such.
So, why is it that we hardly ever add this functionality to other applications? For instance, your application could expose some key functionality to your extension language, like retrieving a list of users and the ability to send the users a message.
You could then define a macro which iterates over the list of users and sends each of them a specific message, for example a message saying that a system is shutting down and the reason for this. The macro could then go and close each users connection.
This is just a simple example how extending an application with its own extension language can be helpful. So, how do you create such an language?
You could of course create your own interpreter, however that would be a very time consuming and expensive exercise. Luckily, other folks out there already made us some nice extension languages; for C and C++ there is Guile which implements a Lisp language, for Java there is Rhino which might do the trick, although I have never used it myself.
The Guile tutorial has a nice example where you create a small drawing program which is first implemented solely in C:
{ int ii; tortoise_pendown(); for (ii=0; ii<4; ii++) { tortoise_move(100); tortoise_turn(90.0); } sleep(10); }
This code draws a nice little square on the screen. Now; when you want to draw something else, you need to rewrite this code. Suppose you had exposed some of your drawing functions (the tortoise-* functions) to Guile? The code above would’ve looked like this:
(define (move-n-turn angle) (tortoise-move 100) (tortoise-turn angle)) (tortoise-pendown) (for-each move-n-turn '(90 90 90 90)) (tortoise-penup)
You can imagine how easy it would be to redefine the move-n-turn method or to change the for-each loop that controls the drawing… you would just tell your application to reload a definitions file or open up an console where you could redefine the functions… endless possibilities.
An example of calling methods and executing hooks with Guile is as follows:
#include <stdio.h> #include <stdlib.h> #include <libguile.h> int main (int argc, char *argv[]) { SCM func_symbol, func, my_hook; scm_init_guile(); my_hook = scm_permanent_object(scm_make_hook (SCM_INUM0)); scm_c_define ("my-hook", my_hook); scm_c_primitive_load ("script.scm"); // Lookup and execute do-hello func_symbol = scm_c_lookup("do-hello"); func = scm_variable_ref(func_symbol); scm_call_0 (func); // run all methods associated with my_hook scm_c_run_hook (my_hook, SCM_EOL); exit(EXIT_SUCCESS); }
With script.scm being:
(add-hook! my-hook (lambda () (display "Executed when my-hook is run") (newline))) (define do-hello (lambda () (display "A method (do-hello) in scheme") (newline))) (display "Script loaded...\\n")
When running the above application, the resulting output is:
Script loaded... A method (do-hello) in scheme Executed when my-hook is run