]>
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