This is Ruby 1.8.7 but should be same as for 1.9.x
I am trying to split a string for example:
a = \"foo.bar.size.split(\'.\').last\"
a = "foo.bar.size.split('.').last"
p a.split(/(?<!')\.(?!')/)
#=> ["foo", "bar", "size", "split('.')", "last"]
You are looking for Lookahead and Lookbehind assertions. http://www.regular-expressions.info/lookaround.html
I think this would do it:
a.split(/\.(?=[\w])/)
I don't know how much you know about regex, but the (?=[\w])
is a lookahead that says "only match the dot if the next character is a letter kind of character". A lookahead won't actually grab the text it matches. It just "looks". So the result is exactly what you're looking for:
> a.split(/\.(?=[\w])/)
=> ["foo", "bar", "size", "split('.')", "last"]
here I don't have ruby env. I tried with python re.split().
In : re.split("(?<!')\.(?!')",a)
Out: ['foo', 'bar', 'size', "split('.')", 'last']
the regex above has negative lookahead AND lookbehind, to make sure only the "dot" between single quotes won't work as separator.
of course, for the given example by you, one of lookbehind or lookahead is sufficient. you can choose the right way for your requirement.
I'm afraid that regular expressions won't take you very far. Consider for example the following expressions (which are also valid Ruby)
"(foo.bar.size.split( '.' )).last"
"(foo.bar.size.split '.').last"
"(foo.bar.size.split '( . ) . .(). .').last"
The problem is, that the list of calls is actually a tree of calls. The easiest solution in sight is probably to use a Ruby parser and transform the parse tree according to your needs (in this example we are recursively descending into the call tree, gathering the calls into a list):
# gem install ruby_parser
# gem install awesome_print
require 'ruby_parser'
require 'ap'
def calls_as_list code
tree = RubyParser.new.parse(code)
t = tree
calls = []
while t
# gather arguments if present
args = nil
if t[3][0] == :arglist
args = t[3][1..-1].to_a
end
# append all information to our list
calls << [t[2].to_s, args]
# descend to next call
t = t[1]
end
calls.reverse
end
p calls_as_list "foo.bar.size.split('.').last"
#=> [["foo", []], ["bar", []], ["size", []], ["split", [[:str, "."]]], ["last", []]]
p calls_as_list "puts 3, 4"
#=> [["puts", [[:lit, 3], [:lit, 4]]]]
And to show the parse tree of any input:
ap RubyParser.new.parse("puts 3, 4")