[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: More Compiled S-Expression Questions



Quoting Brent Fulgham:
> Is it possible to create a single compiled representation of this
> that could be stored and reloaded without going through the
> overhead of re-parsing and evaluating?

I'm not sure what you're asking, though I see a couple of
possibilities:

 1. Your application gets some code at run-time that you want to cache
    in a string-friendly database (possibly the filesystem).

 2. You want to compile some static Scheme code to be embedded in the
    application.

The first seems more likely, and I'll suggest a method for that first.
I have an answer for the second close at hand, though, so I've included
it.

----------------------------------------
Answer 1: Compiling code at run-time and storing it for later use

Compile phase:

 Use the `(begin ...)' trick to get a single expression. 

 Use scheme_make_string_input_port() to get a port. 

 Use scheme_read() to get an S-expression.

 Use scheme_compile() to get compiled code.

 Use scheme_make_string_output_port() to get a new output port.

 Use scheme_write() to write the compiled code to the output port.

 Use scheme_get_sized_string_output() to get the compiled code in
 string form. Beware that the string might contain nul characters.

Eval phase:

 Use scheme_make_sized_string_input_port() to convert the string of
 compiled code into a port of compiled code. (Use the _sized version
 because the string might have nul characters.)

 Enable bytecode reading using
  scheme_set_param(scheme_config, MZCONFIG_CAN_READ_COMPILED, scheme_true)

 Use scheme_read() to get the compiled code.

 Use scheme_eval() to evaluate it.


----------------------------------------
Answer 2: Compiling static code and embedding it in an application

Compile phase:

 Use the following little program that takes any number of
 S-expressions from the current input port, and sends C code to the
 current output port:

 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; 

(let ([DIGITS-PER-LINE 15]
      [p (open-output-string)])
  (let loop ()
    (let ([expr (read)])
      (unless (eof-object? expr)
	(let ([c (compile expr)])
	  (write c p)
	  (loop)))))
  (let ([s (get-output-string p)])
    (printf "static unsigned char expr[] = {~n    ")
    (let loop ([chars (string->list s)][pos 0])
      (unless (null? chars)
	(let ([char (car chars)])
	  (printf "~a, " (char->integer char)))
	(loop (cdr chars)
	      (if (= pos DIGITS-PER-LINE)
		  (begin
		    (printf "~n    ")
		    0)
		  (add1 pos)))))
    (printf "0};~n")
    (printf "#define COMPILED_STR_SIZE ~a~n" 
	    (string-length s))))

 ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; 

 The output C code has the form

    static unsigned char expr[] = {
        ... <a bunch of numbers> ... };
    #define COMPILED_STR_SIZE <number>

 Copy it or #include it into your C code.

Eval phase:

 Convert the string to a port, using
   scheme_make_sized_string_input_port(expr, COMPILED_STR_SIZE)

 Turn on bytecode reading using
   scheme_set_param(scheme_config, MZCONFIG_CAN_READ_COMPILED, scheme_true)

 Read and eval until the port returns EOF:
  while (1) {
    expr = scheme_read(port);
    if (SAME_OBJ(expr, scheme_eof))
      break;
    scheme_eval_multi(expr, env);
  }

Matthew