jsonata-python 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jsonata/__init__.py +10 -0
- jsonata/cli/__init__.py +0 -0
- jsonata/cli/__main__.py +242 -0
- jsonata/constants.py +68 -0
- jsonata/datetimeutils.py +1144 -0
- jsonata/functions.py +2179 -0
- jsonata/jexception.py +232 -0
- jsonata/jsonata.py +2045 -0
- jsonata/parser.py +1397 -0
- jsonata/signature.py +441 -0
- jsonata/timebox.py +89 -0
- jsonata/tokenizer.py +306 -0
- jsonata/utils.py +150 -0
- jsonata_python-0.1.0.dist-info/METADATA +338 -0
- jsonata_python-0.1.0.dist-info/RECORD +17 -0
- jsonata_python-0.1.0.dist-info/WHEEL +4 -0
- jsonata_python-0.1.0.dist-info/licenses/LICENSE +202 -0
jsonata/parser.py
ADDED
|
@@ -0,0 +1,1397 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright Robert Yokota
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License")
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
# Derived from the following code:
|
|
17
|
+
#
|
|
18
|
+
# Project name: jsonata-java
|
|
19
|
+
# Copyright Dashjoin GmbH. https://dashjoin.com
|
|
20
|
+
# Licensed under the Apache License, Version 2.0 (the "License")
|
|
21
|
+
#
|
|
22
|
+
# Project name: JSONata
|
|
23
|
+
# © Copyright IBM Corp. 2016, 2018 All Rights Reserved
|
|
24
|
+
# This project is licensed under the MIT License, see LICENSE
|
|
25
|
+
#
|
|
26
|
+
|
|
27
|
+
import copy
|
|
28
|
+
from typing import Any, MutableSequence, Optional, Sequence
|
|
29
|
+
|
|
30
|
+
from jsonata import jexception, tokenizer, signature, utils
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# var parseSignature = require('./signature')
|
|
34
|
+
class Parser:
|
|
35
|
+
|
|
36
|
+
# This parser implements the 'Top down operator precedence' algorithm developed by Vaughan R Pratt; http://dl.acm.org/citation.cfm?id=512931.
|
|
37
|
+
# and builds on the Javascript framework described by Douglas Crockford at http://javascript.crockford.com/tdop/tdop.html
|
|
38
|
+
# and in 'Beautiful Code', edited by Andy Oram and Greg Wilson, Copyright 2007 O'Reilly Media, Inc. 798-0-596-51004-6
|
|
39
|
+
|
|
40
|
+
# var parser = function (source, recover) {
|
|
41
|
+
|
|
42
|
+
def remaining_tokens(self) -> list[tokenizer.Tokenizer.Token]:
|
|
43
|
+
remaining = []
|
|
44
|
+
if self.node.id != "(end)":
|
|
45
|
+
t = tokenizer.Tokenizer.Token(self.node.type, self.node.value, self.node.position)
|
|
46
|
+
remaining.append(t)
|
|
47
|
+
nxt = self.lexer.next(False)
|
|
48
|
+
while nxt is not None:
|
|
49
|
+
remaining.append(nxt)
|
|
50
|
+
nxt = self.lexer.next(False)
|
|
51
|
+
return remaining
|
|
52
|
+
|
|
53
|
+
class Symbol:
|
|
54
|
+
# Symbol s
|
|
55
|
+
|
|
56
|
+
# Procedure:
|
|
57
|
+
|
|
58
|
+
# Infix attributes
|
|
59
|
+
# where rhs = list of Symbol pairs
|
|
60
|
+
# where rhs = list of Symbols
|
|
61
|
+
|
|
62
|
+
# Ternary operator:
|
|
63
|
+
|
|
64
|
+
# processAST error handling
|
|
65
|
+
|
|
66
|
+
# Prefix attributes
|
|
67
|
+
|
|
68
|
+
# Ancestor attributes
|
|
69
|
+
|
|
70
|
+
def nud(self):
|
|
71
|
+
# error - symbol has been invoked as a unary operator
|
|
72
|
+
_err = jexception.JException("S0211", self.position, self.value)
|
|
73
|
+
|
|
74
|
+
if self._outer_instance.recover:
|
|
75
|
+
#
|
|
76
|
+
# err.remaining = remainingTokens()
|
|
77
|
+
# err.type = "error"
|
|
78
|
+
# errors.add(err)
|
|
79
|
+
# return err
|
|
80
|
+
#
|
|
81
|
+
return Parser.Symbol("(error)")
|
|
82
|
+
else:
|
|
83
|
+
raise _err
|
|
84
|
+
|
|
85
|
+
def led(self, left):
|
|
86
|
+
raise NotImplementedError("led not implemented")
|
|
87
|
+
|
|
88
|
+
_outer_instance: 'Parser'
|
|
89
|
+
id: Optional[str]
|
|
90
|
+
type: Optional[str]
|
|
91
|
+
value: Optional[Any]
|
|
92
|
+
bp: int
|
|
93
|
+
lbp: int
|
|
94
|
+
position: int
|
|
95
|
+
keep_array: bool
|
|
96
|
+
descending: bool
|
|
97
|
+
expression: 'Optional[Parser.Symbol]'
|
|
98
|
+
seeking_parent: 'Optional[MutableSequence[Parser.Symbol]]'
|
|
99
|
+
errors: Optional[Sequence[Exception]]
|
|
100
|
+
steps: 'Optional[MutableSequence[Parser.Symbol]]'
|
|
101
|
+
slot: 'Optional[Parser.Symbol]'
|
|
102
|
+
next_function: 'Optional[Parser.Symbol]'
|
|
103
|
+
keep_singleton_array: bool
|
|
104
|
+
consarray: bool
|
|
105
|
+
level: int
|
|
106
|
+
focus: Optional[Any]
|
|
107
|
+
token: Optional[Any]
|
|
108
|
+
thunk: bool
|
|
109
|
+
|
|
110
|
+
# Procedure:
|
|
111
|
+
procedure: 'Optional[Parser.Symbol]'
|
|
112
|
+
arguments: 'Optional[MutableSequence[Parser.Symbol]]'
|
|
113
|
+
body: 'Optional[Parser.Symbol]'
|
|
114
|
+
predicate: 'Optional[MutableSequence[Parser.Symbol]]'
|
|
115
|
+
stages: 'Optional[MutableSequence[Parser.Symbol]]'
|
|
116
|
+
input: Optional[Any]
|
|
117
|
+
# environment: jsonata.Jsonata.Frame | None # creates circular ref
|
|
118
|
+
tuple: Optional[Any]
|
|
119
|
+
expr: Optional[Any]
|
|
120
|
+
group: 'Optional[Parser.Symbol]'
|
|
121
|
+
name: 'Optional[Parser.Symbol]'
|
|
122
|
+
|
|
123
|
+
# Infix attributes
|
|
124
|
+
lhs: 'Optional[Parser.Symbol]'
|
|
125
|
+
rhs: 'Optional[Parser.Symbol]'
|
|
126
|
+
|
|
127
|
+
# where rhs = list of Symbol pairs
|
|
128
|
+
lhs_object: 'Optional[Sequence[Sequence[Parser.Symbol]]]'
|
|
129
|
+
rhs_object: 'Optional[Sequence[Sequence[Parser.Symbol]]]'
|
|
130
|
+
|
|
131
|
+
# where rhs = list of Symbols
|
|
132
|
+
rhs_terms: 'Optional[Sequence[Parser.Symbol]]'
|
|
133
|
+
terms: 'Optional[Sequence[Parser.Symbol]]'
|
|
134
|
+
|
|
135
|
+
# Ternary operator:
|
|
136
|
+
condition: 'Optional[Parser.Symbol]'
|
|
137
|
+
then: 'Optional[Parser.Symbol]'
|
|
138
|
+
_else: 'Optional[Parser.Symbol]'
|
|
139
|
+
|
|
140
|
+
expressions: 'Optional[MutableSequence[Parser.Symbol]]'
|
|
141
|
+
|
|
142
|
+
# processAST error handling
|
|
143
|
+
error: 'Optional[jexception.JException]'
|
|
144
|
+
signature: 'Optional[Any]'
|
|
145
|
+
|
|
146
|
+
# Prefix attributes
|
|
147
|
+
pattern: 'Optional[Parser.Symbol]'
|
|
148
|
+
update: 'Optional[Parser.Symbol]'
|
|
149
|
+
delete: 'Optional[Parser.Symbol]'
|
|
150
|
+
|
|
151
|
+
# Ancestor attributes
|
|
152
|
+
label: Optional[str]
|
|
153
|
+
index: Optional[Any]
|
|
154
|
+
_jsonata_lambda: bool
|
|
155
|
+
ancestor: 'Optional[Parser.Symbol]'
|
|
156
|
+
|
|
157
|
+
def __init__(self, outer_instance, id=None, bp=0):
|
|
158
|
+
self._outer_instance = outer_instance
|
|
159
|
+
|
|
160
|
+
self.id = id
|
|
161
|
+
self.value = id
|
|
162
|
+
self.bp = bp
|
|
163
|
+
# use register(Symbol) ! Otherwise inheritance doesn't work
|
|
164
|
+
# Symbol s = symbolTable.get(id)
|
|
165
|
+
# //bp = bp != 0 ? bp : 0
|
|
166
|
+
# if (s != null) {
|
|
167
|
+
# if (bp >= s.lbp) {
|
|
168
|
+
# s.lbp = bp
|
|
169
|
+
# }
|
|
170
|
+
# } else {
|
|
171
|
+
# s = new Symbol()
|
|
172
|
+
# s.value = s.id = id
|
|
173
|
+
# s.lbp = bp
|
|
174
|
+
# symbolTable.put(id, s)
|
|
175
|
+
# }
|
|
176
|
+
#
|
|
177
|
+
#
|
|
178
|
+
# return s
|
|
179
|
+
|
|
180
|
+
self.type = None
|
|
181
|
+
self.lbp = 0
|
|
182
|
+
self.position = 0
|
|
183
|
+
self.keep_array = False
|
|
184
|
+
self.descending = False
|
|
185
|
+
self.expression = None
|
|
186
|
+
self.seeking_parent = None
|
|
187
|
+
self.errors = None
|
|
188
|
+
self.steps = None
|
|
189
|
+
self.slot = None
|
|
190
|
+
self.next_function = None
|
|
191
|
+
self.keep_singleton_array = False
|
|
192
|
+
self.consarray = False
|
|
193
|
+
self.level = 0
|
|
194
|
+
self.focus = None
|
|
195
|
+
self.token = None
|
|
196
|
+
self.thunk = False
|
|
197
|
+
self.procedure = None
|
|
198
|
+
self.arguments = None
|
|
199
|
+
self.body = None
|
|
200
|
+
self.predicate = None
|
|
201
|
+
self.stages = None
|
|
202
|
+
self.input = None
|
|
203
|
+
self.environment = None
|
|
204
|
+
self.tuple = None
|
|
205
|
+
self.expr = None
|
|
206
|
+
self.group = None
|
|
207
|
+
self.name = None
|
|
208
|
+
self.lhs = None
|
|
209
|
+
self.rhs = None
|
|
210
|
+
self.lhs_object = None
|
|
211
|
+
self.rhs_object = None
|
|
212
|
+
self.rhs_terms = None
|
|
213
|
+
self.terms = None
|
|
214
|
+
self.condition = None
|
|
215
|
+
self.then = None
|
|
216
|
+
self._else = None
|
|
217
|
+
self.expressions = None
|
|
218
|
+
self.error = None
|
|
219
|
+
self.signature = None
|
|
220
|
+
self.pattern = None
|
|
221
|
+
self.update = None
|
|
222
|
+
self.delete = None
|
|
223
|
+
self.label = None
|
|
224
|
+
self.index = None
|
|
225
|
+
self._jsonata_lambda = False
|
|
226
|
+
self.ancestor = None
|
|
227
|
+
|
|
228
|
+
def create(self):
|
|
229
|
+
# We want a shallow clone (do not duplicate outer class!)
|
|
230
|
+
cl = self.clone()
|
|
231
|
+
# System.err.println("cloning "+this+" clone="+cl)
|
|
232
|
+
return cl
|
|
233
|
+
|
|
234
|
+
def clone(self):
|
|
235
|
+
return copy.copy(self)
|
|
236
|
+
|
|
237
|
+
def __repr__(self):
|
|
238
|
+
return str(type(self)) + " " + self.id + " value=" + self.value
|
|
239
|
+
|
|
240
|
+
def register(self, t: Symbol) -> None:
|
|
241
|
+
|
|
242
|
+
# if (t instanceof Infix || t instanceof InfixR) return
|
|
243
|
+
|
|
244
|
+
s = self.symbol_table.get(t.id)
|
|
245
|
+
if s is not None:
|
|
246
|
+
if self.dbg:
|
|
247
|
+
print("Symbol in table " + t.id + " " + str(type(s)) + " -> " + str(type(t)))
|
|
248
|
+
# symbolTable.put(t.id, t)
|
|
249
|
+
if t.bp >= s.lbp:
|
|
250
|
+
if self.dbg:
|
|
251
|
+
print("Symbol in table " + t.id + " lbp=" + str(s.lbp) + " -> " + str(t.bp))
|
|
252
|
+
s.lbp = t.bp
|
|
253
|
+
else:
|
|
254
|
+
s = t.create()
|
|
255
|
+
s.value = s.id = t.id
|
|
256
|
+
s.lbp = t.bp
|
|
257
|
+
self.symbol_table[t.id] = s
|
|
258
|
+
|
|
259
|
+
def handle_error(self, err: jexception.JException) -> Symbol:
|
|
260
|
+
if self.recover:
|
|
261
|
+
err.remaining = self.remaining_tokens()
|
|
262
|
+
self.errors.append(err)
|
|
263
|
+
# Symbol symbol = symbolTable.get("(error)")
|
|
264
|
+
node = Parser.Symbol(self)
|
|
265
|
+
# FIXME node.error = err
|
|
266
|
+
# node.type = "(error)"
|
|
267
|
+
return node
|
|
268
|
+
else:
|
|
269
|
+
raise err
|
|
270
|
+
|
|
271
|
+
# }
|
|
272
|
+
|
|
273
|
+
def advance(self, id: Optional[str] = None, infix: bool = False) -> Symbol:
|
|
274
|
+
if id is not None and self.node.id != id:
|
|
275
|
+
code = None
|
|
276
|
+
if self.node.id == "(end)":
|
|
277
|
+
# unexpected end of buffer
|
|
278
|
+
code = "S0203"
|
|
279
|
+
else:
|
|
280
|
+
code = "S0202"
|
|
281
|
+
err = jexception.JException(code, self.node.position, id, self.node.value)
|
|
282
|
+
return self.handle_error(err)
|
|
283
|
+
next_token = self.lexer.next(infix)
|
|
284
|
+
if self.dbg:
|
|
285
|
+
print("nextToken " + (next_token.type if next_token is not None else None))
|
|
286
|
+
if next_token is None:
|
|
287
|
+
self.node = self.symbol_table["(end)"]
|
|
288
|
+
self.node.position = len(self.source)
|
|
289
|
+
return self.node
|
|
290
|
+
value = next_token.value
|
|
291
|
+
type = next_token.type
|
|
292
|
+
symbol = None
|
|
293
|
+
if type == "name" or type == "variable":
|
|
294
|
+
symbol = self.symbol_table["(name)"]
|
|
295
|
+
elif type == "operator":
|
|
296
|
+
symbol = self.symbol_table[str(value)]
|
|
297
|
+
if symbol is None:
|
|
298
|
+
return self.handle_error(jexception.JException("S0204", next_token.position, value))
|
|
299
|
+
elif type == "string" or type == "number" or type == "value":
|
|
300
|
+
symbol = self.symbol_table["(literal)"]
|
|
301
|
+
elif type == "regex":
|
|
302
|
+
type = "regex"
|
|
303
|
+
symbol = self.symbol_table["(regex)"]
|
|
304
|
+
# istanbul ignore next
|
|
305
|
+
else:
|
|
306
|
+
return self.handle_error(jexception.JException("S0205", next_token.position, value))
|
|
307
|
+
|
|
308
|
+
self.node = symbol.create()
|
|
309
|
+
# Token node = new Token(); //Object.create(symbol)
|
|
310
|
+
self.node.value = value
|
|
311
|
+
self.node.type = type
|
|
312
|
+
self.node.position = next_token.position
|
|
313
|
+
if self.dbg:
|
|
314
|
+
print("advance " + str(self.node))
|
|
315
|
+
return self.node
|
|
316
|
+
|
|
317
|
+
# Pratt's algorithm
|
|
318
|
+
def expression(self, rbp: int) -> Symbol:
|
|
319
|
+
left = None
|
|
320
|
+
t = self.node
|
|
321
|
+
self.advance(None, True)
|
|
322
|
+
left = t.nud()
|
|
323
|
+
while rbp < self.node.lbp:
|
|
324
|
+
t = self.node
|
|
325
|
+
self.advance(None, False)
|
|
326
|
+
if self.dbg:
|
|
327
|
+
print("t=" + str(t) + ", left=" + left.type)
|
|
328
|
+
left = t.led(left)
|
|
329
|
+
return left
|
|
330
|
+
|
|
331
|
+
class Terminal(Symbol):
|
|
332
|
+
_outer_instance: 'Parser'
|
|
333
|
+
|
|
334
|
+
def __init__(self, outer_instance, id):
|
|
335
|
+
super().__init__(outer_instance, id, 0)
|
|
336
|
+
self._outer_instance = outer_instance
|
|
337
|
+
|
|
338
|
+
def nud(self):
|
|
339
|
+
return self
|
|
340
|
+
|
|
341
|
+
#
|
|
342
|
+
# var terminal = function (id) {
|
|
343
|
+
# var s = Parser.Symbol(id, 0)
|
|
344
|
+
# s.nud = function () {
|
|
345
|
+
# return this
|
|
346
|
+
# }
|
|
347
|
+
# }
|
|
348
|
+
#
|
|
349
|
+
|
|
350
|
+
# match infix operators
|
|
351
|
+
# <expression> <operator> <expression>
|
|
352
|
+
# left associative
|
|
353
|
+
class Infix(Symbol):
|
|
354
|
+
_outer_instance: 'Parser'
|
|
355
|
+
|
|
356
|
+
def __init__(self, outer_instance, id, bp=0):
|
|
357
|
+
super().__init__(outer_instance, id,
|
|
358
|
+
bp if bp != 0 else (tokenizer.Tokenizer.operators[id] if id is not None else 0))
|
|
359
|
+
self._outer_instance = outer_instance
|
|
360
|
+
|
|
361
|
+
def led(self, left):
|
|
362
|
+
self.lhs = left
|
|
363
|
+
self.rhs = self._outer_instance.expression(self.bp)
|
|
364
|
+
self.type = "binary"
|
|
365
|
+
return self
|
|
366
|
+
|
|
367
|
+
class InfixAndPrefix(Infix):
|
|
368
|
+
_outer_instance: 'Parser'
|
|
369
|
+
prefix: 'Parser.Prefix'
|
|
370
|
+
|
|
371
|
+
def __init__(self, outer_instance, id, bp=0):
|
|
372
|
+
super().__init__(outer_instance, id, bp)
|
|
373
|
+
self._outer_instance = outer_instance
|
|
374
|
+
|
|
375
|
+
self.prefix = Parser.Prefix(outer_instance, id)
|
|
376
|
+
|
|
377
|
+
def nud(self):
|
|
378
|
+
return self.prefix.nud()
|
|
379
|
+
# expression(70)
|
|
380
|
+
# type="unary"
|
|
381
|
+
# return this
|
|
382
|
+
|
|
383
|
+
def clone(self):
|
|
384
|
+
c = super().clone()
|
|
385
|
+
# IMPORTANT: make sure to allocate a new Prefix!!!
|
|
386
|
+
c.prefix = Parser.Prefix(self._outer_instance, c.id)
|
|
387
|
+
return c
|
|
388
|
+
|
|
389
|
+
# match infix operators
|
|
390
|
+
# <expression> <operator> <expression>
|
|
391
|
+
# right associative
|
|
392
|
+
class InfixR(Symbol):
|
|
393
|
+
_outer_instance: 'Parser'
|
|
394
|
+
|
|
395
|
+
def __init__(self, outer_instance, id, bp):
|
|
396
|
+
super().__init__(outer_instance, id, bp)
|
|
397
|
+
self._outer_instance = outer_instance
|
|
398
|
+
|
|
399
|
+
# abstract Object led()
|
|
400
|
+
|
|
401
|
+
# match prefix operators
|
|
402
|
+
# <operator> <expression>
|
|
403
|
+
class Prefix(Symbol):
|
|
404
|
+
|
|
405
|
+
# public List<Symbol[]> lhs
|
|
406
|
+
|
|
407
|
+
def __init__(self, outer_instance, id):
|
|
408
|
+
super().__init__(outer_instance, id)
|
|
409
|
+
self._outer_instance = outer_instance
|
|
410
|
+
# type = "unary"
|
|
411
|
+
|
|
412
|
+
# Symbol _expression
|
|
413
|
+
|
|
414
|
+
def nud(self):
|
|
415
|
+
self.expression = self._outer_instance.expression(70)
|
|
416
|
+
self.type = "unary"
|
|
417
|
+
return self
|
|
418
|
+
|
|
419
|
+
dbg: bool
|
|
420
|
+
source: Optional[str]
|
|
421
|
+
recover: bool
|
|
422
|
+
node: Optional[Symbol]
|
|
423
|
+
lexer: Optional[tokenizer.Tokenizer]
|
|
424
|
+
symbol_table: dict[str, Symbol]
|
|
425
|
+
errors: MutableSequence[Exception]
|
|
426
|
+
ancestor_label: int
|
|
427
|
+
ancestor_index: int
|
|
428
|
+
ancestry: MutableSequence[Symbol]
|
|
429
|
+
|
|
430
|
+
def __init__(self):
|
|
431
|
+
self.dbg = False
|
|
432
|
+
self.source = None
|
|
433
|
+
self.recover = False
|
|
434
|
+
self.node = None
|
|
435
|
+
self.lexer = None
|
|
436
|
+
self.symbol_table = {}
|
|
437
|
+
self.errors = []
|
|
438
|
+
self.ancestor_label = 0
|
|
439
|
+
self.ancestor_index = 0
|
|
440
|
+
self.ancestry = []
|
|
441
|
+
|
|
442
|
+
self.register(Parser.Terminal(self, "(end)"))
|
|
443
|
+
self.register(Parser.Terminal(self, "(name)"))
|
|
444
|
+
self.register(Parser.Terminal(self, "(literal)"))
|
|
445
|
+
self.register(Parser.Terminal(self, "(regex)"))
|
|
446
|
+
self.register(Parser.Symbol(self, ":"))
|
|
447
|
+
self.register(Parser.Symbol(self, ";"))
|
|
448
|
+
self.register(Parser.Symbol(self, ","))
|
|
449
|
+
self.register(Parser.Symbol(self, ")"))
|
|
450
|
+
self.register(Parser.Symbol(self, "]"))
|
|
451
|
+
self.register(Parser.Symbol(self, "}"))
|
|
452
|
+
self.register(Parser.Symbol(self, "..")) # range operator
|
|
453
|
+
self.register(Parser.Infix(self, ".")) # map operator
|
|
454
|
+
self.register(Parser.Infix(self, "+")) # numeric addition
|
|
455
|
+
self.register(Parser.InfixAndPrefix(self, "-")) # numeric subtraction
|
|
456
|
+
# unary numeric negation
|
|
457
|
+
|
|
458
|
+
self.register(Parser.InfixFieldWildcard(self))
|
|
459
|
+
# numeric multiplication
|
|
460
|
+
self.register(Parser.Infix(self, "/")) # numeric division
|
|
461
|
+
self.register(Parser.InfixParentOperator(self))
|
|
462
|
+
# numeric modulus
|
|
463
|
+
self.register(Parser.Infix(self, "=")) # equality
|
|
464
|
+
self.register(Parser.Infix(self, "<")) # less than
|
|
465
|
+
self.register(Parser.Infix(self, ">")) # greater than
|
|
466
|
+
self.register(Parser.Infix(self, "!=")) # not equal to
|
|
467
|
+
self.register(Parser.Infix(self, "<=")) # less than or equal
|
|
468
|
+
self.register(Parser.Infix(self, ">=")) # greater than or equal
|
|
469
|
+
self.register(Parser.Infix(self, "&")) # string concatenation
|
|
470
|
+
|
|
471
|
+
self.register(Parser.InfixAnd(self))
|
|
472
|
+
# Boolean AND
|
|
473
|
+
self.register(Parser.InfixOr(self))
|
|
474
|
+
# Boolean OR
|
|
475
|
+
self.register(Parser.InfixIn(self))
|
|
476
|
+
# is member of array
|
|
477
|
+
# merged Infix: register(new Terminal("and")); // the 'keywords' can also be used as terminals (field names)
|
|
478
|
+
# merged Infix: register(new Terminal("or")); //
|
|
479
|
+
# merged Infix: register(new Terminal("in")); //
|
|
480
|
+
# merged Infix: register(new Prefix("-")); // unary numeric negation
|
|
481
|
+
self.register(Parser.Infix(self, "~>")) # function application
|
|
482
|
+
|
|
483
|
+
self.register(Parser.InfixRError(self))
|
|
484
|
+
|
|
485
|
+
# field wildcard (single level)
|
|
486
|
+
# merged with Infix *
|
|
487
|
+
# register(new Prefix("*") {
|
|
488
|
+
# @Override Symbol nud() {
|
|
489
|
+
# type = "wildcard"
|
|
490
|
+
# return this
|
|
491
|
+
# }
|
|
492
|
+
# })
|
|
493
|
+
|
|
494
|
+
# descendant wildcard (multi-level)
|
|
495
|
+
|
|
496
|
+
self.register(Parser.PrefixDescendantWildcard(self))
|
|
497
|
+
|
|
498
|
+
# parent operator
|
|
499
|
+
# merged with Infix %
|
|
500
|
+
# register(new Prefix("%") {
|
|
501
|
+
# @Override Symbol nud() {
|
|
502
|
+
# type = "parent"
|
|
503
|
+
# return this
|
|
504
|
+
# }
|
|
505
|
+
# })
|
|
506
|
+
|
|
507
|
+
# function invocation
|
|
508
|
+
self.register(Parser.InfixFunctionInvocation(self, tokenizer.Tokenizer.operators["("]))
|
|
509
|
+
|
|
510
|
+
# array constructor
|
|
511
|
+
|
|
512
|
+
# merged: register(new Prefix("[") {
|
|
513
|
+
self.register(Parser.InfixArrayConstructor(self, tokenizer.Tokenizer.operators["["]))
|
|
514
|
+
|
|
515
|
+
# order-by
|
|
516
|
+
self.register(Parser.InfixOrderBy(self, tokenizer.Tokenizer.operators["^"]))
|
|
517
|
+
|
|
518
|
+
self.register(Parser.InfixObjectConstructor(self, tokenizer.Tokenizer.operators["{"]))
|
|
519
|
+
|
|
520
|
+
# bind variable
|
|
521
|
+
self.register(Parser.InfixRBindVariable(self, tokenizer.Tokenizer.operators[":="]))
|
|
522
|
+
|
|
523
|
+
# focus variable bind
|
|
524
|
+
self.register(Parser.InfixFocusVariableBind(self, tokenizer.Tokenizer.operators["@"]))
|
|
525
|
+
|
|
526
|
+
# index (position) variable bind
|
|
527
|
+
self.register(Parser.InfixIndexVariableBind(self, tokenizer.Tokenizer.operators["#"]))
|
|
528
|
+
|
|
529
|
+
# if/then/else ternary operator ?:
|
|
530
|
+
self.register(Parser.InfixTernaryOperator(self, tokenizer.Tokenizer.operators["?"]))
|
|
531
|
+
|
|
532
|
+
# object transformer
|
|
533
|
+
self.register(Parser.PrefixObjectTransformer(self))
|
|
534
|
+
|
|
535
|
+
class InfixFieldWildcard(Infix):
|
|
536
|
+
_outer_instance: 'Parser'
|
|
537
|
+
|
|
538
|
+
def __init__(self, outer_instance):
|
|
539
|
+
super().__init__(outer_instance, "*")
|
|
540
|
+
self._outer_instance = outer_instance
|
|
541
|
+
|
|
542
|
+
# field wildcard (single level)
|
|
543
|
+
def nud(self):
|
|
544
|
+
self.type = "wildcard"
|
|
545
|
+
return self
|
|
546
|
+
|
|
547
|
+
class InfixParentOperator(Infix):
|
|
548
|
+
_outer_instance: 'Parser'
|
|
549
|
+
|
|
550
|
+
def __init__(self, outer_instance):
|
|
551
|
+
super().__init__(outer_instance, "%")
|
|
552
|
+
self._outer_instance = outer_instance
|
|
553
|
+
|
|
554
|
+
# parent operator
|
|
555
|
+
def nud(self):
|
|
556
|
+
self.type = "parent"
|
|
557
|
+
return self
|
|
558
|
+
|
|
559
|
+
class InfixAnd(Infix):
|
|
560
|
+
_outer_instance: 'Parser'
|
|
561
|
+
|
|
562
|
+
def __init__(self, outer_instance):
|
|
563
|
+
super().__init__(outer_instance, "and")
|
|
564
|
+
self._outer_instance = outer_instance
|
|
565
|
+
|
|
566
|
+
# allow as terminal
|
|
567
|
+
def nud(self):
|
|
568
|
+
return self
|
|
569
|
+
|
|
570
|
+
class InfixOr(Infix):
|
|
571
|
+
_outer_instance: 'Parser'
|
|
572
|
+
|
|
573
|
+
def __init__(self, outer_instance):
|
|
574
|
+
super().__init__(outer_instance, "or")
|
|
575
|
+
self._outer_instance = outer_instance
|
|
576
|
+
|
|
577
|
+
# allow as terminal
|
|
578
|
+
def nud(self):
|
|
579
|
+
return self
|
|
580
|
+
|
|
581
|
+
class InfixIn(Infix):
|
|
582
|
+
_outer_instance: 'Parser'
|
|
583
|
+
|
|
584
|
+
def __init__(self, outer_instance):
|
|
585
|
+
super().__init__(outer_instance, "in")
|
|
586
|
+
self._outer_instance = outer_instance
|
|
587
|
+
|
|
588
|
+
# allow as terminal
|
|
589
|
+
def nud(self):
|
|
590
|
+
return self
|
|
591
|
+
|
|
592
|
+
class InfixRError(Infix):
|
|
593
|
+
_outer_instance: 'Parser'
|
|
594
|
+
|
|
595
|
+
def __init__(self, outer_instance):
|
|
596
|
+
super().__init__(outer_instance, "(error)", 10)
|
|
597
|
+
self._outer_instance = outer_instance
|
|
598
|
+
|
|
599
|
+
def led(self, left):
|
|
600
|
+
raise NotImplementedError("TODO", None)
|
|
601
|
+
|
|
602
|
+
class PrefixDescendantWildcard(Prefix):
|
|
603
|
+
_outer_instance: 'Parser'
|
|
604
|
+
|
|
605
|
+
def __init__(self, outer_instance):
|
|
606
|
+
super().__init__(outer_instance, "**")
|
|
607
|
+
self._outer_instance = outer_instance
|
|
608
|
+
|
|
609
|
+
def nud(self):
|
|
610
|
+
self.type = "descendant"
|
|
611
|
+
return self
|
|
612
|
+
|
|
613
|
+
class InfixFunctionInvocation(Infix):
|
|
614
|
+
_outer_instance: 'Parser'
|
|
615
|
+
|
|
616
|
+
def __init__(self, outer_instance, get):
|
|
617
|
+
super().__init__(outer_instance, "(", get)
|
|
618
|
+
self._outer_instance = outer_instance
|
|
619
|
+
|
|
620
|
+
def led(self, left):
|
|
621
|
+
# left is is what we are trying to invoke
|
|
622
|
+
self.procedure = left
|
|
623
|
+
self.type = "function"
|
|
624
|
+
self.arguments = []
|
|
625
|
+
if self._outer_instance.node.id != ")":
|
|
626
|
+
while True:
|
|
627
|
+
if "operator" == self._outer_instance.node.type and self._outer_instance.node.id == "?":
|
|
628
|
+
# partial function application
|
|
629
|
+
self.type = "partial"
|
|
630
|
+
self.arguments.append(self._outer_instance.node)
|
|
631
|
+
self._outer_instance.advance("?")
|
|
632
|
+
else:
|
|
633
|
+
self.arguments.append(self._outer_instance.expression(0))
|
|
634
|
+
if self._outer_instance.node.id != ",":
|
|
635
|
+
break
|
|
636
|
+
self._outer_instance.advance(",")
|
|
637
|
+
self._outer_instance.advance(")", True)
|
|
638
|
+
# if the name of the function is 'function' or λ, then this is function definition (lambda function)
|
|
639
|
+
if left.type == "name" and (left.value == "function" or left.value == "\u03BB"):
|
|
640
|
+
# all of the args must be VARIABLE tokens
|
|
641
|
+
# int index = 0
|
|
642
|
+
for arg in self.arguments:
|
|
643
|
+
# this.arguments.forEach(function (arg, index) {
|
|
644
|
+
if arg.type != "variable":
|
|
645
|
+
return self._outer_instance.handle_error(
|
|
646
|
+
jexception.JException("S0208", arg.position, arg.value))
|
|
647
|
+
# index++
|
|
648
|
+
self.type = "lambda"
|
|
649
|
+
# is the next token a '<' - if so, parse the function signature
|
|
650
|
+
if self._outer_instance.node.id == "<":
|
|
651
|
+
depth = 1
|
|
652
|
+
sig = "<"
|
|
653
|
+
while depth > 0 and self._outer_instance.node.id != "{" and self._outer_instance.node.id != "(end)":
|
|
654
|
+
tok = self._outer_instance.advance()
|
|
655
|
+
if tok.id == ">":
|
|
656
|
+
depth -= 1
|
|
657
|
+
elif tok.id == "<":
|
|
658
|
+
depth += 1
|
|
659
|
+
sig += tok.value
|
|
660
|
+
self._outer_instance.advance(">")
|
|
661
|
+
self.signature = signature.Signature(sig, "lambda")
|
|
662
|
+
# parse the function body
|
|
663
|
+
self._outer_instance.advance("{")
|
|
664
|
+
self.body = self._outer_instance.expression(0)
|
|
665
|
+
self._outer_instance.advance("}")
|
|
666
|
+
return self
|
|
667
|
+
|
|
668
|
+
# })
|
|
669
|
+
|
|
670
|
+
# parenthesis - block expression
|
|
671
|
+
# Note: in Java both nud and led are in same class!
|
|
672
|
+
# register(new Prefix("(") {
|
|
673
|
+
|
|
674
|
+
def nud(self):
|
|
675
|
+
if self._outer_instance.dbg:
|
|
676
|
+
print("Prefix (")
|
|
677
|
+
expressions = []
|
|
678
|
+
while self._outer_instance.node.id != ")":
|
|
679
|
+
expressions.append(self._outer_instance.expression(0))
|
|
680
|
+
if self._outer_instance.node.id != ";":
|
|
681
|
+
break
|
|
682
|
+
self._outer_instance.advance(";")
|
|
683
|
+
self._outer_instance.advance(")", True)
|
|
684
|
+
self.type = "block"
|
|
685
|
+
self.expressions = expressions
|
|
686
|
+
return self
|
|
687
|
+
|
|
688
|
+
class InfixArrayConstructor(Infix):
|
|
689
|
+
_outer_instance: 'Parser'
|
|
690
|
+
|
|
691
|
+
def __init__(self, outer_instance, get):
|
|
692
|
+
super().__init__(outer_instance, "[", get)
|
|
693
|
+
self._outer_instance = outer_instance
|
|
694
|
+
|
|
695
|
+
def nud(self):
|
|
696
|
+
a = []
|
|
697
|
+
if self._outer_instance.node.id != "]":
|
|
698
|
+
while True:
|
|
699
|
+
item = self._outer_instance.expression(0)
|
|
700
|
+
if self._outer_instance.node.id == "..":
|
|
701
|
+
# range operator
|
|
702
|
+
range = Parser.Symbol(self._outer_instance)
|
|
703
|
+
range.type = "binary"
|
|
704
|
+
range.value = ".."
|
|
705
|
+
range.position = self._outer_instance.node.position
|
|
706
|
+
range.lhs = item
|
|
707
|
+
self._outer_instance.advance("..")
|
|
708
|
+
range.rhs = self._outer_instance.expression(0)
|
|
709
|
+
item = range
|
|
710
|
+
a.append(item)
|
|
711
|
+
if self._outer_instance.node.id != ",":
|
|
712
|
+
break
|
|
713
|
+
self._outer_instance.advance(",")
|
|
714
|
+
self._outer_instance.advance("]", True)
|
|
715
|
+
self.expressions = a
|
|
716
|
+
self.type = "unary"
|
|
717
|
+
return self
|
|
718
|
+
|
|
719
|
+
# })
|
|
720
|
+
|
|
721
|
+
# filter - predicate or array index
|
|
722
|
+
# register(new Infix("[", tokenizer.Tokenizer.operators.get("[")) {
|
|
723
|
+
|
|
724
|
+
def led(self, left):
|
|
725
|
+
if self._outer_instance.node.id == "]":
|
|
726
|
+
# empty predicate means maintain singleton arrays in the output
|
|
727
|
+
step = left
|
|
728
|
+
while step is not None and step.type == "binary" and step.value == "[":
|
|
729
|
+
step = step.lhs
|
|
730
|
+
step.keep_array = True
|
|
731
|
+
self._outer_instance.advance("]")
|
|
732
|
+
return left
|
|
733
|
+
else:
|
|
734
|
+
self.lhs = left
|
|
735
|
+
self.rhs = self._outer_instance.expression(tokenizer.Tokenizer.operators["]"])
|
|
736
|
+
self.type = "binary"
|
|
737
|
+
self._outer_instance.advance("]", True)
|
|
738
|
+
return self
|
|
739
|
+
|
|
740
|
+
class InfixOrderBy(Infix):
|
|
741
|
+
_outer_instance: 'Parser'
|
|
742
|
+
|
|
743
|
+
def __init__(self, outer_instance, get):
|
|
744
|
+
super().__init__(outer_instance, "^", get)
|
|
745
|
+
self._outer_instance = outer_instance
|
|
746
|
+
|
|
747
|
+
def led(self, left):
|
|
748
|
+
self._outer_instance.advance("(")
|
|
749
|
+
terms = []
|
|
750
|
+
while True:
|
|
751
|
+
term = Parser.Symbol(self._outer_instance)
|
|
752
|
+
term.descending = False
|
|
753
|
+
|
|
754
|
+
if self._outer_instance.node.id == "<":
|
|
755
|
+
# ascending sort
|
|
756
|
+
self._outer_instance.advance("<")
|
|
757
|
+
elif self._outer_instance.node.id == ">":
|
|
758
|
+
# descending sort
|
|
759
|
+
term.descending = True
|
|
760
|
+
self._outer_instance.advance(">")
|
|
761
|
+
else:
|
|
762
|
+
# unspecified - default to ascending
|
|
763
|
+
pass
|
|
764
|
+
term.expression = self._outer_instance.expression(0)
|
|
765
|
+
terms.append(term)
|
|
766
|
+
if self._outer_instance.node.id != ",":
|
|
767
|
+
break
|
|
768
|
+
self._outer_instance.advance(",")
|
|
769
|
+
self._outer_instance.advance(")")
|
|
770
|
+
self.lhs = left
|
|
771
|
+
self.rhs_terms = terms
|
|
772
|
+
self.type = "binary"
|
|
773
|
+
return self
|
|
774
|
+
|
|
775
|
+
class InfixObjectConstructor(Infix):
|
|
776
|
+
_outer_instance: 'Parser'
|
|
777
|
+
|
|
778
|
+
def __init__(self, outer_instance, get):
|
|
779
|
+
super().__init__(outer_instance, "{", get)
|
|
780
|
+
self._outer_instance = outer_instance
|
|
781
|
+
|
|
782
|
+
# merged register(new Prefix("{") {
|
|
783
|
+
|
|
784
|
+
def nud(self):
|
|
785
|
+
return self._outer_instance.object_parser(None)
|
|
786
|
+
|
|
787
|
+
# })
|
|
788
|
+
|
|
789
|
+
# register(new Infix("{", tokenizer.Tokenizer.operators.get("{")) {
|
|
790
|
+
|
|
791
|
+
def led(self, left):
|
|
792
|
+
return self._outer_instance.object_parser(left)
|
|
793
|
+
|
|
794
|
+
class InfixRBindVariable(InfixR):
|
|
795
|
+
_outer_instance: 'Parser'
|
|
796
|
+
|
|
797
|
+
def __init__(self, outer_instance, get):
|
|
798
|
+
super().__init__(outer_instance, ":=", get)
|
|
799
|
+
self._outer_instance = outer_instance
|
|
800
|
+
|
|
801
|
+
def led(self, left):
|
|
802
|
+
if left.type != "variable":
|
|
803
|
+
return self._outer_instance.handle_error(jexception.JException("S0212", left.position, left.value))
|
|
804
|
+
self.lhs = left
|
|
805
|
+
self.rhs = self._outer_instance.expression(
|
|
806
|
+
tokenizer.Tokenizer.operators[":="] - 1) # subtract 1 from bindingPower for right associative operators
|
|
807
|
+
self.type = "binary"
|
|
808
|
+
return self
|
|
809
|
+
|
|
810
|
+
class InfixFocusVariableBind(Infix):
|
|
811
|
+
_outer_instance: 'Parser'
|
|
812
|
+
|
|
813
|
+
def __init__(self, outer_instance, get):
|
|
814
|
+
super().__init__(outer_instance, "@", get)
|
|
815
|
+
self._outer_instance = outer_instance
|
|
816
|
+
|
|
817
|
+
def led(self, left):
|
|
818
|
+
self.lhs = left
|
|
819
|
+
self.rhs = self._outer_instance.expression(tokenizer.Tokenizer.operators["@"])
|
|
820
|
+
if self.rhs.type != "variable":
|
|
821
|
+
return self._outer_instance.handle_error(jexception.JException("S0214", self.rhs.position, "@"))
|
|
822
|
+
self.type = "binary"
|
|
823
|
+
return self
|
|
824
|
+
|
|
825
|
+
class InfixIndexVariableBind(Infix):
|
|
826
|
+
_outer_instance: 'Parser'
|
|
827
|
+
|
|
828
|
+
def __init__(self, outer_instance, get):
|
|
829
|
+
super().__init__(outer_instance, "#", get)
|
|
830
|
+
self._outer_instance = outer_instance
|
|
831
|
+
|
|
832
|
+
def led(self, left):
|
|
833
|
+
self.lhs = left
|
|
834
|
+
self.rhs = self._outer_instance.expression(tokenizer.Tokenizer.operators["#"])
|
|
835
|
+
if self.rhs.type != "variable":
|
|
836
|
+
return self._outer_instance.handle_error(jexception.JException("S0214", self.rhs.position, "#"))
|
|
837
|
+
self.type = "binary"
|
|
838
|
+
return self
|
|
839
|
+
|
|
840
|
+
class InfixTernaryOperator(Infix):
|
|
841
|
+
_outer_instance: 'Parser'
|
|
842
|
+
|
|
843
|
+
def __init__(self, outer_instance, get):
|
|
844
|
+
super().__init__(outer_instance, "?", get)
|
|
845
|
+
self._outer_instance = outer_instance
|
|
846
|
+
|
|
847
|
+
def led(self, left):
|
|
848
|
+
self.type = "condition"
|
|
849
|
+
self.condition = left
|
|
850
|
+
self.then = self._outer_instance.expression(0)
|
|
851
|
+
if self._outer_instance.node.id == ":":
|
|
852
|
+
# else condition
|
|
853
|
+
self._outer_instance.advance(":")
|
|
854
|
+
self._else = self._outer_instance.expression(0)
|
|
855
|
+
return self
|
|
856
|
+
|
|
857
|
+
class PrefixObjectTransformer(Prefix):
|
|
858
|
+
_outer_instance: 'Parser'
|
|
859
|
+
|
|
860
|
+
def __init__(self, outer_instance):
|
|
861
|
+
super().__init__(outer_instance, "|")
|
|
862
|
+
self._outer_instance = outer_instance
|
|
863
|
+
|
|
864
|
+
def nud(self):
|
|
865
|
+
self.type = "transform"
|
|
866
|
+
self.pattern = self._outer_instance.expression(0)
|
|
867
|
+
self._outer_instance.advance("|")
|
|
868
|
+
self.update = self._outer_instance.expression(0)
|
|
869
|
+
if self._outer_instance.node.id == ",":
|
|
870
|
+
self._outer_instance.advance(",")
|
|
871
|
+
self.delete = self._outer_instance.expression(0)
|
|
872
|
+
self._outer_instance.advance("|")
|
|
873
|
+
return self
|
|
874
|
+
|
|
875
|
+
# tail call optimization
|
|
876
|
+
# this is invoked by the post parser to analyse lambda functions to see
|
|
877
|
+
# if they make a tail call. If so, it is replaced by a thunk which will
|
|
878
|
+
# be invoked by the trampoline loop during function application.
|
|
879
|
+
# This enables tail-recursive functions to be written without growing the stack
|
|
880
|
+
def tail_call_optimize(self, expr: Symbol) -> Symbol:
|
|
881
|
+
result = None
|
|
882
|
+
if expr.type == "function" and expr.predicate is None:
|
|
883
|
+
thunk = Parser.Symbol(self)
|
|
884
|
+
thunk.type = "lambda"
|
|
885
|
+
thunk.thunk = True
|
|
886
|
+
thunk.arguments = []
|
|
887
|
+
thunk.position = expr.position
|
|
888
|
+
thunk.body = expr
|
|
889
|
+
result = thunk
|
|
890
|
+
elif expr.type == "condition":
|
|
891
|
+
# analyse both branches
|
|
892
|
+
expr.then = self.tail_call_optimize(expr.then)
|
|
893
|
+
if expr._else is not None:
|
|
894
|
+
expr._else = self.tail_call_optimize(expr._else)
|
|
895
|
+
result = expr
|
|
896
|
+
elif expr.type == "block":
|
|
897
|
+
# only the last expression in the block
|
|
898
|
+
length = len(expr.expressions)
|
|
899
|
+
if length > 0:
|
|
900
|
+
if not (isinstance(expr.expressions, list)):
|
|
901
|
+
expr.expressions = [expr.expressions]
|
|
902
|
+
expr.expressions[length - 1] = self.tail_call_optimize(expr.expressions[length - 1])
|
|
903
|
+
result = expr
|
|
904
|
+
else:
|
|
905
|
+
result = expr
|
|
906
|
+
return result
|
|
907
|
+
|
|
908
|
+
def seek_parent(self, node: Symbol, slot: Symbol) -> Symbol:
|
|
909
|
+
if node.type == "name" or node.type == "wildcard":
|
|
910
|
+
slot.level -= 1
|
|
911
|
+
if slot.level == 0:
|
|
912
|
+
if node.ancestor is None:
|
|
913
|
+
node.ancestor = slot
|
|
914
|
+
else:
|
|
915
|
+
# reuse the existing label
|
|
916
|
+
self.ancestry[int(slot.index)].slot.label = node.ancestor.label
|
|
917
|
+
node.ancestor = slot
|
|
918
|
+
node.tuple = True
|
|
919
|
+
elif node.type == "parent":
|
|
920
|
+
slot.level += 1
|
|
921
|
+
elif node.type == "block":
|
|
922
|
+
# look in last expression in the block
|
|
923
|
+
if len(node.expressions) > 0:
|
|
924
|
+
node.tuple = True
|
|
925
|
+
slot = self.seek_parent(node.expressions[-1], slot)
|
|
926
|
+
elif node.type == "path":
|
|
927
|
+
# last step in path
|
|
928
|
+
node.tuple = True
|
|
929
|
+
index = len(node.steps) - 1
|
|
930
|
+
slot = self.seek_parent(node.steps[index], slot)
|
|
931
|
+
index -= 1
|
|
932
|
+
while slot.level > 0 and index >= 0:
|
|
933
|
+
# check previous steps
|
|
934
|
+
slot = self.seek_parent(node.steps[index], slot)
|
|
935
|
+
index -= 1
|
|
936
|
+
else:
|
|
937
|
+
# error - can't derive ancestor
|
|
938
|
+
raise jexception.JException("S0217", node.position, node.type)
|
|
939
|
+
return slot
|
|
940
|
+
|
|
941
|
+
def push_ancestry(self, result: Symbol, value: Optional[Symbol]) -> None:
|
|
942
|
+
if value is None:
|
|
943
|
+
return # Added NPE check
|
|
944
|
+
if value.seeking_parent is not None or value.type == "parent":
|
|
945
|
+
slots = value.seeking_parent if (value.seeking_parent is not None) else []
|
|
946
|
+
if value.type == "parent":
|
|
947
|
+
slots.append(value.slot)
|
|
948
|
+
if result.seeking_parent is None:
|
|
949
|
+
result.seeking_parent = slots
|
|
950
|
+
else:
|
|
951
|
+
result.seeking_parent.extend(slots)
|
|
952
|
+
|
|
953
|
+
def resolve_ancestry(self, path: Symbol) -> None:
|
|
954
|
+
index = len(path.steps) - 1
|
|
955
|
+
laststep = path.steps[index]
|
|
956
|
+
slots = laststep.seeking_parent if (laststep.seeking_parent is not None) else []
|
|
957
|
+
if laststep.type == "parent":
|
|
958
|
+
slots.append(laststep.slot)
|
|
959
|
+
for _, slot in enumerate(slots):
|
|
960
|
+
index = len(path.steps) - 2
|
|
961
|
+
while slot.level > 0:
|
|
962
|
+
if index < 0:
|
|
963
|
+
if path.seeking_parent is None:
|
|
964
|
+
path.seeking_parent = [slot]
|
|
965
|
+
else:
|
|
966
|
+
path.seeking_parent.append(slot)
|
|
967
|
+
break
|
|
968
|
+
# try previous step
|
|
969
|
+
step = path.steps[index]
|
|
970
|
+
index -= 1
|
|
971
|
+
# multiple contiguous steps that bind the focus should be skipped
|
|
972
|
+
while index >= 0 and step.focus is not None and path.steps[index].focus is not None:
|
|
973
|
+
step = path.steps[index]
|
|
974
|
+
index -= 1
|
|
975
|
+
slot = self.seek_parent(step, slot)
|
|
976
|
+
|
|
977
|
+
# post-parse stage
|
|
978
|
+
# the purpose of this is to add as much semantic value to the parse tree as possible
|
|
979
|
+
# in order to simplify the work of the evaluator.
|
|
980
|
+
# This includes flattening the parts of the AST representing location paths,
|
|
981
|
+
# converting them to arrays of steps which in turn may contain arrays of predicates.
|
|
982
|
+
# following this, nodes containing '.' and '[' should be eliminated from the AST.
|
|
983
|
+
def process_ast(self, expr: Optional[Symbol]) -> Optional[Symbol]:
|
|
984
|
+
result = expr
|
|
985
|
+
if expr is None:
|
|
986
|
+
return None
|
|
987
|
+
if self.dbg:
|
|
988
|
+
print(" > processAST type=" + expr.type + " value='" + expr.value + "'")
|
|
989
|
+
type = expr.type if expr.type is not None else "(null)"
|
|
990
|
+
if type == "binary":
|
|
991
|
+
value = str(expr.value)
|
|
992
|
+
if value == ".":
|
|
993
|
+
lstep = self.process_ast(expr.lhs)
|
|
994
|
+
|
|
995
|
+
if lstep.type == "path":
|
|
996
|
+
result = lstep
|
|
997
|
+
else:
|
|
998
|
+
result = Parser.Infix(self, None)
|
|
999
|
+
result.type = "path"
|
|
1000
|
+
result.steps = [lstep]
|
|
1001
|
+
# result = {type: 'path', steps: [lstep]}
|
|
1002
|
+
if lstep.type == "parent":
|
|
1003
|
+
result.seeking_parent = [lstep.slot]
|
|
1004
|
+
rest = self.process_ast(expr.rhs)
|
|
1005
|
+
if (rest.type == "function" and rest.procedure.type == "path" and len(
|
|
1006
|
+
rest.procedure.steps) == 1 and rest.procedure.steps[0].type == "name" and
|
|
1007
|
+
result.steps[-1].type == "function"):
|
|
1008
|
+
# next function in chain of functions - will override a thenable
|
|
1009
|
+
result.steps[-1].next_function = rest.procedure.steps[0].value
|
|
1010
|
+
if rest.type == "path":
|
|
1011
|
+
result.steps.extend(rest.steps)
|
|
1012
|
+
else:
|
|
1013
|
+
if rest.predicate is not None:
|
|
1014
|
+
rest.stages = rest.predicate
|
|
1015
|
+
rest.predicate = None
|
|
1016
|
+
# delete rest.predicate
|
|
1017
|
+
result.steps.append(rest)
|
|
1018
|
+
# any steps within a path that are string literals, should be changed to 'name'
|
|
1019
|
+
for step in result.steps:
|
|
1020
|
+
if step.type == "number" or step.type == "value":
|
|
1021
|
+
# don't allow steps to be numbers or the values true/false/null
|
|
1022
|
+
raise jexception.JException("S0213", step.position, step.value)
|
|
1023
|
+
# System.out.println("step "+step+" type="+step.type)
|
|
1024
|
+
if step.type == "string":
|
|
1025
|
+
step.type = "name"
|
|
1026
|
+
# for (var lit : step.steps) {
|
|
1027
|
+
# System.out.println("step2 "+lit+" type="+lit.type)
|
|
1028
|
+
# lit.type = "name"
|
|
1029
|
+
# }
|
|
1030
|
+
|
|
1031
|
+
# any step that signals keeping a singleton array, should be flagged on the path
|
|
1032
|
+
if len(list(filter(lambda step: step.keep_array, result.steps))) > 0:
|
|
1033
|
+
result.keep_singleton_array = True
|
|
1034
|
+
# if first step is a path constructor, flag it for special handling
|
|
1035
|
+
firststep = result.steps[0]
|
|
1036
|
+
if firststep.type == "unary" and str(firststep.value) == "[":
|
|
1037
|
+
firststep.consarray = True
|
|
1038
|
+
# if the last step is an array constructor, flag it so it doesn't flatten
|
|
1039
|
+
laststep = result.steps[-1]
|
|
1040
|
+
if laststep.type == "unary" and str(laststep.value) == "[":
|
|
1041
|
+
laststep.consarray = True
|
|
1042
|
+
self.resolve_ancestry(result)
|
|
1043
|
+
elif value == "[":
|
|
1044
|
+
if self.dbg:
|
|
1045
|
+
print("binary [")
|
|
1046
|
+
# predicated step
|
|
1047
|
+
# LHS is a step or a predicated step
|
|
1048
|
+
# RHS is the predicate expr
|
|
1049
|
+
result = self.process_ast(expr.lhs)
|
|
1050
|
+
step = result
|
|
1051
|
+
type = "predicate"
|
|
1052
|
+
if result.type == "path":
|
|
1053
|
+
step = result.steps[-1]
|
|
1054
|
+
type = "stages"
|
|
1055
|
+
if step.group is not None:
|
|
1056
|
+
raise jexception.JException("S0209", expr.position)
|
|
1057
|
+
# if (typeof step[type] === 'undefined') {
|
|
1058
|
+
# step[type] = []
|
|
1059
|
+
# }
|
|
1060
|
+
if type == "stages":
|
|
1061
|
+
if step.stages is None:
|
|
1062
|
+
step.stages = []
|
|
1063
|
+
else:
|
|
1064
|
+
if step.predicate is None:
|
|
1065
|
+
step.predicate = []
|
|
1066
|
+
|
|
1067
|
+
predicate = self.process_ast(expr.rhs)
|
|
1068
|
+
if predicate.seeking_parent is not None:
|
|
1069
|
+
_step = step
|
|
1070
|
+
for slot in predicate.seeking_parent:
|
|
1071
|
+
if slot.level == 1:
|
|
1072
|
+
self.seek_parent(_step, slot)
|
|
1073
|
+
else:
|
|
1074
|
+
slot.level -= 1
|
|
1075
|
+
self.push_ancestry(step, predicate)
|
|
1076
|
+
s = Parser.Symbol(self)
|
|
1077
|
+
s.type = "filter"
|
|
1078
|
+
s.expr = predicate
|
|
1079
|
+
s.position = expr.position
|
|
1080
|
+
|
|
1081
|
+
# FIXED:
|
|
1082
|
+
# this logic is required in Java to fix
|
|
1083
|
+
# for example test: flattening case 045
|
|
1084
|
+
# otherwise we lose the keepArray flag
|
|
1085
|
+
if expr.keep_array:
|
|
1086
|
+
step.keep_array = True
|
|
1087
|
+
|
|
1088
|
+
if type == "stages":
|
|
1089
|
+
step.stages.append(s)
|
|
1090
|
+
else:
|
|
1091
|
+
step.predicate.append(s)
|
|
1092
|
+
# step[type].push({type: 'filter', expr: predicate, position: expr.position})
|
|
1093
|
+
elif value == "{":
|
|
1094
|
+
# group-by
|
|
1095
|
+
# LHS is a step or a predicated step
|
|
1096
|
+
# RHS is the object constructor expr
|
|
1097
|
+
result = self.process_ast(expr.lhs)
|
|
1098
|
+
if result.group is not None:
|
|
1099
|
+
raise jexception.JException("S0210", expr.position)
|
|
1100
|
+
# object constructor - process each pair
|
|
1101
|
+
result.group = Parser.Symbol(self)
|
|
1102
|
+
result.group.lhs_object = list(
|
|
1103
|
+
map(lambda pair: [self.process_ast(pair[0]), self.process_ast(pair[1])], expr.rhs_object))
|
|
1104
|
+
result.group.position = expr.position
|
|
1105
|
+
|
|
1106
|
+
elif value == "^":
|
|
1107
|
+
# order-by
|
|
1108
|
+
# LHS is the array to be ordered
|
|
1109
|
+
# RHS defines the terms
|
|
1110
|
+
result = self.process_ast(expr.lhs)
|
|
1111
|
+
if result.type != "path":
|
|
1112
|
+
_res = Parser.Symbol(self)
|
|
1113
|
+
_res.type = "path"
|
|
1114
|
+
_res.steps = []
|
|
1115
|
+
_res.steps.append(result)
|
|
1116
|
+
result = _res
|
|
1117
|
+
sort_step = Parser.Symbol(self)
|
|
1118
|
+
sort_step.type = "sort"
|
|
1119
|
+
sort_step.position = expr.position
|
|
1120
|
+
|
|
1121
|
+
def lambda1(terms):
|
|
1122
|
+
expression = self.process_ast(terms.expression)
|
|
1123
|
+
self.push_ancestry(sort_step, expression)
|
|
1124
|
+
res = Parser.Symbol(self)
|
|
1125
|
+
res.descending = terms.descending
|
|
1126
|
+
res.expression = expression
|
|
1127
|
+
return res
|
|
1128
|
+
|
|
1129
|
+
sort_step.terms = list(map(lambda1, expr.rhs_terms))
|
|
1130
|
+
result.steps.append(sort_step)
|
|
1131
|
+
self.resolve_ancestry(result)
|
|
1132
|
+
elif value == ":=":
|
|
1133
|
+
result = Parser.Symbol(self)
|
|
1134
|
+
result.type = "bind"
|
|
1135
|
+
result.value = expr.value
|
|
1136
|
+
result.position = expr.position
|
|
1137
|
+
result.lhs = self.process_ast(expr.lhs)
|
|
1138
|
+
result.rhs = self.process_ast(expr.rhs)
|
|
1139
|
+
self.push_ancestry(result, result.rhs)
|
|
1140
|
+
elif value == "@":
|
|
1141
|
+
result = self.process_ast(expr.lhs)
|
|
1142
|
+
step = result
|
|
1143
|
+
if result.type == "path":
|
|
1144
|
+
step = result.steps[-1]
|
|
1145
|
+
# throw error if there are any predicates defined at this point
|
|
1146
|
+
# at this point the only type of stages can be predicates
|
|
1147
|
+
if step.stages is not None or step.predicate is not None:
|
|
1148
|
+
raise jexception.JException("S0215", expr.position)
|
|
1149
|
+
# also throw if this is applied after an 'order-by' clause
|
|
1150
|
+
if step.type == "sort":
|
|
1151
|
+
raise jexception.JException("S0216", expr.position)
|
|
1152
|
+
if expr.keep_array:
|
|
1153
|
+
step.keep_array = True
|
|
1154
|
+
step.focus = expr.rhs.value
|
|
1155
|
+
step.tuple = True
|
|
1156
|
+
elif value == "#":
|
|
1157
|
+
result = self.process_ast(expr.lhs)
|
|
1158
|
+
step = result
|
|
1159
|
+
if result.type == "path":
|
|
1160
|
+
step = result.steps[-1]
|
|
1161
|
+
else:
|
|
1162
|
+
_res = Parser.Symbol(self)
|
|
1163
|
+
_res.type = "path"
|
|
1164
|
+
_res.steps = []
|
|
1165
|
+
_res.steps.append(result)
|
|
1166
|
+
result = _res
|
|
1167
|
+
if step.predicate is not None:
|
|
1168
|
+
step.stages = step.predicate
|
|
1169
|
+
step.predicate = None
|
|
1170
|
+
if step.stages is None:
|
|
1171
|
+
step.index = expr.rhs.value # name of index variable = String
|
|
1172
|
+
else:
|
|
1173
|
+
_res = Parser.Symbol(self)
|
|
1174
|
+
_res.type = "index"
|
|
1175
|
+
_res.value = expr.rhs.value
|
|
1176
|
+
_res.position = expr.position
|
|
1177
|
+
step.stages.append(_res)
|
|
1178
|
+
step.tuple = True
|
|
1179
|
+
elif value == "~>":
|
|
1180
|
+
result = Parser.Symbol(self)
|
|
1181
|
+
result.type = "apply"
|
|
1182
|
+
result.value = expr.value
|
|
1183
|
+
result.position = expr.position
|
|
1184
|
+
result.lhs = self.process_ast(expr.lhs)
|
|
1185
|
+
result.rhs = self.process_ast(expr.rhs)
|
|
1186
|
+
else:
|
|
1187
|
+
_result = Parser.Infix(self, None)
|
|
1188
|
+
_result.type = expr.type
|
|
1189
|
+
_result.value = expr.value
|
|
1190
|
+
_result.position = expr.position
|
|
1191
|
+
_result.lhs = self.process_ast(expr.lhs)
|
|
1192
|
+
_result.rhs = self.process_ast(expr.rhs)
|
|
1193
|
+
self.push_ancestry(_result, _result.lhs)
|
|
1194
|
+
self.push_ancestry(_result, _result.rhs)
|
|
1195
|
+
result = _result
|
|
1196
|
+
|
|
1197
|
+
elif type == "unary":
|
|
1198
|
+
result = Parser.Symbol(self)
|
|
1199
|
+
result.type = expr.type
|
|
1200
|
+
result.value = expr.value
|
|
1201
|
+
result.position = expr.position
|
|
1202
|
+
# expr.value might be Character!
|
|
1203
|
+
expr_value = str(expr.value)
|
|
1204
|
+
if expr_value == "[":
|
|
1205
|
+
if self.dbg:
|
|
1206
|
+
print("unary [ " + str(result))
|
|
1207
|
+
|
|
1208
|
+
# array constructor - process each item
|
|
1209
|
+
def lambda2(item):
|
|
1210
|
+
value = self.process_ast(item)
|
|
1211
|
+
self.push_ancestry(result, value)
|
|
1212
|
+
return value
|
|
1213
|
+
|
|
1214
|
+
result.expressions = list(map(lambda2, expr.expressions))
|
|
1215
|
+
elif expr_value == "{":
|
|
1216
|
+
# object constructor - process each pair
|
|
1217
|
+
# throw new Error("processAST {} unimpl")
|
|
1218
|
+
def lambda3(pair):
|
|
1219
|
+
key = self.process_ast(pair[0])
|
|
1220
|
+
self.push_ancestry(result, key)
|
|
1221
|
+
value = self.process_ast(pair[1])
|
|
1222
|
+
self.push_ancestry(result, value)
|
|
1223
|
+
return [key, value]
|
|
1224
|
+
|
|
1225
|
+
result.lhs_object = list(map(lambda3, expr.lhs_object))
|
|
1226
|
+
else:
|
|
1227
|
+
# all other unary expressions - just process the expression
|
|
1228
|
+
result.expression = self.process_ast(expr.expression)
|
|
1229
|
+
# if unary minus on a number, then pre-process
|
|
1230
|
+
if expr_value == "-" and result.expression.type == "number":
|
|
1231
|
+
result = result.expression
|
|
1232
|
+
result.value = utils.Utils.convert_number(-float(result.value))
|
|
1233
|
+
if self.dbg:
|
|
1234
|
+
print("unary - value=" + str(result.value))
|
|
1235
|
+
else:
|
|
1236
|
+
self.push_ancestry(result, result.expression)
|
|
1237
|
+
|
|
1238
|
+
elif type == "function" or type == "partial":
|
|
1239
|
+
result = Parser.Symbol(self)
|
|
1240
|
+
result.type = expr.type
|
|
1241
|
+
result.name = expr.name
|
|
1242
|
+
result.value = expr.value
|
|
1243
|
+
result.position = expr.position
|
|
1244
|
+
|
|
1245
|
+
def lambda4(arg):
|
|
1246
|
+
arg_ast = self.process_ast(arg)
|
|
1247
|
+
self.push_ancestry(result, arg_ast)
|
|
1248
|
+
return arg_ast
|
|
1249
|
+
|
|
1250
|
+
result.arguments = list(map(lambda4, expr.arguments))
|
|
1251
|
+
result.procedure = self.process_ast(expr.procedure)
|
|
1252
|
+
elif type == "lambda":
|
|
1253
|
+
result = Parser.Symbol(self)
|
|
1254
|
+
result.type = expr.type
|
|
1255
|
+
result.arguments = expr.arguments
|
|
1256
|
+
result.signature = expr.signature
|
|
1257
|
+
result.position = expr.position
|
|
1258
|
+
body = self.process_ast(expr.body)
|
|
1259
|
+
result.body = self.tail_call_optimize(body)
|
|
1260
|
+
elif type == "condition":
|
|
1261
|
+
result = Parser.Symbol(self)
|
|
1262
|
+
result.type = expr.type
|
|
1263
|
+
result.position = expr.position
|
|
1264
|
+
result.condition = self.process_ast(expr.condition)
|
|
1265
|
+
self.push_ancestry(result, result.condition)
|
|
1266
|
+
result.then = self.process_ast(expr.then)
|
|
1267
|
+
self.push_ancestry(result, result.then)
|
|
1268
|
+
if expr._else is not None:
|
|
1269
|
+
result._else = self.process_ast(expr._else)
|
|
1270
|
+
self.push_ancestry(result, result._else)
|
|
1271
|
+
elif type == "transform":
|
|
1272
|
+
result = Parser.Symbol(self)
|
|
1273
|
+
result.type = expr.type
|
|
1274
|
+
result.position = expr.position
|
|
1275
|
+
result.pattern = self.process_ast(expr.pattern)
|
|
1276
|
+
result.update = self.process_ast(expr.update)
|
|
1277
|
+
if expr.delete is not None:
|
|
1278
|
+
result.delete = self.process_ast(expr.delete)
|
|
1279
|
+
elif type == "block":
|
|
1280
|
+
result = Parser.Symbol(self)
|
|
1281
|
+
result.type = expr.type
|
|
1282
|
+
result.position = expr.position
|
|
1283
|
+
|
|
1284
|
+
# array of expressions - process each one
|
|
1285
|
+
def lambda5(item):
|
|
1286
|
+
part = self.process_ast(item)
|
|
1287
|
+
self.push_ancestry(result, part)
|
|
1288
|
+
if part.consarray or (part.type == "path" and part.steps[0].consarray):
|
|
1289
|
+
result.consarray = True
|
|
1290
|
+
return part
|
|
1291
|
+
|
|
1292
|
+
result.expressions = list(map(lambda5, expr.expressions))
|
|
1293
|
+
# TODO scan the array of expressions to see if any of them assign variables
|
|
1294
|
+
# if so, need to mark the block as one that needs to create a new frame
|
|
1295
|
+
elif type == "name":
|
|
1296
|
+
result = Parser.Symbol(self)
|
|
1297
|
+
result.type = "path"
|
|
1298
|
+
result.steps = []
|
|
1299
|
+
result.steps.append(expr)
|
|
1300
|
+
if expr.keep_array:
|
|
1301
|
+
result.keep_singleton_array = True
|
|
1302
|
+
elif type == "parent":
|
|
1303
|
+
result = Parser.Symbol(self)
|
|
1304
|
+
result.type = "parent"
|
|
1305
|
+
result.slot = Parser.Symbol(self)
|
|
1306
|
+
result.slot.label = "!" + str(self.ancestor_label)
|
|
1307
|
+
self.ancestor_label += 1
|
|
1308
|
+
result.slot.level = 1
|
|
1309
|
+
result.slot.index = self.ancestor_index
|
|
1310
|
+
self.ancestor_index += 1
|
|
1311
|
+
# slot: { label: '!' + ancestorLabel++, level: 1, index: ancestorIndex++ } }
|
|
1312
|
+
self.ancestry.append(result)
|
|
1313
|
+
elif (type == "string" or type == "number" or type == "value" or type == "wildcard" or type == "descendant" or
|
|
1314
|
+
type == "variable" or type == "regex"):
|
|
1315
|
+
result = expr
|
|
1316
|
+
elif type == "operator":
|
|
1317
|
+
# the tokens 'and' and 'or' might have been used as a name rather than an operator
|
|
1318
|
+
if expr.value == "and" or expr.value == "or" or expr.value == "in":
|
|
1319
|
+
expr.type = "name"
|
|
1320
|
+
result = self.process_ast(expr)
|
|
1321
|
+
elif str(expr.value) == "?":
|
|
1322
|
+
# partial application
|
|
1323
|
+
result = expr
|
|
1324
|
+
else:
|
|
1325
|
+
raise jexception.JException("S0201", expr.position, expr.value)
|
|
1326
|
+
elif type == "error":
|
|
1327
|
+
result = expr
|
|
1328
|
+
if expr.lhs is not None:
|
|
1329
|
+
result = self.process_ast(expr.lhs)
|
|
1330
|
+
else:
|
|
1331
|
+
code = "S0206"
|
|
1332
|
+
# istanbul ignore else
|
|
1333
|
+
if expr.id == "(end)":
|
|
1334
|
+
code = "S0207"
|
|
1335
|
+
err = jexception.JException(code, expr.position, expr.value)
|
|
1336
|
+
if self.recover:
|
|
1337
|
+
self.errors.append(err)
|
|
1338
|
+
ret = Parser.Symbol(self)
|
|
1339
|
+
ret.type = "error"
|
|
1340
|
+
ret.error = err
|
|
1341
|
+
return ret
|
|
1342
|
+
else:
|
|
1343
|
+
# err.stack = (new Error()).stack
|
|
1344
|
+
raise err
|
|
1345
|
+
if expr.keep_array:
|
|
1346
|
+
result.keep_array = True
|
|
1347
|
+
return result
|
|
1348
|
+
|
|
1349
|
+
def object_parser(self, left: Optional[Symbol]) -> Symbol:
|
|
1350
|
+
|
|
1351
|
+
res = Parser.Infix(self, "{") if left is not None else Parser.Prefix(self, "{")
|
|
1352
|
+
|
|
1353
|
+
a = []
|
|
1354
|
+
if self.node.id != "}":
|
|
1355
|
+
while True:
|
|
1356
|
+
n = self.expression(0)
|
|
1357
|
+
self.advance(":")
|
|
1358
|
+
v = self.expression(0)
|
|
1359
|
+
pair = [n, v]
|
|
1360
|
+
a.append(pair) # holds an array of name/value expression pairs
|
|
1361
|
+
if self.node.id != ",":
|
|
1362
|
+
break
|
|
1363
|
+
self.advance(",")
|
|
1364
|
+
self.advance("}", True)
|
|
1365
|
+
if left is None:
|
|
1366
|
+
# NUD - unary prefix form
|
|
1367
|
+
res.lhs_object = a
|
|
1368
|
+
res.type = "unary"
|
|
1369
|
+
else:
|
|
1370
|
+
# LED - binary infix form
|
|
1371
|
+
res.lhs = left
|
|
1372
|
+
res.rhs_object = a
|
|
1373
|
+
res.type = "binary"
|
|
1374
|
+
return res
|
|
1375
|
+
|
|
1376
|
+
def parse(self, jsonata: Optional[str]) -> Symbol:
|
|
1377
|
+
self.source = jsonata
|
|
1378
|
+
|
|
1379
|
+
# now invoke the tokenizer and the parser and return the syntax tree
|
|
1380
|
+
self.lexer = tokenizer.Tokenizer(self.source)
|
|
1381
|
+
self.advance()
|
|
1382
|
+
# parse the tokens
|
|
1383
|
+
expr = self.expression(0)
|
|
1384
|
+
if self.node.id != "(end)":
|
|
1385
|
+
err = jexception.JException("S0201", self.node.position, self.node.value)
|
|
1386
|
+
self.handle_error(err)
|
|
1387
|
+
|
|
1388
|
+
expr = self.process_ast(expr)
|
|
1389
|
+
|
|
1390
|
+
if expr.type == "parent" or expr.seeking_parent is not None:
|
|
1391
|
+
# error - trying to derive ancestor at top level
|
|
1392
|
+
raise jexception.JException("S0217", expr.position, expr.type)
|
|
1393
|
+
|
|
1394
|
+
if len(self.errors) > 0:
|
|
1395
|
+
expr.errors = self.errors
|
|
1396
|
+
|
|
1397
|
+
return expr
|