-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathparser.py
More file actions
1438 lines (1259 loc) · 54.2 KB
/
Copy pathparser.py
File metadata and controls
1438 lines (1259 loc) · 54.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#
# Copyright Robert Yokota
#
# Licensed under the Apache License, Version 2.0 (the "License")
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Derived from the following code:
#
# Project name: jsonata-java
# Copyright Dashjoin GmbH. https://dashjoin.com
# Licensed under the Apache License, Version 2.0 (the "License")
#
# Project name: JSONata
# © Copyright IBM Corp. 2016, 2018 All Rights Reserved
# This project is licensed under the MIT License, see LICENSE
#
import copy
import re
from typing import Any, MutableSequence, Optional, Sequence
from jsonata import jexception, tokenizer, signature, utils
# var parseSignature = require('./signature')
class Parser:
# This parser implements the 'Top down operator precedence' algorithm developed by Vaughan R Pratt; http://dl.acm.org/citation.cfm?id=512931.
# and builds on the Javascript framework described by Douglas Crockford at http://javascript.crockford.com/tdop/tdop.html
# and in 'Beautiful Code', edited by Andy Oram and Greg Wilson, Copyright 2007 O'Reilly Media, Inc. 798-0-596-51004-6
# var parser = function (source, recover) {
def remaining_tokens(self) -> list[tokenizer.Tokenizer.Token]:
remaining = []
if self.node.id != "(end)":
t = tokenizer.Tokenizer.Token(self.node.type, self.node.value, self.node.position)
remaining.append(t)
nxt = self.lexer.next(False)
while nxt is not None:
remaining.append(nxt)
nxt = self.lexer.next(False)
return remaining
class Symbol:
# Symbol s
# Procedure:
# Infix attributes
# where rhs = list of Symbol pairs
# where rhs = list of Symbols
# Ternary operator:
# processAST error handling
# Prefix attributes
# Ancestor attributes
def nud(self):
# error - symbol has been invoked as a unary operator
err = jexception.JException("S0211", self.position, self.value)
if self._outer_instance.recover:
#
# err.remaining = remainingTokens()
# err.type = "error"
# errors.add(err)
# return err
#
return Parser.Symbol("(error)")
else:
raise err
def led(self, left):
raise NotImplementedError("led not implemented")
_outer_instance: 'Parser'
id: Optional[str]
type: Optional[str]
value: Optional[Any]
bp: int
lbp: int
position: int
keep_array: bool
descending: bool
expression: 'Optional[Parser.Symbol]'
seeking_parent: 'Optional[MutableSequence[Parser.Symbol]]'
errors: Optional[Sequence[Exception]]
steps: 'Optional[MutableSequence[Parser.Symbol]]'
slot: 'Optional[Parser.Symbol]'
next_function: 'Optional[Parser.Symbol]'
keep_singleton_array: bool
consarray: bool
level: int
focus: Optional[Any]
token: Optional[Any]
thunk: bool
# Procedure:
procedure: 'Optional[Parser.Symbol]'
arguments: 'Optional[MutableSequence[Parser.Symbol]]'
body: 'Optional[Parser.Symbol]'
predicate: 'Optional[MutableSequence[Parser.Symbol]]'
stages: 'Optional[MutableSequence[Parser.Symbol]]'
input: Optional[Any]
# environment: jsonata.Jsonata.Frame | None # creates circular ref
tuple: Optional[Any]
expr: Optional[Any]
group: 'Optional[Parser.Symbol]'
name: 'Optional[Parser.Symbol]'
# Infix attributes
lhs: 'Optional[Parser.Symbol]'
rhs: 'Optional[Parser.Symbol]'
# where rhs = list of Symbol pairs
lhs_object: 'Optional[Sequence[Sequence[Parser.Symbol]]]'
rhs_object: 'Optional[Sequence[Sequence[Parser.Symbol]]]'
# where rhs = list of Symbols
rhs_terms: 'Optional[Sequence[Parser.Symbol]]'
terms: 'Optional[Sequence[Parser.Symbol]]'
# Ternary operator:
condition: 'Optional[Parser.Symbol]'
then: 'Optional[Parser.Symbol]'
_else: 'Optional[Parser.Symbol]'
expressions: 'Optional[MutableSequence[Parser.Symbol]]'
# processAST error handling
error: 'Optional[jexception.JException]'
signature: 'Optional[Any]'
# Prefix attributes
pattern: 'Optional[Parser.Symbol]'
update: 'Optional[Parser.Symbol]'
delete: 'Optional[Parser.Symbol]'
# Ancestor attributes
label: Optional[str]
index: Optional[Any]
_jsonata_lambda: bool
ancestor: 'Optional[Parser.Symbol]'
def __init__(self, outer_instance, id=None, bp=0):
self._outer_instance = outer_instance
self.id = id
self.value = id
self.bp = bp
# use register(Symbol) ! Otherwise inheritance doesn't work
# Symbol s = symbolTable.get(id)
# //bp = bp != 0 ? bp : 0
# if (s != null) {
# if (bp >= s.lbp) {
# s.lbp = bp
# }
# } else {
# s = new Symbol()
# s.value = s.id = id
# s.lbp = bp
# symbolTable.put(id, s)
# }
#
#
# return s
self.type = None
self.lbp = 0
self.position = 0
self.keep_array = False
self.descending = False
self.expression = None
self.seeking_parent = None
self.errors = None
self.steps = None
self.slot = None
self.next_function = None
self.keep_singleton_array = False
self.consarray = False
self.level = 0
self.focus = None
self.token = None
self.thunk = False
self.procedure = None
self.arguments = None
self.body = None
self.predicate = None
self.stages = None
self.input = None
self.environment = None
self.tuple = None
self.expr = None
self.group = None
self.name = None
self.lhs = None
self.rhs = None
self.lhs_object = None
self.rhs_object = None
self.rhs_terms = None
self.terms = None
self.condition = None
self.then = None
self._else = None
self.expressions = None
self.error = None
self.signature = None
self.pattern = None
self.update = None
self.delete = None
self.label = None
self.index = None
self._jsonata_lambda = False
self.ancestor = None
def create(self):
# We want a shallow clone (do not duplicate outer class!)
cl = self.clone()
# System.err.println("cloning "+this+" clone="+cl)
return cl
def clone(self):
return copy.copy(self)
def __repr__(self):
return str(type(self)) + " " + self.id + " value=" + self.value
def register(self, t: Symbol) -> None:
# if (t instanceof Infix || t instanceof InfixR) return
s = self.symbol_table.get(t.id)
if s is not None:
if self.dbg:
print("Symbol in table " + t.id + " " + str(type(s)) + " -> " + str(type(t)))
# symbolTable.put(t.id, t)
if t.bp >= s.lbp:
if self.dbg:
print("Symbol in table " + t.id + " lbp=" + str(s.lbp) + " -> " + str(t.bp))
s.lbp = t.bp
else:
s = t.create()
s.value = s.id = t.id
s.lbp = t.bp
self.symbol_table[t.id] = s
def handle_error(self, err: jexception.JException) -> Symbol:
if self.recover:
err.remaining = self.remaining_tokens()
self.errors.append(err)
# Symbol symbol = symbolTable.get("(error)")
node = Parser.Symbol(self)
# FIXME node.error = err
# node.type = "(error)"
return node
else:
raise err
# }
def advance(self, id: Optional[str] = None, infix: bool = False) -> Symbol:
if id is not None and self.node.id != id:
code = None
if self.node.id == "(end)":
# unexpected end of buffer
code = "S0203"
else:
code = "S0202"
err = jexception.JException(code, self.node.position, id, self.node.value)
return self.handle_error(err)
next_token = self.lexer.next(infix)
if self.dbg:
print("nextToken " + (next_token.type if next_token is not None else None))
if next_token is None:
self.node = self.symbol_table["(end)"]
self.node.position = len(self.source)
return self.node
value = next_token.value
type = next_token.type
symbol = None
if type == "name" or type == "variable":
symbol = self.symbol_table["(name)"]
elif type == "operator":
symbol = self.symbol_table[str(value)]
if symbol is None:
return self.handle_error(jexception.JException("S0204", next_token.position, value))
elif type == "string" or type == "number" or type == "value":
symbol = self.symbol_table["(literal)"]
elif type == "regex":
type = "regex"
symbol = self.symbol_table["(regex)"]
# istanbul ignore next
else:
return self.handle_error(jexception.JException("S0205", next_token.position, value))
self.node = symbol.create()
# Token node = new Token(); //Object.create(symbol)
self.node.value = value
self.node.type = type
self.node.position = next_token.position
if self.dbg:
print("advance " + str(self.node))
return self.node
# Pratt's algorithm
def expression(self, rbp: int) -> Symbol:
left = None
t = self.node
self.advance(None, True)
left = t.nud()
while rbp < self.node.lbp:
t = self.node
self.advance(None, False)
if self.dbg:
print("t=" + str(t) + ", left=" + left.type)
left = t.led(left)
return left
class Terminal(Symbol):
_outer_instance: 'Parser'
def __init__(self, outer_instance, id):
super().__init__(outer_instance, id, 0)
self._outer_instance = outer_instance
def nud(self):
return self
#
# var terminal = function (id) {
# var s = Parser.Symbol(id, 0)
# s.nud = function () {
# return this
# }
# }
#
# match infix operators
# <expression> <operator> <expression>
# left associative
class Infix(Symbol):
_outer_instance: 'Parser'
def __init__(self, outer_instance, id, bp=0):
super().__init__(outer_instance, id,
bp if bp != 0 else (tokenizer.Tokenizer.operators[id] if id is not None else 0))
self._outer_instance = outer_instance
def led(self, left):
self.lhs = left
self.rhs = self._outer_instance.expression(self.bp)
self.type = "binary"
return self
class InfixAndPrefix(Infix):
_outer_instance: 'Parser'
prefix: 'Parser.Prefix'
def __init__(self, outer_instance, id, bp=0):
super().__init__(outer_instance, id, bp)
self._outer_instance = outer_instance
self.prefix = Parser.Prefix(outer_instance, id)
def nud(self):
return self.prefix.nud()
# expression(70)
# type="unary"
# return this
def clone(self):
c = super().clone()
# IMPORTANT: make sure to allocate a new Prefix!!!
c.prefix = Parser.Prefix(self._outer_instance, c.id)
return c
# match infix operators
# <expression> <operator> <expression>
# right associative
class InfixR(Symbol):
_outer_instance: 'Parser'
def __init__(self, outer_instance, id, bp):
super().__init__(outer_instance, id, bp)
self._outer_instance = outer_instance
# abstract Object led()
# match prefix operators
# <operator> <expression>
class Prefix(Symbol):
# public List<Symbol[]> lhs
def __init__(self, outer_instance, id):
super().__init__(outer_instance, id)
self._outer_instance = outer_instance
# type = "unary"
# Symbol _expression
def nud(self):
self.expression = self._outer_instance.expression(70)
self.type = "unary"
return self
dbg: bool
source: Optional[str]
recover: bool
node: Optional[Symbol]
lexer: Optional[tokenizer.Tokenizer]
symbol_table: dict[str, Symbol]
errors: MutableSequence[Exception]
ancestor_label: int
ancestor_index: int
ancestry: MutableSequence[Symbol]
def __init__(self):
self.dbg = False
self.source = None
self.recover = False
self.node = None
self.lexer = None
self.symbol_table = {}
self.errors = []
self.ancestor_label = 0
self.ancestor_index = 0
self.ancestry = []
self.register(Parser.Terminal(self, "(end)"))
self.register(Parser.Terminal(self, "(name)"))
self.register(Parser.Terminal(self, "(literal)"))
self.register(Parser.Terminal(self, "(regex)"))
self.register(Parser.Symbol(self, ":"))
self.register(Parser.Symbol(self, ";"))
self.register(Parser.Symbol(self, ","))
self.register(Parser.Symbol(self, ")"))
self.register(Parser.Symbol(self, "]"))
self.register(Parser.Symbol(self, "}"))
self.register(Parser.Symbol(self, "..")) # range operator
self.register(Parser.Infix(self, ".")) # map operator
self.register(Parser.Infix(self, "+")) # numeric addition
self.register(Parser.InfixAndPrefix(self, "-")) # numeric subtraction
# unary numeric negation
self.register(Parser.InfixFieldWildcard(self))
# numeric multiplication
self.register(Parser.Infix(self, "/")) # numeric division
self.register(Parser.InfixParentOperator(self))
# numeric modulus
self.register(Parser.Infix(self, "=")) # equality
self.register(Parser.Infix(self, "<")) # less than
self.register(Parser.Infix(self, ">")) # greater than
self.register(Parser.Infix(self, "!=")) # not equal to
self.register(Parser.Infix(self, "<=")) # less than or equal
self.register(Parser.Infix(self, ">=")) # greater than or equal
self.register(Parser.Infix(self, "&")) # string concatenation
self.register(Parser.InfixAnd(self))
# Boolean AND
self.register(Parser.InfixOr(self))
# Boolean OR
self.register(Parser.InfixIn(self))
# is member of array
# merged Infix: register(new Terminal("and")); // the 'keywords' can also be used as terminals (field names)
# merged Infix: register(new Terminal("or")); //
# merged Infix: register(new Terminal("in")); //
# merged Infix: register(new Prefix("-")); // unary numeric negation
self.register(Parser.Infix(self, "~>")) # function application
self.register(Parser.InfixRError(self))
# field wildcard (single level)
# merged with Infix *
# register(new Prefix("*") {
# @Override Symbol nud() {
# type = "wildcard"
# return this
# }
# })
# descendant wildcard (multi-level)
self.register(Parser.PrefixDescendantWildcard(self))
# parent operator
# merged with Infix %
# register(new Prefix("%") {
# @Override Symbol nud() {
# type = "parent"
# return this
# }
# })
# function invocation
self.register(Parser.InfixFunctionInvocation(self, tokenizer.Tokenizer.operators["("]))
# array constructor
# merged: register(new Prefix("[") {
self.register(Parser.InfixArrayConstructor(self, tokenizer.Tokenizer.operators["["]))
# order-by
self.register(Parser.InfixOrderBy(self, tokenizer.Tokenizer.operators["^"]))
self.register(Parser.InfixObjectConstructor(self, tokenizer.Tokenizer.operators["{"]))
# bind variable
self.register(Parser.InfixRBindVariable(self, tokenizer.Tokenizer.operators[":="]))
# focus variable bind
self.register(Parser.InfixFocusVariableBind(self, tokenizer.Tokenizer.operators["@"]))
# index (position) variable bind
self.register(Parser.InfixIndexVariableBind(self, tokenizer.Tokenizer.operators["#"]))
# if/then/else ternary operator ?:
self.register(Parser.InfixTernaryOperator(self, tokenizer.Tokenizer.operators["?"]))
# coalescing operator ??
self.register(Parser.InfixCoalesce(self, tokenizer.Tokenizer.operators["??"]))
# elvis/default operator ?:
self.register(Parser.InfixDefault(self, tokenizer.Tokenizer.operators["?:"]))
# object transformer
self.register(Parser.PrefixObjectTransformer(self))
class InfixFieldWildcard(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "*")
self._outer_instance = outer_instance
# field wildcard (single level)
def nud(self):
self.type = "wildcard"
return self
class InfixParentOperator(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "%")
self._outer_instance = outer_instance
# parent operator
def nud(self):
self.type = "parent"
return self
class InfixAnd(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "and")
self._outer_instance = outer_instance
# allow as terminal
def nud(self):
return self
class InfixOr(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "or")
self._outer_instance = outer_instance
# allow as terminal
def nud(self):
return self
class InfixIn(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "in")
self._outer_instance = outer_instance
# allow as terminal
def nud(self):
return self
class InfixRError(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "(error)", 10)
self._outer_instance = outer_instance
def led(self, left):
raise NotImplementedError("TODO", None)
class PrefixDescendantWildcard(Prefix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "**")
self._outer_instance = outer_instance
def nud(self):
self.type = "descendant"
return self
class InfixFunctionInvocation(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "(", get)
self._outer_instance = outer_instance
def led(self, left):
# left is is what we are trying to invoke
self.procedure = left
self.type = "function"
self.arguments = []
if self._outer_instance.node.id != ")":
while True:
if "operator" == self._outer_instance.node.type and self._outer_instance.node.id == "?":
# partial function application
self.type = "partial"
self.arguments.append(self._outer_instance.node)
self._outer_instance.advance("?")
else:
self.arguments.append(self._outer_instance.expression(0))
if self._outer_instance.node.id != ",":
break
self._outer_instance.advance(",")
self._outer_instance.advance(")", True)
# if the name of the function is 'function' or λ, then this is function definition (lambda function)
if left.type == "name" and (left.value == "function" or left.value == "\u03BB"):
# all of the args must be VARIABLE tokens
# int index = 0
for arg in self.arguments:
# this.arguments.forEach(function (arg, index) {
if arg.type != "variable":
return self._outer_instance.handle_error(
jexception.JException("S0208", arg.position, arg.value))
# index++
self.type = "lambda"
# is the next token a '<' - if so, parse the function signature
if self._outer_instance.node.id == "<":
depth = 1
sig = "<"
while depth > 0 and self._outer_instance.node.id != "{" and self._outer_instance.node.id != "(end)":
tok = self._outer_instance.advance()
if tok.id == ">":
depth -= 1
elif tok.id == "<":
depth += 1
sig += tok.value
self._outer_instance.advance(">")
self.signature = signature.Signature(sig, "lambda")
# parse the function body
self._outer_instance.advance("{")
self.body = self._outer_instance.expression(0)
self._outer_instance.advance("}")
return self
# })
# parenthesis - block expression
# Note: in Java both nud and led are in same class!
# register(new Prefix("(") {
def nud(self):
if self._outer_instance.dbg:
print("Prefix (")
expressions = []
while self._outer_instance.node.id != ")":
expressions.append(self._outer_instance.expression(0))
if self._outer_instance.node.id != ";":
break
self._outer_instance.advance(";")
self._outer_instance.advance(")", True)
self.type = "block"
self.expressions = expressions
return self
class InfixArrayConstructor(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "[", get)
self._outer_instance = outer_instance
def nud(self):
a = []
if self._outer_instance.node.id != "]":
while True:
item = self._outer_instance.expression(0)
if self._outer_instance.node.id == "..":
# range operator
range = Parser.Symbol(self._outer_instance)
range.type = "binary"
range.value = ".."
range.position = self._outer_instance.node.position
range.lhs = item
self._outer_instance.advance("..")
range.rhs = self._outer_instance.expression(0)
item = range
a.append(item)
if self._outer_instance.node.id != ",":
break
self._outer_instance.advance(",")
self._outer_instance.advance("]", True)
self.expressions = a
self.type = "unary"
return self
# })
# filter - predicate or array index
# register(new Infix("[", tokenizer.Tokenizer.operators.get("[")) {
def led(self, left):
if self._outer_instance.node.id == "]":
# empty predicate means maintain singleton arrays in the output
step = left
while step is not None and step.type == "binary" and step.value == "[":
step = step.lhs
step.keep_array = True
self._outer_instance.advance("]")
return left
else:
self.lhs = left
self.rhs = self._outer_instance.expression(tokenizer.Tokenizer.operators["]"])
self.type = "binary"
self._outer_instance.advance("]", True)
return self
class InfixOrderBy(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "^", get)
self._outer_instance = outer_instance
def led(self, left):
self._outer_instance.advance("(")
terms = []
while True:
term = Parser.Symbol(self._outer_instance)
term.descending = False
if self._outer_instance.node.id == "<":
# ascending sort
self._outer_instance.advance("<")
elif self._outer_instance.node.id == ">":
# descending sort
term.descending = True
self._outer_instance.advance(">")
else:
# unspecified - default to ascending
pass
term.expression = self._outer_instance.expression(0)
terms.append(term)
if self._outer_instance.node.id != ",":
break
self._outer_instance.advance(",")
self._outer_instance.advance(")")
self.lhs = left
self.rhs_terms = terms
self.type = "binary"
return self
class InfixObjectConstructor(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "{", get)
self._outer_instance = outer_instance
# merged register(new Prefix("{") {
def nud(self):
return self._outer_instance.object_parser(None)
# })
# register(new Infix("{", tokenizer.Tokenizer.operators.get("{")) {
def led(self, left):
return self._outer_instance.object_parser(left)
class InfixRBindVariable(InfixR):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, ":=", get)
self._outer_instance = outer_instance
def led(self, left):
if left.type != "variable":
return self._outer_instance.handle_error(jexception.JException("S0212", left.position, left.value))
self.lhs = left
self.rhs = self._outer_instance.expression(
tokenizer.Tokenizer.operators[":="] - 1) # subtract 1 from bindingPower for right associative operators
self.type = "binary"
return self
class InfixFocusVariableBind(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "@", get)
self._outer_instance = outer_instance
def led(self, left):
self.lhs = left
self.rhs = self._outer_instance.expression(tokenizer.Tokenizer.operators["@"])
if self.rhs.type != "variable":
return self._outer_instance.handle_error(jexception.JException("S0214", self.rhs.position, "@"))
self.type = "binary"
return self
class InfixIndexVariableBind(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "#", get)
self._outer_instance = outer_instance
def led(self, left):
self.lhs = left
self.rhs = self._outer_instance.expression(tokenizer.Tokenizer.operators["#"])
if self.rhs.type != "variable":
return self._outer_instance.handle_error(jexception.JException("S0214", self.rhs.position, "#"))
self.type = "binary"
return self
class InfixTernaryOperator(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "?", get)
self._outer_instance = outer_instance
def led(self, left):
self.type = "condition"
self.condition = left
self.then = self._outer_instance.expression(0)
if self._outer_instance.node.id == ":":
# else condition
self._outer_instance.advance(":")
self._else = self._outer_instance.expression(0)
return self
class InfixCoalesce(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "??", get)
self._outer_instance = outer_instance
def led(self, left):
self.type = "condition"
# condition becomes function exists(left)
cond = Parser.Symbol(self._outer_instance)
cond.type = "function"
cond.value = "("
proc = Parser.Symbol(self._outer_instance)
proc.type = "variable"
proc.value = "exists"
cond.procedure = proc
cond.arguments = [left]
self.condition = cond
self.then = left
self._else = self._outer_instance.expression(0)
return self
class InfixDefault(Infix):
_outer_instance: 'Parser'
def __init__(self, outer_instance, get):
super().__init__(outer_instance, "?:", get)
self._outer_instance = outer_instance
def led(self, left):
self.type = "condition"
self.condition = left
self.then = left
self._else = self._outer_instance.expression(0)
return self
class PrefixObjectTransformer(Prefix):
_outer_instance: 'Parser'
def __init__(self, outer_instance):
super().__init__(outer_instance, "|")
self._outer_instance = outer_instance
def nud(self):
self.type = "transform"
self.pattern = self._outer_instance.expression(0)
self._outer_instance.advance("|")
self.update = self._outer_instance.expression(0)
if self._outer_instance.node.id == ",":
self._outer_instance.advance(",")
self.delete = self._outer_instance.expression(0)
self._outer_instance.advance("|")
return self
# tail call optimization
# this is invoked by the post parser to analyse lambda functions to see
# if they make a tail call. If so, it is replaced by a thunk which will
# be invoked by the trampoline loop during function application.
# This enables tail-recursive functions to be written without growing the stack
def tail_call_optimize(self, expr: Symbol) -> Symbol:
result = None
if expr.type == "function" and expr.predicate is None:
thunk = Parser.Symbol(self)
thunk.type = "lambda"
thunk.thunk = True
thunk.arguments = []
thunk.position = expr.position
thunk.body = expr
result = thunk
elif expr.type == "condition":
# analyse both branches
expr.then = self.tail_call_optimize(expr.then)
if expr._else is not None:
expr._else = self.tail_call_optimize(expr._else)
result = expr
elif expr.type == "block":
# only the last expression in the block
length = len(expr.expressions)
if length > 0:
if not (isinstance(expr.expressions, list)):
expr.expressions = [expr.expressions]
expr.expressions[length - 1] = self.tail_call_optimize(expr.expressions[length - 1])
result = expr
else:
result = expr
return result
def seek_parent(self, node: Symbol, slot: Symbol) -> Symbol:
if node.type == "name" or node.type == "wildcard":
slot.level -= 1
if slot.level == 0:
if node.ancestor is None:
node.ancestor = slot
else:
# reuse the existing label
self.ancestry[int(slot.index)].slot.label = node.ancestor.label
node.ancestor = slot
node.tuple = True
elif node.type == "parent":
slot.level += 1
elif node.type == "block":
# look in last expression in the block
if node.expressions:
node.tuple = True
slot = self.seek_parent(node.expressions[-1], slot)
elif node.type == "path":
# last step in path
node.tuple = True
index = len(node.steps) - 1
slot = self.seek_parent(node.steps[index], slot)
index -= 1
while slot.level > 0 and index >= 0:
# check previous steps
slot = self.seek_parent(node.steps[index], slot)
index -= 1
else:
# error - can't derive ancestor
raise jexception.JException("S0217", node.position, node.type)
return slot
def push_ancestry(self, result: Symbol, value: Optional[Symbol]) -> None:
if value is None:
return # Added NPE check
if value.seeking_parent is not None or value.type == "parent":
slots = value.seeking_parent if (value.seeking_parent is not None) else []
if value.type == "parent":
slots.append(value.slot)
if result.seeking_parent is None:
result.seeking_parent = slots
else:
result.seeking_parent.extend(slots)
def resolve_ancestry(self, path: Symbol) -> None:
index = len(path.steps) - 1
laststep = path.steps[index]
slots = laststep.seeking_parent if (laststep.seeking_parent is not None) else []