Phrogz
12/15/2007 4:54:00 AM
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.
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