Code to S-Exp (#95)

On Tue, 26 Sep 2006 05:49:48 +0900, Robin S. wrote:

Hi all,

Here’s my solution which uses Ruby only and modifies core classes and
restores them at the end of the function. I wouldn’t trust it ;).

Maybe this would be a task for Why’s Sandbox library? Create a sandbox,
modify the core classes there and evaluate the blocks there. It’s just
an idea, I don’t know if it’s possible.

Very elegant solution, much more elegant than my own. I’m thinking of
using your code to implement my desired SQL expression generation. I do
suggest blanking the SxpGenerator class first that way built-in methods
will call method_missing (which is the desired behavior).

class SxpGenerator

[SOME CODE SNIPPED as I want people to see what I wrote and not miss it
in
the code.]

def self.restore_methods(mod)
BINARY_METHODS.each do |method|
mod.module_eval do
orig_method = “orig#{method}_”
if method_defined? orig_method

After looking at Borris’ post, where he mentioned throwing lots of
warnings, I tested your code with warnings on. You can keep your code
from
generating warnings by adding one line here (in the space where this
comment is):

remove_method method

(this way, the alias_method doesn’t replace an existing method, thereby
generating warnings)

       alias_method method, orig_method
       remove_method orig_method
     else
       remove_method method
     end
   end
 end

end

end

[REST OF CODE SNIPPED]

def test_variables_from_outside
var=:count
assert_equal [:-,:count,3], sxp { var-3 }
end

This test, which your code passes, also happens to be pretty useful for
my
purposes.

–Ken

On Mon, 25 Sep 2006 23:15:47 +0900, Dominik B. wrote:

fixed argument lists are represented as :array nodes, e.g.

:call nodes: method call with explicit receiver:

