]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/util/parser.py
1 #----------------------------------------------------------------------------
3 # Purpose: parsing utilities
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
14 from activegrid
.util
.lang
import *
20 XPATH_ROOT_VAR
= '__rootObj__'
21 GETOBJECTPARTNAMES
= ["primaryRef", "ref", "orderings", "limit"]
23 class Tokenizer(object):
29 ## TOKEN_PLACEHOLDER = 5
31 def __init__(self
, text
, identStart
=None, tokenSep
=None, ignoreWhitespace
=True):
33 Turn a string into individual tokens. Three types of tokens are recognized:
34 TOKEN_IDENT: identifiers (those that start with the identStart pattern)
35 TOKEN_STRING: quoted string
36 TOKEN_OP: everything else
37 Tokens are separated by white space or the tokenSep pattern.
38 Constructor parameters:
39 text: The string to tokenize
40 identStart: A regular expression describing characters which start an identifier
41 The default expression accepts letters, "_", and "/".
42 tokenSep: A regular expression describing the characters which end a token
43 (in addition to whitespace). The default expression accepts
44 anything except alpha-numerics, "_", "/", and ":".
46 Invoke getNextToken (or next) to get the next token. The instance variables
47 token, and tokenVal will be populated with the current token type (TOKEN_IDENT,
48 TOKEN_STRING, or TOEKN_OP) and value respectively. nextToken and nextTokenVal
49 will also be available for lookahead. The next method is similar to
50 getNextToken but also returns the token value. A value of None signals end
53 self
.ignoreWhitespace
=ignoreWhitespace
55 if (isinstance(text
, array
.array
)):
56 text
= text
.tostring()
58 self
.text
= asString(text
)
60 self
.textLen
= len(self
.text
)
64 self
.nextTokenVal
= None
65 if (identStart
== None):
66 identStart
= "[a-zA-Z_/]"
67 if (tokenSep
== None):
68 tokenSep
= "[^a-zA-Z0-9_/:]"
69 self
.identStart
= re
.compile(identStart
)
70 self
.tokenSep
= re
.compile(tokenSep
)
71 self
.getNextToken() # Prime the pump
73 def isEscaped(text
, index
):
74 if ((index
> 0) and (text
[index
-1] == '\\') and ((index
< 2) or (text
[index
-2] != '\\'))):
77 isEscaped
= staticmethod(isEscaped
)
79 def findClosingQuote(text
, index
, char
):
82 endIndex
= text
.find(char
, index
)
85 if (Tokenizer
.isEscaped(text
, endIndex
)):
90 findClosingQuote
= staticmethod(findClosingQuote
)
92 def _findClosing(self
, char
):
93 if (self
.textIndex
>= self
.textLen
):
94 raise Exception("The text \"%s\" has an unmatched string starting at %d" % (self
.text
, self
.textIndex
))
95 index
= Tokenizer
.findClosingQuote(self
.text
, self
.textIndex
, char
)
97 raise Exception("The text \"%s\" has an unmatched string starting at %d" % (self
.text
, self
.textIndex
-1))
102 if (self
.token
== None):
103 raise StopIteration()
106 def getNextToken(self
):
107 self
.token
= self
.nextToken
108 self
.tokenVal
= self
.nextTokenVal
109 while (self
.textIndex
< self
.textLen
):
110 c
= self
.text
[self
.textIndex
]
111 if (c
not in string
.whitespace
):
112 if (c
== '"' or c
== "'" or c
== '`'):
113 endIndex
= self
._findClosing
(c
)
114 self
.nextToken
= self
.TOKEN_STRING
115 self
.nextTokenVal
= self
.text
[self
.textIndex
:endIndex
]
116 self
.textIndex
= endIndex
118 elif (self
.identStart
.search(c
)):
119 endMatch
= self
.tokenSep
.search(self
.text
, self
.textIndex
+1)
121 endIndex
= endMatch
.start()
123 endIndex
= self
.textLen
124 self
.nextToken
= self
.TOKEN_IDENT
125 self
.nextTokenVal
= self
.text
[self
.textIndex
:endIndex
]
126 self
.textIndex
= endIndex
129 self
.nextToken
= self
.TOKEN_OP
130 endIndex
= self
.textIndex
+ 1
131 if (c
== '<' or c
== '>' or c
== '!' or c
== '='):
132 if ((endIndex
< self
.textLen
) and (self
.text
[endIndex
] == '=')):
134 elif ((c
== '%') and (endIndex
< self
.textLen
)):
135 c
= self
.text
[endIndex
]
136 if (c
in ['d', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f', 'F', 'g', 'G', 'c', 'r', 's', '%']):
138 ## self.nextToken = self.TOKEN_PLACEHOLDER # Should really be this but no one can handle it yet
139 self
.nextTokenVal
= self
.text
[self
.textIndex
:endIndex
]
140 self
.textIndex
= endIndex
142 elif not self
.ignoreWhitespace
:
143 self
.nextToken
=self
.TOKEN_WS
145 while c
in string
.whitespace
:
148 if self
.textIndex
==len(self
.text
):
150 c
=self
.text
[self
.textIndex
]
153 self
.nextToken
= None
154 self
.nextTokenVal
= None
156 def isXPathNonVar(var
):
157 """Returns true iff var is a string ("foo" or 'foo') or a number."""
158 if (var
.startswith("'") and var
.endswith("'")) or \
159 (var
.startswith('"') and var
.endswith('"')):
162 # list from XPathToCode, below
163 if var
.lower() in ["count", "empty", "true", "false", "null", "and", "or", \
172 except ValueError, e
:
177 def xpathToCode(xpaths
, convertBracket
=True):
178 if ((xpaths
== None) or (len(xpaths
) < 1)):
180 if (not isinstance(xpaths
, (list, tuple))):
184 t
= Tokenizer(xpath
, "[a-zA-Z0-9_/:\.]", "[^a-zA-Z0-9_/:\.]", ignoreWhitespace
=False)
187 while t
.nextToken
!= None:
189 if (t
.token
== Tokenizer
.TOKEN_WS
):
191 elif (t
.token
== Tokenizer
.TOKEN_OP
):
192 if (t
.tokenVal
== "="):
194 elif (t
.tokenVal
== "[" and convertBracket
):
196 elif (t
.tokenVal
== "]" and convertBracket
):
199 expr
.append(t
.tokenVal
)
200 elif (t
.token
== Tokenizer
.TOKEN_IDENT
):
201 if (t
.tokenVal
== "and"):
203 elif (t
.tokenVal
== "or"):
205 elif (t
.tokenVal
== "not"):
207 elif (t
.tokenVal
== "like"):
208 # REVIEW stoens@activegrid.com 02-Nov-05 --
209 # This is very limited support for like:
210 # typically like queries look like this: "foo like 'blah%'".
211 # So translate this into "foo.startswith(blah)".
212 # We should use a regular expression to support '%'s in
213 # arbitrary places in the string. After 1.1.
214 if t
.nextToken
and t
.nextTokenVal
.endswith("%'"):
215 t
.getNextToken() # throw away the "like" token
217 expr
[last
] = "%s.startswith(%s')"\
218 % (expr
[last
], t
.tokenVal
[:-2])
221 expr
.append(t
.tokenVal
)
223 elif (t
.tokenVal
== "count"):
225 elif (t
.tokenVal
== 'empty'):
226 expr
.append('ctx.isEmptyPath')
227 elif (t
.tokenVal
== 'true'):
228 expr
.append(_parseConstantFunction(t
, 'True'))
229 elif (t
.tokenVal
== 'false'):
230 expr
.append(_parseConstantFunction(t
, 'False'))
231 elif (t
.tokenVal
== 'null'):
232 expr
.append(_parseConstantFunction(t
, 'None'))
233 elif (-1!=t
.tokenVal
.find(':')):
234 serviceDef
, args
=_parseServiceFunction(t
)
236 # XXX handle serviceDef, args being None
238 for i
in range(len(args
)):
239 args
[i
]=xpathToCode(args
[i
], False)
240 jargs
="[%s]" % (",".join(args
))
242 # XXX should be processmodel.DATASERVICE_PROCESS_NAME, not "dataservice"
243 if serviceDef
[0]=='dataservice':
244 expr
.append("runtimesupport.invokeDataServiceWrapper(%s, %s, ctx, locals())" % \
247 expr
.append("runtimesupport.invokeServiceWrapper(%s, %s, ctx)" % \
250 if (lastToken
==')' or lastToken
==']'):
254 if (t
.tokenVal
.startswith('/')) and not wasFunc
:
255 expr
.append(XPATH_ROOT_VAR
)
256 expr
.append(t
.tokenVal
.replace('/','.'))
259 expr
.append(t
.tokenVal
)
262 if (len(expr
) == 2 and expr
[0]==" "):
265 elif (len(expr
) > 1):
267 result
.append("(%s)" % expr
)
268 elif (len(expr
) > 0):
269 result
.append(expr
[0])
271 return " and ".join(result
)
279 if t
.nextTokenVal
==')':
287 raise Exception("parameters list with no closing ) after token: %s" % t
.tokenVal
)
295 if depth
==0 or (depth
==1 and t
.tokenVal
==','):
302 def _parseServiceFunction(t
):
303 """Parses what appears to be a service function call into serviceDefs and args lists.
305 Returns None, None if the serviceFunction appears to be invalid.
307 if t
.nextTokenVal
!='(':
308 return t
.tokenVal
, None
310 serviceDef
=t
.tokenVal
.split(':')
314 return serviceDef
, args
316 def _parseConstantFunction(t
, outputValue
):
317 firstVal
= t
.tokenVal
318 if t
.nextTokenVal
!= '(':
321 if t
.nextTokenVal
!= ')':
322 return "%s%s" % (firstVal
, '(')
326 def parseDSPredicate(ctx
, str, vars, valueList
=None):
327 from activegrid
.util
.utillang
import evalCode
328 from activegrid
.util
.utillang
import ObjAsDict
330 if valueList
== None:
338 oldIndexVar
= indexVar
339 dollarCurlForm
= False
341 indexVar
= sourceStr
.find("bpws:getVariableData", indexVar
)
343 indexVar
= sourceStr
.find("${", oldIndexVar
)
346 dollarCurlForm
= True
347 if indexVar
> 0 and sourceStr
[indexVar
-1] in ('"',"'"):
349 if not dollarCurlForm
:
350 openParen
= sourceStr
.find("(", indexVar
)
353 closeParen
= sourceStr
.find(")", openParen
)
357 openParen
= indexVar
+1
358 closeParen
= sourceStr
.find("}", openParen
)
361 varRef
= sourceStr
[openParen
+1: closeParen
]
362 if varRef
.startswith('"') or varRef
.startswith("'"):
364 if varRef
.endswith('"') or varRef
.endswith("'"):
366 if isinstance(vars, dict) or isinstance(vars, ObjAsDict
):
367 varRefCode
= xpathToCode(varRef
)
368 value
= evalCode(varRefCode
, vars)
370 value
= ctx
.evalPath(vars, varRef
)
371 inlinedPredicate
.append(sourceStr
[oldIndexVar
:indexVar
])
373 inlinedPredicate
.append("%s" % value
)
375 inlinedPredicate
.append('%s')
376 valueList
.append(value
)
377 indexVar
= closeParen
+1
378 inlinedPredicate
.append(sourceStr
[oldIndexVar
:])
379 qualifications
.append(''.join(inlinedPredicate
))
380 return qualifications
, valueList