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

> Here is my solution.
> 
> It uses RubyNode (which is available as gem now, see  
> http://rubynode.rubyforge.org/) to access the block's body node and then  
> transforms that body node into the s-expression.
> 
> It is pretty similar to Ryan's ParseTree solution, but supports some  
> additional node types and has some more tests.
> 
> Dominik
> 
> 
> require "rubynode"
> 
> class Node2Sexp
>    # (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
> end
> 
> def sxp(&block)
>    body = block.body_node
>    return nil unless body
>    Node2Sexp.new.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_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

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


-- 
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/
I've added a signing subkey to my GPG key. Please update your keyring.