Angel \”Java\” Lopez on Blog

December 2, 2011

AjLisp in Ruby (1) Structure, Classes and Tests

Filed under: AjLisp, Lisp, Programming Languages, Ruby, Test-Driven Development — ajlopez @ 10:57 am

Next Post

I’m learning and practicing Ruby writing my AjLisp interpreter, as usual (early this year, I ported it to Javascript). TDD is my friend: write a test, run in red, code, run in green, refactor, and so on. My code, work in progress, is at:

https://github.com/ajlopez/AjLispRb

Initially, I wrote a single file, following the great and simple example:

http://kanemar.com/2006/03/04/screencast-of-test-driven-development-with-ruby-part-1-a-simple-example/

Notice that Ruby has a ‘test/unit’ package, already in place, ready to use, after installation. After some research, I spitted the file in production code, and test code. I want to build a gem (Ruby package), so I studied the tutorial:

http://guides.rubygems.org/

I have pending readings:

http://speakerdeck.com/u/pat/p/cut-polish-a-guide-to-crafting-gems
http://blog.thepete.net/2010/11/creating-and-publishing-your-first-ruby.html

My code is not a gem, yet. But it have some gem structure:

The lib contains a single file:

require 'ajlisp/list.rb'
require 'ajlisp/named_atom.rb'
require 'ajlisp/context.rb'
require 'ajlisp/string_source.rb'
require 'ajlisp/token.rb'
require 'ajlisp/lexer.rb'
require 'ajlisp/parser.rb'
require 'ajlisp/primitive.rb'
require 'ajlisp/primitive_first.rb'
require 'ajlisp/primitive_rest.rb'
require 'ajlisp/primitive_cons.rb'
require 'ajlisp/primitive_list.rb'
require 'ajlisp/primitive_closure.rb'
require 'ajlisp/fprimitive.rb'
require 'ajlisp/fprimitive_quote.rb'
require 'ajlisp/fprimitive_lambda.rb'
require 'ajlisp/fprimitive_flambda.rb'
require 'ajlisp/fprimitive_let.rb'
require 'ajlisp/fprimitive_closure.rb'
require 'ajlisp/fprimitive_define.rb'
require 'ajlisp/primitive_add.rb'
module AjLisp
@context = Context.new
@context.setValue "quote", FPrimitiveQuote.instance
@context.setValue "first", PrimitiveFirst.instance
@context.setValue "rest", PrimitiveRest.instance
@context.setValue "cons", PrimitiveCons.instance
@context.setValue "list", PrimitiveList.instance
@context.setValue "lambda", FPrimitiveLambda.instance
@context.setValue "flambda", FPrimitiveFLambda.instance
@context.setValue "let", FPrimitiveLet.instance
@context.setValue "define", FPrimitiveDefine.instance
@context.setValue "+", PrimitiveAdd.instance
def self.context
    return @context
end
def self.evaluate(context, item)
    if item.is_a? List or item.is_a? NamedAtom
        return item.evaluate(context)
    end

    return item
end
end

I wrote some primitives (normal forms, and special forms: the latter don’t evaluate their parameters before apply, like quote or define). Notice that the additional files are in a subfolder ajlisp inside lib, why? Because when this code is installed as a gem, all lib file code could be include with require(‘thenameofrubyfile’). If I put other files than ajlisp.rb, i.e. list.rb or atom.rb, those file names could be required, and maybe a name collision with other gems could occur. Then, the recommended practice (see other gem code in your Ruby installation folder) is to put additional files below lib folder.

In the test folder there is a top test.rb file that includes other test files:

require 'ajlisp'
require 'test/unit'
require "test_list.rb"
require "test_named_atom.rb"
require "test_context.rb"
require "test_string_source.rb"
require "test_token.rb"
require "test_lexer.rb"
require "test_parser.rb"
require "test_primitive_first.rb"
require "test_primitive_rest.rb"
require "test_primitive_cons.rb"
require "test_primitive_list.rb"
require "test_primitive_closure.rb"
require "test_primitive_add.rb"
require "test_fprimitive_quote.rb"
require "test_fprimitive_lambda.rb"
require "test_fprimitive_let.rb"
require "test_fprimitive_closure.rb"
require "test_fprimitive_flambda.rb"
require "test_fprimitive_define.rb"
require "test_evaluate"

so you can run the tests from command line:

ruby –Ilib;test test\test.rb

If you have Windows, the runtest.cmd file contains this line.

Some test code:

require 'ajlisp'
require 'test/unit'
class TestList < Test::Unit::TestCase
#... 
    def test_create_with_first
        list = AjLisp::List.new("foo")
        assert_equal("foo", list.first)
        assert_nil(list.rest)
    end

    def test_create_with_first_and_rest
        rest = AjLisp::List.new("bar")
        list = AjLisp::List.new("foo", rest)
        assert_equal("foo", list.first)
        assert_not_nil(list.rest)
        assert_equal("bar", list.rest.first)
        assert_nil(list.rest.rest)
    end

    def test_create_from_array
        list = AjLisp::List.make [1, "a", "foo"]
        assert_not_nil list
        assert_equal 1, list.first
        assert_equal "a", list.rest.first
        assert_equal "foo", list.rest.rest.first
        assert_nil list.rest.rest.rest
    end
#..
end

Every list is an object of this class, list.rb:

module AjLisp
class List
    attr_reader :first
    attr_reader :rest

    def initialize(first=nil, rest=nil)
        @first = first
        @rest = rest
    end

    def evaluate(context)
        form = AjLisp::evaluate(context, @first)
        form.evaluate(context, self)
    end

    def self.make(array)
        if array and array.length > 0
            first = array.shift

            if first.is_a? Array
                first = make(first)
            elsif first.is_a? Symbol
                first = NamedAtom.new first.to_s
            end

            return List.new first, make(array)
        end 

        return nil
    end
end
end

The accessors first and rest are read-only. Thanks to the untyped nature of Ruby (like in Javascript) the implementation of this interpreter is straightforward, or without much code ceremony, at least.

In my new tests, I included the code into the AjLisp module, so I don’t need to write AjLisp:: prefix before referencing a class:

require 'ajlisp'
require 'test/unit'
module AjLisp
class TestLexer < Test::Unit::TestCase
    def test_get_atom_token
        source = StringSource.new "atom"
        lexer = Lexer.new source
        token = lexer.nextToken

        assert_not_nil token
        assert_equal "atom", token.value
        assert_equal TokenType::ATOM, token.type
        assert_nil lexer.nextToken
    end
    def test_get_atom_token_with_spaces
        source = StringSource.new " atom "
        lexer = Lexer.new source
        token = lexer.nextToken

        assert_not_nil token
        assert_equal "atom", token.value
        assert_equal TokenType::ATOM, token.type
        assert_nil lexer.nextToken
    end
#...
end

Next topics: some implementation details, primitives vs fprimitives, context (nested environments with name/values), lambda and closures, lexer and parser.

Next steps: complete primitives (let, letrec, definef, do, if…), macro (mlambda, definem, macro expansion…)

Keep tuned!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

1 Comment »

  1. [...] Previous Post [...]

    Pingback by AjLisp in Ruby (2) Context with Name and Values « Angel “Java” Lopez on Blog — December 15, 2011 @ 5:41 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Theme: Shocking Blue Green. Get a free blog at WordPress.com

Follow

Get every new post delivered to your Inbox.

Join 67 other followers

%d bloggers like this: