One of the first classes I implemented in AjLispRb is the environment. This time I called it a context: a dictionary where to save name/values, to store the atom values. The code:
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 endThe class was coded using TDD, the first tests at test/test_context.rb. Some 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)) endInitially, I used strings for names, but now I'm using Ruby symbols like :foo. I have a test that ensures the independence of parent and child context values:
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)) endEach context can have a parent. There is a top context, defined in 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 # ... endNew context are created in many primitives. At the begin, there is a top context like:
![]()
If you have a defined form that returns the second element of a list:
(define second (a) (first (rest a)))
now you have a top context with the new slot:
![]()
The top context has a new slot named second with a lambda value (lambda (a) (first (rest a))
When you invoke:
(second (quote (one two three)))
during the invocation a new context is created, its parent set to the top context, and having a new name/value pair:
![]()
That context is discarded after second evaluation (unless the context were referenced by recently created closure, details in upcoming post). When you define a simple value:
(define one 1)
the top context is modified, to have a new name/value pair:
![]()
Next topics: primitives and special form primitives, their invocation, closures, macros, native methods invocation.
Keep tuned!
Angel “Java” Lopez
http://www.ajlopez.com
[...] by AjLisp in Ruby (2) Context with Name and Values « Angel “Java” Lopez on Blog — December 15, 2011 @ 5:41 pm [...]
Pingback by AjLisp in Ruby (1) Structure, Classes and Tests « Angel “Java” Lopez on Blog — December 15, 2011 @ 5:43 pm