omlish 0.0.0.dev306__py3-none-any.whl → 0.0.0.dev308__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.
- omlish/__about__.py +2 -2
- omlish/asyncs/anyio/subprocesses.py +4 -4
- omlish/formats/edn/__init__.py +2 -1
- omlish/formats/edn/lexing.py +315 -0
- omlish/formats/edn/parsing.py +272 -251
- omlish/formats/edn/values.py +10 -10
- omlish/formats/json/__init__.py +2 -2
- omlish/formats/json/stream/{build.py → building.py} +8 -8
- omlish/formats/json/stream/{lex.py → lexing.py} +17 -2
- omlish/formats/json/stream/{parse.py → parsing.py} +19 -10
- omlish/formats/json/stream/{render.py → rendering.py} +7 -7
- omlish/formats/json/stream/utils.py +3 -3
- omlish/funcs/genmachine.py +22 -18
- omlish/lang/__init__.py +2 -3
- omlish/lang/generators.py +19 -28
- omlish/specs/jsonrpc/__init__.py +1 -0
- omlish/specs/jsonrpc/conns.py +222 -0
- omlish/specs/jsonrpc/types.py +19 -0
- {omlish-0.0.0.dev306.dist-info → omlish-0.0.0.dev308.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev306.dist-info → omlish-0.0.0.dev308.dist-info}/RECORD +25 -24
- {omlish-0.0.0.dev306.dist-info → omlish-0.0.0.dev308.dist-info}/WHEEL +1 -1
- omlish/formats/edn/LICENSE +0 -16
- /omlish/formats/json/{render.py → rendering.py} +0 -0
- {omlish-0.0.0.dev306.dist-info → omlish-0.0.0.dev308.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev306.dist-info → omlish-0.0.0.dev308.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev306.dist-info → omlish-0.0.0.dev308.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
@@ -50,11 +50,11 @@ class AnyioSubprocesses(AbstractAsyncSubprocesses):
|
|
50
50
|
stderr = io.BytesIO()
|
51
51
|
tg.start_soon(read_output, proc.stderr, stderr)
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
if proc.stdin and run.input is not None:
|
54
|
+
await proc.stdin.send(run.input)
|
55
|
+
await proc.stdin.aclose()
|
56
56
|
|
57
|
-
|
57
|
+
await proc.wait()
|
58
58
|
|
59
59
|
if run.check and proc.returncode != 0:
|
60
60
|
raise subprocess.CalledProcessError(
|
omlish/formats/edn/__init__.py
CHANGED
@@ -0,0 +1,315 @@
|
|
1
|
+
r"""
|
2
|
+
https://github.com/edn-format/edn
|
3
|
+
https://github.com/antlr/grammars-v4/blob/master/edn/edn.g4
|
4
|
+
https://github.com/jorinvo/edn-data/blob/1e5824f63803eb58f35e98839352000053d47115/src/parse.ts
|
5
|
+
https://clojure.org/reference/reader#_extensible_data_notation_edn
|
6
|
+
"""
|
7
|
+
import dataclasses as dc
|
8
|
+
import io
|
9
|
+
import typing as ta
|
10
|
+
|
11
|
+
from ... import check
|
12
|
+
from ...funcs.genmachine import GenMachine
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
|
17
|
+
|
18
|
+
TokenKind: ta.TypeAlias = ta.Literal[
|
19
|
+
'STRING',
|
20
|
+
'CHAR',
|
21
|
+
'WORD',
|
22
|
+
'COMMENT',
|
23
|
+
|
24
|
+
'LPAREN',
|
25
|
+
'RPAREN',
|
26
|
+
'LBRACKET',
|
27
|
+
'RBRACKET',
|
28
|
+
'HASH_LBRACE',
|
29
|
+
'LBRACE',
|
30
|
+
'RBRACE',
|
31
|
+
|
32
|
+
'HASH_UNDERSCORE',
|
33
|
+
'META',
|
34
|
+
'QUOTE',
|
35
|
+
|
36
|
+
'SPACE',
|
37
|
+
]
|
38
|
+
|
39
|
+
|
40
|
+
class Position(ta.NamedTuple):
|
41
|
+
ofs: int
|
42
|
+
line: int
|
43
|
+
col: int
|
44
|
+
|
45
|
+
|
46
|
+
class Token(ta.NamedTuple):
|
47
|
+
kind: TokenKind
|
48
|
+
src: str
|
49
|
+
|
50
|
+
pos: Position
|
51
|
+
|
52
|
+
def __iter__(self):
|
53
|
+
raise TypeError
|
54
|
+
|
55
|
+
|
56
|
+
##
|
57
|
+
|
58
|
+
|
59
|
+
SINGLE_TOKENS: ta.Mapping[str, TokenKind] = {
|
60
|
+
'(': 'LPAREN',
|
61
|
+
')': 'RPAREN',
|
62
|
+
'[': 'LBRACKET',
|
63
|
+
']': 'RBRACKET',
|
64
|
+
'{': 'LBRACE',
|
65
|
+
'}': 'RBRACE',
|
66
|
+
|
67
|
+
'^': 'META',
|
68
|
+
"'": 'QUOTE',
|
69
|
+
}
|
70
|
+
|
71
|
+
|
72
|
+
HASH_TOKENS: ta.Mapping[str, TokenKind] = {
|
73
|
+
'{': 'HASH_LBRACE',
|
74
|
+
'_': 'HASH_UNDERSCORE',
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
WORD_FIRST_SPECIAL_CHARS = ':.*+!-_?$%&=<>.'
|
79
|
+
WORD_BODY_SPECIAL_CHARS = '/'
|
80
|
+
|
81
|
+
|
82
|
+
##
|
83
|
+
|
84
|
+
|
85
|
+
@dc.dataclass()
|
86
|
+
class StreamLexError(Exception):
|
87
|
+
message: str
|
88
|
+
|
89
|
+
pos: Position
|
90
|
+
|
91
|
+
|
92
|
+
class StreamLexer(GenMachine[str, Token]):
|
93
|
+
def __init__(
|
94
|
+
self,
|
95
|
+
*,
|
96
|
+
include_space: bool = False,
|
97
|
+
) -> None:
|
98
|
+
self._include_space = include_space
|
99
|
+
|
100
|
+
self._ofs = 0
|
101
|
+
self._line = 1
|
102
|
+
self._col = 0
|
103
|
+
|
104
|
+
self._buf = io.StringIO()
|
105
|
+
|
106
|
+
super().__init__(self._do_main())
|
107
|
+
|
108
|
+
@property
|
109
|
+
def pos(self) -> Position:
|
110
|
+
return Position(
|
111
|
+
self._ofs,
|
112
|
+
self._line,
|
113
|
+
self._col,
|
114
|
+
)
|
115
|
+
|
116
|
+
def _char_in(self, c: str) -> str:
|
117
|
+
if not isinstance(c, str):
|
118
|
+
raise TypeError(c)
|
119
|
+
if c and len(c) != 1:
|
120
|
+
raise ValueError(c)
|
121
|
+
|
122
|
+
self._ofs += 1
|
123
|
+
|
124
|
+
if c == '\n':
|
125
|
+
self._line += 1
|
126
|
+
self._col = 0
|
127
|
+
else:
|
128
|
+
self._col += 1
|
129
|
+
|
130
|
+
return c
|
131
|
+
|
132
|
+
def _make_tok(
|
133
|
+
self,
|
134
|
+
kind: TokenKind,
|
135
|
+
src: str,
|
136
|
+
pos: Position,
|
137
|
+
) -> ta.Sequence[Token]:
|
138
|
+
tok = Token(
|
139
|
+
kind,
|
140
|
+
src,
|
141
|
+
pos,
|
142
|
+
)
|
143
|
+
return (tok,)
|
144
|
+
|
145
|
+
def _flip_buf(self) -> str:
|
146
|
+
src = self._buf.getvalue()
|
147
|
+
self._buf.seek(0)
|
148
|
+
self._buf.truncate()
|
149
|
+
return src
|
150
|
+
|
151
|
+
def _raise(self, msg: str, src: Exception | None = None) -> ta.NoReturn:
|
152
|
+
raise StreamLexError(msg, self.pos) from src
|
153
|
+
|
154
|
+
def _do_main(self, p: str | None = None):
|
155
|
+
while True:
|
156
|
+
if p is not None:
|
157
|
+
c = p
|
158
|
+
p = None
|
159
|
+
else:
|
160
|
+
c = self._char_in((yield None)) # noqa
|
161
|
+
|
162
|
+
if not c:
|
163
|
+
return None
|
164
|
+
|
165
|
+
if c.isspace() or c == ',':
|
166
|
+
if self._include_space:
|
167
|
+
yield self._make_tok('SPACE', c, self.pos)
|
168
|
+
continue
|
169
|
+
|
170
|
+
if c in SINGLE_TOKENS:
|
171
|
+
yield self._make_tok(SINGLE_TOKENS[c], c, self.pos)
|
172
|
+
continue
|
173
|
+
|
174
|
+
if c == ';':
|
175
|
+
return self._do_comment()
|
176
|
+
|
177
|
+
if c == '"':
|
178
|
+
return self._do_string()
|
179
|
+
|
180
|
+
if c == '\\':
|
181
|
+
return self._do_char()
|
182
|
+
|
183
|
+
if c == '#':
|
184
|
+
return self._do_hash()
|
185
|
+
|
186
|
+
if (
|
187
|
+
c.isalnum() or
|
188
|
+
c in WORD_FIRST_SPECIAL_CHARS
|
189
|
+
):
|
190
|
+
return self._do_word(c)
|
191
|
+
|
192
|
+
self._raise(f'Unexpected input: {c}')
|
193
|
+
|
194
|
+
def _do_comment(self):
|
195
|
+
check.state(self._buf.tell() == 0)
|
196
|
+
self._buf.write(';')
|
197
|
+
|
198
|
+
pos = self.pos
|
199
|
+
|
200
|
+
while True:
|
201
|
+
try:
|
202
|
+
c = self._char_in((yield None)) # noqa
|
203
|
+
except GeneratorExit:
|
204
|
+
self._raise('Unexpected end of input')
|
205
|
+
|
206
|
+
if not c or c == '\n':
|
207
|
+
break
|
208
|
+
|
209
|
+
self._buf.write(c)
|
210
|
+
|
211
|
+
src = self._flip_buf()
|
212
|
+
yield self._make_tok('COMMENT', src, pos)
|
213
|
+
return self._do_main()
|
214
|
+
|
215
|
+
def _do_string(self):
|
216
|
+
check.state(self._buf.tell() == 0)
|
217
|
+
self._buf.write('"')
|
218
|
+
|
219
|
+
pos = self.pos
|
220
|
+
|
221
|
+
esc = False
|
222
|
+
while True:
|
223
|
+
try:
|
224
|
+
c = self._char_in((yield None)) # noqa
|
225
|
+
except GeneratorExit:
|
226
|
+
self._raise('Unexpected end of input')
|
227
|
+
|
228
|
+
if not c:
|
229
|
+
self._raise(f'Unterminated string literal: {self._buf.getvalue()}')
|
230
|
+
|
231
|
+
self._buf.write(c)
|
232
|
+
if esc:
|
233
|
+
esc = False
|
234
|
+
elif c == '\\':
|
235
|
+
esc = True
|
236
|
+
elif c == '"':
|
237
|
+
break
|
238
|
+
|
239
|
+
src = self._flip_buf()
|
240
|
+
yield self._make_tok('STRING', src, pos)
|
241
|
+
return self._do_main()
|
242
|
+
|
243
|
+
def _do_char(self):
|
244
|
+
check.state(self._buf.tell() == 0)
|
245
|
+
self._buf.write('\\')
|
246
|
+
|
247
|
+
pos = self.pos
|
248
|
+
|
249
|
+
while True:
|
250
|
+
try:
|
251
|
+
c = self._char_in((yield None)) # noqa
|
252
|
+
except GeneratorExit:
|
253
|
+
self._raise('Unexpected end of input')
|
254
|
+
|
255
|
+
if not c or not (
|
256
|
+
c.isalnum() or
|
257
|
+
c == '\\'
|
258
|
+
):
|
259
|
+
break
|
260
|
+
|
261
|
+
self._buf.write(c)
|
262
|
+
|
263
|
+
src = self._flip_buf()
|
264
|
+
yield self._make_tok('CHAR', src, pos)
|
265
|
+
return self._do_main(c)
|
266
|
+
|
267
|
+
def _do_hash(self):
|
268
|
+
check.state(self._buf.tell() == 0)
|
269
|
+
|
270
|
+
pos = self.pos
|
271
|
+
|
272
|
+
try:
|
273
|
+
c = self._char_in((yield None)) # noqa
|
274
|
+
except GeneratorExit:
|
275
|
+
self._raise('Unexpected end of input')
|
276
|
+
|
277
|
+
if (ht := HASH_TOKENS.get(c)) is not None:
|
278
|
+
yield self._make_tok(ht, '#' + c, pos)
|
279
|
+
return self._do_main()
|
280
|
+
|
281
|
+
elif (
|
282
|
+
c.isalnum() or
|
283
|
+
c == '#' or
|
284
|
+
c in WORD_FIRST_SPECIAL_CHARS
|
285
|
+
):
|
286
|
+
return self._do_word('#' + c, pos=pos)
|
287
|
+
|
288
|
+
else:
|
289
|
+
self._raise(f'Unexpected input: {c}')
|
290
|
+
|
291
|
+
def _do_word(self, pfx: str, *, pos: Position | None = None):
|
292
|
+
check.state(self._buf.tell() == 0)
|
293
|
+
self._buf.write(pfx)
|
294
|
+
|
295
|
+
if pos is None:
|
296
|
+
pos = self.pos
|
297
|
+
|
298
|
+
while True:
|
299
|
+
try:
|
300
|
+
c = self._char_in((yield None)) # noqa
|
301
|
+
except GeneratorExit:
|
302
|
+
self._raise('Unexpected end of input')
|
303
|
+
|
304
|
+
if not c or not (
|
305
|
+
c.isalnum() or
|
306
|
+
c in WORD_FIRST_SPECIAL_CHARS or
|
307
|
+
c in WORD_BODY_SPECIAL_CHARS
|
308
|
+
):
|
309
|
+
break
|
310
|
+
|
311
|
+
self._buf.write(c)
|
312
|
+
|
313
|
+
src = self._flip_buf()
|
314
|
+
yield self._make_tok('WORD', src, pos)
|
315
|
+
return self._do_main(c)
|