AjLisp en Ruby (2) Contexto con Nombres y Valores

Published on Author lopezLeave a comment

Anterior Post

Una de las primeras clases que implemente en AjLispRb es el ambiente (“environment”). Esta vez lo llamé contexto: un diccionaro donde guardar pares nombre/valor, los valores de los átomos con nombre. El código:

module AjLisp
class Context
    def initialize(parent = nil)
        @parent = parent
        @values = Hash.new
    end
    
    def getValue(name)
        if @values.has_key?(name)
            return @values[name]
        end
        
        if @parent != nil
            return @parent.getValue(name)
        end
        
        return nil
    end
    
    def setValue(name, value)
        @values[name] = value
    end
end
end

La clase fue codificada usando TDD (Test-Driven Development), los primeros tests en test/test_context.rb. Algunos tests:

def test_not_defined_is_nil
    context = AjLisp::Context.new
    assert_nil(context.getValue(:foo))
end
def test_set_and_get_value
    context = AjLisp::Context.new
    context.setValue(:foo, "bar")
    assert_equal("bar", context.getValue(:foo))
end
def test_get_value_from_parent
    parent = AjLisp::Context.new
    parent.setValue(:foo, "bar")
    context = AjLisp::Context.new(parent)
    assert_equal("bar", context.getValue(:foo))
end

Inicialmente, yo usaba strings para los nombre, pero ahora estoy usando los símbolos de Ruby como :foo. Tengo un test que asegura la independencia de los valores del contexto padre y del hijo:

def test_override_value_from_parent
    parent = AjLisp::Context.new
    parent.setValue(:foo, "bar")
    context = AjLisp::Context.new(parent)
    context.setValue(:foo, "bar2")
    assert_equal("bar2", context.getValue(:foo))
    assert_equal("bar", parent.getValue(:foo))
end

Cada contexto puede tener un contexto padre. Hay un contexto “tope”, definido lib/ajlisp.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 :mlambda, FPrimitiveMLambda.instance
@context.setValue :let, FPrimitiveLet.instance
@context.setValue :define, FPrimitiveDefine.instance
@context.setValue :do, FPrimitiveDo.instance
@context.setValue :if, FPrimitiveIf.instance
@context.setValue :definef, FPrimitiveDefinef.instance
@context.setValue :definem, FPrimitiveDefinem.instance
@context.setValue :+, PrimitiveAdd.instance
@context.setValue :-, PrimitiveSubtract.instance
@context.setValue :*, PrimitiveMultiply.instance
@context.setValue :/, PrimitiveDivide.instance
def self.context
    return @context
end
# ...
end

Los nuevos contexts son creado por varias primitivas. En el comienzo, existe el contexto tope definido arriba, como:

Si tenemos que definir una función que retorna el segundo elemento de una lista, podemos usar la primitiva define:

(define second (a) (first (rest a)))

ahora tenemos en el contexto tope una nueva entrada para second:

El contexto tope tiene ahí en esa nueva entrada el valor que representa un lambda  (lambda (a) (first (rest a))  (en realidad, debería aclarar que se guarda el resultado de haber evaluado el lambda, que es un closure (clausura) pero ya llegaremos al tema en próximo post).

Cuando invocamos:

(second (quote (one two three)))

un nuevo contexto se crea, con su padre apuntando al contexto tope (de nuevo, es algo más complejo, veremos clausuras). Ese nuevo contexto tiene un par nombre/valor:

Este contexto es descartado LUEGO de esta evaluaciónThat de second  (puede sobrevivir indirectamente si luego de la evaluación quedó referenciado por una clausura). Cuando definimos un valor simple:

(define one 1)

el contexto tope es modificado, para tener un nuevo par nombre/valor:

Próximos temas: primitivas y formas especiales, su invocación, clausuras, macros, e invocación de métodos nativos de Ruby.

Nos leemos!

Angel “Java” Lopez

http://www.ajlopez.com

http://twitter.com/ajlopez

Leave a Reply

Your email address will not be published. Required fields are marked *