Fixed / overloading when enabling true division.
[tx] / pattern.py
1 #!/usr/bin/env python
2 # -*- coding:utf-8 -*-
3
4 # XSLT Pattern
5 # Copyright (C) 2005  Frédéric Jolliton <frederic@jolliton.com>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 from error import Error
22 import xpath
23 from xpath_misc import lispy
24 import patternparser
25 from context import Context
26 from parser import NoMatch
27
28 def naive( e ) :
29
30         '''Naive pattern implementation.'''
31
32         def processPath( e ) :
33
34                 result = []
35                 last = None
36                 for step in e[ ::-1 ] :
37                         pred = None
38                         if step[ 0 ] == 'predicates' :
39                                 step , pred = step[ 1 ] , step[ 2 : ]
40                         if step[ 0 ] == '/' :
41                                 s = ( 'call' , 'fn:root' )
42                         elif step[ 0 ] == 'call' :
43                                 s = step
44                         elif isinstance( step , tuple ) and len( step ) == 2 :
45                                 axis , test = step
46                                 if last is None :
47                                         if test[ 0 ] == 'document-node' :
48                                                 if axis == 'implicit-child' :
49                                                         axis = 'self'
50                                         elif axis in ( 'implicit-child' , 'child' ) :
51                                                 axis = 'child-or-top'
52                                         elif axis == 'attribute' :
53                                                 axis = 'attribute-or-top'
54                                         else :
55                                                 assert False , 'unexpected axis %r' % ( axis , )
56                                 if axis == 'implicit-child' :
57                                         axis = 'child'
58                                 s = ( axis , test )
59                         else :
60                                 assert False , 'unexpected step %r' % ( step , )
61                         if pred is not None :
62                                 s = ( 'predicates' , s ) + pred
63                         result.append( s )
64                         last = step
65                 return ( 'path' , ) + tuple( result )
66
67         def processEither( e ) :
68
69                 #print 'EITHER' , e
70                 assert False , 'Not implemented yet'
71
72         def process( e ) :
73
74                 if e[ 0 ] == 'path' :
75                         p = processPath( e[ 1 : ] )
76                 elif e[ 0 ] == 'either' :
77                         p = processEither( e[ 1 : ] )
78                 else :
79                         assert False , 'unexpected expr %r' % ( e , )
80                 el = ( 'exprlist' , p )
81                 return ( 'exprlist' , ( 'path' ,
82                                                                 ( 'call' , 'fn:root' ) ,
83                                                                 ( 'descendant-or-self' , ( 'node' , ) ) ,
84                                                                 el ) )
85
86         def translateToXPath( e ) :
87
88                 assert len( e ) == 2 and e[ 0 ] == 'pattern' , 'expected (pattern,<expr>)'
89                 return process( e[ 1 ] )
90
91         return translateToXPath( e )
92
93 def reverse( e ) :
94
95         '''Reverse pattern implementation.'''
96
97         def process( e ) :
98
99                 if e[ 0 ] == 'path' :
100                         return makePath( e )
101                 elif e[ 0 ] == 'either' :
102                         return ( 'union' ,
103                                          process( e[ 1 ] ) ,
104                                          process( e[ 2 ] ) )
105                 else :
106                         assert False , 'Unexpected %r' % ( e , )
107
108         def makePath( e ) :
109
110                 assert e[ 0 ] == 'path'
111                 steps = e[ 1 : ]
112                 result = []
113                 nextAxis = 'self'
114                 for i , step in enumerate( reversed( steps ) ) :
115                         if step == '/' :
116                                 result.append( ( nextAxis , ( 'document' , ) ) )
117                         elif step == '//' :
118                                 nextAxis = 'ancestor'
119                         elif step[ 0 ] == 'call' :
120                                 result.append( step )
121                         else :
122                                 pred = None
123                                 if step[ 0 ] == 'predicates' :
124                                         pred = step[ 2 : ]
125                                         step = step[ 1 ]
126                                 axis , test = step
127                                 if pred is None :
128                                         result.append( ( nextAxis , test ) )
129                                 else :
130                                         # self::<TEST>[PRED] -> ../child::<TEST>[PRED]
131                                         if nextAxis == 'self' :
132                                                 s = ( 'exprlist',
133                                                           ( 'intersect' ,
134                                                                 ( 'path' , '.' ) ,
135                                                                 ( 'path' ,
136                                                                   ( 'parent' , ( 'node' , ) ) ,
137                                                                   ( 'predicates' , ( 'child' , test ) ) + pred ) ) )
138                                         else :
139                                                 s = ( 'predicates' , ( nextAxis , test ) + pred )
140                                         result.append( s )
141                                 nextAxis = 'parent'
142                 return ( 'path' , ) + tuple( result )
143
144         def translateToXPath( e ) :
145
146                 assert len( e ) == 2 and e[ 0 ] == 'pattern' , 'expected (pattern,<expr>)'
147                 return ( 'exprlist' , process( e[ 1 ] ) )
148
149         return translateToXPath( e )
150
151 def compile( s ) :
152
153         e = patternparser.parser( s )
154         if False :
155                 r = naive( e )
156         else :
157                 r = reverse( e )
158         return xpath.makeExprList( r )
159
160 _cache = {}
161
162 class Pattern( object ) :
163
164         __slots__ = [ 'pattern' , 'fun' ]
165
166         def __init__( self , pat ) :
167
168                 self.pattern = pat
169                 if pat in _cache :
170                         self.fun = _cache[ pat ]
171                 else :
172                         try :
173                                 self.fun = compile( pat )
174                         except NoMatch :
175                                 raise Error( 'Unable to parse pattern %r' % pat )
176                         _cache[ pat ] = self.fun
177                         if len( _cache ) > 50 :
178                                 _cache.clear() # FIXME: OUCH!
179
180         def eval( self , node ) :
181
182                 f = self.fun
183                 context = Context()
184                 context.item = node
185                 context.position = 1
186                 context.last = 1
187                 return bool( f( context ) )
188
189 _cachePattern = {}
190
191 def test( node , pat ) :
192
193         patternObject = _cachePattern.get( pat )
194         if patternObject is None :
195                 patternObject = Pattern( pat )
196                 _cachePattern[ pat ] = patternObject
197         return patternObject.eval( node )
198
199 # Local Variables:
200 # tab-width: 4
201 # python-indent: 4
202 # End: