lispython 0.3.2__tar.gz → 0.3.3__tar.gz

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.
Files changed (44) hide show
  1. {lispython-0.3.2 → lispython-0.3.3}/PKG-INFO +1 -1
  2. {lispython-0.3.2 → lispython-0.3.3}/docs/syntax/expressions.md +3 -3
  3. lispython-0.3.3/docs/version_macro.py +11 -0
  4. {lispython-0.3.2 → lispython-0.3.3}/pyproject.toml +1 -1
  5. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/compiler/expr.py +1 -1
  6. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/compiler/stmt.py +511 -511
  7. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/tools.lpy +8 -6
  8. {lispython-0.3.2 → lispython-0.3.3}/tests/test_meta_functions.py +2 -2
  9. {lispython-0.3.2 → lispython-0.3.3}/uv.lock +1 -1
  10. lispython-0.3.2/docs/version_macro.py +0 -10
  11. {lispython-0.3.2 → lispython-0.3.3}/.claude/settings.local.json +0 -0
  12. {lispython-0.3.2 → lispython-0.3.3}/.gitignore +0 -0
  13. {lispython-0.3.2 → lispython-0.3.3}/.pre-commit-config.yaml +0 -0
  14. {lispython-0.3.2 → lispython-0.3.3}/LICENSE.md +0 -0
  15. {lispython-0.3.2 → lispython-0.3.3}/README.md +0 -0
  16. {lispython-0.3.2 → lispython-0.3.3}/docs/index.md +0 -0
  17. {lispython-0.3.2 → lispython-0.3.3}/docs/macros.md +0 -0
  18. {lispython-0.3.2 → lispython-0.3.3}/docs/syntax/overview.md +0 -0
  19. {lispython-0.3.2 → lispython-0.3.3}/docs/syntax/statements.md +0 -0
  20. {lispython-0.3.2 → lispython-0.3.3}/docs/usage/cli.md +0 -0
  21. {lispython-0.3.2 → lispython-0.3.3}/docs/usage/getting-started.md +0 -0
  22. {lispython-0.3.2 → lispython-0.3.3}/docs/why-lispy.md +0 -0
  23. {lispython-0.3.2 → lispython-0.3.3}/mkdocs.yml +0 -0
  24. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/__init__.py +0 -0
  25. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/compiler/__init__.py +0 -0
  26. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/compiler/literal.py +0 -0
  27. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/compiler/utils.py +0 -0
  28. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/importer.py +0 -0
  29. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/macro.py +0 -0
  30. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/meta_functions.py +0 -0
  31. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/nodes.py +0 -0
  32. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/parser.py +0 -0
  33. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core/utils.py +0 -0
  34. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/core_meta_functions.lpy +0 -0
  35. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/macros/__init__.py +0 -0
  36. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/macros/init.lpy +0 -0
  37. {lispython-0.3.2 → lispython-0.3.3}/src/lispy/macros/sugar.lpy +0 -0
  38. {lispython-0.3.2 → lispython-0.3.3}/tests/__init__.py +0 -0
  39. {lispython-0.3.2 → lispython-0.3.3}/tests/test_expr.py +0 -0
  40. {lispython-0.3.2 → lispython-0.3.3}/tests/test_include_meta.py +0 -0
  41. {lispython-0.3.2 → lispython-0.3.3}/tests/test_literal.py +0 -0
  42. {lispython-0.3.2 → lispython-0.3.3}/tests/test_parser.py +0 -0
  43. {lispython-0.3.2 → lispython-0.3.3}/tests/test_stmt.py +0 -0
  44. {lispython-0.3.2 → lispython-0.3.3}/tests/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lispython
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Lisp-like Syntax for Python with Lisp-like Macros
5
5
  Project-URL: Homepage, https://jetack.github.io/lispython
6
6
  Project-URL: Repository, https://github.com/jetack/lispython
@@ -250,13 +250,13 @@ obj[start:stop:step]
250
250
  ### Slice with Emptiness
251
251
  LisPy version
252
252
  ```python
253
- (sub obj [: start])
254
- (sub obj [: _ stop])
253
+ (sub obj [: stop])
254
+ (sub obj [: start _])
255
255
  (sub obj [: start _ step])
256
256
  ```
257
257
  Python version
258
258
  ```python
259
- obj[start:]
260
259
  obj[:stop]
260
+ obj[start:]
261
261
  obj[start::step]
262
262
  ```
@@ -0,0 +1,11 @@
1
+ import tomllib
2
+
3
+ def define_env(env):
4
+ try:
5
+ with open("pyproject.toml", "rb") as f:
6
+ pyproject = tomllib.load(f)
7
+ version = pyproject["project"]["version"]
8
+ except Exception:
9
+ version = "dev"
10
+
11
+ env.variables["version"] = version
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lispython"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  description = "Lisp-like Syntax for Python with Lisp-like Macros"
5
5
  authors = [{ name = "Jetack", email = "jetack23@gmail.com" }]
6
6
  license = { text = "MIT" }
@@ -359,7 +359,7 @@ def paren_compiler(sexp, ctx):
359
359
 
360
360
  def slice_compile(sexp):
361
361
  [_, *args] = sexp.list
362
- assert 2 <= len(args) <= 3 # temporarily block length 1 slice for breaking change alert
362
+ assert 1 <= len(args) <= 3
363
363
  [lower, upper, step] = args if len(args) == 3 else args + ["_"] if len(args) == 2 else ["_"] + args + ["_"]
364
364
  args_dict = {}
365
365
  if lower != "None" and lower != "_":
