Skip to content

Instantly share code, notes, and snippets.

@agenticsim
Forked from p8/lithp.rb
Created January 5, 2013 05:24

Revisions

  1. @p8 p8 revised this gist Jan 28, 2012. 2 changed files with 79 additions and 78 deletions.
    79 changes: 79 additions & 0 deletions lithp.rb
    Original file line number Diff line number Diff line change
    @@ -14,4 +14,83 @@ def self.parse fn, *sexp
    args = sexp.map { |a| Fs[:atom].call(a) ? a : parse(*a) }
    Fs.key?(fn) ? Fs[fn].call(*args) : fn
    end
    end

    describe Lisp do

    it "works with nested sexps" do
    Lisp.parse(:eq, [:quote, [1, 2, 3]], [:cons, 1, [:quote, [2,3]]]).should == true
    Lisp.parse(*Lisp.parse(:quote, [:cons, 1, [:quote, [2,3]]])).should == [1, 2, 3]
    end

    describe ":quote" do
    it "returns the sexp" do
    Lisp.parse(:quote, [:eq, 1, 2]).should == [:eq, 1, 2]
    end

    it "returns the value" do
    Lisp.parse(:quote, [1, 2]).should == [1, 2]
    end
    end

    describe ":car" do
    it "returns the head of the sexp" do
    Lisp.parse(:car, [:quote, [1, 2]]).should == 1
    end
    end

    describe ":cdr" do
    it "returns the tail of the sexp" do
    Lisp.parse(:cdr, [:quote, [1, 2, 3]]).should == [2, 3]
    end
    end

    describe ":cons" do
    it "concats the the first argument to the second" do
    Lisp.parse(:cons, 1, [:quote, [2, 3]]).should == [1, 2, 3]
    end
    end

    describe ":if" do
    it "returns the third sexp if the first is true" do
    Lisp.parse(:if, [:eq, 2, 2], 42, 43).should == 42
    end

    it "returns the second sexp if the first is false" do
    Lisp.parse(:if, [:eq, 1, 2], 42, 43).should == 43
    end

    end

    describe ":atom" do
    it "returns true if it's an atom" do
    Lisp.parse(:atom, 42).should == true
    end

    it "returns false if it's not an atom" do
    Lisp.parse(:atom, [:quote, [42]]).should == false
    end

    it "returns false if it's not an atom" do
    Lisp.parse(:atom, [:quote, [42, 43]]).should == false
    end
    end

    describe ":eq" do
    it "returns true if values are equal" do
    Lisp.parse(:eq, 42, 42).should == true
    end

    it "returns false if values are equal" do
    Lisp.parse(:eq, 42, 43).should == false
    end
    end

    describe ":label" do
    it "sets the value" do
    Lisp.parse(:label, :a, 42)
    Lisp.parse(:a).should == 42
    end
    end

    end
    78 changes: 0 additions & 78 deletions uthing.rb
    Original file line number Diff line number Diff line change
    @@ -1,78 +0,0 @@
    describe Lisp do

    it "works with nested sexps" do
    Lisp.parse(:eq, [:quote, [1, 2, 3]], [:cons, 1, [:quote, [2,3]]]).should == true
    Lisp.parse(*Lisp.parse(:quote, [:cons, 1, [:quote, [2,3]]])).should == [1, 2, 3]
    end

    describe ":quote" do
    it "returns the sexp" do
    Lisp.parse(:quote, [:eq, 1, 2]).should == [:eq, 1, 2]
    end

    it "returns the value" do
    Lisp.parse(:quote, [1, 2]).should == [1, 2]
    end
    end

    describe ":car" do
    it "returns the head of the sexp" do
    Lisp.parse(:car, [:quote, [1, 2]]).should == 1
    end
    end

    describe ":cdr" do
    it "returns the tail of the sexp" do
    Lisp.parse(:cdr, [:quote, [1, 2, 3]]).should == [2, 3]
    end
    end

    describe ":cons" do
    it "concats the the first argument to the second" do
    Lisp.parse(:cons, 1, [:quote, [2, 3]]).should == [1, 2, 3]
    end
    end

    describe ":if" do
    it "returns the third sexp if the first is true" do
    Lisp.parse(:if, [:eq, 2, 2], 42, 43).should == 42
    end

    it "returns the second sexp if the first is false" do
    Lisp.parse(:if, [:eq, 1, 2], 42, 43).should == 43
    end

    end

    describe ":atom" do
    it "returns true if it's an atom" do
    Lisp.parse(:atom, 42).should == true
    end

    it "returns false if it's not an atom" do
    Lisp.parse(:atom, [:quote, [42]]).should == false
    end

    it "returns false if it's not an atom" do
    Lisp.parse(:atom, [:quote, [42, 43]]).should == false
    end
    end

    describe ":eq" do
    it "returns true if values are equal" do
    Lisp.parse(:eq, 42, 42).should == true
    end

    it "returns false if values are equal" do
    Lisp.parse(:eq, 42, 43).should == false
    end
    end

    describe ":label" do
    it "sets the value" do
    Lisp.parse(:label, :a, 42)
    Lisp.parse(:a).should == 42
    end
    end

    end
  2. @p8 p8 revised this gist Jan 28, 2012. 2 changed files with 78 additions and 33 deletions.
    30 changes: 13 additions & 17 deletions lithp.rb
    Original file line number Diff line number Diff line change
    @@ -1,21 +1,17 @@
    class Lisp
    Fs = Hash.new(lambda {|fn, *sexp| [fn] + sexp }).merge({
    :label => lambda {|name, value| Fs[name] = lambda { value } },
    :eq => lambda {|a,b| a == b },
    :quote => lambda {|value| value },
    :car => lambda {|list| list[0] },
    :cdr => lambda {|list| list.slice(1, list.size) },
    :cons => lambda {|value, list| [value[0]] + list },
    :if => lambda {|condition, thn, els| condition ? thn : els },
    :atom => lambda {|value| !value.is_a? Array },
    :apply => lambda {|*sexp| Fs.key?(sexp[0]) ? Fs[sexp[0]].call(*sexp.slice(1, sexp.size)) : sexp[0] }
    })
    Fs = {
    :label => lambda {|name,value| Fs[name] = lambda { value } },
    :car => lambda {|sexp| sexp.first },
    :cdr => lambda {|sexp| sexp.slice(1, sexp.size) },
    :cons => lambda {|head,tail| [head] + tail },
    :atom => lambda {|sexp| !sexp.is_a?(Array) },
    :eq => lambda {|a,b| a == b },
    :if => lambda {|cnd,thn,els| cnd ? thn : els }
    }

    def self.eval sexp
    fn, *args = sexp
    if Fs.key?(fn)
    sexp = args.map{|a| Fs[:atom].call(a) ? Fs[:apply].call(a) : eval(a) }
    end
    Fs[fn].call(*sexp)
    def self.parse fn, *sexp
    return sexp.flatten(1) if fn == :quote
    args = sexp.map { |a| Fs[:atom].call(a) ? a : parse(*a) }
    Fs.key?(fn) ? Fs[fn].call(*args) : fn
    end
    end
    81 changes: 65 additions & 16 deletions uthing.rb
    Original file line number Diff line number Diff line change
    @@ -1,29 +1,78 @@
    def assert_equal(a, b)
    a == b or raise "#{a.inspect} should equal #{b.inspect}"
    end
    describe Lisp do

    l = Lisp
    it "works with nested sexps" do
    Lisp.parse(:eq, [:quote, [1, 2, 3]], [:cons, 1, [:quote, [2,3]]]).should == true
    Lisp.parse(*Lisp.parse(:quote, [:cons, 1, [:quote, [2,3]]])).should == [1, 2, 3]
    end

    l.eval [:label, :a, 42]
    describe ":quote" do
    it "returns the sexp" do
    Lisp.parse(:quote, [:eq, 1, 2]).should == [:eq, 1, 2]
    end

    assert_equal 42, l.eval([:a])
    it "returns the value" do
    Lisp.parse(:quote, [1, 2]).should == [1, 2]
    end
    end

    assert_equal true, l.eval([:eq, 42, :a])
    describe ":car" do
    it "returns the head of the sexp" do
    Lisp.parse(:car, [:quote, [1, 2]]).should == 1
    end
    end

    assert_equal true, l.eval([:eq, :a, 42])
    describe ":cdr" do
    it "returns the tail of the sexp" do
    Lisp.parse(:cdr, [:quote, [1, 2, 3]]).should == [2, 3]
    end
    end

    assert_equal [1,2], l.eval([:quote, [1, 2]])
    describe ":cons" do
    it "concats the the first argument to the second" do
    Lisp.parse(:cons, 1, [:quote, [2, 3]]).should == [1, 2, 3]
    end
    end

    assert_equal 1, l.eval([:car, [:quote, [1, 2]]])
    describe ":if" do
    it "returns the third sexp if the first is true" do
    Lisp.parse(:if, [:eq, 2, 2], 42, 43).should == 42
    end

    assert_equal [2], l.eval([:cdr, [:quote, [1, 2]]])
    it "returns the second sexp if the first is false" do
    Lisp.parse(:if, [:eq, 1, 2], 42, 43).should == 43
    end

    assert_equal [1,2,3], l.eval([:cons, 1, [:quote, [2,3]]])
    end

    assert_equal 43, l.eval([:if, [:eq, 1, 2], 42, 43])
    describe ":atom" do
    it "returns true if it's an atom" do
    Lisp.parse(:atom, 42).should == true
    end

    assert_equal false, l.eval([:atom, [:quote, [1,2]]])
    it "returns false if it's not an atom" do
    Lisp.parse(:atom, [:quote, [42]]).should == false
    end

    assert_equal true, l.eval([:atom, 1])
    it "returns false if it's not an atom" do
    Lisp.parse(:atom, [:quote, [42, 43]]).should == false
    end
    end

    assert_equal true, l.eval([:eq, [:quote, [1, 2, 3]], [:cons, 1, [:quote, [2,3]]]])
    describe ":eq" do
    it "returns true if values are equal" do
    Lisp.parse(:eq, 42, 42).should == true
    end

    it "returns false if values are equal" do
    Lisp.parse(:eq, 42, 43).should == false
    end
    end

    describe ":label" do
    it "sets the value" do
    Lisp.parse(:label, :a, 42)
    Lisp.parse(:a).should == 42
    end
    end

    end
  3. @p8 p8 revised this gist Jan 26, 2012. 2 changed files with 33 additions and 47 deletions.
    43 changes: 16 additions & 27 deletions lithp.rb
    Original file line number Diff line number Diff line change
    @@ -1,32 +1,21 @@
    class Lisp
    def initialize
    @env = {
    :label => lambda { |(name,val), _| @env[name] = val },
    :quote => lambda { |sexpr, _| sexpr[0] },
    :car => lambda { |(list), _| list[0] },
    :cdr => lambda { |(list), _| list.drop 1 },
    :cons => lambda { |(e,cell), _| [e] + cell },
    :eq => lambda { |(l,r), _| l == r },
    :if => lambda { |(cond, thn, els), ctx| eval(cond, ctx) ? eval(thn, ctx) : eval(els, ctx) },
    :atom => lambda { |(sexpr), _| (sexpr.is_a? Symbol) or (sexpr.is_a? Numeric) }
    }
    end

    def apply fn, args, ctx=@env
    return @env[fn].call(args, ctx) if @env[fn].respond_to? :call
    Fs = Hash.new(lambda {|fn, *sexp| [fn] + sexp }).merge({
    :label => lambda {|name, value| Fs[name] = lambda { value } },
    :eq => lambda {|a,b| a == b },
    :quote => lambda {|value| value },
    :car => lambda {|list| list[0] },
    :cdr => lambda {|list| list.slice(1, list.size) },
    :cons => lambda {|value, list| [value[0]] + list },
    :if => lambda {|condition, thn, els| condition ? thn : els },
    :atom => lambda {|value| !value.is_a? Array },
    :apply => lambda {|*sexp| Fs.key?(sexp[0]) ? Fs[sexp[0]].call(*sexp.slice(1, sexp.size)) : sexp[0] }
    })

    self.eval @env[fn][2], Hash[*(@env[fn][1].zip args).flatten(1)]
    end

    def eval sexpr, ctx=@env
    if @env[:atom].call [sexpr], ctx
    return ctx[sexpr] if ctx[sexpr]
    return sexpr
    def self.eval sexp
    fn, *args = sexp
    if Fs.key?(fn)
    sexp = args.map{|a| Fs[:atom].call(a) ? Fs[:apply].call(a) : eval(a) }
    end

    fn = sexpr[0]
    args = (sexpr.drop 1)
    args = args.map { |a| self.eval(a, ctx) } if not [:quote, :if].member? fn
    apply(fn, args, ctx)
    Fs[fn].call(*sexp)
    end
    end
    37 changes: 17 additions & 20 deletions uthing.rb
    Original file line number Diff line number Diff line change
    @@ -1,32 +1,29 @@
    l = Lisp.new
    def assert_equal(a, b)
    a == b or raise "#{a.inspect} should equal #{b.inspect}"
    end

    l = Lisp

    l.eval [:label, :a, 42]

    l.eval :a
    #=> 42
    assert_equal 42, l.eval([:a])

    assert_equal true, l.eval([:eq, 42, :a])

    l.eval [:eq, 42, :a]
    #=> true
    assert_equal true, l.eval([:eq, :a, 42])

    l.eval [:quote, [1, 2]]
    #=> [1, 2]
    assert_equal [1,2], l.eval([:quote, [1, 2]])

    l.eval [:car, [:quote, [1, 2]]]
    #=> 1
    assert_equal 1, l.eval([:car, [:quote, [1, 2]]])

    l.eval [:cdr, [:quote, [1, 2]]]
    #=> [2]
    assert_equal [2], l.eval([:cdr, [:quote, [1, 2]]])

    l.eval [:cons, 1, [:quote, [2,3]]]
    #=> [1, 2, 3]
    assert_equal [1,2,3], l.eval([:cons, 1, [:quote, [2,3]]])

    l.eval [:if, [:eq, 1, 2], 42, 43]
    #=> 43
    assert_equal 43, l.eval([:if, [:eq, 1, 2], 42, 43])

    l.eval [:atom, [:quote, [1,2]]]
    #=> false
    assert_equal false, l.eval([:atom, [:quote, [1,2]]])

    l.eval [:label, :second, [:quote, [:lambda, [:x], [:car, [:cdr, :x]]]]]
    assert_equal true, l.eval([:atom, 1])

    l.eval [:second, [:quote, [1, 2, 3]]]
    #=> 2
    assert_equal true, l.eval([:eq, [:quote, [1, 2, 3]], [:cons, 1, [:quote, [2,3]]]])
  4. @fogus fogus revised this gist Jan 25, 2012. 2 changed files with 6 additions and 1 deletion.
    2 changes: 1 addition & 1 deletion lithp.rb
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ def initialize
    def apply fn, args, ctx=@env
    return @env[fn].call(args, ctx) if @env[fn].respond_to? :call

    self.eval @env[fn][2], Hash[*(@env[fn][1].zip args).flatten]
    self.eval @env[fn][2], Hash[*(@env[fn][1].zip args).flatten(1)]
    end

    def eval sexpr, ctx=@env
    5 changes: 5 additions & 0 deletions uthing.rb
    Original file line number Diff line number Diff line change
    @@ -25,3 +25,8 @@

    l.eval [:atom, [:quote, [1,2]]]
    #=> false

    l.eval [:label, :second, [:quote, [:lambda, [:x], [:car, [:cdr, :x]]]]]

    l.eval [:second, [:quote, [1, 2, 3]]]
    #=> 2
  5. @fogus fogus revised this gist Jan 25, 2012. 1 changed file with 4 additions and 12 deletions.
    16 changes: 4 additions & 12 deletions lithp.rb
    Original file line number Diff line number Diff line change
    @@ -8,18 +8,18 @@ def initialize
    :cons => lambda { |(e,cell), _| [e] + cell },
    :eq => lambda { |(l,r), _| l == r },
    :if => lambda { |(cond, thn, els), ctx| eval(cond, ctx) ? eval(thn, ctx) : eval(els, ctx) },
    :atom => lambda { |(thing), _| atom? thing }
    :atom => lambda { |(sexpr), _| (sexpr.is_a? Symbol) or (sexpr.is_a? Numeric) }
    }
    end

    def apply fn, args, ctx=@env
    return @env[fn].call(args, ctx) if primitive? fn
    return @env[fn].call(args, ctx) if @env[fn].respond_to? :call

    self.eval @env[fn][2], Hash[*(@env[fn][1].zip args).flatten]
    end

    def eval sexpr, ctx=@env
    if atom? sexpr
    if @env[:atom].call [sexpr], ctx
    return ctx[sexpr] if ctx[sexpr]
    return sexpr
    end
    @@ -29,12 +29,4 @@ def eval sexpr, ctx=@env
    args = args.map { |a| self.eval(a, ctx) } if not [:quote, :if].member? fn
    apply(fn, args, ctx)
    end

    def primitive? name
    @env[name].respond_to? :call
    end

    def atom? sexpr
    sexpr.is_a? Symbol or sexpr.is_a? Numeric
    end
    end
    end
  6. @fogus fogus revised this gist Jan 25, 2012. 2 changed files with 27 additions and 32 deletions.
    32 changes: 0 additions & 32 deletions lithp.rb
    Original file line number Diff line number Diff line change
    @@ -38,35 +38,3 @@ def atom? sexpr
    sexpr.is_a? Symbol or sexpr.is_a? Numeric
    end
    end

    <<-LISP
    l = Lisp.new
    l.eval [:label, :a, 42]
    l.eval :a
    #=> 42
    l.eval [:eq, 42, :a]
    #=> true
    l.eval [:quote, [1, 2]]
    #=> [1, 2]
    l.eval [:car, [:quote, [1, 2]]]
    #=> 1
    l.eval [:cdr, [:quote, [1, 2]]]
    #=> [2]
    l.eval [:cons, 1, [:quote, [2,3]]]
    #=> [1, 2, 3]
    l.eval [:if, [:eq, 1, 2], 42, 43]
    #=> 43
    l.eval [:atom, [:quote, [1,2]]]
    #=> false
    LISP
    27 changes: 27 additions & 0 deletions uthing.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    l = Lisp.new

    l.eval [:label, :a, 42]

    l.eval :a
    #=> 42

    l.eval [:eq, 42, :a]
    #=> true

    l.eval [:quote, [1, 2]]
    #=> [1, 2]

    l.eval [:car, [:quote, [1, 2]]]
    #=> 1

    l.eval [:cdr, [:quote, [1, 2]]]
    #=> [2]

    l.eval [:cons, 1, [:quote, [2,3]]]
    #=> [1, 2, 3]

    l.eval [:if, [:eq, 1, 2], 42, 43]
    #=> 43

    l.eval [:atom, [:quote, [1,2]]]
    #=> false
  7. @fogus fogus created this gist Jan 25, 2012.
    72 changes: 72 additions & 0 deletions lithp.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    class Lisp
    def initialize
    @env = {
    :label => lambda { |(name,val), _| @env[name] = val },
    :quote => lambda { |sexpr, _| sexpr[0] },
    :car => lambda { |(list), _| list[0] },
    :cdr => lambda { |(list), _| list.drop 1 },
    :cons => lambda { |(e,cell), _| [e] + cell },
    :eq => lambda { |(l,r), _| l == r },
    :if => lambda { |(cond, thn, els), ctx| eval(cond, ctx) ? eval(thn, ctx) : eval(els, ctx) },
    :atom => lambda { |(thing), _| atom? thing }
    }
    end

    def apply fn, args, ctx=@env
    return @env[fn].call(args, ctx) if primitive? fn

    self.eval @env[fn][2], Hash[*(@env[fn][1].zip args).flatten]
    end

    def eval sexpr, ctx=@env
    if atom? sexpr
    return ctx[sexpr] if ctx[sexpr]
    return sexpr
    end

    fn = sexpr[0]
    args = (sexpr.drop 1)
    args = args.map { |a| self.eval(a, ctx) } if not [:quote, :if].member? fn
    apply(fn, args, ctx)
    end

    def primitive? name
    @env[name].respond_to? :call
    end

    def atom? sexpr
    sexpr.is_a? Symbol or sexpr.is_a? Numeric
    end
    end

    <<-LISP
    l = Lisp.new
    l.eval [:label, :a, 42]
    l.eval :a
    #=> 42
    l.eval [:eq, 42, :a]
    #=> true
    l.eval [:quote, [1, 2]]
    #=> [1, 2]
    l.eval [:car, [:quote, [1, 2]]]
    #=> 1
    l.eval [:cdr, [:quote, [1, 2]]]
    #=> [2]
    l.eval [:cons, 1, [:quote, [2,3]]]
    #=> [1, 2, 3]
    l.eval [:if, [:eq, 1, 2], 42, 43]
    #=> 43
    l.eval [:atom, [:quote, [1,2]]]
    #=> false
    LISP