On Fri, 09 Jul 2004 10:52:17 +0000, gabriele renzi wrote:

> 
> usually people seem to think that ruby is smalltalk or lisp or perl
> done properly. Probably all of this are just unlambda done properly :)

FYI, I have written a unlambda interpreter in Ruby.  (I don't know
if I should be proud of it...)  It was ridiculously easy, because
Ruby already provides everything (continuations, closures,...). I think
the implementation is even easier to understand than the spec.
It is not a very efficient implementation, since it uses recursion.
But you don't have very large unlambda programs that you absolutely
need to run, do you?

Here it is:
(Don't continue reading if you don't want to waste your time...)

#!/usr/bin/env ruby

#Unlambda interpreter
#written by Kristof Bastiaensen <kristof.bastiaensen / vleeuwen.org>

require "stringio"

def readchar
  $char_read = getc
end

K = lambda {|a| lambda { |b| a }}
S = lambda {|a| lambda { |b| lambda { |c| a[c][b[c]] }}}
I = lambda { |a| a }
V = lambda { |a| V }
R = lambda { |a| puts; a }
C = lambda { |a| callcc { |cc| a[cc] } }
D = lambda { |a| a}
AT = lambda { |a| (readchar() ? a : V ) }
PIPE = lambda { |a| $char_read ? a[$char_read] : V }

CharLookup = { ?k => K, ?s => S, ?i => I,
              ?v => V, ?r => R, ?c => C,
              ?@ => AT, ?| => PIPE,
              ?d => D, ?e => :dummy }

def compile(stream)
  loop do
    raise "Premature end of stream!" if stream.eof
    ch = stream.getc
    if ch == ?` #`
      return [compile(stream), compile(stream)]
    elsif ch == ?.
      a = stream.getc
      return lambda { |b| print a.chr; b }
    elsif ch == ??
      a = stream.getc
      return lambda { |b| b == a ? a : V }
    elsif CharLookup.member?(ch)
      return CharLookup[ch]
    end
  end
end

def eval_expr(expr)
  if expr.class == Array
    r = eval_expr(expr[0])
    e2 = expr[1]
    if (r == D and e2.class == Array)
      return lambda { |a| eval_expr(e2)[a] }
    else
      return r[ eval_expr(e2) ]
    end
  else
    return expr
  end
end

def eval(string)
  string = string.gsub(/#.*$/, "")
  stream = StringIO.new(string, "r")
  callcc do |exit|
    CharLookup[?e] = lambda { |a| exit[a] }
    tree = compile(stream)
    eval_expr(tree)
  end
end

if(ARGV.length != 1)
  $stderr << "Usage: %s <unlambda file>\n" % $0
else
  str = IO.read(ARGV[0])
  eval(str)
end