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 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 + 232 +
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