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.