@@ -1,511 +1,511 @@
1
- import ast
2
- from collections import deque
3
- from functools import reduce
4
-
5
- from lispy.core.compiler.expr import def_args_parse, expr_compile
6
- from lispy.core.compiler.utils import *
7
- from lispy.core.utils import *
8
-
9
-
10
- def expr_wrapper(sexp):
11
- return ast.Expr(value=expr_compile(sexp), **sexp.position_info)
12
-
13
-
14
- def do_p(sexp):
15
- return str(sexp.op) == "do"
16
-
17
-
18
- def do_compile(sexp):
19
- [op, *sexps] = sexp.list
20
- return stmt_list_compile(sexps)
21
-
22
-
23
- def assign_p(sexp):
24
- return str(sexp.op) == "="
25
-
26
-
27
- def assign_compile(sexp):
28
- body = sexp.operands
29
- if isinstance(body[1], Annotation):
30
- [target, annotation, *value] = body
31
- value_dict = {"value": expr_compile(value[0])} if value else {}
32
- return ast.AnnAssign(
33
- target=expr_compile(target, ctx=ast.Store),
34
- annotation=expr_compile(annotation),
35
- simple=isinstance(target, Symbol) and (not "." in str(target)),
36
- **value_dict,
37
- **sexp.position_info
38
- )
39
- else:
40
- [*targets, value] = body
41
- return ast.Assign(
42
- targets=list(map(lambda x: expr_compile(x, ctx=ast.Store), targets)),
43
- value=expr_compile(value),
44
- **sexp.position_info
45
- )
46
-
47
-
48
- def augassign_p(sexp):
49
- return str(sexp.op) in augassignop_dict
50
-
51
-
52
- def augassign_compile(sexp):
53
- [op, target, *args] = sexp.list
54
- op = augassignop_dict[str(op)]
55
- value = reduce(
56
- lambda x, y: ast.BinOp(x, op(), y, **sexp.position_info),
57
- map(expr_compile, args),
58
- )
59
- return ast.AugAssign(
60
- target=expr_compile(target, ast.Store),
61
- op=op(),
62
- value=value,
63
- **sexp.position_info
64
- )
65
-
66
-
67
- def del_compile(sexp):
68
- [op, *args] = sexp.list
69
- return ast.Delete(
70
- targets=list(map(lambda x: expr_compile(x, ast.Del), args)),
71
- **sexp.position_info
72
- )
73
-
74
-
75
- def parse_names(names):
76
- rst = []
77
- q = deque([names] if names == "*" else names)
78
- while q:
79
- n = q.popleft()
80
- if n == "as":
81
- rst[-1].asname = str(q.popleft()).replace("-", "_")
82
- else:
83
- rst.append(ast.alias(name=n.name, **n.position_info))
84
- return rst
85
-
86
-
87
- def import_compile(sexp):
88
- [_, *names] = sexp.list
89
- return ast.Import(names=parse_names(names), **sexp.position_info)
90
-
91
-
92
- def importfrom_compile(sexp):
93
- [_, *args] = sexp.list
94
- modules = args[::2]
95
- namess = args[1::2]
96
-
97
- def helper(module):
98
- i = 0
99
- x = module.name
100
- while x[i] == ".":
101
- i += 1
102
- return [x[i:], i, module.position_info]
103
-
104
- module_level = map(helper, modules)
105
- return [
106
- ast.ImportFrom(
107
- module=module,
108
- names=parse_names(names),
109
- level=level,
110
- **merge_position_infos(module_pos_info, names.position_info)
111
- )
112
- for [[module, level, module_pos_info], names] in zip(module_level, namess)
113
- ]
114
-
115
-
116
- def if_p(sexp):
117
- return str(sexp.op) == "if"
118
-
119
-
120
- def if_stmt_compile(sexp):
121
- [_, test, then, *orelse] = sexp.list
122
- return ast.If(
123
- test=expr_compile(test),
124
- body=stmt_list_compile([then]),
125
- orelse=stmt_list_compile([*orelse]),
126
- **sexp.position_info
127
- )
128
-
129
-
130
- def while_p(sexp):
131
- return str(sexp.op) == "while"
132
-
133
-
134
- def while_compile(sexp):
135
- [_, test, *body] = sexp.list
136
- lastx = body[-1]
137
- [then, orelse] = (
138
- [body[:-1], lastx.operands]
139
- if isinstance(lastx, Paren) and lastx.op == "else"
140
- else [body, []]
141
- )
142
- return ast.While(
143
- test=expr_compile(test),
144
- body=stmt_list_compile(then),
145
- orelse=stmt_list_compile(orelse),
146
- **sexp.position_info
147
- )
148
-
149
-
150
- def for_p(sexp):
151
- return str(sexp.op) == "for"
152
-
153
-
154
- def for_compile(sexp, async_p=False):
155
- body = deque(sexp.operands)
156
- target = body.popleft()
157
- if body[0] == "in":
158
- body.popleft()
159
- iterable = body.popleft()
160
- lastx = body[-1]
161
- [then, orelse] = (
162
- [list(body)[:-1], lastx.operands]
163
- if isinstance(lastx, Paren) and lastx.op == "else"
164
- else [body, []]
165
- )
166
- return (ast.AsyncFor if async_p else ast.For)(
167
- target=expr_compile(target, ast.Store),
168
- iter=expr_compile(iterable),
169
- body=stmt_list_compile(then),
170
- orelse=stmt_list_compile(orelse),
171
- **sexp.position_info
172
- )
173
-
174
-
175
- def deco_p(sexp):
176
- return str(sexp.op) == "deco"
177
-
178
-
179
- def raise_compile(sexp):
180
- body = sexp.operands
181
- assert len(body) == 1 or (len(body) == 3 and body[1] == "from")
182
- kwargs = {"exc": expr_compile(body[0])}
183
- if len(body) > 1:
184
- kwargs["cause"] = expr_compile(body[-1])
185
- return ast.Raise(**kwargs, **sexp.position_info)
186
-
187
-
188
- def assert_compile(sexp):
189
- kwargs = {"test": expr_compile(sexp[1])}
190
- if len(sexp) > 2:
191
- kwargs["msg"] = expr_compile(sexp[2])
192
- return ast.Assert(**kwargs, **sexp.position_info)
193
-
194
-
195
- def parse_exception_bracket(bracket):
196
- lst = bracket.list
197
- name = (
198
- str([lst.pop(), lst.pop()][0]).replace("-", "_")
199
- if len(lst) > 2 and lst[-2] == "as"
200
- else None
201
- )
202
- type = (
203
- ast.Tuple(
204
- elts=list(map(expr_compile, lst)), ctx=ast.Load(), **bracket.position_info
205
- )
206
- if len(lst) > 1
207
- else expr_compile(lst[0])
208
- )
209
- return [type, name]
210
-
211
-
212
- def parse_except(handler):
213
- body = handler.operands
214
- if isinstance(body[0], Bracket):
215
- body = deque(handler.operands)
216
- [type, name] = parse_exception_bracket(body.popleft())
217
- else:
218
- [type, name] = [None, None]
219
- kwargs = {"body": stmt_list_compile(body)}
220
- if type:
221
- kwargs["type"] = type
222
- if name:
223
- kwargs["name"] = name
224
- return ast.ExceptHandler(**kwargs, **handler.position_info)
225
-
226
-
227
- def try_compile(sexp):
228
- body = sexp.operands
229
- # finally
230
- finalbody = (
231
- body.pop().operands
232
- if isinstance(body[-1], Paren) and body[-1].op == "finally"
233
- else []
234
- )
235
- # else
236
- orelse = (
237
- body.pop().operands
238
- if isinstance(body[-1], Paren) and body[-1].op == "else"
239
- else []
240
- )
241
- # excepts
242
- handlers = deque()
243
- while isinstance(body[-1], Paren) and body[-1].op == "except":
244
- handlers.appendleft(body.pop())
245
- handlers = list(map(parse_except, handlers))
246
- # except*s
247
- starhandlers = deque()
248
- while isinstance(body[-1], Paren) and body[-1].op == "except*":
249
- starhandlers.appendleft(body.pop())
250
- starhandlers = list(map(parse_except, starhandlers))
251
- assert not starhandlers or not handlers
252
- return (ast.Try if not starhandlers else ast.TryStar)(
253
- body=stmt_list_compile(body),
254
- handlers=handlers or starhandlers,
255
- orelse=stmt_list_compile(orelse),
256
- finalbody=stmt_list_compile(finalbody),
257
- **sexp.position_info
258
- )
259
-
260
-
261
- def with_items_parse(sexp):
262
- rst = []
263
- q = deque(sexp.list)
264
- while q:
265
- elt = q.popleft()
266
- if str(elt) == "as":
267
- rst[-1].optional_vars = expr_compile(q.popleft(), ctx=ast.Store)
268
- else:
269
- rst.append(ast.withitem(context_expr=expr_compile(elt)))
270
- return rst
271
-
272
-
273
- def with_compile(sexp, async_p=False):
274
- [bracket_sexp, *body] = sexp.operands
275
- items = with_items_parse(bracket_sexp)
276
- return (ast.AsyncWith if async_p else ast.With)(
277
- items=items, body=stmt_list_compile(body), **sexp.position_info
278
- )
279
-
280
-
281
- def match_mapping_parse(lst):
282
- keys = lst[::2]
283
- patterns = lst[1::2]
284
- rst = {}
285
- if isinstance(keys[-1], DoubleStarred):
286
- rst["rest"] = keys.pop().value.name
287
- rst["keys"] = list(map(expr_compile, keys))
288
- rst["patterns"] = list(map(pattern_parse, patterns))
289
- return rst
290
-
291
-
292
- def match_class_parse(lst):
293
- q = deque(lst)
294
- patterns = []
295
- kwd_attrs = []
296
- kwd_patterns = []
297
- while q:
298
- arg = q.popleft()
299
- if isinstance(arg, Keyword):
300
- kwd_attrs.append(arg.value.name)
301
- kwd_patterns.append(pattern_parse(q.popleft()))
302
- else:
303
- patterns.append(pattern_parse(arg))
304
- return {"patterns": patterns, "kwd_attrs": kwd_attrs, "kwd_patterns": kwd_patterns}
305
-
306
-
307
- def pattern_parse(sexp):
308
- return (
309
- ast.MatchAs(**sexp.position_info)
310
- if str(sexp) == "_"
311
- else ast.MatchAs(name=sexp.name, **sexp.position_info)
312
- if isinstance(sexp, Symbol)
313
- else ast.MatchSingleton(value=eval(str(sexp)), **sexp.position_info)
314
- if str(sexp) in ["True", "False", "None"]
315
- else ast.MatchStar(
316
- **{} if str(sexp.value) == "_" else {"name": sexp.value.name},
317
- **sexp.position_info
318
- )
319
- if isinstance(sexp, Starred)
320
- else ast.MatchValue(value=expr_compile(sexp), **sexp.position_info)
321
- if not isinstance(sexp, Expression)
322
- else ast.MatchSequence(
323
- patterns=list(map(pattern_parse, sexp.list)), **sexp.position_info
324
- )
325
- if isinstance(sexp, Bracket)
326
- else ast.MatchMapping(**match_mapping_parse(sexp.list), **sexp.position_info)
327
- if isinstance(sexp, Brace)
328
- else ast.MatchOr(
329
- patterns=list(map(pattern_parse, sexp.operands)), **sexp.position_info
330
- )
331
- if sexp.op == "|"
332
- else ast.MatchClass(
333
- cls=expr_compile(sexp.op),
334
- **match_class_parse(sexp.operands),
335
- **sexp.position_info
336
- )
337
- )
338
-
339
-
340
- def case_parse(case):
341
- [pattern_expr, *body] = case.operands
342
- pattern = pattern_parse(pattern_expr)
343
- if body[0] == "as":
344
- pattern = ast.MatchAs(
345
- pattern=pattern,
346
- name=body[1].name.replace("-", "_"),
347
- **merge_position_infos(pattern_expr.position_info, body[1].position_info)
348
- )
349
- body = body[2:]
350
- if body[0] == "if":
351
- guard_dict = {"guard": expr_compile(body[1])}
352
- body = body[2:]
353
- else:
354
- guard_dict = {}
355
- return ast.match_case(pattern=pattern, **guard_dict, body=stmt_list_compile(body))
356
-
357
-
358
- def match_compile(sexp):
359
- [subject, *cases] = sexp.operands
360
- cases = list(map(case_parse, cases))
361
- return ast.Match(subject=expr_compile(subject), cases=cases, **sexp.position_info)
362
-
363
-
364
- def deco_compile(sexp, decorator_list):
365
- [op, decorator, def_statement] = sexp.list
366
- new_deco_list = decorator.list if isinstance(decorator, Bracket) else [decorator]
367
- return stmt_compile(
368
- def_statement, (decorator_list if decorator_list else []) + new_deco_list
369
- )
370
-
371
-
372
- def functiondef_p(sexp):
373
- return str(sexp.op) == "def"
374
-
375
-
376
- def functiondef_compile(sexp, decorator_list, async_p=False):
377
- [op, fnname, args, *body] = sexp.list
378
- if body and isinstance(body[0], Annotation):
379
- [ann, *body] = body
380
- else:
381
- ann = None
382
- return (ast.AsyncFunctionDef if async_p else ast.FunctionDef)(
383
- name=fnname.name,
384
- args=def_args_parse(args),
385
- body=stmt_list_compile(body),
386
- decorator_list=list(map(expr_compile, decorator_list))
387
- if decorator_list
388
- else [],
389
- returns=expr_compile(ann.value) if ann else None,
390
- **sexp.position_info
391
- )
392
-
393
-
394
- def return_p(sexp):
395
- return str(sexp.op) == "return"
396
-
397
-
398
- def return_compile(sexp):
399
- [op, value] = sexp.list if len(sexp) > 1 else [None, None]
400
- return ast.Return(value=expr_compile(value) if value else None, **sexp.position_info)
401
-
402
-
403
- def global_compile(sexp):
404
- [_, *args] = sexp.list
405
- return ast.Global(names=list(map(lambda x: x.name, args)), **sexp.position_info)
406
-
407
-
408
- def nonlocal_compile(sexp):
409
- [_, *args] = sexp.list
410
- return ast.Nonlocal(names=list(map(lambda x: x.name, args)), **sexp.position_info)
411
-
412
-
413
- def classdef_p(sexp):
414
- return str(sexp.op) == "class"
415
-
416
-
417
- def classdef_args_parse(args):
418
- q = deque(args)
419
- bases = []
420
- keywords = []
421
- while q:
422
- arg = q.popleft()
423
- keywords.append(
424
- ast.keyword(
425
- arg=arg.name, value=expr_compile(q.popleft()), **arg.position_info
426
- )
427
- ) if keyword_arg_p(arg) else bases.append(expr_compile(arg))
428
- return [bases, keywords]
429
-
430
-
431
- def classdef_compile(sexp, decorator_list):
432
- [_, clsname, args, *body] = sexp.list
433
- [bases, keywords] = classdef_args_parse(args)
434
- return ast.ClassDef(
435
- name=clsname.name,
436
- bases=bases,
437
- keywords=keywords,
438
- body=stmt_list_compile(body),
439
- decorator_list=list(map(expr_compile, decorator_list))
440
- if decorator_list
441
- else [],
442
- **sexp.position_info
443
- )
444
-
445
-
446
- def stmt_compile(sexp, decorator_list=None):
447
- return (
448
- expr_wrapper(sexp)
449
- if not paren_p(sexp)
450
- else do_compile(sexp)
451
- if do_p(sexp)
452
- else assign_compile(sexp)
453
- if assign_p(sexp)
454
- else augassign_compile(sexp)
455
- if augassign_p(sexp)
456
- else del_compile(sexp)
457
- if str(sexp.op) == "del"
458
- else ast.Pass(**sexp.position_info)
459
- if str(sexp.op) == "pass"
460
- else import_compile(sexp)
461
- if str(sexp.op) == "import"
462
- else importfrom_compile(sexp)
463
- if str(sexp.op) == "from"
464
- else if_stmt_compile(sexp)
465
- if if_p(sexp)
466
- else while_compile(sexp)
467
- if while_p(sexp)
468
- else for_compile(sexp)
469
- if for_p(sexp)
470
- else ast.Break(**sexp.position_info)
471
- if str(sexp.op) == "break"
472
- else ast.Continue(**sexp.position_info)
473
- if str(sexp.op) == "continue"
474
- else raise_compile(sexp)
475
- if str(sexp.op) == "raise"
476
- else assert_compile(sexp)
477
- if str(sexp.op) == "assert"
478
- else try_compile(sexp)
479
- if str(sexp.op) == "try"
480
- else with_compile(sexp)
481
- if str(sexp.op) == "with"
482
- else match_compile(sexp)
483
- if str(sexp.op) == "match"
484
- else deco_compile(sexp, decorator_list)
485
- if deco_p(sexp)
486
- else functiondef_compile(sexp, decorator_list)
487
- if functiondef_p(sexp)
488
- else return_compile(sexp)
489
- if return_p(sexp)
490
- else global_compile(sexp)
491
- if str(sexp.op) == "global"
492
- else nonlocal_compile(sexp)
493
- if str(sexp.op) == "nonlocal"
494
- else classdef_compile(sexp, decorator_list)
495
- if classdef_p(sexp)
496
- else functiondef_compile(sexp, decorator_list, async_p=True)
497
- if str(sexp.op) == "async-def"
498
- else for_compile(sexp, async_p=True)
499
- if str(sexp.op) == "async-for"
500
- else with_compile(sexp, async_p=True)
501
- if str(sexp.op) == "async-with"
502
- else expr_wrapper(sexp)
503
- )
504
-
505
-
506
- def stmt_list_compile(sexp_list):
507
- return reduce(
508
- lambda x, y: x + (y if isinstance(y, list) else [y]),
509
- map(stmt_compile, sexp_list),
510
- [],
511
- )
1
+ import ast
2
+ from collections import deque
3
+ from functools import reduce
4
+
5
+ from lispy.core.compiler.expr import def_args_parse, expr_compile
6
+ from lispy.core.compiler.utils import *
7
+ from lispy.core.utils import *
8
+
9
+
10
+ def expr_wrapper(sexp):
11
+ return ast.Expr(value=expr_compile(sexp), **sexp.position_info)
12
+
13
+
14
+ def do_p(sexp):
15
+ return str(sexp.op) == "do"
16
+
17
+
18
+ def do_compile(sexp):
19
+ [op, *sexps] = sexp.list
20
+ return stmt_list_compile(sexps)
21
+
22
+
23
+ def assign_p(sexp):
24
+ return str(sexp.op) == "="
25
+
26
+
27
+ def assign_compile(sexp):
28
+ body = sexp.operands
29
+ if isinstance(body[1], Annotation):
30
+ [target, annotation, *value] = body
31
+ value_dict = {"value": expr_compile(value[0])} if value else {}
32
+ return ast.AnnAssign(
33
+ target=expr_compile(target, ctx=ast.Store),
34
+ annotation=expr_compile(annotation),
35
+ simple=isinstance(target, Symbol) and (not "." in str(target)),
36
+ **value_dict,
37
+ **sexp.position_info
38
+ )
39
+ else:
40
+ [*targets, value] = body
41
+ return ast.Assign(
42
+ targets=list(map(lambda x: expr_compile(x, ctx=ast.Store), targets)),
43
+ value=expr_compile(value),
44
+ **sexp.position_info
45
+ )
46
+
47
+
48
+ def augassign_p(sexp):
49
+ return str(sexp.op) in augassignop_dict
50
+
51
+
52
+ def augassign_compile(sexp):
53
+ [op, target, *args] = sexp.list
54
+ op = augassignop_dict[str(op)]
55
+ value = reduce(
56
+ lambda x, y: ast.BinOp(x, op(), y, **sexp.position_info),
57
+ map(expr_compile, args),
58
+ )
59
+ return ast.AugAssign(
60
+ target=expr_compile(target, ast.Store),
61
+ op=op(),
62
+ value=value,
63
+ **sexp.position_info
64
+ )
65
+
66
+
67
+ def del_compile(sexp):
68
+ [op, *args] = sexp.list
69
+ return ast.Delete(
70
+ targets=list(map(lambda x: expr_compile(x, ast.Del), args)),
71
+ **sexp.position_info
72
+ )
73
+
74
+
75
+ def parse_names(names):
76
+ rst = []
77
+ q = deque([names] if names == "*" else names)
78
+ while q:
79
+ n = q.popleft()
80
+ if n == "as":
81
+ rst[-1].asname = str(q.popleft()).replace("-", "_")
82
+ else:
83
+ rst.append(ast.alias(name=n.name, **n.position_info))
84
+ return rst
85
+
86
+
87
+ def import_compile(sexp):
88
+ [_, *names] = sexp.list
89
+ return ast.Import(names=parse_names(names), **sexp.position_info)
90
+
91
+
92
+ def importfrom_compile(sexp):
93
+ [_, *args] = sexp.list
94
+ modules = args[::2]
95
+ namess = args[1::2]
96
+
97
+ def helper(module):
98
+ i = 0
99
+ x = module.name
100
+ while x[i] == ".":
101
+ i += 1
102
+ return [x[i:], i, module.position_info]
103
+
104
+ module_level = map(helper, modules)
105
+ return [
106
+ ast.ImportFrom(
107
+ module=module,
108
+ names=parse_names(names),
109
+ level=level,
110
+ **merge_position_infos(module_pos_info, names.position_info)
111
+ )
112
+ for [[module, level, module_pos_info], names] in zip(module_level, namess)
113
+ ]
114
+
115
+
116
+ def if_p(sexp):
117
+ return str(sexp.op) == "if"
118
+
119
+
120
+ def if_stmt_compile(sexp):
121
+ [_, test, then, *orelse] = sexp.list
122
+ return ast.If(
123
+ test=expr_compile(test),
124
+ body=stmt_list_compile([then]),
125
+ orelse=stmt_list_compile([*orelse]),
126
+ **sexp.position_info
127
+ )
128
+
129
+
130
+ def while_p(sexp):
131
+ return str(sexp.op) == "while"
132
+
133
+
134
+ def while_compile(sexp):
135
+ [_, test, *body] = sexp.list
136
+ lastx = body[-1]
137
+ [then, orelse] = (
138
+ [body[:-1], lastx.operands]
139
+ if isinstance(lastx, Paren) and lastx.op == "else"
140
+ else [body, []]
141
+ )
142
+ return ast.While(
143
+ test=expr_compile(test),
144
+ body=stmt_list_compile(then),
145
+ orelse=stmt_list_compile(orelse),
146
+ **sexp.position_info
147
+ )
148
+
149
+
150
+ def for_p(sexp):
151
+ return str(sexp.op) == "for"
152
+
153
+
154
+ def for_compile(sexp, async_p=False):
155
+ body = deque(sexp.operands)
156
+ target = body.popleft()
157
+ if body[0] == "in":
158
+ body.popleft()
159
+ iterable = body.popleft()
160
+ lastx = body[-1]
161
+ [then, orelse] = (
162
+ [list(body)[:-1], lastx.operands]
163
+ if isinstance(lastx, Paren) and lastx.op == "else"
164
+ else [body, []]
165
+ )
166
+ return (ast.AsyncFor if async_p else ast.For)(
167
+ target=expr_compile(target, ast.Store),
168
+ iter=expr_compile(iterable),
169
+ body=stmt_list_compile(then),
170
+ orelse=stmt_list_compile(orelse),
171
+ **sexp.position_info
172
+ )
173
+
174
+
175
+ def deco_p(sexp):
176
+ return str(sexp.op) == "deco"
177
+
178
+
179
+ def raise_compile(sexp):
180
+ body = sexp.operands
181
+ assert len(body) == 1 or (len(body) == 3 and body[1] == "from")
182
+ kwargs = {"exc": expr_compile(body[0])}
183
+ if len(body) > 1:
184
+ kwargs["cause"] = expr_compile(body[-1])
185
+ return ast.Raise(**kwargs, **sexp.position_info)
186
+
187
+
188
+ def assert_compile(sexp):
189
+ kwargs = {"test": expr_compile(sexp[1])}
190
+ if len(sexp) > 2:
191
+ kwargs["msg"] = expr_compile(sexp[2])
192
+ return ast.Assert(**kwargs, **sexp.position_info)
193
+
194
+
195
+ def parse_exception_bracket(bracket):
196
+ lst = bracket.list
197
+ name = (
198
+ str([lst.pop(), lst.pop()][0]).replace("-", "_")
199
+ if len(lst) > 2 and lst[-2] == "as"
200
+ else None
201
+ )
202
+ type = (
203
+ ast.Tuple(
204
+ elts=list(map(expr_compile, lst)), ctx=ast.Load(), **bracket.position_info
205
+ )
206
+ if len(lst) > 1
207
+ else expr_compile(lst[0])
208
+ )
209
+ return [type, name]
210
+
211
+
212
+ def parse_except(handler):
213
+ body = handler.operands
214
+ if isinstance(body[0], Bracket):
215
+ body = deque(handler.operands)
216
+ [type, name] = parse_exception_bracket(body.popleft())
217
+ else:
218
+ [type, name] = [None, None]
219
+ kwargs = {"body": stmt_list_compile(body)}
220
+ if type:
221
+ kwargs["type"] = type
222
+ if name:
223
+ kwargs["name"] = name
224
+ return ast.ExceptHandler(**kwargs, **handler.position_info)
225
+
226
+
227
+ def try_compile(sexp):
228
+ body = sexp.operands
229
+ # finally
230
+ finalbody = (
231
+ body.pop().operands
232
+ if isinstance(body[-1], Paren) and body[-1].op == "finally"
233
+ else []
234
+ )
235
+ # else
236
+ orelse = (
237
+ body.pop().operands
238
+ if isinstance(body[-1], Paren) and body[-1].op == "else"
239
+ else []
240
+ )
241
+ # excepts
242
+ handlers = deque()
243
+ while isinstance(body[-1], Paren) and body[-1].op == "except":
244
+ handlers.appendleft(body.pop())
245
+ handlers = list(map(parse_except, handlers))
246
+ # except*s
247
+ starhandlers = deque()
248
+ while isinstance(body[-1], Paren) and body[-1].op == "except*":
249
+ starhandlers.appendleft(body.pop())
250
+ starhandlers = list(map(parse_except, starhandlers))
251
+ assert not starhandlers or not handlers
252
+ return (ast.Try if not starhandlers else ast.TryStar)(
253
+ body=stmt_list_compile(body),
254
+ handlers=handlers or starhandlers,
255
+ orelse=stmt_list_compile(orelse),
256
+ finalbody=stmt_list_compile(finalbody),
257
+ **sexp.position_info
258
+ )
259
+
260
+
261
+ def with_items_parse(sexp):
262
+ rst = []
263
+ q = deque(sexp.list)
264
+ while q:
265
+ elt = q.popleft()
266
+ if str(elt) == "as":
267
+ rst[-1].optional_vars = expr_compile(q.popleft(), ctx=ast.Store)
268
+ else:
269
+ rst.append(ast.withitem(context_expr=expr_compile(elt)))
270
+ return rst
271
+
272
+
273
+ def with_compile(sexp, async_p=False):
274
+ [bracket_sexp, *body] = sexp.operands
275
+ items = with_items_parse(bracket_sexp)
276
+ return (ast.AsyncWith if async_p else ast.With)(
277
+ items=items, body=stmt_list_compile(body), **sexp.position_info
278
+ )
279
+
280
+
281
+ def match_mapping_parse(lst):
282
+ keys = lst[::2]
283
+ patterns = lst[1::2]
284
+ rst = {}
285
+ if isinstance(keys[-1], DoubleStarred):
286
+ rst["rest"] = keys.pop().value.name
287
+ rst["keys"] = list(map(expr_compile, keys))
288
+ rst["patterns"] = list(map(pattern_parse, patterns))
289
+ return rst
290
+
291
+
292
+ def match_class_parse(lst):
293
+ q = deque(lst)
294
+ patterns = []
295
+ kwd_attrs = []
296
+ kwd_patterns = []
297
+ while q:
298
+ arg = q.popleft()
299
+ if isinstance(arg, Keyword):
300
+ kwd_attrs.append(arg.value.name)
301
+ kwd_patterns.append(pattern_parse(q.popleft()))
302
+ else:
303
+ patterns.append(pattern_parse(arg))
304
+ return {"patterns": patterns, "kwd_attrs": kwd_attrs, "kwd_patterns": kwd_patterns}
305
+
306
+
307
+ def pattern_parse(sexp):
308
+ return (
309
+ ast.MatchAs(**sexp.position_info)
310
+ if str(sexp) == "_"
311
+ else ast.MatchAs(name=sexp.name, **sexp.position_info)
312
+ if isinstance(sexp, Symbol)
313
+ else ast.MatchSingleton(value=eval(str(sexp)), **sexp.position_info)
314
+ if str(sexp) in ["True", "False", "None"]
315
+ else ast.MatchStar(
316
+ **{} if str(sexp.value) == "_" else {"name": sexp.value.name},
317
+ **sexp.position_info
318
+ )
319
+ if isinstance(sexp, Starred)
320
+ else ast.MatchValue(value=expr_compile(sexp), **sexp.position_info)
321
+ if not isinstance(sexp, Expression)
322
+ else ast.MatchSequence(
323
+ patterns=list(map(pattern_parse, sexp.list)), **sexp.position_info
324
+ )
325
+ if isinstance(sexp, Bracket)
326
+ else ast.MatchMapping(**match_mapping_parse(sexp.list), **sexp.position_info)
327
+ if isinstance(sexp, Brace)
328
+ else ast.MatchOr(
329
+ patterns=list(map(pattern_parse, sexp.operands)), **sexp.position_info
330
+ )
331
+ if sexp.op == "|"
332
+ else ast.MatchClass(
333
+ cls=expr_compile(sexp.op),
334
+ **match_class_parse(sexp.operands),
335
+ **sexp.position_info
336
+ )
337
+ )
338
+
339
+
340
+ def case_parse(case):
341
+ [pattern_expr, *body] = case.operands
342
+ pattern = pattern_parse(pattern_expr)
343
+ if body[0] == "as":
344
+ pattern = ast.MatchAs(
345
+ pattern=pattern,
346
+ name=body[1].name.replace("-", "_"),
347
+ **merge_position_infos(pattern_expr.position_info, body[1].position_info)
348
+ )
349
+ body = body[2:]
350
+ if body[0] == "if":
351
+ guard_dict = {"guard": expr_compile(body[1])}
352
+ body = body[2:]
353
+ else:
354
+ guard_dict = {}
355
+ return ast.match_case(pattern=pattern, **guard_dict, body=stmt_list_compile(body))
356
+
357
+
358
+ def match_compile(sexp):
359
+ [subject, *cases] = sexp.operands
360
+ cases = list(map(case_parse, cases))
361
+ return ast.Match(subject=expr_compile(subject), cases=cases, **sexp.position_info)
362
+
363
+
364
+ def deco_compile(sexp, decorator_list):
365
+ [op, decorator, def_statement] = sexp.list
366
+ new_deco_list = decorator.list if isinstance(decorator, Bracket) else [decorator]
367
+ return stmt_compile(
368
+ def_statement, (decorator_list if decorator_list else []) + new_deco_list
369
+ )
370
+
371
+
372
+ def functiondef_p(sexp):
373
+ return str(sexp.op) == "def"
374
+
375
+
376
+ def functiondef_compile(sexp, decorator_list, async_p=False):
377
+ [op, fnname, args, *body] = sexp.list
378
+ if body and isinstance(body[0], Annotation):
379
+ [ann, *body] = body
380
+ else:
381
+ ann = None
382
+ return (ast.AsyncFunctionDef if async_p else ast.FunctionDef)(
383
+ name=fnname.name,
384
+ args=def_args_parse(args),
385
+ body=stmt_list_compile(body) if body else [ast.Pass(**sexp.position_info)],
386
+ decorator_list=list(map(expr_compile, decorator_list))
387
+ if decorator_list
388
+ else [],
389
+ returns=expr_compile(ann.value) if ann else None,
390
+ **sexp.position_info
391
+ )
392
+
393
+
394
+ def return_p(sexp):
395
+ return str(sexp.op) == "return"
396
+
397
+
398
+ def return_compile(sexp):
399
+ [op, value] = sexp.list if len(sexp) > 1 else [None, None]
400
+ return ast.Return(value=expr_compile(value) if value else None, **sexp.position_info)
401
+
402
+
403
+ def global_compile(sexp):
404
+ [_, *args] = sexp.list
405
+ return ast.Global(names=list(map(lambda x: x.name, args)), **sexp.position_info)
406
+
407
+
408
+ def nonlocal_compile(sexp):
409
+ [_, *args] = sexp.list
410
+ return ast.Nonlocal(names=list(map(lambda x: x.name, args)), **sexp.position_info)
411
+
412
+
413
+ def classdef_p(sexp):
414
+ return str(sexp.op) == "class"
415
+
416
+
417
+ def classdef_args_parse(args):
418
+ q = deque(args)
419
+ bases = []
420
+ keywords = []
421
+ while q:
422
+ arg = q.popleft()
423
+ keywords.append(
424
+ ast.keyword(
425
+ arg=arg.name, value=expr_compile(q.popleft()), **arg.position_info
426
+ )
427
+ ) if keyword_arg_p(arg) else bases.append(expr_compile(arg))
428
+ return [bases, keywords]
429
+
430
+
431
+ def classdef_compile(sexp, decorator_list):
432
+ [_, clsname, args, *body] = sexp.list
433
+ [bases, keywords] = classdef_args_parse(args)
434
+ return ast.ClassDef(
435
+ name=clsname.name,
436
+ bases=bases,
437
+ keywords=keywords,
438
+ body=stmt_list_compile(body) if body else [ast.Pass(**sexp.position_info)],
439
+ decorator_list=list(map(expr_compile, decorator_list))
440
+ if decorator_list
441
+ else [],
442
+ **sexp.position_info
443
+ )
444
+
445
+
446
+ def stmt_compile(sexp, decorator_list=None):
447
+ return (
448
+ expr_wrapper(sexp)
449
+ if not paren_p(sexp)
450
+ else do_compile(sexp)
451
+ if do_p(sexp)
452
+ else assign_compile(sexp)
453
+ if assign_p(sexp)
454
+ else augassign_compile(sexp)
455
+ if augassign_p(sexp)
456
+ else del_compile(sexp)
457
+ if str(sexp.op) == "del"
458
+ else ast.Pass(**sexp.position_info)
459
+ if str(sexp.op) == "pass"
460
+ else import_compile(sexp)
461
+ if str(sexp.op) == "import"
462
+ else importfrom_compile(sexp)
463
+ if str(sexp.op) == "from"
464
+ else if_stmt_compile(sexp)
465
+ if if_p(sexp)
466
+ else while_compile(sexp)
467
+ if while_p(sexp)
468
+ else for_compile(sexp)
469
+ if for_p(sexp)
470
+ else ast.Break(**sexp.position_info)
471
+ if str(sexp.op) == "break"
472
+ else ast.Continue(**sexp.position_info)
473
+ if str(sexp.op) == "continue"
474
+ else raise_compile(sexp)
475
+ if str(sexp.op) == "raise"
476
+ else assert_compile(sexp)
477
+ if str(sexp.op) == "assert"
478
+ else try_compile(sexp)
479
+ if str(sexp.op) == "try"
480
+ else with_compile(sexp)
481
+ if str(sexp.op) == "with"
482
+ else match_compile(sexp)
483
+ if str(sexp.op) == "match"
484
+ else deco_compile(sexp, decorator_list)
485
+ if deco_p(sexp)
486
+ else functiondef_compile(sexp, decorator_list)
487
+ if functiondef_p(sexp)
488
+ else return_compile(sexp)
489
+ if return_p(sexp)
490
+ else global_compile(sexp)
491
+ if str(sexp.op) == "global"
492
+ else nonlocal_compile(sexp)
493
+ if str(sexp.op) == "nonlocal"
494
+ else classdef_compile(sexp, decorator_list)
495
+ if classdef_p(sexp)
496
+ else functiondef_compile(sexp, decorator_list, async_p=True)
497
+ if str(sexp.op) == "async-def"
498
+ else for_compile(sexp, async_p=True)
499
+ if str(sexp.op) == "async-for"
500
+ else with_compile(sexp, async_p=True)
501
+ if str(sexp.op) == "async-with"
502
+ else expr_wrapper(sexp)
503
+ )
504
+
505
+
506
+ def stmt_list_compile(sexp_list):
507
+ return reduce(
508
+ lambda x, y: x + (y if isinstance(y, list) else [y]),
509
+ map(stmt_compile, sexp_list),
510
+ [],
511
+ )
@@ -58,11 +58,13 @@
58
58
  scope
