| The Fexl interpreter is simple, elegant, and fast. The core "reduce" routine runs about 43.5 million cycles per second on a simple infinite loop. If I set max_cycles to a billion it stops after about 23 seconds. To test it with something more real, I wrote the "cat" program in Fexl as follows: \cat = (get_char \ch eq EOF ch I; put_char ch cat)
Then I pumped 30MB of data into it, which took about 42 seconds: time (head -c 30000000 /dev/zero | fexl_cat >/dev/null)
Of course that's a lot slower that pushing the data through the Unix cat program, which only takes about 0.14 seconds: time (head -c 30000000 /dev/zero | cat >/dev/null)
But think about all the gyrations happening here: \cat = (get_char \ch eq EOF ch I; put_char ch cat)
That is translated into the closed (variable-free) form: (Y (S (C get_char) (S (C (S (S (eq EOF) (C I))))
(S (C (S put_char)) C))))
Then that runs like the wind, applying combinator rules such as these many millions of times: C x y = x
S x y z = x z (y z)
I x = x
Y x = x (Y x)
Not to mention that get_char and put_char are handling and building characters one at a time, and the "eq" function runs as a combinator as well.By the way it is quite trivial to add new combinators written in C. For example, the fabled Y combinator is defined in a single file "type_Y.c" as follows: #include "node.h"
#include "type_Y.h"
/*
Fixpoint function (Y combinator): (Y f) = (f (Y f))
*/
static void step(void)
{
int f1 = pop();
if (!f1) return;
set_pair(f1, R(f1), P(L(f1),R(f1)));
}
int type_Y(void)
{
static int node = 0;
if (node == 0) node = new_combinator(step);
return node;
}
The type_Y.h file just says: extern int type_Y(void);
|