Officially I am still doing the review of Vicare’s expander code (see Expander code review and apology (2015 April 18)), but I needed a break; so I have refactored the compiler in smaller libraries and merged the compiler review branch into master. In addition I did some random stuff.
Everything discussed here is in the head of the master
branch, which is an
unstable, development branch.
program
formLet’s take, as reference, the scenario in which we compile and install both libraries
and programs. When we import a library with import
, the library is
associated to the program and it is loaded whenever the program is run. This is
somewhat like linking a host’s shared object to a C language program at
compile–time.
It is also possible to dynamically load a Scheme library, so that the program itself
contains the logic needed to load (or not) an external library. This is somewhat
like loading a host’s shared object with dlopen()
from a C language program at
run–time. The code of this feature is really small, so it is in the boot image
(yes, the already huge boot image, which is around 20 MiB on my 64-bit platform).
This is how it works; we prepare a program:
(import (vicare) (prefix (vicare libraries) libs.)) (define-values (pregexp-match) (let ((lib (libs.library-dynamic-load-and-intern '(vicare pregexp)))) (values (libs.library-dynamic-retrieve lib 'pregexp-match)))) (pretty-print (pregexp-match "[a-z]+" "ciao123")) (flush-output-port (current-output-port))
compile it:
$ vicare -c demo.sps -o demo
and run it (I am on gnu+Linux and I use the binfmt_misc
support to run
Vicare programs):
$ ./demo ("ciao")
the function library-dynamic-load-and-intern
loads a Scheme library using the
usual search path and the function library-dynamic-retrieve
retrieves the
Scheme object bound to an exported syntactic binding.
library-dynamic-load-and-intern
is like dlopen()
and
library-dynamic-retrieve
is like dlsym()
.
Only global variables exported by the library can be accessed this way: it makes no
sense to access macro transformers when the program is already running; this api
is not a substitute of eval
and environment
.
program
form ¶The r6rs document specifies a compliant top–level program as:
… a delimited piece of text, typically a file, that has the following form:
?import-form ?top-level-body
so it only specifies that it is “delimited”; it means:
Vicare gathers this freedom to accept two formats of top–level programs:
(import ?import-spec ...) ?body ...
program
form with the following syntax:
(program ?program-name ?config-form ... (import ?import-spec ...) . ?program-body)
where ?program-name is meant to be a descriptive list of symbols (currently unused) and the ?config-form clauses allow additional configuration and behaviour specification.
Recently there was a thread on Reddit with a subdiscussion about start–up times for different Scheme implementations. Ahem… Vicare is not the fastest Scheme implementation around, and I know it. Anyway, why shy away?
The “Hello World!” program under Vicare is this:
(import (vicare)) (display "Hello World!\n") (flush-output-port (current-output-port))
I compile it:
$ vicare -c demo.sps -o demo
and run it:
$ ./demo Hello World!
after running it (so that the Linux kernel loads the boot image from the file system and caches it in memory), I time it; a typical execution is this:
$ /usr/bin/time -p ./demo Hello World! real 0.13 user 0.08 sys 0.05
on my:
$ cat /proc/cpuinfo | head --lines 9 processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 58 model name : Intel(R) Core(TM) i5-3337U CPU 1.80GHz stepping : 9 microcode : 0x15 cpu MHz : 2682.000 cache size : 3072 KB
There is no point in trying hard to heat up the host so that the cpu does its best, and stuff like that.