μLithp interop

μLithp - a Lisp in 27 lines of Ruby source

Primordial ooze | A reader | REPL | A compiler | Ruby interop

Extending μLithp with Ruby interop

Since μLithp is built on Ruby, it would be nice to be able to access the underlying language classes for silly purposes. Fortunately, extending μLithp to handle Ruby interop is a matter of inheritance:

require 'lithp'

class ULithp < Lisp
  def initialize()

Extending the Lisp class like this allows one to enhance the basic offerings (i.e. the 9 core forms) with other capabilities. For interop purposes, I'll add two special forms: new and invoke.

:new

The :new special form is used to create instances of Ruby classes. The Lispy syntax of :new is (new Classname arg1 arg2). Adding new is pretty straight-forward:

    @add = {:new => proc { |(klass, *args), ctx| 
                              eargs = args.map { |a| self.eval(a, ctx) }
                              Kernel.const_get(klass).new(*eargs)},  

Of course, I do not want to selectively evaluate certain parts of the :new form, specifically the arguments to the constructor. This allows me to use :new in the following ways:

require 'interop'

l = ULithp.new

l.eval [:new, :Array, 2]
#=> [nil, nil]

l.eval [:new, :Array, [:car, [:quote, [3, 2, 1]]]]
#=> [nil, nil, nil]

These are of limited use without a way to invoke methods on them. Currently, I'll show how to create another special form named :invoke.

invoke

Creating a special form to invoke instance methods is just a bit harder than :new, and is as follows:

            :invoke => proc { |(instance, meth, *args), ctx| 
                              eargs = args.map { |a| self.eval(a, ctx) }
                              einst = self.eval(instance, ctx)
                              einst.send(meth, *args)}
    }

    super(@add)
  end
end

The Proc used for implementation selectively evaluates the instance and the method's arguments. This allows me to use :invoke as follows:

require 'interop'

l = ULithp.new

l.eval [:label, :r, [:new, :Range, 0, 10]]

l.eval :r
#=> 0..10

l.eval [:invoke, :r, :last]
#=> 10

l.eval [:invoke, :r, :include?, 5]
#=> true

l.eval [:invoke, [:new, :Range, 0, 10], :include?, 15]
#=> false

And just like that, μLithp has a rudimentary interop story.

Date: 2012-12-28 15:40:26 EST

Author: Fogus

Org version 7.8.11 with Emacs version 24

Validate XHTML 1.0