comparison env/lib/python3.7/site-packages/rdflib/plugins/sparql/parserutils.py @ 2:6af9afd405e9 draft

"planemo upload commit 0a63dd5f4d38a1f6944587f52a8cd79874177fc1"
author shellac
date Thu, 14 May 2020 14:56:58 -0400
parents 26e78fe6e8c4
children
comparison
equal deleted inserted replaced
1:75ca89e9b81c 2:6af9afd405e9
1
2 from types import MethodType
3
4 from rdflib.plugins.sparql.compat import OrderedDict
5
6 from pyparsing import TokenConverter, ParseResults
7
8 from rdflib import BNode, Variable, URIRef
9
10 DEBUG = True
11 DEBUG = False
12 if DEBUG:
13 import traceback
14
15 """
16
17 NOTE: PyParsing setResultName/__call__ provides a very similar solution to this
18 I didn't realise at the time of writing and I will remove a
19 lot of this code at some point
20
21 Utility classes for creating an abstract-syntax tree out with pyparsing actions
22
23 Lets you label and group parts of parser production rules
24
25 For example:
26
27 # [5] BaseDecl ::= 'BASE' IRIREF
28 BaseDecl = Comp('Base', Keyword('BASE') + Param('iri',IRIREF))
29
30 After parsing, this gives you back an CompValue object,
31 which is a dict/object with the paramters specified.
32 So you can access the parameters are attributes or as keys:
33
34 baseDecl.iri
35
36 Comp lets you set an evalFn that is bound to the eval method of
37 the resulting CompValue
38
39
40 """
41
42
43 # This is an alternative
44
45 # Comp('Sum')( Param('x')(Number) + '+' + Param('y')(Number) )
46
47 def value(ctx, val, variables=False, errors=False):
48
49 """
50 utility function for evaluating something...
51
52 Variables will be looked up in the context
53 Normally, non-bound vars is an error,
54 set variables=True to return unbound vars
55
56 Normally, an error raises the error,
57 set errors=True to return error
58
59 """
60
61 if isinstance(val, Expr):
62 return val.eval(ctx) # recurse?
63 elif isinstance(val, CompValue):
64 raise Exception("What do I do with this CompValue? %s" % val)
65
66 elif isinstance(val, list):
67 return [value(ctx, x, variables, errors) for x in val]
68
69 elif isinstance(val, (BNode, Variable)):
70 r = ctx.get(val)
71 if isinstance(r, SPARQLError) and not errors:
72 raise r
73 if r is not None:
74 return r
75
76 # not bound
77 if variables:
78 return val
79 else:
80 raise NotBoundError
81
82 elif isinstance(val, ParseResults) and len(val) == 1:
83 return value(ctx, val[0], variables, errors)
84 else:
85 return val
86
87
88 class ParamValue(object):
89 """
90 The result of parsing a Param
91 This just keeps the name/value
92 All cleverness is in the CompValue
93 """
94 def __init__(self, name, tokenList, isList):
95 self.isList = isList
96 self.name = name
97 if isinstance(tokenList, (list, ParseResults)) and len(tokenList) == 1:
98 tokenList = tokenList[0]
99
100 self.tokenList = tokenList
101
102 def __str__(self):
103 return "Param(%s, %s)" % (self.name, self.tokenList)
104
105
106 class Param(TokenConverter):
107 """
108 A pyparsing token for labelling a part of the parse-tree
109 if isList is true repeat occurrences of ParamList have
110 their values merged in a list
111 """
112 def __init__(self, name, expr, isList=False):
113 self.name = name
114 self.isList = isList
115 TokenConverter.__init__(self, expr)
116 self.addParseAction(self.postParse2)
117
118 def postParse2(self, tokenList):
119 return ParamValue(self.name, tokenList, self.isList)
120
121
122 class ParamList(Param):
123 """
124 A shortcut for a Param with isList=True
125 """
126 def __init__(self, name, expr):
127 Param.__init__(self, name, expr, True)
128
129
130 class plist(list):
131 """this is just a list, but we want our own type to check for"""
132
133 pass
134
135
136 class CompValue(OrderedDict):
137
138 """
139 The result of parsing a Comp
140 Any included Params are avaiable as Dict keys
141 or as attributes
142
143 """
144
145 def __init__(self, name, **values):
146 OrderedDict.__init__(self)
147 self.name = name
148 self.update(values)
149
150 def clone(self):
151 return CompValue(self.name, **self)
152
153 def __str__(self):
154 return self.name + "_" + OrderedDict.__str__(self)
155
156 def __repr__(self):
157 return self.name + "_" + dict.__repr__(self)
158
159 def _value(self, val, variables=False, errors=False):
160 if self.ctx is not None:
161 return value(self.ctx, val, variables)
162 else:
163 return val
164
165 def __getitem__(self, a):
166 return self._value(OrderedDict.__getitem__(self, a))
167
168 def get(self, a, variables=False, errors=False):
169 return self._value(OrderedDict.get(self, a, a), variables, errors)
170
171 def __getattr__(self, a):
172 # Hack hack: OrderedDict relies on this
173 if a in ('_OrderedDict__root', '_OrderedDict__end'):
174 raise AttributeError
175 try:
176 return self[a]
177 except KeyError:
178 # raise AttributeError('no such attribute '+a)
179 return None
180
181
182 class Expr(CompValue):
183 """
184 A CompValue that is evaluatable
185 """
186
187 def __init__(self, name, evalfn=None, **values):
188 super(Expr, self).__init__(name, **values)
189
190 self._evalfn = None
191 if evalfn:
192 self._evalfn = MethodType(evalfn, self)
193
194 def eval(self, ctx={}):
195 try:
196 self.ctx = ctx
197 return self._evalfn(ctx)
198 except SPARQLError as e:
199 return e
200 finally:
201 self.ctx = None
202
203
204 class Comp(TokenConverter):
205
206 """
207 A pyparsing token for grouping together things with a label
208 Any sub-tokens that are not Params will be ignored.
209
210 Returns CompValue / Expr objects - depending on whether evalFn is set.
211 """
212
213 def __init__(self, name, expr):
214 TokenConverter.__init__(self, expr)
215 self.name = name
216 self.evalfn = None
217
218 def postParse(self, instring, loc, tokenList):
219
220 if self.evalfn:
221 res = Expr(self.name)
222 res._evalfn = MethodType(self.evalfn, res)
223 else:
224 res = CompValue(self.name)
225
226 for t in tokenList:
227 if isinstance(t, ParamValue):
228 if t.isList:
229 if not t.name in res:
230 res[t.name] = plist()
231 res[t.name].append(t.tokenList)
232 else:
233 res[t.name] = t.tokenList
234 # res.append(t.tokenList)
235 # if isinstance(t,CompValue):
236 # res.update(t)
237 return res
238
239 def setEvalFn(self, evalfn):
240 self.evalfn = evalfn
241 return self
242
243
244 def prettify_parsetree(t, indent='', depth=0):
245 out = []
246 if isinstance(t, ParseResults):
247 for e in t.asList():
248 out.append(prettify_parsetree(e, indent, depth + 1))
249 for k, v in sorted(t.items()):
250 out.append("%s%s- %s:\n" % (indent, ' ' * depth, k))
251 out.append(prettify_parsetree(v, indent, depth + 1))
252 elif isinstance(t, CompValue):
253 out.append("%s%s> %s:\n" % (indent, ' ' * depth, t.name))
254 for k, v in list(t.items()):
255 out.append("%s%s- %s:\n" % (indent, ' ' * (depth + 1), k))
256 out.append(prettify_parsetree(v, indent, depth + 2))
257 elif isinstance(t, dict):
258 for k, v in list(t.items()):
259 out.append("%s%s- %s:\n" % (indent, ' ' * (depth + 1), k))
260 out.append(prettify_parsetree(v, indent, depth + 2))
261 elif isinstance(t, list):
262 for e in t:
263 out.append(prettify_parsetree(e, indent, depth + 1))
264 else:
265 out.append("%s%s- %r\n" % (indent, ' ' * depth, t))
266 return "".join(out)
267
268
269 if __name__ == '__main__':
270 from pyparsing import Word, nums
271 import sys
272
273 Number = Word(nums)
274 Number.setParseAction(lambda x: int(x[0]))
275 Plus = Comp('plus', Param('a', Number) + '+' + Param('b', Number))
276 Plus.setEvalFn(lambda self, ctx: self.a + self.b)
277
278 r = Plus.parseString(sys.argv[1])
279 print(r)
280 print(r[0].eval({}))
281
282 # hurrah for circular imports
283 from rdflib.plugins.sparql.sparql import SPARQLError, NotBoundError