59
59
  :include-meta include-meta)))))
60
60
 
61
- (def src-to-python [src :include-meta False :scope None]
61
+ (def src-to-python [src :include-meta False :scope None :format True]
62
62
  (if (is scope None)
63
63
  (= scope SCOPE))
64
64
  (= raw-pysrc (src-to-python-org src :include-meta include-meta :scope scope))
65
- (return (format-python-source raw-pysrc)))
65
+ (if format
66
+ (return (format-python-source raw-pysrc))
67
+ (return raw-pysrc)))
66
68
 
67
69
  (def format-python-source [pysrc]
68
70
  (if (not HAS_RUFF)
@@ -93,19 +95,19 @@
93
95
  :dest "no_lispy"
94
96
  :action "store_true")
95
97
 
96
- (def l2py-s [src :include-meta False :fresh-scope False :package "" :no-lispy False]
98
+ (def l2py-s [src :include-meta False :fresh-scope False :package "" :no-lispy False :format True]
97
99
  "Translate lispy source string to Python source string"
98
100
  (if fresh-scope
99
101
  (= scope {"__builtins__" __builtins__
100
102
  "__package__" package
101
103
  "__macro_namespace" {}})
102
104
  (= scope SCOPE))
103
- (= pysrc (src-to-python src :include-meta include_meta :scope scope))
105
+ (= pysrc (src-to-python src :include-meta include_meta :scope scope :format format))
104
106
  (if no_lispy
105
107
  (return pysrc))
106
108
  (return (+ "import lispy\n\n" pysrc)))
107
109
 
108
- (def l2py-f [file-path :include-meta False :no-lispy False]
110
+ (def l2py-f [file-path :include-meta False :no-lispy False :format True]
109
111
  "Translate lispy file to Python source string (standalone with sys.path setup)"
110
112
  (= script-dir (osp.dirname (osp.abspath file-path)))
111
113
  (if (not (in script-dir sys.path))
@@ -126,7 +128,7 @@
126
128
  (with [(open file-path "rb") as f]
127
129
  (= src (.decode (f.read) "utf-8")))
128
130
  (= pkg (detect-package-name :path file-path))
129
- (return (+ (l2py-s src :include-meta include_meta :fresh-scope True :package pkg :no-lispy no_lispy) "\n")))
131
+ (return (+ (l2py-s src :include-meta include_meta :fresh-scope True :package pkg :no-lispy no_lispy :format format) "\n")))
130
132
 
131
133
  (def l2py []
132
134
  "CLI entry point: parse args and translate file"
@@ -18,9 +18,9 @@ class TestMetaFunctionsSync:
18
18
  with open(py_path, "r") as f:
19
19
  py_contents = f.read()
20
20
 
21
- transpiled = l2py_s(lpy_src)
21
+ transpiled = l2py_s(lpy_src, no_lispy=True)
22
22
 
23
23
  assert transpiled == py_contents, (
24
24
  "core/meta_functions.py is out of sync with core_meta_functions.lpy. "
25
- "Run `lispy src/lispy/core_meta_functions.lpy` to regenerate."
25
+ "Run `lpy src/lispy/core_meta_functions.lpy` to regenerate."
26
26
  )
@@ -238,7 +238,7 @@ wheels = [
238
238
 
239
239
  [[package]]
240
240
  name = "lispython"
241
- version = "0.3.1"
241
+ version = "0.3.3"
242
242
  source = { editable = "." }
243
243
 
244
244
  [package.optional-dependencies]
@@ -1,10 +0,0 @@
1
- import toml
2
-
3
- def define_env(env):
4
- try:
5
- pyproject = toml.load("pyproject.toml")
6
- version = pyproject["tool"]["poetry"]["version"]
7
- except Exception:
8
- version = "dev"
9
-
10
- env.variables["version"] = version
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes