User modules at work: the re_test example

Since we're now able to export entire Python modules with relative ease, let's put one to work. One of the often-touted features of the Python language is its ability to manipulate text easily, and one of the keys to this is its powerful abilities to use and manipulate Unix-style regular expressions. Its main tool for this is the simple but powerful re module, which makes use of the extremely complex but powerful regular expression language. Using re from within Python is extremely easy:

Example 10-6. Using re from within Python

Python 1.5.2 (#1, Sep 17 1999, 20:15:36)  [GCC egcs-2.91.66 19990314/Linux (egcs- on linux-i386
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import re 
>>> r = re.compile( r"^\D*(\d*)" )
>>> m = r.match( "blahblah42blahblah" )
>>> m.groups(0)
('42',)
>>> re.split( "--", "this--is a simple--test" )
['this', 'is a simple', 'test']

It would be nice to have something even close to that expressive power under Eiffel. Using the make targets listed in the previous section, the make_all_eiffel_stubs.py script was able to export the re module and its enclosed classes, allowing us to write the single source program below. If you remove the many std_output.put_string lines which exist to produce output illustrating what's happening, the actual lines of code doing the job are remarkably similar; for example, the Python line

r = re.compile( r"^\D*(\d*)" )

Maps pretty directly into the equivalent Eiffel program line

create r.make_from_python_object( re.compile( << "^\D(\d*)" >> ) )

...which is a little more verbose but still extremely handy. Complete source for the re_test program is below:

Example 10-7. The re_test sample application

class RE_TEST
   
inherit
   PYTHON_CLIENT
      
creation
   make

feature
   
   make is
      local
	 r : RE_REGEXOBJECT
	 s : STRING
	 m : RE_MATCHOBJECT
	 o : PYTHON_OBJECT
	 re : RE
      do
	 std_output.put_string( "%Nre_test : uses Python 're' module to %
                                %manipulate Eiffel strings%N%N" )
	 py_initialize
	 create re.make
	 --create a simple regex that should find the first number in a string...
	 create r.make_from_python_object( re.compile( << "^\D*(\d*)" >> ) )
	 std_output.put_string( r.to_string + "%N" )
	 std_output.put_string( "Regex object created from pattern " + r.get_attr_string( "pattern" ).to_string + "%N" )
	 --now search in a string for the regex
	 s := "blahblah42blahblah"
	 std_output.put_string( "Searching in " + s + "%N" )
	 create m.make_from_python_object( r.match( << s >> ) )
	 std_output.put_string( "Found group: " + m.groups(<<0>>).to_string + "%N" )
	 std_output.put_new_line
	 --now use the module's "split" command to split a string
	 std_output.put_string( "Splitting 'this--is a simple--test' %
                                %on '--'%N" )
	 std_output.put_string( "Result: " + re.split( << "--", "this--is a simple--test" >> ).to_string )
	 std_output.put_new_line
      rescue
	 if not py_err_occurred.is_null then
	    py_err_print
	 end
      end
   
end
   

...which, when executed, produces the following output, strikingly similar to that produced by the Python interpreter above:

Example 10-8. Output from the re_test program

[vputz@yak_prime re]$ ./re_test
re_test : uses Python 're' module to manipulate Eiffel strings

<re.RegexObject instance at 80f3f08>
Regex object created from pattern '^\\D*(\\d*)'
Searching in blahblah42blahblah
Found group: ('42',)

Splitting 'this--is a simple--test' on '--'
Result: ['this', 'is a simple', 'test']