14 Apr 2011 tampe   » (Journeyer)

functionasm

Alex Shinn, has written a library for scheme that does formatting tasks in a functional style, the fmt library. In this library there is a package for outputting c code. Let's play with that.

A next step might be to be able schemify c code. e.g. introduce <define>, <let*>, <begin>, <if> and <recur> + the operators. The innovation needed to do this is to make sure that everything return values and are able to be composed together with some kind of "sewing" mechanism.

The idea of attacking this is to try keep everything functional as far as possible and then at the final step add syntactic sugars in the form of macros. When trying to do this one realizes that we need two phases of evaluation. The reason are this, consider a function application:


int f(int),
----------------
f((<let*> ((a (+ b c))) (<if> q a b)) + b)?
Now the idea of modelling is to let the statments evaluate to a lambda that takes as an argument #f or a symbol. The lambda applied to a symbol will mean that the result of that expression will generate c-code where the tail expressions will set the symbol to the return result. so we do stuff like,

(define (f-if p x y)
    (lambda (ret)
       (let ((pred (gensym "pred"))
         (c-block
           (c-var 'int pred)   ; define pred integer
           (p pred)            ; execute p expression
                               ; pred is the result
           (c-if pred (x ret) (y ret))))))
This will illustrate the technique (we are using autoquoting features in the macro package). Over to the function application. Here we conclude that in order to be general we need to use

   (int arg)
   ((((<let*> ((a (+ b c))) (<if> q a b)) + b)
    arg)
   (c= ret `(f ,arg))
E.g. we introduce an arg symbol to be able to set it. Doing this we need to know about the function signature. Designing the <define> as a function means that we are not able to register the function signature before the body is evaluated and the function signature needed in case of recursion. So one need one extra lambda dereference. I thught that was too complicated though and used macros in stead to accomplish correct registration order.

The function example lack some wow factor. But for the loop construct in clambda, the <recur> statement, it's a must. Here is how the idea of this works


(<recur> loop ((int n 100) (int s 0)
    (<if> (< n 0)
          s
          (<next> loop (+ s 314))))
====================================
Aproximate translation
====================================
 int n = 100;
 int s = 0
loop:
 if (n < 0)
    ret = s;
 else
 {
    s = s + 314;
    goto loop;
  }

This is kind of handy loop construct and again one need to register the loop symbols this time in order to be able to expand the <next> statement later on.

This is about 300 lines of code. For 600 I guess you would be able to introduce a good enough c to produce workable code with syntax line numbers introduced as well to be able to debug after gcc has found your bugs.

Now this is not that complicated and everything is just a quite simple idea of syntactic sugars. But this will enable scheme macro facilities to your c-code (for one thing you will be able to track source code line numbers to be able to debug your macros correctly. Also going between clambda and pure scheme is a smaller step then between c and scheme this means that you can keep a large part of say guile c-code both in c and scheme at the same time.

Have fun

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!