Eigen Class
Posts: 358
Nickname: eigenclass
Registered: Oct, 2005
|
Eigenclass is a hardcore Ruby blog.
|
|
|
|
Ruby interpreting Prolog' interpreting Lisp interpreting Lisp solving FizzBuzz
|
Posted: Mar 3, 2007 9:46 AM
|
|
I've written a Lisp 1 interpreter in a Lisp dialect interpreted by the
Prolog-ish language interpreted in Ruby
I have recently blogged about.
FizzBuzz is poised to become
the programming task of choice for discriminating hackers,
right? It surely deserves being the first program to be executed
by that interpreter.
This Ruby code would not get you to
be designated an extremely cool hacker,
but it exercises 4 interpretation levels and it actually
solves FizzBuzz:
require 'lisp'
fizzbuzz = lisp do
L(lambda, L(x, y),
L(cond,
L(L(">=", x, y), 1),
L(LTRUE, L(fizzbuzz_, L("+", L(output, x), 1), y))))
end
fizzbuzz2 = lisp{ L(L(label, fizzbuzz_, fizzbuzz), 1, 101) }
show = lisp{ L(lambda, L(text, ret), L(car, L(_cons, ret, L(puts, text)))) }
output = lisp do
L(lambda, L(i),
L(cond,
L(L("==", L("%", i, 15), 0), L(show, "FizzBuzz", i)),
L(L("==", L("%", i, 3), 0), L(show, "Fizz", i)),
L(L("==", L("%", i, 5), 0), L(show, "Buzz", i)),
L(LTRUE, L(puts, i))))
end
le_fizzbuzz = _eval_[L("fizzbuzz_", 1, 101),
L(L("output", output),
L("fizzbuzz_", fizzbuzz),
L("show", show)),
:X]
resolve(le_fizzbuzz){ }
The interpreter stack
Lisp can be written in itself, and the S-expressions in the above program are
processed by a Lisp 1 interpreter written in a (somewhat different) Lisp dialect
interpreted by a logical language defined atop Ruby. Here follows a rough
overview of some of these layers.
Primitive operators
The Lisp 1 interpreter is implemented in terms of seven
primitive operators (quote, atom, car, cdr, eq, cons,
cond), which
themselves are written using the above-mentioned logical language.
The only primitive operator that requires some (a couple lines of) Ruby code
is atom, but the other six can be expressed very succinctly.
A function is but a special case of a relation, so
eq(x, x) := true, otherwise
eq(x, y) := false
can be expressed as "EQ[X, X, TRUE], otherwise EQ[X, Y, FALSE]". In the following
definitions, I leverage the fact that predicates will be tested in order,
allowing me to use the cut to express disjunction:
_quote[:X, :X] <<= :CUT
_eq[:X, :X, LTRUE] <<= :CUT
_eq[:X, :Y, LFALSE] <<= :CUT
_car[cons(:X, _), :X] <<= :CUT
_car[:X, LFALSE] <<= :CUT
_cdr[cons(_, :X), :X] <<= :CUT
_cons[:X, :Y, cons(:X, :Y)].fact
_cond[cons(list(LTRUE, :E), _), :E] <<= :CUT
_cond[cons(_, :R), :Ret] <<= _cond[:R, :Ret]
The Lisp 1 interpreter
A Lisp interpreter written in itself
takes only 60 lines of code. I did a straightforward translation using the
above operators, and although it didn't grow that much in terms of lines of code
(around 75), it's considerably more dense:
Read more...
Read: Ruby interpreting Prolog' interpreting Lisp interpreting Lisp solving FizzBuzz
|
|