# String splitting problem

**URL:** <https://www.ruby-forum.com/t/string-splitting-problem/123160>\
**Category:** Ruby\
**Created:** [December 14, 2007, 3:03pm UTC](https://www.ruby-forum.com/t/string-splitting-problem/123160 "2007-12-14T15:03:08Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![saladinmundi](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/d07c76/32.png) [@saladinmundi](https://www.ruby-forum.com/u/saladinmundi)\
**Post date:** [December 14, 2007, 3:03pm UTC](https://www.ruby-forum.com/t/string-splitting-problem/123160/1 "2007-12-14T15:03:08Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![saladinmundi](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/d07c76/32.png) [@saladinmundi](https://www.ruby-forum.com/u/saladinmundi)\
**Post date:** [December 15, 2007, 1:44am UTC](https://www.ruby-forum.com/t/string-splitting-problem/123160/2 "2007-12-15T01:44:26Z")

</div>

hey,

I didn’t test this too well, but it works for your string. I doubt  
it’s the most clever way to do it, but it should get the job done.

stack = Array.new  
input = “z.methodx(z.methody(),z.methodz(z.methodx))”

last = 0  
count = 0

while last \< input.length  
cur = input.index(/[)(,]/, last)  
if cur then  
b = input[cur,1]  
if (/[(,]/.match(input[last-1,1]) &&  
/[),]/.match(b)) && cur-last \> 1 then  
count += 1  
puts count.to\_s + " " + input[last, cur-last]  
stack[-1] += count.to\_s  
end

```
            case b
                    when '('
                            stack.push(input[last, cur-last] + "(")
                    when ')'
                            count += 1
                            puts count.to_s + " " + stack.pop + ")"
                            stack[-1] += count.to_s
                    when ','
                            stack[-1] += ','
            end

            last = cur+1
    end

```

end

Joe

---

<div class="post-metadata">

**Author:** ![saladinmundi](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/d07c76/32.png) [@saladinmundi](https://www.ruby-forum.com/u/saladinmundi)\
**Post date:** [December 15, 2007, 2:37am UTC](https://www.ruby-forum.com/t/string-splitting-problem/123160/3 "2007-12-15T02:37:40Z")

</div>

I made a small mistake

change  
stack[-1] += count.to\_s  
to  
stack[-1] += count.to\_s if stack[-1]

---

<div class="post-metadata">

**Author:** ![saladinmundi](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/d07c76/32.png) [@saladinmundi](https://www.ruby-forum.com/u/saladinmundi)\
**Post date:** [December 17, 2007, 1:29pm UTC](https://www.ruby-forum.com/t/string-splitting-problem/123160/4 "2007-12-17T13:29:15Z")

</div>

thank you for all of your great code. I’ll have a close look on it and  
check if its similar to what I had thought of 🙂

thanks again!

---

<div class="post-metadata">

**Author:** ![saladinmundi](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/d07c76/32.png) [@saladinmundi](https://www.ruby-forum.com/u/saladinmundi)\
**Post date:** [December 15, 2007, 5:55am UTC](https://www.ruby-forum.com/t/string-splitting-problem/123160/5 "2007-12-15T05:55:52Z")

</div>

On Dec 14, 7:03 am, Saladin M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> ...
> 
> ```
> 
> [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 M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> ```
> ...
> 
> ```
> 
> [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
