Wow, that was harder than I expected. The big issue was parsing nested
commands; I couldn't use regexps, so I had to create a little stack-
based parser. Thanks for the fun challenge.
In summary:
puts
MethodMunger.variable_stack( "z.methodx(z.methody(),z.methodz(z.methodx))" )
#=> var0 = z.methodx()
#=> var1 = z.methody()
#=> var2 = z.methodz( var0 )
#=> z.methodx( var1, var2 )
And now, the code, all unit tested:
# Put MWI in the core! enum_for is retarded!
module Enumerable
def map_with_index
idx = -1
map{ |v| yield v,idx+=1 }
end
end
module MethodMunger
require 'strscan'
# MethodMunger.parse_method( "z.foo( bar( x ), y )" )
# #=> {
# #=> :name=>"z.foo",
# #=> :args=>[
# #=> {
# #=> :name=>"bar",
# #=> :args=>[
# #=> {:name=>"x"}
# #=> ]
# #=> },
# #=> {:name=>"y"}
# #=> ]
# #=> }
def self.parse_method( source )
stack =
current_method = nil
mode = :prepare_method
scanner = StringScanner.new( source )
until scanner.eos?
case mode
when :prepare_method
new_method = {}
current_method = new_method
mode = :find_content
when :add_argument
new_method = {}
(current_method[:args] ||= ) << new_method
stack << current_method
current_method = new_method
mode = :find_content
when :find_content
if chunk = scanner.scan( /[\w.]+/ )
current_method[ :name ] = chunk
elsif chunk = scanner.scan( /\(\s*/ )
mode = :add_argument
elsif chunk = scanner.scan( /\s*,\s*/ )
current_method.delete(:args) if current_method[:args] ==
[{}]
current_method = stack.pop
mode = :add_argument
elsif chunk = scanner.scan( /\s*\)/ )
current_method.delete(:args) if current_method[:args] ==
[{}]
current_method = stack.pop
end
end
end
current_method.delete(:args) if current_method[:args] ==
[{}]
current_method
end
def self.variable_stack( source )
method = parse_method( source )
variable_stack_from_parse( method )
end
private
def self.variable_stack_from_parse( method )
if method[:args]
lines =
params = method[:args].map{ |arg|
var_lines = variable_stack_from_parse( arg )
arg_close = var_lines.pop
lines.concat( var_lines )
arg_close
}
total_lines = lines.length
params.each_with_index{ |var,i|
lines << "var#{i+total_lines} = #{var}"
}
lines << "#{method[:name]}( #{
params.map_with_index{ |v,i|
"var#{i + total_lines}"
}.join(', ')
} )"
else
["#{method[:name]}()"]
end
end
end
puts
MethodMunger.variable_stack( "z.methodx(z.methody(),z.methodz(z.methodx))" )
#=> var0 = z.methodx()
#=> var1 = z.methody()
#=> var2 = z.methodz( var0 )
#=> z.methodx( var1, var2 )
if __FILE__==$0
require 'test/unit'
class TestParser < Test::Unit::TestCase
def testa_no_arguments
result = MethodMunger.parse_method "z.go( )"
assert_equal( {
:name=>"z.go"
}, result )
result = MethodMunger.parse_method "z.go()"
assert_equal( {
:name=>"z.go"
}, result )
result = MethodMunger.parse_method "z.go"
assert_equal( {
:name=>"z.go"
}, result )
end
def testb_no_nesting
result = MethodMunger.parse_method "z.go( x )"
assert_equal( {
:name=>"z.go",
:args=>[
{ :name=>"x" }
]
}, result )
result = MethodMunger.parse_method "z.go( x, y )"
assert_equal( {
:name=>"z.go",
:args=>[
{ :name=>"x" },
{ :name=>"y" },
]
}, result )
result = MethodMunger.parse_method "z.go( w, x, y )"
assert_equal( {
:name=>"z.go",
:args=>[
{ :name=>"w" },
{ :name=>"x" },
{ :name=>"y" },
]
}, result )
end
def testc_nesting
result = MethodMunger.parse_method "z.go( x( y ) )"
assert_equal( {
:name=>"z.go",
:args=>[
{
:name=>"x",
:args=>[
{ :name=>"y" }
]
},
]
}, result )
result = MethodMunger.parse_method "z.go( x( y, w ) )"
assert_equal( {
:name=>"z.go",
:args=>[
{
:name=>"x",
:args=>[
{ :name=>"y" },
{ :name=>"w" },
]
},
]
}, result )
result = MethodMunger.parse_method "z.go( foo( bar ),x( y,
w ) )"
assert_equal( {
:name=>"z.go",
:args=>[
{
:name=>"foo",
:args=>[
{ :name=>"bar" },
]
},
{
:name=>"x",
:args=>[
{ :name=>"y" },
{ :name=>"w" },
]
},
]
}, result )
end
end
class TestMunger < Test::Unit::TestCase
def test_a_no_args
result = MethodMunger.variable_stack "z.go()"
assert_equal( ["z.go()"], result )
result = MethodMunger.variable_stack "z.go( )"
assert_equal( ["z.go()"], result )
result = MethodMunger.variable_stack "z.go"
assert_equal( ["z.go()"], result )
end
def test_b_one_arg
result = MethodMunger.variable_stack "z.go(y.foo)"
assert_equal( ["var0 = y.foo()","z.go( var0 )"], result )
result = MethodMunger.variable_stack "z.go( y.foo( ) )"
assert_equal( ["var0 = y.foo()","z.go( var0 )"], result )
end
def test_c_multi_args
result = MethodMunger.variable_stack "z.go(y.foo,x.bar)"
assert_equal( [
"var0 = y.foo()",
"var1 = x.bar()",
"z.go( var0, var1 )"
], result )
result = MethodMunger.variable_stack "z.go( y.foo(), x.bar() )"
assert_equal( [
"var0 = y.foo()",
"var1 = x.bar()",
"z.go( var0, var1 )"
], result )
end
def test_d_nest_singles
result = MethodMunger.variable_stack "z.go( y.foo( x.bar ) )"
assert_equal( [
"var0 = x.bar()",
"var1 = y.foo( var0 )",
"z.go( var1 )"
], result )
result = MethodMunger.variable_stack
"z.go( y.foo( x.bar( w.jim ) ) )"
assert_equal( [
"var0 = w.jim()",
"var1 = x.bar( var0 )",
"var2 = y.foo( var1 )",
"z.go( var2 )"
], result )
end
def test_e_nest_more
result = MethodMunger.variable_stack "z.go( y.foo( x.bar,
w.jim ) )"
assert_equal( [
"var0 = x.bar()",
"var1 = w.jim()",
"var2 = y.foo( var0, var1 )",
"z.go( var2 )"
], result )
end
end
end
···
On Dec 14, 7:03 am, Saladin Mundi <saladin.mu...@gmx.de> wrote:
hey guys
I'm trying to split an string (which contains various method calls).
The result of the splitting of the string should be an array (or
something like that) which contains a sequence which an interpreter
would have called the methods.
Example
class X
def test(input)
...
end
end
x.new.test("z.methodx(z.methody(),z.methodz(z.methodx))")
If I pass "z.methodx(z.methody(),z.methodz(z.methodx))" there should be
afterwards an arry like this:
[0] -> var1 = z.methody()
[1] -> var2 = z.methodx
[2] -> var3 = z.methodz(var2)
[3] -> z.methodx(var1,var3)
so the call(s on different methods) given by an input string shall be
extracted into the real execution sequence like an interpreter would
excecute this string it.
On Dec 14, 7:03 am, Saladin Mundi <saladin.mu...@gmx.de> wrote:
hey guys
I'm trying to split an string (which contains various method calls).
The result of the splitting of the string should be an array (or
something like that) which contains a sequence which an interpreter
would have called the methods.
Example
class X
def test(input)
...
end
end
x.new.test("z.methodx(z.methody(),z.methodz(z.methodx))")
If I pass "z.methodx(z.methody(),z.methodz(z.methodx))" there should be
afterwards an arry like this:
[0] -> var1 = z.methody()
[1] -> var2 = z.methodx
[2] -> var3 = z.methodz(var2)
[3] -> z.methodx(var1,var3)
so the call(s on different methods) given by an input string shall be
extracted into the real execution sequence like an interpreter would
excecute this string it.
I hope you can help me with my problem.