pyscriptc 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. pyscriptc/__init__.py +3 -0
  2. pyscriptc/__main__.py +3 -0
  3. pyscriptc/_posthog_token.py +1 -0
  4. pyscriptc/builtins.py +156 -0
  5. pyscriptc/canonical.py +167 -0
  6. pyscriptc/check.py +49 -0
  7. pyscriptc/cli.py +271 -0
  8. pyscriptc/coverage.py +388 -0
  9. pyscriptc/debug.py +120 -0
  10. pyscriptc/diagnostics.py +179 -0
  11. pyscriptc/errors.py +56 -0
  12. pyscriptc/ir.py +365 -0
  13. pyscriptc/llvm.py +2129 -0
  14. pyscriptc/log.py +127 -0
  15. pyscriptc/lower/__init__.py +1071 -0
  16. pyscriptc/lower/_calls.py +1499 -0
  17. pyscriptc/lower/_classes.py +969 -0
  18. pyscriptc/lower/_core.py +301 -0
  19. pyscriptc/lower/_expr.py +886 -0
  20. pyscriptc/lower/_stdlib.py +491 -0
  21. pyscriptc/lower/_stdlib_shared.py +93 -0
  22. pyscriptc/lower/_stmt.py +1173 -0
  23. pyscriptc/package.py +249 -0
  24. pyscriptc/restlower.py +1008 -0
  25. pyscriptc/runtime/ps_argv.c +28 -0
  26. pyscriptc/runtime/ps_b64.c +168 -0
  27. pyscriptc/runtime/ps_coll.c +133 -0
  28. pyscriptc/runtime/ps_dt.c +388 -0
  29. pyscriptc/runtime/ps_hash.c +411 -0
  30. pyscriptc/runtime/ps_help.c +219 -0
  31. pyscriptc/runtime/ps_hmac.c +120 -0
  32. pyscriptc/runtime/ps_io.c +198 -0
  33. pyscriptc/runtime/ps_island.c +272 -0
  34. pyscriptc/runtime/ps_json.c +402 -0
  35. pyscriptc/runtime/ps_mix.c +47 -0
  36. pyscriptc/runtime/ps_more.c +1089 -0
  37. pyscriptc/runtime/ps_osmore.c +447 -0
  38. pyscriptc/runtime/ps_path.c +547 -0
  39. pyscriptc/runtime/ps_re.c +1102 -0
  40. pyscriptc/runtime/ps_rt.c +1175 -0
  41. pyscriptc/runtime/ps_rt.h +189 -0
  42. pyscriptc/runtime/ps_struct.c +291 -0
  43. pyscriptc/runtime/ps_time.c +207 -0
  44. pyscriptc/runtime/ps_unicode.c +1521 -0
  45. pyscriptc/runtime/ps_url.c +327 -0
  46. pyscriptc/stdlib_queue.py +191 -0
  47. pyscriptc/stdmore.py +1596 -0
  48. pyscriptc/telemetry.py +219 -0
  49. pyscriptc-0.1.0.dist-info/METADATA +187 -0
  50. pyscriptc-0.1.0.dist-info/RECORD +54 -0
  51. pyscriptc-0.1.0.dist-info/WHEEL +5 -0
  52. pyscriptc-0.1.0.dist-info/entry_points.txt +2 -0
  53. pyscriptc-0.1.0.dist-info/licenses/LICENSE +201 -0
  54. pyscriptc-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,301 @@