def fcall_to_sexp(hash)
def lit_to_sexp(hash)
end
class TestQuiz < Test::Unit::TestCase
end
assert_equal [:+, 3, [:count, :field]], sxp{3+count(:field)}
def test_sxp_lits
end
[:==, [:+, :hello, :world], :helloworld],
sxp{ 3.factorial.abs + 4.factorial * 42 == 4_000_000 + 2**32 + 2.7
end
Is there a way to make your code pass this test (even though it’s not
strictly turning the parse tree into an s-expression anymore?)

 def test_varaible
var=:field
assert_equal [:count, :field], sxp{ count(var) }
 end

On Mon, 25 Sep 2006 23:15:47 +0900, Dominik B. wrote:

fixed argument lists are represented as :array nodes, e.g.

:call nodes: method call with explicit receiver:

def fcall_to_sexp(hash)
def lit_to_sexp(hash)
end
class TestQuiz < Test::Unit::TestCase
end
assert_equal [:+, 3, [:count, :field]], sxp{3+count(:field)}
def test_sxp_lits
end
[:==, [:+, :hello, :world], :helloworld],
sxp{ 3.factorial.abs + 4.factorial * 42 == 4_000_000 + 2**32 + 2.7
end
Is there a way to make your code pass this test (even though it’s not
strictly turning the parse tree into an s-expression anymore?)

 def test_varaible
var=:field
assert_equal [:count, :field], sxp{ count(var) }
 end

Ryan D. wrote:

the current tests pass. I will second the vote that more tests would be
appreciated.

Hit us with your trivial solution I say. It’ll be great advertising
for the project!

I’m wondering how many others do the same thing…

I was going to write it in Scheme, use a Scheme-to-C compiler, and then
interface the C code to Ruby.

On 9/23/06, M. Edward (Ed) Borasky [email protected] wrote:

I was going to write it in Scheme, use a Scheme-to-C compiler, and then
interface the C code to Ruby.

you see when the scales fall from the eyes the world can be seen in an
amazingly clear light.
And that is thanks to you Ed.

First he was typing, than he was ducking!

Modestly I suggest to call Ed the creator of ducktyping, it is second
nature
to me to step back.

Robert

– My psy is a rich man.

Robert D. wrote:

Robert

– My psy is a rich man.

Speaking of duck typing, the first time I heard the phrase “walks like a
duck, quacks like a duck …” was way back when the leader of the
Republican party in the Senate was a man named Everett Dirksen. At one
point there was a debate about whether something was or was not a tax,
and my recollection is that Dirksen used the duck phrase to claim that
it was, in fact, a tax.

“Put it on my bill? What kind of duck do you think I am?”

On Wed, 27 Sep 2006 17:25:12 +0200, Ken B. [email protected] wrote:

Is there a way to make your code pass this test (even though it’s not
strictly turning the parse tree into an s-expression anymore?)
def test_varaible
var=:field
assert_equal [:count, :field], sxp{ count(var) }
end

Yes that is possible by using eval and the block as binding. I
implemented
it for lvars and dvars, but it would be possible to do the same for
instance variables, class variables, …

I also implemented :not nodes as you suggested in the other mail.

Below is the diff and also the complete solution again.

Dominik

— sexp_1.rb 2006-09-27 19:30:05.000000000 +0200
+++ sexp.rb 2006-09-27 19:52:02.000000000 +0200
@@ -2,6 +2,10 @@
require “rubynode”

class Node2Sexp

  • def initialize(binding)
  • @binding = binding
  • end
  • (transformed) nodes are arrays, that look like:

    [:type, attribute hash or array of nodes]

    def to_sexp(node)
    @@ -52,12 +56,33 @@
    def nil_to_sexp(hash) nil end
    def false_to_sexp(hash) false end
    def true_to_sexp(hash) true end
  • :lvar nodes: local variables

  • var => [:lvar, {:cnt=>3, :vid=>:var}] # cnt is the index in the

lvar
table

  • def lvar_to_sexp(hash)
  • eval(hash[:vid].to_s, @binding)
  • end
  • :dvar nodes: block local variables

  • var => [:dvar, {:vid=>:var}]

  • alias dvar_to_sexp lvar_to_sexp
  • :not nodes: boolean negation

  • not :field => [:not, {:body=>[:lit, {:lit=>:field}]}]

  • !:field => [:not, {:body=>[:lit, {:lit=>:field}]}]

  • def not_to_sexp(hash)
  • body = to_sexp(hash[:body])
  • if Array === body && body[0] == :== && body.size == 3
  •  [:"!=", body[1], body[2]]
    
  • else
  •  [:not, body]
    
  • end
  • end
    end

def sxp(&block)
body = block.body_node
return nil unless body

  • Node2Sexp.new.to_sexp(body.transform)
  • Node2Sexp.new(block).to_sexp(body.transform)
    end

if $0 == FILE then
@@ -113,6 +138,20 @@
assert_equal [:==, :field1, :field2], sxp{:field1 == :field2 }
end

  • def test_sxp_variables
  •  lvar = :field # local variable
    
  •  assert_equal [:count, :field], sxp{ count(lvar) }
    
  •  proc {
    
  •    dvar = :field2 # dynavar (block local variable)
    
  •    assert_equal [:==, :field, :field2], sxp{ lvar == dvar }
    
  •  }.call
    
  • end
  • def test_sxp_not
  •  assert_equal [:not, :field], sxp{ not :field }
    
  •  assert_equal [:"!=", :a, :b], sxp{ :a != :b }
    
  • end
  • def test_sxp_from_sander_dot_land_at_gmail_com
      assert_equal [:==,[:^, 2, 3], [:^, 1, 1]], sxp{ 2^3 == 1^1}
      assert_equal [:==, [:+, 3.0, 0.1415], 3], sxp{3.0 + 0.1415 == 3}
    

==================================

require “rubynode”

class Node2Sexp
def initialize(binding)
@binding = binding
end

(transformed) nodes are arrays, that look like:

[:type, attribute hash or array of nodes]

def to_sexp(node)
node && send(“#{node.first}_to_sexp”, node.last)
end

fixed argument lists are represented as :array nodes, e.g.

[:array, [argnode1, argnode2, …]]

def process_args(args_node)
return [] unless args_node
if args_node.first == :array
args_node.last.map { |node| to_sexp(node) }
else
raise “variable arguments not allowed”
end
end

:call nodes: method call with explicit receiver:

nil.foo => [:call, {:args=>false, :mid=>:foo, :recv=>[:nil, {}]}]

nil == nil =>

[:call, {:args=>[:array, [[:nil, {}]]], :mid=>:==, :recv=>[:nil,

{}]}]
def call_to_sexp(hash)
[hash[:mid], to_sexp(hash[:recv]), *process_args(hash[:args])]
end

:fcall nodes: function call (no explicit receiver):

foo() => [:fcall, {:args=>false, :mid=>:foo}]

foo(nil) => [:fcall, {:args=>[:array, [[:nil, {}]]], :mid=>:foo]

def fcall_to_sexp(hash)
[hash[:mid], *process_args(hash[:args])]
end

:vcall nodes: function call that looks like variable

foo => [:vcall, {:mid=>:foo}]

alias vcall_to_sexp fcall_to_sexp

:lit nodes: literals

1 => [:lit, {:lit=>1}]

:abc => [:lit, {:lit=>:abc}]

def lit_to_sexp(hash)
hash[:lit]
end

:str nodes: strings without interpolation

“abc” => [:str, {:lit=>“abc”}]

alias str_to_sexp lit_to_sexp

def nil_to_sexp(hash) nil end
def false_to_sexp(hash) false end
def true_to_sexp(hash) true end

:lvar nodes: local variables

var => [:lvar, {:cnt=>3, :vid=>:var}] # cnt is the index in the

lvar
table
def lvar_to_sexp(hash)
eval(hash[:vid].to_s, @binding)
end

:dvar nodes: block local variables

var => [:dvar, {:vid=>:var}]

alias dvar_to_sexp lvar_to_sexp

:not nodes: boolean negation

not :field => [:not, {:body=>[:lit, {:lit=>:field}]}]

!:field => [:not, {:body=>[:lit, {:lit=>:field}]}]

def not_to_sexp(hash)
body = to_sexp(hash[:body])
if Array === body && body[0] == :== && body.size == 3
[:“!=”, body[1], body[2]]
else
[:not, body]
end
end
end

def sxp(&block)
body = block.body_node
return nil unless body
Node2Sexp.new(block).to_sexp(body.transform)
end

if $0 == FILE then
require ‘test/unit’

class TestQuiz < Test::Unit::TestCase
def test_sxp_nested_calls
assert_equal [:max, [:count, :name]], sxp{max(count(:name))}
end

 def test_sxp_vcall
   assert_equal [:abc], sxp{abc}
 end

 def test_sxp_call_plus_eval
   assert_equal [:count, [:+, 3, 7]], sxp{count(3+7)}
 end

 def test_sxp_call_with_multiple_args
   assert_equal [:count, 3, 7], sxp{count(3,7)}
 end

 def test_sxp_binarymsg_mixed_1
   assert_equal [:+, 3, :symbol], sxp{3+:symbol}
 end

 def test_sxp_binarymsg_mixed_call
   assert_equal [:+, 3, [:count, :field]], sxp{3+count(:field)}
 end

 def test_sxp_binarymsg_mixed_2
   assert_equal [:/, 7, :field], sxp{7/:field}
 end

 def test_sxp_binarymsg_mixed_3
   assert_equal [:>, :field, 5], sxp{:field > 5}
 end

 def test_sxp_lits
   assert_equal 8, sxp{8}
 end

 def test_sxp_true_false_nil
   assert_equal [:+, true, false], sxp{true+false}
   assert_equal nil, sxp{nil}
 end

 def test_sxp_empty
   assert_equal nil, sxp{}
 end

 def test_sxp_binarymsg_syms
   assert_equal [:==, :field1, :field2], sxp{:field1 == :field2 }
 end

 def test_sxp_variables
   lvar = :field # local variable
   assert_equal [:count, :field], sxp{ count(lvar) }
   proc {
     dvar = :field2 # dynavar (block local variable)
     assert_equal [:==, :field, :field2], sxp{ lvar == dvar }
   }.call
 end

 def test_sxp_not
   assert_equal [:not, :field], sxp{ not :field }
   assert_equal [:"!=", :a, :b], sxp{ :a != :b }
 end

 def test_sxp_from_sander_dot_land_at_gmail_com
   assert_equal [:==,[:^, 2, 3], [:^, 1, 1]], sxp{ 2^3 == 1^1}
   assert_equal [:==, [:+, 3.0, 0.1415], 3], sxp{3.0 + 0.1415 == 3}

   assert_equal([:|,
                 [:==, [:+, :hello, :world], :helloworld],
                 [:==, [:+, [:+, "hello", " "], "world"], "hello

world"]] ,
sxp {
(:hello + :world == :helloworld) |
(‘hello’ + ’ ’ + ‘world’ == ‘hello world’)
})

   assert_equal  [:==, [:+, [:abs, [:factorial, 3]], [:*, 

[:factorial,
4], 42]],
[:+, [:+, 4000000, [:, 2, 32]], [:%, 2.7,
1.1]]],
sxp{ 3.factorial.abs + 4.factorial * 42 == 4_000_000 + 2
32 +
2.7
% 1.1 }
end

 def test_ihavenocluewhy
   assert_equal 11, 5 + 6
   assert_raise(TypeError) { 7 / :field }
   assert_raise(NoMethodError) { 7+count(:field) }
   assert_raise(NoMethodError) { :field > 5 }
 end

end
end

M. Edward (Ed) Borasky wrote:

Speaking of duck typing, the first time I heard the phrase “walks like a
duck, quacks like a duck …” was way back when the leader of the
Republican party in the Senate was a man named Everett Dirksen. At one
point there was a debate about whether something was or was not a tax,
and my recollection is that Dirksen used the duck phrase to claim that
it was, in fact, a tax.

The first time I heard the phrase was in 1980 or '81 when my friend
Jamie used it to me. I think that was long after Dirksen’s time,
but I’m not much on history.

Hal

Hal F. wrote:

The first time I heard the phrase was in 1980 or '81 when my friend
Jamie used it to me. I think that was long after Dirksen’s time,
but I’m not much on history.

Hal

Well, it turns out Dirksen most likely got it from Richard Cardinal
Cushing – no telling where Cushing got it from:

Hal F. wrote:

The first time I heard the phrase was in 1980 or '81 when my friend
Jamie used it to me. I think that was long after Dirksen’s time,
but I’m not much on history.

I thought it came from:

“If it looks like a duck, walks like a duck, and quacks like a duck,
it’s probably a duck.” - Senator Joseph McCarthy, in a 1952 speech,
suggesting a method for identifying communists and communist
sympathizers.

Referenced at http://www.gantthead.com/content/articles/86476.cfm

Maybe there was an even earlier reference.

-flp

Music: http://www.myspace.com/acidredux

Links: http://del.icio.us/furlan

Home: http://thispaceavailable.uxb.net/index.html

We are here to laugh at the odds and live our lives so well that Death
will tremble to take us.
– Charles Bukowski