hython-lang 2.0.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.
hython/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ """Hython: Python pronounced in Hangul."""
2
+
3
+ from .environment import activate_package_store
4
+
5
+ activate_package_store()
6
+
7
+ __version__ = "2.0.0"
8
+
9
+ from .translator import audit_english, koreanize, to_hython, to_python
10
+ from .importer import install_importer
11
+
12
+ __all__ = [
13
+ "audit_english", "koreanize", "to_hython", "to_python",
14
+ "install_importer", "__version__",
15
+ ]
hython/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .cli import entrypoint
2
+
3
+ raise SystemExit(entrypoint())
hython/bytecode.py ADDED
@@ -0,0 +1,368 @@
1
+ """HBC container and instruction model; unrelated to CPython bytecode."""
2
+ from __future__ import annotations
3
+ import hashlib
4
+ import json
5
+ import struct
6
+ import zlib
7
+ import base64
8
+ from dataclasses import asdict, dataclass, field
9
+ from pathlib import Path
10
+
11
+ MAGIC = b"HYBC"
12
+ VERSION = 6
13
+ MAX_COMPRESSED_SIZE = 64 * 1024 * 1024
14
+ MAX_INSTRUCTIONS = 1_000_000
15
+ _NO_ARG = {"RETURN","POP","NOP","ITER","SET_ITEM","GET_ITEM","FORMAT","NEG","POS","NOT","IMPORT_STAR",
16
+ "ADD","SUB","MUL","DIV","FLOORDIV","MOD","POW","BIT_OR","BIT_XOR","BIT_AND","LSHIFT","RSHIFT","MATMUL","IADD","ISUB","IMUL","IDIV","IFLOORDIV","IMOD","IPOW","IBIT_OR","IBIT_XOR","IBIT_AND","ILSHIFT","IRSHIFT","IMATMUL","EQ","NE","LT","LE","GT","GE","IN","IS","NOT_IN","IS_NOT","BOOL_AND","BOOL_OR","INVERT","RAISE","RAISE_FROM","RERAISE","DELETE_ITEM","ASSERT","DUP","DUP2","BUILD_SLICE","YIELD","YIELD_FROM","AWAIT","SIGNAL_RETURN","SIGNAL_BREAK","SIGNAL_CONTINUE","MAKE_INTERPOLATION","CALL_BEGIN","CALL_READY","CLASS_BEGIN","COLLECTION_READY"}
17
+ _NAME_ARG = {"LOAD","STORE","STORE_GLOBAL","STORE_NONLOCAL","DELETE","DELETE_GLOBAL","DELETE_NONLOCAL","GET_ATTR","SET_ATTR","DELETE_ATTR","IMPORT","ANNOTATE","SUPER","SAVE_NAME","RESTORE_NAME"}
18
+ _INT_ARG = {"CALL","UNPACK","UNPACK_EX","BUILD_LIST","BUILD_TUPLE","BUILD_SET","BUILD_DICT","BUILD_STRING","BUILD_TEMPLATE"}
19
+
20
+ @dataclass
21
+ class CodeObject:
22
+ name: str
23
+ parameters: list[str]
24
+ constants: list[object]
25
+ instructions: list[list]
26
+ lines: list[int] = field(default_factory=list)
27
+
28
+ def to_dict(self) -> dict:
29
+ return {"name":self.name,"parameters":self.parameters,"constants":[_encode_constant(value) for value in self.constants],"instructions":self.instructions,"lines":self.lines}
30
+
31
+ @classmethod
32
+ def from_dict(cls, value: dict) -> "CodeObject":
33
+ return cls(value["name"], value["parameters"], [_decode_constant(item) for item in value["constants"]], value["instructions"],value.get("lines",[]))
34
+
35
+ def _encode_constant(value):
36
+ if value is Ellipsis: return {"$hython_constant":"ellipsis"}
37
+ if isinstance(value,bytes): return {"$hython_constant":"bytes","data":base64.b64encode(value).decode("ascii")}
38
+ if isinstance(value,complex): return {"$hython_constant":"complex","real":value.real,"imag":value.imag}
39
+ if isinstance(value,tuple): return {"$hython_constant":"tuple","items":[_encode_constant(item) for item in value]}
40
+ return value
41
+
42
+ def _decode_constant(value):
43
+ if not isinstance(value,dict) or "$hython_constant" not in value: return value
44
+ kind=value["$hython_constant"]
45
+ if kind=="ellipsis": return Ellipsis
46
+ if kind=="bytes": return base64.b64decode(value["data"],validate=True)
47
+ if kind=="complex": return complex(value["real"],value["imag"])
48
+ if kind=="tuple": return tuple(_decode_constant(item) for item in value["items"])
49
+ raise ValueError("알 수 없는 HBC 상수 형식")
50
+
51
+ class BytecodeError(ValueError):
52
+ pass
53
+
54
+ def dumps(code: CodeObject) -> bytes:
55
+ raw = json.dumps(code.to_dict(), ensure_ascii=False, separators=(",", ":")).encode("utf-8")
56
+ payload = zlib.compress(raw, level=9)
57
+ digest = hashlib.sha256(payload).digest()
58
+ return MAGIC + bytes([VERSION]) + struct.pack(">I", len(payload)) + digest + payload
59
+
60
+ def loads(data: bytes) -> CodeObject:
61
+ if len(data) < 41 or data[:4] != MAGIC:
62
+ raise BytecodeError("하이썬 HBC 파일이 아닙니다.")
63
+ if data[4] != VERSION:
64
+ raise BytecodeError(f"지원하지 않는 HBC 버전: {data[4]}")
65
+ size = struct.unpack(">I", data[5:9])[0]
66
+ if size > MAX_COMPRESSED_SIZE:
67
+ raise BytecodeError("HBC 파일이 허용 크기를 초과합니다.")
68
+ digest, payload = data[9:41], data[41:]
69
+ if size != len(payload) or hashlib.sha256(payload).digest() != digest:
70
+ raise BytecodeError("HBC 크기 또는 SHA-256 무결성 검사가 실패했습니다.")
71
+ try:
72
+ decompressor=zlib.decompressobj()
73
+ raw=decompressor.decompress(payload,MAX_COMPRESSED_SIZE+1)
74
+ if len(raw)>MAX_COMPRESSED_SIZE or decompressor.unconsumed_tail:
75
+ raise BytecodeError("HBC 압축 해제 크기가 제한을 초과합니다.")
76
+ value = json.loads(raw.decode("utf-8"))
77
+ code=CodeObject.from_dict(value)
78
+ verify(code)
79
+ return code
80
+ except BytecodeError:
81
+ raise
82
+ except (ValueError, KeyError, TypeError, zlib.error) as exc:
83
+ raise BytecodeError("손상된 HBC 내용입니다.") from exc
84
+
85
+ def write(path: Path, code: CodeObject) -> None:
86
+ path.write_bytes(dumps(code))
87
+
88
+ def read(path: Path) -> CodeObject:
89
+ return loads(path.read_bytes())
90
+
91
+ def _verify_target(spec) -> None:
92
+ if isinstance(spec,str): return
93
+ if not isinstance(spec,dict): raise TypeError
94
+ kind=spec.get("kind")
95
+ if kind=="name":
96
+ if not isinstance(spec.get("name"),str) or spec.get("scope","local") not in ("local","global","nonlocal"): raise TypeError
97
+ elif kind=="starred": _verify_target(spec["target"])
98
+ elif kind=="sequence":
99
+ if not isinstance(spec.get("items"),list): raise TypeError
100
+ for item in spec["items"]: _verify_target(item)
101
+ elif kind=="attribute":
102
+ if not isinstance(spec.get("name"),str): raise TypeError
103
+ verify(CodeObject.from_dict(spec["object"]))
104
+ elif kind=="subscript":
105
+ verify(CodeObject.from_dict(spec["object"])); verify(CodeObject.from_dict(spec["index"]))
106
+ else: raise TypeError
107
+
108
+ def _verify_pattern(spec) -> None:
109
+ if not isinstance(spec,dict): raise TypeError
110
+ kind=spec.get("kind"); scopes=("local","global","nonlocal")
111
+ if kind in ("wildcard","literal","singleton"): return
112
+ if kind in ("capture","star"):
113
+ if not isinstance(spec.get("name"),str) or spec.get("scope","local") not in scopes: raise TypeError
114
+ return
115
+ if kind=="value":
116
+ if not isinstance(spec.get("path"),str): raise TypeError
117
+ return
118
+ if kind=="or" or kind=="sequence":
119
+ key="items"
120
+ if not isinstance(spec.get(key),list): raise TypeError
121
+ for item in spec[key]: _verify_pattern(item)
122
+ return
123
+ if kind=="as":
124
+ if not isinstance(spec.get("name"),str) or spec.get("scope","local") not in scopes: raise TypeError
125
+ _verify_pattern(spec["pattern"]); return
126
+ if kind=="mapping":
127
+ if spec.get("rest") is not None and not isinstance(spec["rest"],str): raise TypeError
128
+ if spec.get("rest_scope","local") not in scopes or not isinstance(spec.get("pairs"),list): raise TypeError
129
+ for pair in spec["pairs"]:
130
+ key=pair["key"]
131
+ if not isinstance(key,dict) or key.get("kind") not in ("literal","value"): raise TypeError
132
+ if key["kind"]=="value" and not isinstance(key.get("path"),str): raise TypeError
133
+ _verify_pattern(pair["pattern"])
134
+ return
135
+ if kind=="class":
136
+ if not isinstance(spec.get("name"),str) or not isinstance(spec.get("positional"),list) or not isinstance(spec.get("keywords"),list): raise TypeError
137
+ for item in spec["positional"]: _verify_pattern(item)
138
+ for item in spec["keywords"]:
139
+ if not isinstance(item.get("name"),str): raise TypeError
140
+ _verify_pattern(item["pattern"])
141
+ return
142
+ raise TypeError
143
+
144
+ def verify(code: CodeObject) -> None:
145
+ """Reject malformed programs before the VM executes any instruction."""
146
+ if not isinstance(code.name,str) or not isinstance(code.parameters,list) or not all(isinstance(x,str) for x in code.parameters):
147
+ raise BytecodeError("잘못된 HBC 코드 객체입니다.")
148
+ if len(code.instructions)>MAX_INSTRUCTIONS: raise BytecodeError("HBC 명령어 제한을 초과합니다.")
149
+ if code.lines and (len(code.lines)!=len(code.instructions) or not all(isinstance(line,int) and line>=0 for line in code.lines)):
150
+ raise BytecodeError("HBC 소스 위치 테이블 오류")
151
+ count=len(code.instructions)
152
+ for index, ins in enumerate(code.instructions):
153
+ if not isinstance(ins,list) or not ins or not isinstance(ins[0],str):
154
+ raise BytecodeError(f"잘못된 명령어: {index}")
155
+ op=ins[0]; args=ins[1:]
156
+ if op in _NO_ARG:
157
+ if args: raise BytecodeError(f"{op} 명령어 인자 오류")
158
+ elif op in _NAME_ARG:
159
+ if len(args)!=1 or not isinstance(args[0],str): raise BytecodeError(f"{op} 이름 인자 오류")
160
+ elif op in _INT_ARG:
161
+ if len(args)!=1 or not isinstance(args[0],int) or args[0]<0: raise BytecodeError(f"{op} 정수 인자 오류")
162
+ elif op=="CONST":
163
+ if len(args)!=1 or not isinstance(args[0],int) or not 0<=args[0]<len(code.constants): raise BytecodeError("CONST 인덱스 오류")
164
+ elif op in ("JUMP","JUMP_FALSE","FOR_ITER","JUMP_IF_FALSE_OR_POP","JUMP_IF_TRUE_OR_POP"):
165
+ if len(args)!=1 or not isinstance(args[0],int) or not 0<=args[0]<=count: raise BytecodeError(f"{op} 점프 범위 오류")
166
+ elif op=="MAKE_FUNCTION":
167
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("함수 코드 객체 오류")
168
+ try:
169
+ if not isinstance(args[0]["defaults"],list) or not all(isinstance(x,str) for x in args[0]["defaults"]): raise TypeError
170
+ if not isinstance(args[0].get("annotations",[]),list) or not all(isinstance(x,str) for x in args[0].get("annotations",[])): raise TypeError
171
+ if not isinstance(args[0].get("annotation_codes",{}),dict) or not all(isinstance(name,str) and isinstance(code,dict) for name,code in args[0].get("annotation_codes",{}).items()): raise TypeError
172
+ if not isinstance(args[0].get("annotation_strings",{}),dict) or not all(isinstance(name,str) and isinstance(text,str) for name,text in args[0].get("annotation_strings",{}).items()): raise TypeError
173
+ for annotation_code in args[0].get("annotation_codes",{}).values(): verify(CodeObject.from_dict(annotation_code))
174
+ if not isinstance(args[0].get("type_params",[]),list) or not all(isinstance(x,str) for x in args[0].get("type_params",[])): raise TypeError
175
+ if not isinstance(args[0].get("local_names",[]),list) or not all(isinstance(x,str) for x in args[0].get("local_names",[])): raise TypeError
176
+ if not isinstance(args[0].get("free_names",[]),list) or not all(isinstance(x,str) for x in args[0].get("free_names",[])): raise TypeError
177
+ signature=args[0]["signature"]
178
+ if not isinstance(args[0]["generator"],bool): raise TypeError
179
+ if not isinstance(args[0]["async"],bool): raise TypeError
180
+ if not isinstance(signature["positional"],list) or not isinstance(signature["keyword_only"],list): raise TypeError
181
+ if not isinstance(signature["positional_only"],list): raise TypeError
182
+ if not all(isinstance(x,str) for x in signature["positional"]+signature["positional_only"]+signature["keyword_only"]): raise TypeError
183
+ if signature["vararg"] is not None and not isinstance(signature["vararg"],str): raise TypeError
184
+ if signature["kwarg"] is not None and not isinstance(signature["kwarg"],str): raise TypeError
185
+ verify(CodeObject.from_dict(args[0]["code"]))
186
+ except (KeyError,TypeError) as exc: raise BytecodeError("함수 코드 객체 오류") from exc
187
+ elif op=="IMPORT_FROM":
188
+ if len(args)!=1 or not isinstance(args[0],dict) or not isinstance(args[0].get("module"),str) or not isinstance(args[0].get("name"),str):
189
+ raise BytecodeError("IMPORT_FROM 인자 오류")
190
+ elif op=="ANNOTATE_LAZY":
191
+ if len(args)!=1 or not isinstance(args[0],dict) or not isinstance(args[0].get("name"),str) or not isinstance(args[0].get("text",""),str) or not isinstance(args[0].get("code"),dict): raise BytecodeError("ANNOTATE_LAZY 인자 오류")
192
+ verify(CodeObject.from_dict(args[0]["code"]))
193
+ elif op=="MAKE_CLASS":
194
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("중첩 코드 객체 오류")
195
+ try:
196
+ if not isinstance(args[0]["bases"],int) or args[0]["bases"]<0: raise TypeError
197
+ if not isinstance(args[0].get("incremental_arguments",False),bool): raise TypeError
198
+ if not isinstance(args[0].get("base_starred",[False]*args[0]["bases"]),list) or len(args[0].get("base_starred",[False]*args[0]["bases"]))!=args[0]["bases"] or not all(isinstance(x,bool) for x in args[0].get("base_starred",[False]*args[0]["bases"])): raise TypeError
199
+ if not isinstance(args[0].get("keywords",[]),list) or not all(x is None or isinstance(x,str) for x in args[0].get("keywords",[])): raise TypeError
200
+ if not isinstance(args[0].get("type_params",[]),list) or not all(isinstance(x,str) for x in args[0].get("type_params",[])): raise TypeError
201
+ if not isinstance(args[0].get("local_names",[]),list) or not all(isinstance(x,str) for x in args[0].get("local_names",[])): raise TypeError
202
+ verify(CodeObject.from_dict(args[0]["code"]))
203
+ except (KeyError,TypeError) as exc: raise BytecodeError("중첩 코드 객체 오류") from exc
204
+ elif op in ("CALL_EX","CALL_ARG","CLASS_ARG"):
205
+ if len(args)!=1 or not isinstance(args[0],list): raise BytecodeError("CALL_EX 인자 오류")
206
+ descriptors=args[0] if op=="CALL_EX" else [args[0]]
207
+ for descriptor in descriptors:
208
+ allowed=("base","keyword","star","kwstar") if op=="CLASS_ARG" else ("positional","keyword","star","kwstar")
209
+ if not isinstance(descriptor,list) or len(descriptor)!=2 or descriptor[0] not in allowed: raise BytecodeError("CALL_EX 설명자 오류")
210
+ if descriptor[0]=="keyword" and not isinstance(descriptor[1],str): raise BytecodeError("CALL_EX 키워드 오류")
211
+ elif op=="BUILD_UNPACK":
212
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("BUILD_UNPACK 인자 오류")
213
+ try:
214
+ if args[0]["kind"] not in ("list","tuple","set"): raise TypeError
215
+ if not isinstance(args[0]["starred"],list) or not all(isinstance(x,bool) for x in args[0]["starred"]): raise TypeError
216
+ except (KeyError,TypeError) as exc: raise BytecodeError("BUILD_UNPACK 인자 오류") from exc
217
+ elif op=="BUILD_DICT_UNPACK":
218
+ if len(args)!=1 or not isinstance(args[0],list) or not all(item in ("pair","unpack") for item in args[0]): raise BytecodeError("BUILD_DICT_UNPACK 인자 오류")
219
+ elif op=="COLLECTION_BEGIN":
220
+ if len(args)!=1 or args[0] not in ("list","tuple","set","dict"): raise BytecodeError("COLLECTION_BEGIN 인자 오류")
221
+ elif op=="COLLECTION_ADD":
222
+ if len(args)!=1 or args[0] not in ("item","star","pair","unpack"): raise BytecodeError("COLLECTION_ADD 인자 오류")
223
+ elif op=="FORMAT_VALUE":
224
+ if len(args)!=1 or not isinstance(args[0],dict) or args[0].get("conversion") not in (None,"r","s","a") or not isinstance(args[0].get("has_spec"),bool): raise BytecodeError("FORMAT_VALUE 인자 오류")
225
+ elif op=="CHAIN_COMPARE":
226
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("CHAIN_COMPARE 인자 오류")
227
+ try:
228
+ valid={"==","!=","<","<=",">",">=","in","is","not in","is not"}
229
+ if not args[0]["operators"] or not all(item in valid for item in args[0]["operators"]): raise TypeError
230
+ if len(args[0]["operands"])!=len(args[0]["operators"])+1: raise TypeError
231
+ for item in args[0]["operands"]: verify(CodeObject.from_dict(item))
232
+ except (KeyError,TypeError) as exc: raise BytecodeError("CHAIN_COMPARE 인자 오류") from exc
233
+ elif op=="MAKE_TYPE_ALIAS":
234
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("MAKE_TYPE_ALIAS 인자 오류")
235
+ try:
236
+ if not isinstance(args[0]["name"],str): raise TypeError
237
+ if args[0].get("value_text") is not None and not isinstance(args[0]["value_text"],str): raise TypeError
238
+ if not isinstance(args[0]["parameters"],list): raise TypeError
239
+ for item in args[0]["parameters"]:
240
+ if isinstance(item,(list,tuple)) and len(item)==2 and item[0] in ("typevar","typevartuple","paramspec") and isinstance(item[1],str): continue
241
+ if not isinstance(item,dict) or item.get("kind") not in ("typevar","typevartuple","paramspec") or not isinstance(item.get("name"),str): raise TypeError
242
+ for key in ("bound","default"):
243
+ if item.get(key) is not None: verify(CodeObject.from_dict(item[key]))
244
+ for key in ("bound_text","default_text"):
245
+ if item.get(key) is not None and not isinstance(item[key],str): raise TypeError
246
+ verify(CodeObject.from_dict(args[0]["value"]))
247
+ except (KeyError,TypeError) as exc: raise BytecodeError("MAKE_TYPE_ALIAS 인자 오류") from exc
248
+ elif op=="MAKE_TYPE_PARAMETER":
249
+ if len(args)!=1 or not isinstance(args[0],dict) or args[0].get("kind") not in ("typevar","typevartuple","paramspec") or not isinstance(args[0].get("name"),str): raise BytecodeError("MAKE_TYPE_PARAMETER 인자 오류")
250
+ for key in ("bound","default"):
251
+ if args[0].get(key) is not None: verify(CodeObject.from_dict(args[0][key]))
252
+ for key in ("bound_text","default_text"):
253
+ if args[0].get(key) is not None and not isinstance(args[0][key],str): raise BytecodeError("MAKE_TYPE_PARAMETER 인자 오류")
254
+ elif op=="MATCH_PATTERN":
255
+ if len(args)!=1: raise BytecodeError("MATCH_PATTERN 인자 오류")
256
+ try: _verify_pattern(args[0])
257
+ except (KeyError,TypeError) as exc: raise BytecodeError("MATCH_PATTERN 인자 오류") from exc
258
+ elif op=="TRY":
259
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("TRY 코드 객체 오류")
260
+ payload=args[0]
261
+ try:
262
+ verify(CodeObject.from_dict(payload["body"]))
263
+ for handler in payload["handlers"]:
264
+ if handler["type"] is not None:
265
+ if not isinstance(handler["type"],dict): raise TypeError
266
+ verify(CodeObject.from_dict(handler["type"]))
267
+ if handler["alias"] is not None and not isinstance(handler["alias"],str): raise TypeError
268
+ if handler.get("alias_scope","local") not in ("local","global","nonlocal"): raise TypeError
269
+ if not isinstance(handler.get("star",False),bool): raise TypeError
270
+ verify(CodeObject.from_dict(handler["code"]))
271
+ for key in ("else","finally"):
272
+ if payload[key] is not None: verify(CodeObject.from_dict(payload[key]))
273
+ for key in ("break_target","continue_target"):
274
+ if payload[key] is not None and (not isinstance(payload[key],int) or not 0<=payload[key]<=len(code.instructions)): raise TypeError
275
+ except (KeyError,TypeError) as exc: raise BytecodeError("TRY 코드 객체 오류") from exc
276
+ elif op=="COMPREHENSION":
277
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("컴프리헨션 코드 객체 오류")
278
+ payload=args[0]
279
+ try:
280
+ if payload["kind"] not in ("listcomp","setcomp","dictcomp","generatorexpr") or not payload["clauses"]: raise TypeError
281
+ if not isinstance(payload.get("bindings",[]),list) or not all(isinstance(name,str) for name in payload.get("bindings",[])): raise TypeError
282
+ for clause in payload["clauses"]:
283
+ _verify_target(clause["target"])
284
+ if not isinstance(clause.get("async",False),bool): raise TypeError
285
+ verify(CodeObject.from_dict(clause["iter"]))
286
+ for item in clause["filters"]: verify(CodeObject.from_dict(item))
287
+ keys=("key","value") if payload["kind"]=="dictcomp" else ("element",)
288
+ for key in keys: verify(CodeObject.from_dict(payload[key]))
289
+ except (KeyError,TypeError) as exc: raise BytecodeError("컴프리헨션 코드 객체 오류") from exc
290
+ elif op=="WITH":
291
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError("WITH 코드 객체 오류")
292
+ try:
293
+ verify(CodeObject.from_dict(args[0]["body"]))
294
+ for manager in args[0]["managers"]:
295
+ if manager["alias"] is not None: _verify_target(manager["alias"])
296
+ verify(CodeObject.from_dict(manager["code"]))
297
+ except (KeyError,TypeError) as exc: raise BytecodeError("WITH 코드 객체 오류") from exc
298
+ elif op in ("ASYNC_FOR","ASYNC_WITH"):
299
+ if len(args)!=1 or not isinstance(args[0],dict): raise BytecodeError(f"{op} 코드 객체 오류")
300
+ try:
301
+ if op=="ASYNC_FOR":
302
+ _verify_target(args[0]["target"])
303
+ verify(CodeObject.from_dict(args[0]["iter"])); verify(CodeObject.from_dict(args[0]["body"]))
304
+ if args[0].get("else") is not None: verify(CodeObject.from_dict(args[0]["else"]))
305
+ else:
306
+ verify(CodeObject.from_dict(args[0]["body"]))
307
+ for manager in args[0]["managers"]:
308
+ if manager.get("alias") is not None: _verify_target(manager["alias"])
309
+ verify(CodeObject.from_dict(manager["code"]))
310
+ except (KeyError,TypeError) as exc: raise BytecodeError(f"{op} 코드 객체 오류") from exc
311
+ else: raise BytecodeError(f"허용되지 않은 HBC 명령어: {op}")
312
+ _verify_stack(code)
313
+
314
+ def _verify_stack(code: CodeObject) -> None:
315
+ """Follow every reachable branch and reject stack underflow/inconsistent merges."""
316
+ instructions=code.instructions; count=len(instructions)
317
+ pending=[(0,0)]; seen: dict[int,int]={}
318
+ simple_effect={
319
+ "CONST":1,"LOAD":1,"IMPORT":1,"IMPORT_FROM":1,"IMPORT_STAR":-1,"MAKE_TYPE_ALIAS":1,"MAKE_TYPE_PARAMETER":1,"SUPER":1,
320
+ "STORE":-1,"STORE_GLOBAL":-1,"STORE_NONLOCAL":-1,"ANNOTATE":-1,"DELETE":0,"DELETE_GLOBAL":0,"DELETE_NONLOCAL":0,"SAVE_NAME":0,"RESTORE_NAME":0,"POP":-1,"ITER":0,"NOP":0,"FORMAT":0,"GET_ATTR":0,
321
+ "SET_ATTR":-2,"DELETE_ATTR":-1,"GET_ITEM":-1,"SET_ITEM":-3,"DELETE_ITEM":-2,"ASSERT":-2,"DUP":1,"DUP2":2,"BUILD_SLICE":-2,
322
+ "NEG":0,"POS":0,"NOT":0,"INVERT":0,
323
+ "ADD":-1,"SUB":-1,"MUL":-1,"DIV":-1,"FLOORDIV":-1,"MOD":-1,"POW":-1,
324
+ "IADD":-1,"ISUB":-1,"IMUL":-1,"IDIV":-1,"IFLOORDIV":-1,"IMOD":-1,"IPOW":-1,
325
+ "EQ":-1,"NE":-1,"LT":-1,"LE":-1,"GT":-1,"GE":-1,"IN":-1,"IS":-1,"NOT_IN":-1,"IS_NOT":-1,
326
+ "BIT_OR":-1,"BIT_XOR":-1,"BIT_AND":-1,"LSHIFT":-1,"RSHIFT":-1,"MATMUL":-1,
327
+ "IBIT_OR":-1,"IBIT_XOR":-1,"IBIT_AND":-1,"ILSHIFT":-1,"IRSHIFT":-1,"IMATMUL":-1,
328
+ "BOOL_AND":-1,"BOOL_OR":-1,
329
+ "RAISE":-1,"RAISE_FROM":-2,"RERAISE":0,"SIGNAL_RETURN":-1,"SIGNAL_BREAK":0,"SIGNAL_CONTINUE":0,"YIELD":0,"YIELD_FROM":0,"AWAIT":0,"MATCH_PATTERN":0,"TRY":0,"COMPREHENSION":1,"WITH":0,"ASYNC_FOR":0,"ASYNC_WITH":0,"MAKE_INTERPOLATION":-3,"ANNOTATE_LAZY":0,"CALL_BEGIN":1,"CALL_ARG":-1,"CALL_READY":-1,"CLASS_BEGIN":1,"CLASS_ARG":-1,"COLLECTION_BEGIN":1,"COLLECTION_READY":0,
330
+ }
331
+ while pending:
332
+ ip,depth=pending.pop()
333
+ if ip==count: continue
334
+ previous=seen.get(ip)
335
+ if previous is not None:
336
+ if previous!=depth: raise BytecodeError(f"명령어 {ip}의 스택 깊이가 일치하지 않습니다.")
337
+ continue
338
+ seen[ip]=depth; ins=instructions[ip]; op=ins[0]; arg=ins[1] if len(ins)>1 else None
339
+ if op=="RETURN":
340
+ if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
341
+ continue
342
+ if op=="JUMP": pending.append((arg,depth)); continue
343
+ if op=="JUMP_FALSE":
344
+ if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
345
+ pending.extend(((arg,depth-1),(ip+1,depth-1))); continue
346
+ if op in ("JUMP_IF_FALSE_OR_POP","JUMP_IF_TRUE_OR_POP"):
347
+ if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
348
+ pending.extend(((arg,depth),(ip+1,depth-1))); continue
349
+ if op=="FOR_ITER":
350
+ if depth<1: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
351
+ pending.extend(((arg,depth-1),(ip+1,depth+1))); continue
352
+ if op=="CALL": effect=-arg
353
+ elif op=="UNPACK": effect=arg-1
354
+ elif op=="UNPACK_EX": effect=((arg>>16)+(arg&0xFFFF)+1)-1
355
+ elif op=="CALL_EX": effect=-len(arg)
356
+ elif op=="MAKE_FUNCTION": effect=1-len(arg["defaults"])-len(arg.get("annotations",[]))-len(arg.get("type_params",[]))
357
+ elif op=="MAKE_CLASS": effect=-len(arg.get("type_params",[])) if arg.get("incremental_arguments") else 1-arg["bases"]-len(arg.get("keywords",[]))-len(arg.get("type_params",[]))
358
+ elif op in ("BUILD_LIST","BUILD_TUPLE","BUILD_SET","BUILD_STRING","BUILD_TEMPLATE"): effect=1-arg
359
+ elif op=="BUILD_DICT": effect=1-(arg*2)
360
+ elif op=="BUILD_UNPACK": effect=1-len(arg["starred"])
361
+ elif op=="BUILD_DICT_UNPACK": effect=1-sum(2 if item=="pair" else 1 for item in arg)
362
+ elif op=="COLLECTION_ADD": effect=-2 if arg=="pair" else -1
363
+ elif op=="FORMAT_VALUE": effect=-1 if arg["has_spec"] else 0
364
+ elif op=="CHAIN_COMPARE": effect=1
365
+ else: effect=simple_effect[op]
366
+ new_depth=depth+effect
367
+ if new_depth<0: raise BytecodeError(f"명령어 {ip}에서 스택 언더플로")
368
+ pending.append((ip+1,new_depth))