1
+ """Leaf definitions shared by every module in the `lower` package.
2
+
3
+ `Fence`, `Binding`, and the small pure helpers live here so sibling modules
4
+ (`_stdlib.py`, `__init__.py`) can import them without a cycle. Moved verbatim
5
+ from `lower.py`; no signature or behavior change.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import ast
11
+ from typing import Any
12
+
13
+ from pyscriptc.ir import types_equal
14
+
15
+
16
+ class Fence(Exception):
17
+ def __init__(self, code: str, message: str, span: dict[str, Any], function: str | None) -> None:
18
+ super().__init__(message)
19
+ self.code = code
20
+ self.message = message
21
+ self.span = span
22
+ self.function = function
23
+
24
+ def as_dict(self) -> dict[str, Any]:
25
+ return {
26
+ "code": self.code,
27
+ "message": self.message,
28
+ "function": self.function,
29
+ "span": self.span,
30
+ }
31
+
32
+
33
+ class Binding:
34
+ def __init__(
35
+ self,
36
+ kind: str,
37
+ module: str,
38
+ attr: str,
39
+ queued: bool,
40
+ typ: dict[str, Any] | None = None,
41
+ rooted: bool = False,
42
+ ) -> None:
43
+ self.kind = kind
44
+ self.module = module
45
+ self.attr = attr
46
+ self.queued = queued
47
+ self.type = typ
48
+ # True when `import urllib.parse` binds `urllib`, not the whole module.
49
+ self.rooted = rooted
50
+
51
+
52
+ def _assign(target: str, value: dict[str, Any], span: dict[str, Any]) -> dict[str, Any]:
53
+ return {"kind": "assign", "target": target, "value": value, "span": span}
54
+
55
+
56
+ def _const_str_join(parts: list[dict[str, Any]], span: dict[str, Any]) -> dict[str, Any] | None:
57
+ """Module-level f-string pieces that are already str constants are one str constant."""
58
+
59
+ if any(part.get("kind") != "const" or part.get("type", {}).get("kind") != "str" for part in parts):
60
+ return None
61
+ text = "".join(str(part["value"]) for part in parts)
62
+ return {"kind": "const", "value": text, "type": {"kind": "str"}, "span": span}
63
+
64
+
65
+ def _concat_str(parts: list[dict[str, Any]], span: dict[str, Any]) -> dict[str, Any]:
66
+ if not parts:
67
+ return {"kind": "const", "value": "", "type": {"kind": "str"}, "span": span}
68
+ acc = parts[0]
69
+ for part in parts[1:]:
70
+ acc = {"kind": "binary", "op": "+", "left": acc, "right": part, "type": {"kind": "str"}, "span": span}
71
+ return acc
72
+
73
+
74
+ def _percent_pieces(fmt: str) -> list[tuple[str, str]] | None:
75
+ pieces: list[tuple[str, str]] = []
76
+ buf: list[str] = []
77
+ index = 0
78
+ size = len(fmt)
79
+ while index < size:
80
+ if fmt[index] != "%":
81
+ buf.append(fmt[index])
82
+ index += 1
83
+ continue
84
+ if index + 1 >= size:
85
+ return None
86
+ nxt = fmt[index + 1]
87
+ if nxt == "%":
88
+ buf.append("%")
89
+ index += 2
90
+ continue
91
+ if nxt not in {"s", "d"}:
92
+ return None
93
+ pieces.append(("lit", "".join(buf)))
94
+ buf = []
95
+ pieces.append((nxt, ""))
96
+ index += 2
97
+ pieces.append(("lit", "".join(buf)))
98
+ return pieces
99
+
100
+
101
+ def _const_list_concat(left: dict[str, Any], right: dict[str, Any], span: dict[str, Any]) -> dict[str, Any] | None:
102
+ """Two constant lists of one element type are one list. A runtime list stays fenced."""
103
+
104
+ if left.get("kind") != "list" or right.get("kind") != "list":
105
+ return None
106
+ if left["type"]["kind"] != "list" or right["type"]["kind"] != "list":
107
+ return None
108
+ if left["type"].get("set") or right["type"].get("set"):
109
+ return None
110
+ if not types_equal(left["type"]["elem"], right["type"]["elem"]):
111
+ return None
112
+ return {
113
+ "kind": "list",
114
+ "elems": [*left["elems"], *right["elems"]],
115
+ "type": {"kind": "list", "elem": left["type"]["elem"]},
116
+ "span": span,
117
+ }
118
+
119
+
120
+ def _name(name: str, typ: dict[str, Any], span: dict[str, Any]) -> dict[str, Any]:
121
+ return {"kind": "name", "id": name, "type": typ, "span": span}
122
+
123
+
124
+ def _const_int(value: int, span: dict[str, Any]) -> dict[str, Any]:
125
+ return {"kind": "const", "value": str(value), "type": {"kind": "int"}, "span": span}
126
+
127
+
128
+ def _int_text(value: str) -> bool:
129
+ body = value[1:] if value.startswith("-") else value
130
+ return bool(body) and body.isdigit()
131
+
132
+
133
+ def _static_int(expr: dict[str, Any]) -> int | None:
134
+ if expr.get("kind") == "const" and expr.get("type", {}).get("kind") == "int":
135
+ value = expr.get("value")
136
+ if isinstance(value, str) and _int_text(value):
137
+ return int(value)
138
+ if expr.get("kind") == "unary" and expr.get("op") == "-" and isinstance(expr.get("operand"), dict):
139
+ inner = _static_int(expr["operand"])
140
+ if inner is not None:
141
+ return -inner
142
+ return None
143
+
144
+
145
+ def _const_bool(value: bool, span: dict[str, Any]) -> dict[str, Any]:
146
+ return {"kind": "const", "value": value, "type": {"kind": "bool"}, "span": span}
147
+
148
+
149
+ def _const_none(span: dict[str, Any]) -> dict[str, Any]:
150
+ return {"kind": "const", "value": None, "type": {"kind": "none"}, "span": span}
151
+
152
+
153
+ def _len_call(seq: dict[str, Any], span: dict[str, Any]) -> dict[str, Any]:
154
+ return {
155
+ "kind": "call",
156
+ "callee": {"kind": "builtin", "name": "len"},
157
+ "args": [seq],
158
+ "type": {"kind": "int"},
159
+ "span": span,
160
+ }
161
+
162
+
163
+ def _binary_int(op: str, left: dict[str, Any], right: dict[str, Any], span: dict[str, Any]) -> dict[str, Any]:
164
+ return {"kind": "binary", "op": op, "left": left, "right": right, "type": {"kind": "int"}, "span": span}
165
+
166
+
167
+ def _compare(op: str, left: dict[str, Any], right: dict[str, Any], span: dict[str, Any]) -> dict[str, Any]:
168
+ return {"kind": "compare", "op": op, "left": left, "right": right, "type": {"kind": "bool"}, "span": span}
169
+
170
+
171
+ def _show(typ: dict[str, Any]) -> str:
172
+ if typ["kind"] == "list":
173
+ head = "set" if typ.get("set") else "list"
174
+ return f"{head}[{_show(typ['elem'])}]"
175
+ if typ["kind"] == "dict":
176
+ return f"dict[{_show(typ['key'])}, {_show(typ['value'])}]"
177
+ if typ["kind"] == "class":
178
+ return str(typ.get("label", typ["name"]))
179
+ return str(typ["kind"])
180
+
181
+
182
+ def _plain_const_kind(node: ast.expr) -> str | None:
183
+ """A str constant or an int constant. Bool is not an int here."""
184
+ if not isinstance(node, ast.Constant) or isinstance(node.value, bool):
185
+ return None
186
+ if isinstance(node.value, str):
187
+ return "str"
188
+ if isinstance(node.value, int):
189
+ return "int"
190
+ return None
191
+
192
+
193
+ def _dotted(node: ast.Attribute) -> str:
194
+ parts: list[str] = [node.attr]
195
+ cur: ast.AST = node.value
196
+ while isinstance(cur, ast.Attribute):
197
+ parts.append(cur.attr)
198
+ cur = cur.value
199
+ if isinstance(cur, ast.Name):
200
+ parts.append(cur.id)
201
+ parts.reverse()
202
+ return ".".join(parts)
203
+
204
+
205
+ _CMP = {
206
+ ast.Lt: "<",
207
+ ast.LtE: "<=",
208
+ ast.Gt: ">",
209
+ ast.GtE: ">=",
210
+ ast.Eq: "==",
211
+ ast.NotEq: "!=",
212
+ }
213
+ _SCALAR = {"int", "float", "str", "bytes"}
214
+ _TRUTHY = frozenset({"str", "bytes", "int", "list", "dict"})
215
+ _LIST_FORMS = frozenset({"List"})
216
+ _DICT_FORMS = frozenset({"Dict"})
217
+ _SET_FORMS = frozenset({"Set"})
218
+
219
+
220
+ _FUTURE_OK = frozenset(
221
+ {
222
+ "annotations",
223
+ "generator_stop",
224
+ "nested_scopes",
225
+ "generators",
226
+ "division",
227
+ "absolute_import",
228
+ "with_statement",
229
+ "print_function",
230
+ "unicode_literals",
231
+ }
232
+ )
233
+
234
+
235
+ def _package_base(module_name: str, is_package: bool, level: int) -> list[str] | None:
236
+ if level < 1 or not module_name:
237
+ return None
238
+ bits = module_name.split(".")
239
+ if not is_package:
240
+ bits = bits[:-1]
241
+ drop = level - 1
242
+ if drop > len(bits):
243
+ return None
244
+ return bits[: len(bits) - drop]
245
+
246
+
247
+ def _tuple_assign(
248
+ dest: str, typ: dict[str, Any], elems: list[dict[str, Any]], span: dict[str, Any]
249
+ ) -> list[dict[str, Any]]:
250
+ made = {
251
+ "kind": "call",
252
+ "callee": {"kind": "class", "name": typ["name"]},
253
+ "args": [],
254
+ "type": typ,
255
+ "span": span,
256
+ }
257
+ stmts = [_assign(dest, made, span)]
258
+ for index, elem in enumerate(elems):
259
+ stmts.append(
260
+ {
261
+ "kind": "setattr",
262
+ "object": _name(dest, typ, span),
263
+ "name": f"_{index}",
264
+ "value": elem,
265
+ "span": span,
266
+ }
267
+ )
268
+ return stmts
269
+
270
+
271
+ def _zero(typ: dict[str, Any], span: dict[str, Any]) -> dict[str, Any]:
272
+ if typ["kind"] == "int":
273
+ return _const_int(0, span)
274
+ if typ["kind"] == "bool":
275
+ return _const_bool(False, span)
276
+ if typ["kind"] == "float":
277
+ return {"kind": "const", "value": "0.0", "type": typ, "span": span}
278
+ if typ["kind"] == "class":
279
+ # Fresh for-targets of a synthetic tuple class start as a zeroed
280
+ # instance (ps_alloc calloc); the loop step then rebinding every field.
281
+ return {
282
+ "kind": "call",
283
+ "callee": {"kind": "class", "name": typ["name"]},
284
+ "args": [],
285
+ "type": typ,
286
+ "span": span,
287
+ }
288
+ return {"kind": "const", "value": "", "type": typ, "span": span}
289
+
290
+
291
+ def _is_none_expr(node: ast.expr) -> bool:
292
+ return isinstance(node, ast.Constant) and node.value is None
293
+
294
+
295
+ def _declared_globals(body: list[ast.stmt]) -> set[str]:
296
+ names: set[str] = set()
297
+ for stmt in body:
298
+ for node in ast.walk(stmt):
299
+ if isinstance(node, ast.Global):
300
+ names.update(node.names)
301
+ return names