omlish 0.0.0.dev360__py3-none-any.whl → 0.0.0.dev362__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.
Potentially problematic release.
This version of omlish might be problematic. Click here for more details.
- omlish/__about__.py +2 -2
- omlish/formats/json/Json.g4 +77 -0
- omlish/formats/json/_antlr/JsonLexer.py +109 -0
- omlish/formats/json/_antlr/JsonListener.py +61 -0
- omlish/formats/json/_antlr/JsonParser.py +457 -0
- omlish/formats/json/_antlr/JsonVisitor.py +42 -0
- omlish/formats/json/_antlr/__init__.py +0 -0
- omlish/formats/json/literals.py +27 -13
- omlish/formats/json/stream/__init__.py +57 -0
- omlish/formats/json/stream/building.py +1 -1
- omlish/formats/json/stream/utils.py +14 -6
- omlish/formats/json5/Json5.g4 +8 -9
- omlish/formats/json5/_antlr/Json5Lexer.py +1 -0
- omlish/formats/json5/_antlr/Json5Listener.py +1 -0
- omlish/formats/json5/_antlr/Json5Parser.py +1 -0
- omlish/formats/json5/_antlr/Json5Visitor.py +1 -0
- omlish/formats/json5/parsing.py +30 -8
- omlish/formats/json5/rendering.py +121 -10
- omlish/lang/__init__.py +1 -0
- omlish/lang/classes/namespaces.py +8 -3
- omlish/lang/strings.py +15 -0
- omlish/specs/jmespath/__init__.py +1 -0
- omlish/specs/jmespath/ast.py +17 -0
- omlish/specs/jmespath/cli.py +3 -0
- omlish/specs/jmespath/errors.py +3 -0
- omlish/specs/jmespath/lexer.py +4 -0
- omlish/specs/jmespath/parser.py +13 -2
- omlish/specs/jmespath/scope.py +3 -0
- omlish/specs/jmespath/visitor.py +9 -0
- omlish/specs/proto/_antlr/Protobuf3Lexer.py +1 -0
- omlish/specs/proto/_antlr/Protobuf3Listener.py +1 -0
- omlish/specs/proto/_antlr/Protobuf3Parser.py +1 -0
- omlish/specs/proto/_antlr/Protobuf3Visitor.py +1 -0
- omlish/sql/parsing/_antlr/MinisqlLexer.py +1 -0
- omlish/sql/parsing/_antlr/MinisqlListener.py +1 -0
- omlish/sql/parsing/_antlr/MinisqlParser.py +1 -0
- omlish/sql/parsing/_antlr/MinisqlVisitor.py +1 -0
- {omlish-0.0.0.dev360.dist-info → omlish-0.0.0.dev362.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev360.dist-info → omlish-0.0.0.dev362.dist-info}/RECORD +43 -37
- {omlish-0.0.0.dev360.dist-info → omlish-0.0.0.dev362.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev360.dist-info → omlish-0.0.0.dev362.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev360.dist-info → omlish-0.0.0.dev362.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev360.dist-info → omlish-0.0.0.dev362.dist-info}/top_level.txt +0 -0
omlish/formats/json5/Json5.g4
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Student Main
|
|
2
1
|
// 2020-07-22
|
|
3
2
|
// Public domain
|
|
4
3
|
|
|
@@ -79,18 +78,18 @@ fragment SINGLE_QUOTE_CHAR
|
|
|
79
78
|
fragment ESCAPE_SEQUENCE
|
|
80
79
|
: '\\' (
|
|
81
80
|
NEWLINE
|
|
82
|
-
| UNICODE_SEQUENCE
|
|
83
|
-
| ['"\\/bfnrtv]
|
|
84
|
-
| ~['"\\bfnrtv0-9xu\r\n]
|
|
85
|
-
| '0'
|
|
86
|
-
| 'x' HEX HEX
|
|
81
|
+
| UNICODE_SEQUENCE // \u1234
|
|
82
|
+
| ['"\\/bfnrtv] // single escape char
|
|
83
|
+
| ~['"\\bfnrtv0-9xu\r\n] // non escape char
|
|
84
|
+
| '0' // \0
|
|
85
|
+
| 'x' HEX HEX // \x3a
|
|
87
86
|
)
|
|
88
87
|
;
|
|
89
88
|
|
|
90
89
|
NUMBER
|
|
91
|
-
: INT ('.' [0-9]*)? EXP?
|
|
92
|
-
| '.' [0-9]+ EXP?
|
|
93
|
-
| '0' [xX] HEX+
|
|
90
|
+
: INT ('.' [0-9]*)? EXP? // +1.e2, 1234, 1234.5
|
|
91
|
+
| '.' [0-9]+ EXP? // -.2e3
|
|
92
|
+
| '0' [xX] HEX+ // 0x12345678
|
|
94
93
|
;
|
|
95
94
|
|
|
96
95
|
NUMERIC_LITERAL
|
omlish/formats/json5/parsing.py
CHANGED
|
@@ -11,6 +11,9 @@ from .literals import parse_number_literal
|
|
|
11
11
|
from .literals import parse_string_literal
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
##
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
class Json5ParseVisitor(antlr.parsing.StandardParseTreeVisitor, Json5Visitor):
|
|
15
18
|
def visitArr(self, ctx: Json5Parser.ArrContext):
|
|
16
19
|
return [self.visit(e) for e in ctx.value()]
|
|
@@ -57,16 +60,18 @@ class Json5ParseVisitor(antlr.parsing.StandardParseTreeVisitor, Json5Visitor):
|
|
|
57
60
|
return super().visitChildren(ctx)
|
|
58
61
|
|
|
59
62
|
|
|
63
|
+
def _make_parser(buf: str) -> Json5Parser:
|
|
64
|
+
return antlr.parsing.make_parser(
|
|
65
|
+
buf,
|
|
66
|
+
Json5Lexer,
|
|
67
|
+
Json5Parser,
|
|
68
|
+
silent_errors=True,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
60
72
|
def parse(buf: str) -> ta.Any:
|
|
61
73
|
try:
|
|
62
|
-
|
|
63
|
-
buf,
|
|
64
|
-
Json5Lexer,
|
|
65
|
-
Json5Parser,
|
|
66
|
-
silent_errors=True,
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
root = parser.json5()
|
|
74
|
+
root = _make_parser(buf).json5()
|
|
70
75
|
|
|
71
76
|
except antlr.errors.ParseError as e:
|
|
72
77
|
raise Json5Error from e
|
|
@@ -76,3 +81,20 @@ def parse(buf: str) -> ta.Any:
|
|
|
76
81
|
|
|
77
82
|
visitor = Json5ParseVisitor()
|
|
78
83
|
return visitor.visit(root)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def parse_many(buf: str) -> ta.Generator[ta.Any]:
|
|
87
|
+
try:
|
|
88
|
+
parser = _make_parser(buf)
|
|
89
|
+
|
|
90
|
+
while True:
|
|
91
|
+
if parser.getInputStream().LT(1).type == antlr.runtime.Token.EOF:
|
|
92
|
+
break
|
|
93
|
+
|
|
94
|
+
value = parser.value()
|
|
95
|
+
|
|
96
|
+
visitor = Json5ParseVisitor()
|
|
97
|
+
yield visitor.visit(value)
|
|
98
|
+
|
|
99
|
+
except antlr.errors.ParseError as e:
|
|
100
|
+
raise Json5Error from e
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import re
|
|
1
3
|
import typing as ta
|
|
2
4
|
|
|
5
|
+
from ... import check
|
|
6
|
+
from ... import lang
|
|
3
7
|
from ..json import Scalar
|
|
4
8
|
from ..json.literals import ESCAPE_MAP
|
|
5
9
|
from ..json.literals import encode_string
|
|
@@ -10,11 +14,18 @@ from ..json.rendering import JsonRendererOut
|
|
|
10
14
|
##
|
|
11
15
|
|
|
12
16
|
|
|
17
|
+
MULTILINE_STRINGS_ENDL = '\\\n'
|
|
18
|
+
MULTILINE_STRINGS_LQ = '"' + MULTILINE_STRINGS_ENDL
|
|
19
|
+
MULTILINE_STRINGS_RQ = MULTILINE_STRINGS_ENDL + '"'
|
|
20
|
+
MULTILINE_STRINGS_NL = '\\n' + MULTILINE_STRINGS_ENDL
|
|
21
|
+
|
|
13
22
|
MULTILINE_STRINGS_ESCAPE_MAP = {
|
|
14
23
|
**ESCAPE_MAP,
|
|
15
|
-
'\n':
|
|
24
|
+
'\n': MULTILINE_STRINGS_NL,
|
|
16
25
|
}
|
|
17
26
|
|
|
27
|
+
SOFTWRAP_WS_PAT = re.compile(r'\s+')
|
|
28
|
+
|
|
18
29
|
|
|
19
30
|
class Json5Renderer(JsonRenderer):
|
|
20
31
|
def __init__(
|
|
@@ -22,24 +33,124 @@ class Json5Renderer(JsonRenderer):
|
|
|
22
33
|
out: JsonRendererOut,
|
|
23
34
|
*,
|
|
24
35
|
multiline_strings: bool = False,
|
|
36
|
+
softwrap_length: int | None = None,
|
|
25
37
|
**kwargs: ta.Any,
|
|
26
38
|
) -> None:
|
|
27
39
|
super().__init__(out, **kwargs)
|
|
28
40
|
|
|
29
41
|
self._multiline_strings = multiline_strings
|
|
42
|
+
self._softwrap_length = softwrap_length
|
|
43
|
+
|
|
44
|
+
def _softwrap_string_chunks(self, chunks: list[str]) -> str:
|
|
45
|
+
multiline_strings = self._multiline_strings
|
|
46
|
+
softwrap_len = check.not_none(self._softwrap_length)
|
|
47
|
+
|
|
48
|
+
out = io.StringIO()
|
|
49
|
+
out.write(MULTILINE_STRINGS_LQ)
|
|
50
|
+
|
|
51
|
+
l = 0
|
|
52
|
+
|
|
53
|
+
def write(s: str) -> None:
|
|
54
|
+
nonlocal l
|
|
55
|
+
if l >= (softwrap_len - (sl := len(s))):
|
|
56
|
+
out.write(MULTILINE_STRINGS_ENDL)
|
|
57
|
+
l = 0
|
|
58
|
+
|
|
59
|
+
out.write(s)
|
|
60
|
+
l += sl
|
|
61
|
+
|
|
62
|
+
for c in chunks:
|
|
63
|
+
if not c:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
if c == '\\n' and multiline_strings:
|
|
67
|
+
if (softwrap_len - l) > 2:
|
|
68
|
+
out.write('\\n\\\n')
|
|
69
|
+
else:
|
|
70
|
+
out.write('\\\n\\n\\\n')
|
|
71
|
+
l = 0
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
it: ta.Iterable[str | re.Match]
|
|
75
|
+
if c[0] == '\\':
|
|
76
|
+
it = [c]
|
|
77
|
+
else:
|
|
78
|
+
it = lang.iter_matches(SOFTWRAP_WS_PAT, c)
|
|
79
|
+
|
|
80
|
+
for x in it:
|
|
81
|
+
if isinstance(x, re.Match):
|
|
82
|
+
ws = x.group(0)
|
|
83
|
+
r = softwrap_len - l
|
|
84
|
+
if len(ws) > r:
|
|
85
|
+
write(ws[:r])
|
|
86
|
+
p = r
|
|
87
|
+
while p < len(ws):
|
|
88
|
+
write(ws[p:(np := (p + softwrap_len))])
|
|
89
|
+
p = np
|
|
90
|
+
|
|
91
|
+
else:
|
|
92
|
+
write(ws)
|
|
93
|
+
|
|
94
|
+
else:
|
|
95
|
+
write(x)
|
|
96
|
+
|
|
97
|
+
out.write(MULTILINE_STRINGS_RQ)
|
|
98
|
+
return out.getvalue()
|
|
99
|
+
|
|
100
|
+
def _format_string(self, s: str, state: JsonRenderer.State | None = None) -> str:
|
|
101
|
+
num_nls = s.count('\n')
|
|
102
|
+
is_multiline = self._multiline_strings and num_nls
|
|
103
|
+
|
|
104
|
+
if (softwrap_len := self._softwrap_length) is not None:
|
|
105
|
+
def process_chunks(chunks: list[str]) -> list[str]:
|
|
106
|
+
naive_len = sum(map(len, chunks))
|
|
107
|
+
|
|
108
|
+
if is_multiline:
|
|
109
|
+
if naive_len < (
|
|
110
|
+
softwrap_len -
|
|
111
|
+
len(MULTILINE_STRINGS_LQ) -
|
|
112
|
+
(num_nls * (len(MULTILINE_STRINGS_NL) - 1)) -
|
|
113
|
+
len(MULTILINE_STRINGS_RQ)
|
|
114
|
+
):
|
|
115
|
+
return [
|
|
116
|
+
MULTILINE_STRINGS_LQ,
|
|
117
|
+
*[
|
|
118
|
+
MULTILINE_STRINGS_NL if c == '\\n' else c
|
|
119
|
+
for c in chunks
|
|
120
|
+
],
|
|
121
|
+
MULTILINE_STRINGS_RQ,
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
elif naive_len < softwrap_len:
|
|
125
|
+
return [
|
|
126
|
+
'"',
|
|
127
|
+
*chunks,
|
|
128
|
+
'"',
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
return [self._softwrap_string_chunks(chunks)]
|
|
30
132
|
|
|
31
|
-
def _format_scalar(self, o: Scalar, state: JsonRenderer.State | None = None) -> str:
|
|
32
|
-
if (
|
|
33
|
-
self._multiline_strings and
|
|
34
|
-
isinstance(o, str) and
|
|
35
|
-
'\n' in o
|
|
36
|
-
):
|
|
37
133
|
return encode_string(
|
|
38
|
-
|
|
39
|
-
|
|
134
|
+
s,
|
|
135
|
+
q='',
|
|
136
|
+
ensure_ascii=self._ensure_ascii,
|
|
137
|
+
process_chunks=process_chunks,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
if is_multiline:
|
|
141
|
+
return encode_string(
|
|
142
|
+
s,
|
|
143
|
+
lq=MULTILINE_STRINGS_LQ,
|
|
144
|
+
rq=MULTILINE_STRINGS_RQ,
|
|
40
145
|
escape_map=MULTILINE_STRINGS_ESCAPE_MAP,
|
|
41
146
|
ensure_ascii=self._ensure_ascii,
|
|
42
147
|
)
|
|
43
148
|
|
|
149
|
+
return super()._format_scalar(s, state=state)
|
|
150
|
+
|
|
151
|
+
def _format_scalar(self, o: Scalar, state: JsonRenderer.State | None = None) -> str:
|
|
152
|
+
if isinstance(o, str):
|
|
153
|
+
return self._format_string(o)
|
|
154
|
+
|
|
44
155
|
else:
|
|
45
|
-
return super()._format_scalar(o)
|
|
156
|
+
return super()._format_scalar(o, state=state)
|
omlish/lang/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import typing as ta
|
|
3
3
|
|
|
4
|
+
from .abstract import Abstract
|
|
4
5
|
from .restrict import NotInstantiable
|
|
5
6
|
|
|
6
7
|
|
|
@@ -50,9 +51,13 @@ class GenericNamespaceMeta(abc.ABCMeta, ta.Generic[V]):
|
|
|
50
51
|
**kwargs,
|
|
51
52
|
):
|
|
52
53
|
if bases:
|
|
53
|
-
|
|
54
|
-
if
|
|
55
|
-
|
|
54
|
+
if NotInstantiable not in bases and not any(NotInstantiable in b.__mro__ for b in bases):
|
|
55
|
+
if Abstract in bases:
|
|
56
|
+
# Must go before Abstract for mro because NotInstantiable is itself Abstract
|
|
57
|
+
ai = bases.index(Abstract)
|
|
58
|
+
bases = (*bases[:ai], NotInstantiable, *bases[ai:])
|
|
59
|
+
else:
|
|
60
|
+
bases += (NotInstantiable,)
|
|
56
61
|
|
|
57
62
|
if check_values is _NOT_SET:
|
|
58
63
|
check_values = mcls.__namespace_check_values__
|
omlish/lang/strings.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
import typing as ta
|
|
2
3
|
import unicodedata
|
|
3
4
|
|
|
@@ -210,3 +211,17 @@ STRING_BOOL_VALUES: ta.Mapping[str, bool] = {
|
|
|
210
211
|
]
|
|
211
212
|
for k in ks
|
|
212
213
|
}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
##
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def iter_matches(pat: re.Pattern[str], s: str, **kwargs: ta.Any) -> ta.Generator[str | re.Match]:
|
|
220
|
+
p = 0
|
|
221
|
+
for m in re.finditer(pat, s, **kwargs):
|
|
222
|
+
if p < (l := m.start()):
|
|
223
|
+
yield s[p:l]
|
|
224
|
+
yield m
|
|
225
|
+
p = m.end()
|
|
226
|
+
if p < len(s):
|
|
227
|
+
yield s[p:]
|
|
@@ -20,6 +20,7 @@ From community:
|
|
|
20
20
|
- JEP-18 Grouping - https://github.com/jmespath-community/jmespath.spec/discussions/96#discussion-4282156
|
|
21
21
|
- JEP-19 Evaluation of Pipe Expressions - https://github.com/jmespath-community/jmespath.spec/discussions/113#discussioncomment-4000862
|
|
22
22
|
- JEP-20 Compact syntax for multi-select-hash - https://github.com/jmespath-community/jmespath.spec/discussions/19
|
|
23
|
+
- JEP-21 Ternary Conditional Expression - https://github.com/jmespath-community/jmespath.spec/pull/182
|
|
23
24
|
|
|
24
25
|
See:
|
|
25
26
|
- https://github.com/jmespath-community/jmespath.spec/tree/main
|
omlish/specs/jmespath/ast.py
CHANGED
|
@@ -5,6 +5,9 @@ import typing as ta
|
|
|
5
5
|
from ... import lang
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
8
11
|
@dc.dataclass(frozen=True)
|
|
9
12
|
class Node(lang.Abstract):
|
|
10
13
|
@property
|
|
@@ -20,6 +23,9 @@ class LeafNode(Node, lang.Abstract):
|
|
|
20
23
|
return []
|
|
21
24
|
|
|
22
25
|
|
|
26
|
+
##
|
|
27
|
+
|
|
28
|
+
|
|
23
29
|
UnaryArithmeticOperator: ta.TypeAlias = ta.Literal[
|
|
24
30
|
'plus',
|
|
25
31
|
'minus',
|
|
@@ -269,6 +275,17 @@ class Slice(LeafNode, lang.Final):
|
|
|
269
275
|
step: int | None
|
|
270
276
|
|
|
271
277
|
|
|
278
|
+
@dc.dataclass(frozen=True)
|
|
279
|
+
class TernaryOperator(Node, lang.Final):
|
|
280
|
+
condition: Node
|
|
281
|
+
if_truthy: Node
|
|
282
|
+
if_falsy: Node
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def children(self) -> ta.Sequence[Node]:
|
|
286
|
+
return [self.condition, self.if_truthy, self.if_falsy]
|
|
287
|
+
|
|
288
|
+
|
|
272
289
|
@dc.dataclass(frozen=True)
|
|
273
290
|
class ValueProjection(Node, lang.Final):
|
|
274
291
|
left: Node
|
omlish/specs/jmespath/cli.py
CHANGED
omlish/specs/jmespath/errors.py
CHANGED
omlish/specs/jmespath/lexer.py
CHANGED
|
@@ -9,6 +9,9 @@ from .errors import LexerError
|
|
|
9
9
|
from .visitor import Options
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
12
15
|
class Token(ta.TypedDict):
|
|
13
16
|
type: str
|
|
14
17
|
value: ta.Any
|
|
@@ -37,6 +40,7 @@ class Lexer:
|
|
|
37
40
|
'}': 'rbrace',
|
|
38
41
|
'+': 'plus',
|
|
39
42
|
'%': 'modulo',
|
|
43
|
+
'?': 'question',
|
|
40
44
|
'\u2212': 'minus',
|
|
41
45
|
'\u00d7': 'multiply',
|
|
42
46
|
'\u00f7': 'divide',
|
omlish/specs/jmespath/parser.py
CHANGED
|
@@ -54,6 +54,7 @@ from .ast import Projection
|
|
|
54
54
|
from .ast import RootNode
|
|
55
55
|
from .ast import Slice
|
|
56
56
|
from .ast import Subexpression
|
|
57
|
+
from .ast import TernaryOperator
|
|
57
58
|
from .ast import UnaryArithmeticOperator
|
|
58
59
|
from .ast import ValueProjection
|
|
59
60
|
from .ast import VariableRef
|
|
@@ -67,6 +68,9 @@ from .visitor import Options
|
|
|
67
68
|
from .visitor import TreeInterpreter
|
|
68
69
|
|
|
69
70
|
|
|
71
|
+
##
|
|
72
|
+
|
|
73
|
+
|
|
70
74
|
class Parser:
|
|
71
75
|
BINDING_POWER: ta.Mapping[str, int] = {
|
|
72
76
|
'eof': 0,
|
|
@@ -85,8 +89,9 @@ class Parser:
|
|
|
85
89
|
'expref': 0,
|
|
86
90
|
'colon': 0,
|
|
87
91
|
'pipe': 1,
|
|
88
|
-
'
|
|
89
|
-
'
|
|
92
|
+
'question': 2,
|
|
93
|
+
'or': 3,
|
|
94
|
+
'and': 4,
|
|
90
95
|
'eq': 5,
|
|
91
96
|
'gt': 5,
|
|
92
97
|
'lt': 5,
|
|
@@ -482,6 +487,12 @@ class Parser:
|
|
|
482
487
|
right = self._parse_projection_rhs(self.BINDING_POWER['star'])
|
|
483
488
|
return Projection(left, right)
|
|
484
489
|
|
|
490
|
+
def _token_led_question(self, condition: Node) -> Node:
|
|
491
|
+
left = self._expression()
|
|
492
|
+
self._match('colon')
|
|
493
|
+
right = self._expression()
|
|
494
|
+
return TernaryOperator(condition, left, right)
|
|
495
|
+
|
|
485
496
|
def _project_if_slice(self, left: Node, right: Node) -> Node:
|
|
486
497
|
index_expr = IndexExpression([left, right])
|
|
487
498
|
if isinstance(right, Slice):
|
omlish/specs/jmespath/scope.py
CHANGED
omlish/specs/jmespath/visitor.py
CHANGED
|
@@ -37,6 +37,7 @@ from .ast import Projection
|
|
|
37
37
|
from .ast import RootNode
|
|
38
38
|
from .ast import Slice
|
|
39
39
|
from .ast import Subexpression
|
|
40
|
+
from .ast import TernaryOperator
|
|
40
41
|
from .ast import UnaryArithmeticOperator
|
|
41
42
|
from .ast import ValueProjection
|
|
42
43
|
from .ast import VariableRef
|
|
@@ -445,6 +446,14 @@ class TreeInterpreter(Visitor):
|
|
|
445
446
|
except KeyError:
|
|
446
447
|
raise UndefinedVariableError(node.name) # noqa
|
|
447
448
|
|
|
449
|
+
def visit_ternary_operator(self, node: TernaryOperator, value: ta.Any) -> ta.Any:
|
|
450
|
+
evaluation = self.visit(node.condition, value)
|
|
451
|
+
|
|
452
|
+
if self._is_false(evaluation):
|
|
453
|
+
return self.visit(node.if_falsy, value)
|
|
454
|
+
else:
|
|
455
|
+
return self.visit(node.if_truthy, value)
|
|
456
|
+
|
|
448
457
|
def visit_value_projection(self, node: ValueProjection, value: ta.Any) -> ta.Any:
|
|
449
458
|
base = self.visit(node.left, value)
|
|
450
459
|
try:
|