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/frontend.py ADDED
@@ -0,0 +1,870 @@
1
+ """Hython-owned AST and parser for the native compiler path."""
2
+ from __future__ import annotations
3
+ from dataclasses import dataclass, field
4
+ import io
5
+ import token as token_types
6
+ import tokenize
7
+ from .package_manager import load_dictionaries
8
+ from .translator import normalize_literal_prefixes
9
+ from .vocabulary import HYTHON_TO_PYTHON
10
+
11
+ @dataclass
12
+ class Node:
13
+ kind: str
14
+ value: object = None
15
+ children: list["Node"] = field(default_factory=list)
16
+ line: int = 0
17
+ grouped: bool = False
18
+
19
+ class ParseError(SyntaxError): pass
20
+
21
+ def lex(source: str) -> list[tuple[int,str,int,int,int]]:
22
+ source = normalize_literal_prefixes(source)
23
+ names = load_dictionaries() | HYTHON_TO_PYTHON
24
+ result=[]; line_offsets=[]; offset=0
25
+ for source_line in source.splitlines(keepends=True): line_offsets.append(offset); offset+=len(source_line)
26
+ line_offsets.append(offset)
27
+ def absolute(position):
28
+ row,column=position
29
+ return line_offsets[min(row-1,len(line_offsets)-1)]+column
30
+ try: stream=list(tokenize.generate_tokens(io.StringIO(source).readline))
31
+ except Exception as exc: raise ParseError(str(exc)) from exc
32
+ aliases={stream[index+1].string for index,item in enumerate(stream[:-1]) if item.type==tokenize.NAME and item.string in ("as","애즈") and stream[index+1].type==tokenize.NAME}
33
+ try:
34
+ for index,item in enumerate(stream):
35
+ if item.type in (tokenize.ENCODING, tokenize.COMMENT, tokenize.NL): continue
36
+ alias_reference=item.string in aliases and (index==0 or stream[index-1].string!=".")
37
+ text=names.get(item.string,item.string) if item.type==tokenize.NAME and not alias_reference else item.string
38
+ result.append((item.type,text,item.start[0],absolute(item.start),absolute(item.end)))
39
+ except (tokenize.TokenError,IndentationError) as exc: raise ParseError(str(exc)) from exc
40
+ return result
41
+
42
+ class Parser:
43
+ def __init__(self, source: str, filename: str = "<하이썬>"):
44
+ self.source=source; self.filename=filename; self.tokens=lex(source); self.pos=0; self.future_features=set()
45
+ for index,token in enumerate(self.tokens):
46
+ if token[1]=="barry_as_FLUFL" and index>0 and self.tokens[index-1][1] in ("import","(",","):
47
+ statement_start=max((i for i in range(index) if self.tokens[i][0]==tokenize.NEWLINE),default=-1)+1
48
+ words=[item[1] for item in self.tokens[statement_start:index]]
49
+ if len(words)>=3 and words[0:3]==["from","__future__","import"]: self.future_features.add("barry_as_FLUFL")
50
+ def current(self): return self.tokens[self.pos] if self.pos<len(self.tokens) else (tokenize.ENDMARKER,"",0,len(self.source),len(self.source))
51
+ def text(self): return self.current()[1]
52
+ def take(self, text=None):
53
+ token=self.current()
54
+ if text is not None and token[1]!=text: raise ParseError(f"'{text}' 필요 (줄 {token[2]})")
55
+ self.pos+=1; return token
56
+ @staticmethod
57
+ def require_grouped_namedexpr(node:Node,line:int,context:str):
58
+ items=node.children if node.kind=="tuple" else [node]
59
+ if any(item.kind=="namedexpr" and not item.grouped for item in items): raise ParseError(f"{context}의 명명식에는 괄호가 필요합니다 (줄 {line})")
60
+ @staticmethod
61
+ def reject_namedexpr(node:Node,line:int,context:str):
62
+ if Parser._contains_kind(node,"namedexpr"): raise ParseError(f"{context}에는 명명식을 사용할 수 없습니다 (줄 {line})")
63
+ @staticmethod
64
+ def validate_unpack_target(node:Node,line:int,star_allowed:bool=False):
65
+ if node.kind=="starred":
66
+ if not star_allowed: raise ParseError(f"별표 대입 대상은 튜플이나 리스트 안에 있어야 합니다 (줄 {line})")
67
+ Parser.validate_unpack_target(node.children[0],line,False); return
68
+ if node.kind in ("tuple","list"):
69
+ if sum(child.kind=="starred" for child in node.children)>1: raise ParseError(f"구조 분해 별표 대상은 한 수준에 하나만 허용됩니다 (줄 {line})")
70
+ for child in node.children: Parser.validate_unpack_target(child,line,True)
71
+ return
72
+ if node.kind not in ("name","attribute","subscript"): raise ParseError(f"잘못된 대입 대상 (줄 {line})")
73
+ @staticmethod
74
+ def validate_delete_target(node:Node,line:int):
75
+ if node.kind in ("tuple","list"):
76
+ for child in node.children: Parser.validate_delete_target(child,line)
77
+ return
78
+ if node.kind not in ("name","attribute","subscript"): raise ParseError(f"잘못된 델 대상 (줄 {line})")
79
+ @staticmethod
80
+ def _contains_kind(node:Node,kind:str) -> bool:
81
+ if node.kind in ("lambda","def","class"): return False
82
+ return node.kind==kind or any(Parser._contains_kind(child,kind) for child in node.children)
83
+ def skip_newlines(self):
84
+ while self.current()[0] in (tokenize.NEWLINE,): self.pos+=1
85
+ def parse(self):
86
+ body=[]; self.skip_newlines()
87
+ while self.current()[0]!=tokenize.ENDMARKER:
88
+ body.append(self.statement()); self.skip_newlines()
89
+ return Node("module",children=body)
90
+ def suite(self):
91
+ self.take(":")
92
+ if self.current()[0]!=tokenize.NEWLINE:
93
+ if self.text() in (";","@","def","async","class","if","while","try","for","with","match"):
94
+ raise ParseError(f"한 줄 suite에는 단순 문장만 사용할 수 있습니다 (줄 {self.current()[2]})")
95
+ line=self.current()[2]; body=[self.statement()]
96
+ while self.current()[0] not in (tokenize.NEWLINE,tokenize.ENDMARKER) and self.current()[2]==line: body.append(self.statement())
97
+ return body
98
+ self.take_newline(); self.expect_type(tokenize.INDENT)
99
+ body=[]; self.skip_newlines()
100
+ while self.current()[0] not in (tokenize.DEDENT,tokenize.ENDMARKER):
101
+ body.append(self.statement()); self.skip_newlines()
102
+ self.expect_type(tokenize.DEDENT); return body
103
+ def take_newline(self):
104
+ if self.text()==";": self.pos+=1; return
105
+ if self.current()[0]!=tokenize.NEWLINE: raise ParseError(f"줄 끝 필요 (줄 {self.current()[2]})")
106
+ self.pos+=1
107
+ def expect_type(self, kind):
108
+ if self.current()[0]!=kind: raise ParseError(f"잘못된 들여쓰기 (줄 {self.current()[2]})")
109
+ self.pos+=1
110
+ def has_suite_colon(self):
111
+ depth=0
112
+ for token in self.tokens[self.pos:]:
113
+ if token[0] in (tokenize.NEWLINE,tokenize.ENDMARKER): return False
114
+ if token[1] in ("(","[","{"): depth+=1
115
+ elif token[1] in (")","]","}"): depth=max(0,depth-1)
116
+ elif token[1]==":" and depth==0: return True
117
+ return False
118
+ def dotted_name(self,allow_relative=False):
119
+ dots=0
120
+ if allow_relative:
121
+ while self.text() in (".","..."):
122
+ marker=self.take()[1]; dots+=len(marker)
123
+ if dots and self.text()=="import": return "."*dots
124
+ parts=[self.take()[1]]
125
+ while self.text()==".": self.take("."); parts.append(self.take()[1])
126
+ return "."*dots+".".join(parts)
127
+ def statement(self):
128
+ line=self.current()[2]; word=self.text()
129
+ if word==";": raise ParseError(f"세미콜론 앞에는 단순 문장이 필요합니다 (줄 {line})")
130
+ if word in ("elif","else","except","finally"):
131
+ raise ParseError(f"앞선 복합문에 연결되지 않은 {word} 절입니다 (줄 {line})")
132
+ if word=="@":
133
+ decorators=[]
134
+ while self.text()=="@":
135
+ self.take("@"); decorators.append(self.expression()); self.take_newline()
136
+ target=self.statement()
137
+ if target.kind not in ("def","class"): raise ParseError(f"데코레이터 뒤에는 함수나 클래스가 필요합니다 (줄 {line})")
138
+ return Node("decorated",target.value,[*decorators,target],line)
139
+ if word=="import":
140
+ self.take(); imports=[]
141
+ while True:
142
+ if self.current()[0]!=tokenize.NAME: raise ParseError(f"import에는 모듈 이름이 필요합니다 (줄 {line})")
143
+ module=self.dotted_name(); alias=module.split(".")[0]
144
+ if self.text()=="as": self.take(); alias=self.take()[1]
145
+ imports.append((module,alias))
146
+ if self.text()!=",": break
147
+ self.take(",")
148
+ self.take_newline(); return Node("imports",imports,line=line)
149
+ if word=="from":
150
+ self.take(); module=self.dotted_name(True); self.take("import"); names=[]
151
+ parenthesized=self.text()=="("
152
+ if parenthesized: self.take("(")
153
+ while True:
154
+ if self.text()=="*":
155
+ if parenthesized: raise ParseError(f"별표 import는 괄호 안에 둘 수 없습니다 (줄 {line})")
156
+ self.take("*"); names.append(("*","*"))
157
+ if self.current()[0]!=tokenize.NEWLINE: raise ParseError(f"별표 import는 별칭이나 다른 이름과 섞을 수 없습니다 (줄 {line})")
158
+ break
159
+ name=self.take()[1]; alias=name
160
+ if self.text()=="as": self.take(); alias=self.take()[1]
161
+ names.append((name,alias))
162
+ if self.text()!=",": break
163
+ self.take(",")
164
+ if parenthesized and self.text()==")": break
165
+ if parenthesized: self.take(")")
166
+ self.take_newline(); return Node("fromimport",(module,names),line=line)
167
+ if word=="global":
168
+ self.take(); names=[]
169
+ while True:
170
+ names.append(self.take()[1])
171
+ if self.text()!=",": break
172
+ self.take(",")
173
+ self.take_newline(); return Node("global",names,line=line)
174
+ if word=="nonlocal":
175
+ self.take(); names=[]
176
+ while True:
177
+ names.append(self.take()[1])
178
+ if self.text()!=",": break
179
+ self.take(",")
180
+ self.take_newline(); return Node("nonlocal",names,line=line)
181
+ if word=="del":
182
+ self.take(); targets=[]
183
+ while True:
184
+ target=self.expression()
185
+ self.validate_delete_target(target,line)
186
+ targets.append(target)
187
+ if self.text()!=",": break
188
+ self.take(",")
189
+ if self.current()[0]==tokenize.NEWLINE: break
190
+ self.take_newline(); return Node("dels",children=targets,line=line)
191
+ if word=="assert":
192
+ self.take(); condition=self.expression(); message=Node("constant",None,line=line)
193
+ self.require_grouped_namedexpr(condition,line,"assert")
194
+ if self.text()==",": self.take(","); message=self.expression()
195
+ self.take_newline(); return Node("assert",children=[condition,message],line=line)
196
+ if word=="type" and self.pos+1<len(self.tokens) and self.tokens[self.pos+1][0]==tokenize.NAME:
197
+ self.take(); name=self.take()[1]; parameters=self.parse_type_parameters()
198
+ self.take("="); start=self.pos; value=self.expression(); self.reject_namedexpr(value,line,"타입 별칭"); value_text="".join(token[1] for token in self.tokens[start:self.pos]); self.take_newline()
199
+ return Node("type_alias",(name,parameters,value_text),[value],line)
200
+ if word=="def": self.take(); return self.function_statement(line,False)
201
+ if word=="async":
202
+ self.take()
203
+ if self.text()=="def": self.take("def"); return self.function_statement(line,True)
204
+ if self.text()=="for":
205
+ self.take("for"); target=self.loop_target(); self.take("in"); iterable=self.for_iterable_list()
206
+ self.require_grouped_namedexpr(iterable,line,"async for iterable")
207
+ body=self.suite(); other=[]
208
+ if self.text()=="else": self.take(); other=self.suite()
209
+ return Node("async_for",target,[iterable,Node("body",children=body),Node("body",children=other)],line)
210
+ if self.text()=="with":
211
+ self.take("with"); managers=self.parse_managers(line)
212
+ return Node("async_with",None,[Node("managers",children=managers),Node("body",children=self.suite())],line)
213
+ raise ParseError(f"어싱크 뒤에는 데프, 포 또는 위드가 필요합니다 (줄 {line})")
214
+ if word=="class":
215
+ self.take(); name=self.take()[1]; type_parameters=self.parse_type_parameters(); bases=[]; keywords=[]; arguments=[]; saw_keyword=False; saw_kwstar=False
216
+ if self.text()=="(":
217
+ self.take("(")
218
+ if self.text()!=")":
219
+ while True:
220
+ if self.text()=="*":
221
+ if saw_kwstar: raise ParseError(f"클래스 ** 키워드 unpack 뒤에는 * base를 둘 수 없습니다 (줄 {line})")
222
+ self.take("*"); value=self.expression(); bases.append(Node("starred",children=[value],line=line)); arguments.append(("star",None,value))
223
+ elif self.text()=="**":
224
+ self.take("**"); value=self.expression(); keywords.append((None,value)); arguments.append(("kwstar",None,value)); saw_keyword=True; saw_kwstar=True
225
+ elif self.current()[0]==tokenize.NAME and self.pos+1<len(self.tokens) and self.tokens[self.pos+1][1]=="=":
226
+ keyword=self.take()[1]
227
+ if any(existing==keyword for existing,_ in keywords): raise ParseError(f"클래스 키워드 인자 중복: {keyword} (줄 {line})")
228
+ self.take("="); value=self.expression(); keywords.append((keyword,value)); arguments.append(("keyword",keyword,value)); saw_keyword=True
229
+ else:
230
+ if saw_keyword: raise ParseError(f"클래스 키워드 인자 뒤에는 일반 base를 둘 수 없습니다 (줄 {line})")
231
+ value=self.expression(); bases.append(value); arguments.append(("base",None,value))
232
+ if self.text()!=",": break
233
+ self.take(",")
234
+ if self.text()==")": break
235
+ self.take(")")
236
+ return Node("class",(name,bases,type_parameters,keywords,arguments),self.suite(),line)
237
+ if word=="if":
238
+ self.take(); return self._if_statement(line)
239
+ if word=="while":
240
+ self.take(); test=self.expression(); body=self.suite(); other=[]
241
+ if self.text()=="else": self.take(); other=self.suite()
242
+ return Node("while",None,[test,Node("body",children=body),Node("body",children=other)],line)
243
+ if word=="try":
244
+ self.take(); body=self.suite(); handlers=[]; otherwise=[]; final=[]
245
+ while self.text()=="except":
246
+ handler_line=self.current()[2]; self.take(); is_star=False
247
+ if self.text()=="*": self.take("*"); is_star=True
248
+ type_expression=None; alias=None
249
+ if self.text()!=":":
250
+ types=[self.expression()]
251
+ while self.text()==",": self.take(","); types.append(self.expression())
252
+ type_expression=Node("tuple",children=types,line=handler_line) if len(types)>1 else types[0]
253
+ if self.text()=="as":
254
+ if len(types)>1: raise ParseError(f"괄호 없는 다중 예외 타입에는 as를 사용할 수 없습니다 (줄 {handler_line})")
255
+ self.take(); alias=self.take()[1]
256
+ elif is_star: raise ParseError(f"익셉트*에는 예외 타입이 필요합니다 (줄 {handler_line})")
257
+ handlers.append(Node("except",(type_expression,alias,is_star),self.suite(),handler_line))
258
+ if self.text()=="else":
259
+ if not handlers: raise ParseError(f"익셉트 없는 트라이에는 엘스를 사용할 수 없습니다 (줄 {line})")
260
+ self.take(); otherwise=self.suite()
261
+ if self.text()=="finally": self.take(); final=self.suite()
262
+ if not handlers and not final: raise ParseError(f"트라이는 익셉트 또는 파이널리가 필요합니다 (줄 {line})")
263
+ if any(handler.value[0] is None for handler in handlers[:-1]): raise ParseError(f"타입 없는 익셉트는 마지막이어야 합니다 (줄 {line})")
264
+ if handlers and any(item.value[2] for item in handlers) and not all(item.value[2] for item in handlers): raise ParseError(f"익셉트와 익셉트*는 같은 트라이에서 섞을 수 없습니다 (줄 {line})")
265
+ return Node("try",None,[Node("body",children=body),Node("handlers",children=handlers),Node("body",children=otherwise),Node("body",children=final)],line)
266
+ if word=="with":
267
+ self.take(); managers=self.parse_managers(line)
268
+ return Node("with",None,[Node("managers",children=managers),Node("body",children=self.suite())],line)
269
+ if word=="match" and self.pos+1<len(self.tokens) and self.tokens[self.pos+1][1] not in ("=",":",".",":=","+=","-=","*=","/=","|=","&=","^=") and self.has_suite_colon():
270
+ self.take(); subject=self.for_iterable_list(); self.take(":"); self.take_newline(); self.expect_type(tokenize.INDENT)
271
+ cases=[]; self.skip_newlines()
272
+ while self.text()=="case":
273
+ case_line=self.current()[2]; self.take(); pattern=self.pattern(True); guard=None
274
+ if self.text()=="if": self.take(); guard=self.expression()
275
+ body=self.suite(); cases.append(Node("case",guard,[pattern,Node("body",children=body)],case_line)); self.skip_newlines()
276
+ self.expect_type(tokenize.DEDENT)
277
+ if not cases: raise ParseError(f"매치에는 케이스가 필요합니다 (줄 {line})")
278
+ return Node("match",children=[subject,*cases],line=line)
279
+ if word=="for":
280
+ self.take(); target=self.loop_target(); self.take("in"); iterable=self.for_iterable_list()
281
+ self.require_grouped_namedexpr(iterable,line,"for iterable")
282
+ body=self.suite(); other=[]
283
+ if self.text()=="else": self.take(); other=self.suite()
284
+ return Node("for",target,[iterable,Node("body",children=body),Node("body",children=other)],line)
285
+ if word=="return":
286
+ self.take(); has_value=self.current()[0]!=tokenize.NEWLINE
287
+ value=self.expression_list() if has_value else Node("constant",None,line=line)
288
+ if has_value: self.require_grouped_namedexpr(value,line,"return")
289
+ self.take_newline(); return Node("return",has_value,children=[value],line=line)
290
+ if word=="yield":
291
+ self.take(); delegated=False
292
+ if self.text()=="from": self.take(); delegated=True
293
+ if delegated and self.current()[0]==tokenize.NEWLINE: raise ParseError(f"yield from에는 피연산자가 필요합니다 (줄 {line})")
294
+ value=Node("constant",None,line=line) if self.current()[0]==tokenize.NEWLINE else self.expression() if delegated else self.expression_list()
295
+ self.require_grouped_namedexpr(value,line,"yield")
296
+ self.take_newline(); return Node("yield_from" if delegated else "yield",children=[value],line=line)
297
+ if word=="raise":
298
+ self.take()
299
+ if self.current()[0]==tokenize.NEWLINE:
300
+ self.take_newline(); return Node("reraise",line=line)
301
+ value=self.expression(); cause=None
302
+ self.require_grouped_namedexpr(value,line,"raise")
303
+ if self.text()=="from": self.take(); cause=self.expression()
304
+ if cause is not None: self.require_grouped_namedexpr(cause,line,"raise from")
305
+ self.take_newline(); return Node("raise",children=[value,*([cause] if cause else [])],line=line)
306
+ if word in ("break","continue"):
307
+ self.take(); self.take_newline(); return Node(word,line=line)
308
+ if word=="pass": self.take(); self.take_newline(); return Node("pass",line=line)
309
+ expression=self.expression()
310
+ if self.text()==":" and expression.kind in ("name","attribute","subscript"):
311
+ self.take(":"); annotation_start=self.pos; annotation=self.expression(1); annotation_text="".join(token[1] for token in self.tokens[annotation_start:self.pos]); value=None
312
+ self.reject_namedexpr(annotation,line,"주석")
313
+ if self.text()=="=": self.take("="); value=self.expression_list()
314
+ if value is not None: self.require_grouped_namedexpr(value,line,"주석 대입")
315
+ self.take_newline(); return Node("annassign",annotation_text,[expression,annotation,*([value] if value else [])],line)
316
+ if self.text()==",":
317
+ values=[expression]
318
+ while self.text()==",":
319
+ self.take(",")
320
+ if self.text()=="=": break
321
+ values.append(self.expression())
322
+ expression=Node("tuple",children=values,line=line)
323
+ if self.text() in ("+=","-=","*=","/=","//=","%=","**=","|=","&=","^=","<<=",">>=","@="):
324
+ if expression.kind not in ("name","attribute","subscript"): raise ParseError(f"잘못된 복합 대입 대상 (줄 {line})")
325
+ op=self.take()[1][:-1]; value=self.expression_list(); self.take_newline()
326
+ self.require_grouped_namedexpr(value,line,"복합 대입")
327
+ return Node("augassign",op,[expression,value],line)
328
+ if self.text()=="=":
329
+ if expression.kind not in ("name","attribute","subscript","tuple","list"): raise ParseError(f"잘못된 대입 대상 (줄 {line})")
330
+ targets=[expression]; self.take("="); value=self.expression_list()
331
+ self.require_grouped_namedexpr(value,line,"대입")
332
+ while self.text()=="=":
333
+ if value.kind not in ("name","attribute","subscript"): raise ParseError(f"잘못된 연쇄 대입 대상 (줄 {line})")
334
+ targets.append(value); self.take("="); value=self.expression_list()
335
+ self.take_newline()
336
+ return Node("assign_chain",children=[*targets,value],line=line) if len(targets)>1 else Node("assign",children=[expression,value],line=line)
337
+ self.require_grouped_namedexpr(expression,line,"표현식 문장")
338
+ self.take_newline(); return Node("expr",children=[expression],line=line)
339
+
340
+ def expression_list(self):
341
+ line=self.current()[2]; values=[self.expression()]; had_comma=False
342
+ while self.text()==",":
343
+ self.take(","); had_comma=True
344
+ if self.current()[0]==tokenize.NEWLINE or self.text() in (")","]","}",":"): break
345
+ values.append(self.expression())
346
+ return Node("tuple",children=values,line=line) if had_comma else values[0]
347
+
348
+ def for_iterable_list(self):
349
+ line=self.current()[2]; values=[self.expression()]; had_comma=False
350
+ while self.text()==",":
351
+ self.take(","); had_comma=True
352
+ if self.text()==":": break
353
+ values.append(self.expression())
354
+ return Node("tuple",children=values,line=line) if had_comma else values[0]
355
+
356
+ def parse_managers(self,line):
357
+ managers=[]; parenthesized=self.text()=="("
358
+ if parenthesized: self.take("(")
359
+ if parenthesized and self.text()==")": self.take(")"); return managers
360
+ while True:
361
+ expression=self.expression(); target=None
362
+ if not parenthesized: self.require_grouped_namedexpr(expression,line,"with")
363
+ if self.text()=="as": self.take(); target=self.expression(); self.validate_unpack_target(target,line)
364
+ managers.append(Node("manager",None,[expression] if target is None else [expression,target],line))
365
+ if self.text()!=",": break
366
+ self.take(",")
367
+ if parenthesized and self.text()==")": break
368
+ if parenthesized: self.take(")")
369
+ return managers
370
+
371
+ def _if_statement(self,line):
372
+ test=self.expression(); body=self.suite(); other=[]
373
+ if self.text()=="elif":
374
+ self.take(); other=[self._if_statement(self.current()[2])]
375
+ elif self.text()=="else":
376
+ self.take(); other=self.suite()
377
+ return Node("if",None,[test,Node("body",children=body),Node("body",children=other)],line)
378
+
379
+ def function_statement(self,line,is_async):
380
+ name=self.take()[1]; type_parameters=self.parse_type_parameters(); self.take("("); params=[]; posonly=[]; kwonly=[]; defaults={}; annotations={}; saw_default=False; vararg=None; kwarg=None; keyword_mode=False
381
+ if self.text()!=")":
382
+ while True:
383
+ if self.text()=="*":
384
+ self.take("*"); keyword_mode=True
385
+ if self.text() not in (",",")"):
386
+ vararg=self.take()[1]
387
+ if self.text()==":":
388
+ self.take(":"); start=self.pos; annotation=self.expression(1); self.reject_namedexpr(annotation,line,"함수 주석"); annotations[vararg]=(annotation,"".join(token[1] for token in self.tokens[start:self.pos]))
389
+ if self.text()==",":
390
+ self.take(",")
391
+ if self.text()==")": break
392
+ continue
393
+ break
394
+ if self.text()=="**":
395
+ self.take("**"); kwarg=self.take()[1]
396
+ if self.text()==":":
397
+ self.take(":"); start=self.pos; annotation=self.expression(1); self.reject_namedexpr(annotation,line,"함수 주석"); annotations[kwarg]=(annotation,"".join(token[1] for token in self.tokens[start:self.pos]))
398
+ if self.text()==",": self.take(",")
399
+ break
400
+ if self.text()=="/":
401
+ if not params or posonly: raise ParseError(f"잘못된 위치 전용 인자 구분자 (줄 {line})")
402
+ self.take("/"); posonly=list(params)
403
+ if self.text()==",":
404
+ self.take(",")
405
+ if self.text()==")": break
406
+ continue
407
+ break
408
+ parameter=self.take()[1]; (kwonly if keyword_mode else params).append(parameter)
409
+ if self.text()==":":
410
+ self.take(":"); start=self.pos; annotation=self.expression(1); self.reject_namedexpr(annotation,line,"함수 주석"); annotations[parameter]=(annotation,"".join(token[1] for token in self.tokens[start:self.pos]))
411
+ if self.text()=="=":
412
+ self.take("="); default=self.expression(); self.require_grouped_namedexpr(default,line,"함수 기본값"); defaults[parameter]=default; saw_default=True
413
+ elif saw_default and not keyword_mode: raise ParseError(f"기본값 없는 인자가 기본값 인자 뒤에 있습니다 (줄 {line})")
414
+ if self.text()!=",": break
415
+ self.take(",")
416
+ if self.text()==")": break
417
+ self.take(")")
418
+ all_parameters=[*params,*kwonly,*([vararg] if vararg else []),*([kwarg] if kwarg else [])]
419
+ duplicate=next((name for name in all_parameters if all_parameters.count(name)>1),None)
420
+ if duplicate is not None: raise ParseError(f"매개변수 이름 중복: {duplicate} (줄 {line})")
421
+ if keyword_mode and vararg is None and not kwonly: raise ParseError(f"단독 * 뒤에는 키워드 전용 매개변수가 필요합니다 (줄 {line})")
422
+ if self.text()=="->":
423
+ self.take("->"); start=self.pos; annotation=self.expression(1); self.reject_namedexpr(annotation,line,"반환 주석"); annotations["return"]=(annotation,"".join(token[1] for token in self.tokens[start:self.pos]))
424
+ return Node("def",(name,params,defaults,vararg,kwarg,kwonly,is_async,posonly,annotations,type_parameters),self.suite(),line)
425
+
426
+ def parse_type_parameters(self):
427
+ parameters=[]
428
+ if self.text()!="[": return parameters
429
+ self.take("[")
430
+ if self.text()=="]": raise ParseError("타입 매개변수 목록은 비어 있을 수 없습니다")
431
+ while self.text()!="]":
432
+ kind="typevar"
433
+ if self.text()=="*": self.take("*"); kind="typevartuple"
434
+ elif self.text()=="**": self.take("**"); kind="paramspec"
435
+ name=self.take()[1]; bound=None; default=None; bound_text=None; default_text=None
436
+ if self.text()==":":
437
+ if kind!="typevar": raise ParseError(f"{name} 타입 매개변수 종류에는 바운드를 사용할 수 없습니다")
438
+ self.take(":"); start=self.pos; bound=self.expression(1); self.reject_namedexpr(bound,bound.line,"타입 매개변수 바운드"); bound_text="".join(token[1] for token in self.tokens[start:self.pos])
439
+ if self.text()=="=":
440
+ self.take("="); start=self.pos; default=self.expression(1); self.reject_namedexpr(default,default.line,"타입 매개변수 기본값"); default_text="".join(token[1] for token in self.tokens[start:self.pos])
441
+ parameters.append({"kind":kind,"name":name,"bound":bound,"default":default,"bound_text":bound_text,"default_text":default_text})
442
+ if self.text()!=",": break
443
+ self.take(",")
444
+ self.take("]")
445
+ names=[item["name"] for item in parameters]
446
+ duplicate=next((name for name in names if names.count(name)>1),None)
447
+ if duplicate is not None: raise ParseError(f"타입 매개변수 이름 중복: {duplicate}")
448
+ saw_default=False
449
+ for item in parameters:
450
+ if item["default"] is not None: saw_default=True
451
+ elif saw_default: raise ParseError("기본값 없는 타입 매개변수가 기본값 매개변수 뒤에 있습니다")
452
+ return parameters
453
+
454
+ def pattern(self,allow_open_sequence=False):
455
+ line=self.current()[2]; patterns=[self.single_pattern()]
456
+ while self.text()=="|": self.take("|"); patterns.append(self.single_pattern())
457
+ result=Node("pattern_or",children=patterns,line=line) if len(patterns)>1 else patterns[0]
458
+ if self.text()=="as": self.take("as"); result=Node("pattern_as",self.take()[1],[result],line)
459
+ if allow_open_sequence and self.text()==",":
460
+ items=[result]
461
+ while self.text()==",":
462
+ self.take(",")
463
+ if self.text() in (":","if"): break
464
+ if self.text()=="*": self.take("*"); items.append(Node("pattern_star",self.take()[1],line=line))
465
+ else: items.append(self.pattern())
466
+ result=Node("pattern_sequence",children=items,line=line)
467
+ return result
468
+
469
+ def single_pattern(self):
470
+ line=self.current()[2]; word=self.text()
471
+ if word=="_": self.take(); return Node("pattern_wildcard",line=line)
472
+ if word in ("[","("):
473
+ opening=word; closing="]" if word=="[" else ")"; self.take(); items=[]; had_comma=False
474
+ if self.text()!=closing:
475
+ while True:
476
+ if self.text()=="*": self.take("*"); items.append(Node("pattern_star",self.take()[1],line=line))
477
+ else: items.append(self.pattern())
478
+ if self.text()!=",": break
479
+ self.take(","); had_comma=True
480
+ if self.text()==closing: break
481
+ self.take(closing)
482
+ if opening=="(" and len(items)==1 and not had_comma: return items[0]
483
+ return Node("pattern_sequence",children=items,line=line)
484
+ if word=="{":
485
+ self.take("{"); pairs=[]; rest=None
486
+ if self.text()!="}":
487
+ while True:
488
+ if self.text()=="**":
489
+ if rest is not None: raise ParseError(f"매핑 패턴의 ** 캡처는 하나만 허용됩니다 (줄 {line})")
490
+ self.take("**"); rest=self.take()[1]
491
+ if rest=="_": raise ParseError(f"매핑 패턴의 **_는 허용되지 않습니다 (줄 {line})")
492
+ if self.text()==",": self.take(",")
493
+ if self.text()!="}": raise ParseError(f"매핑 패턴의 ** 캡처는 마지막이어야 합니다 (줄 {line})")
494
+ break
495
+ else:
496
+ key=self.expression(7); self.take(":"); pairs.append(Node("pattern_pair",children=[key,self.pattern()],line=line))
497
+ if self.text()!=",": break
498
+ self.take(",")
499
+ if self.text()=="}": break
500
+ self.take("}"); return Node("pattern_mapping",rest,pairs,line)
501
+ if self.current()[0] in (tokenize.NUMBER,tokenize.STRING) or word in ("-","+") or word in ("True","False","None"):
502
+ value=self.expression(7)
503
+ def fold_numeric(node):
504
+ if node.kind=="constant": return node.value
505
+ if node.kind=="unary" and node.value in ("-","+"): return -fold_numeric(node.children[0]) if node.value=="-" else +fold_numeric(node.children[0])
506
+ if node.kind=="binary" and node.value in ("+","-"):
507
+ left,right=map(fold_numeric,node.children); return left+right if node.value=="+" else left-right
508
+ raise ValueError
509
+ if value.kind!="constant":
510
+ try: value=Node("constant",fold_numeric(value),line=line)
511
+ except (TypeError,ValueError): pass
512
+ return Node("pattern_literal",children=[value],line=line)
513
+ parts=[self.take()[1]]
514
+ while self.text()==".": self.take("."); parts.append(self.take()[1])
515
+ name=".".join(parts)
516
+ if self.text()=="(":
517
+ self.take("("); positional=[]; keywords=[]; saw_keyword=False
518
+ if self.text()!=")":
519
+ while True:
520
+ if self.current()[0]==tokenize.NAME and self.pos+1<len(self.tokens) and self.tokens[self.pos+1][1]=="=":
521
+ attribute=self.take()[1]; self.take("="); keywords.append(Node("pattern_keyword",attribute,[self.pattern()],line)); saw_keyword=True
522
+ else:
523
+ if saw_keyword: raise ParseError(f"클래스 키워드 패턴 뒤에는 위치 패턴을 둘 수 없습니다 (줄 {line})")
524
+ positional.append(self.pattern())
525
+ if self.text()!=",": break
526
+ self.take(",")
527
+ self.take(")"); return Node("pattern_class",name,[*positional,*keywords],line)
528
+ if len(parts)>1: return Node("pattern_value",name,line=line)
529
+ return Node("pattern_capture",name,line=line)
530
+
531
+ PRECEDENCE={"or":1,"and":2,"|":3,"^":4,"&":5,"==":6,"!=":6,"<>":6,"<":6,"<=":6,">":6,">=":6,"in":6,"is":6,"not in":6,"is not":6,
532
+ "<<":7,">>":7,"+":8,"-":8,"*":9,"/":9,"//":9,"%":9,"@":9,"**":10}
533
+ def expression(self, minimum=0, atom_only=False):
534
+ line=self.current()[2]; word=self.text()
535
+ if word=="not":
536
+ self.take(); left=Node("unary",word,[self.expression(3)],line)
537
+ elif word in ("-","+","~"):
538
+ self.take(); left=Node("unary",word,[self.expression(10)],line)
539
+ elif word=="await":
540
+ self.take(); operand=self.expression(11)
541
+ if operand.kind in ("unary","await","yield_expr","yield_from_expr","lambda","starred","binary","compare_chain","conditional","namedexpr") and not operand.grouped:
542
+ raise ParseError(f"await 뒤에는 primary 표현식 또는 괄호식이 필요합니다 (줄 {line})")
543
+ left=Node("await",children=[operand],line=line)
544
+ elif word=="yield":
545
+ self.take(); delegated=False
546
+ if self.text()=="from": self.take(); delegated=True
547
+ if delegated and self.text() in (")","]",",",":"): raise ParseError(f"yield from에는 피연산자가 필요합니다 (줄 {line})")
548
+ value=Node("constant",None,line=line) if self.text() in (")","]",",",":") else self.expression(1)
549
+ left=Node("yield_from_expr" if delegated else "yield_expr",children=[value],line=line)
550
+ elif word=="*":
551
+ self.take("*"); left=Node("starred",children=[self.expression(10)],line=line)
552
+ elif word=="lambda":
553
+ self.take(); params=[]; posonly=[]; kwonly=[]; defaults={}; vararg=None; kwarg=None; keyword_mode=False; saw_default=False
554
+ if self.text()!=":":
555
+ while True:
556
+ if self.text()=="*":
557
+ self.take("*"); keyword_mode=True
558
+ if self.text() not in (",",":"): vararg=self.take()[1]
559
+ if self.text()==",":
560
+ self.take(",")
561
+ if self.text()==":": break
562
+ continue
563
+ break
564
+ if self.text()=="**":
565
+ self.take("**"); kwarg=self.take()[1]
566
+ if self.text()==",": self.take(",")
567
+ break
568
+ if self.text()=="/":
569
+ if not params or posonly: raise ParseError(f"잘못된 위치 전용 인자 구분자 (줄 {line})")
570
+ self.take("/"); posonly=list(params)
571
+ if self.text()==",":
572
+ self.take(",")
573
+ if self.text()==":": break
574
+ continue
575
+ break
576
+ parameter=self.take()[1]; (kwonly if keyword_mode else params).append(parameter)
577
+ if self.text()=="=":
578
+ self.take("="); default=self.expression(1); self.require_grouped_namedexpr(default,line,"lambda 기본값"); defaults[parameter]=default; saw_default=True
579
+ elif saw_default and not keyword_mode: raise ParseError(f"기본값 없는 인자가 기본값 인자 뒤에 있습니다 (줄 {line})")
580
+ if self.text()!=",": break
581
+ self.take(",")
582
+ if self.text()==":": break
583
+ all_parameters=[*params,*kwonly,*([vararg] if vararg else []),*([kwarg] if kwarg else [])]
584
+ duplicate=next((name for name in all_parameters if all_parameters.count(name)>1),None)
585
+ if duplicate is not None: raise ParseError(f"매개변수 이름 중복: {duplicate} (줄 {line})")
586
+ if keyword_mode and vararg is None and not kwonly: raise ParseError(f"단독 * 뒤에는 키워드 전용 매개변수가 필요합니다 (줄 {line})")
587
+ self.take(":"); signature={"params":params,"posonly":posonly,"kwonly":kwonly,"defaults":defaults,"vararg":vararg,"kwarg":kwarg}
588
+ body=self.expression(1); self.require_grouped_namedexpr(body,line,"lambda 본문"); left=Node("lambda",signature,[body],line)
589
+ elif word=="(":
590
+ self.take()
591
+ if self.text()==")": self.take(")"); left=Node("tuple",children=[],line=line)
592
+ else:
593
+ first=self.expression()
594
+ first.grouped=True
595
+ if self.text() in ("for","async"):
596
+ left=self.comprehension("generatorexpr",first,line,")")
597
+ return self.postfix_and_binary(left,minimum)
598
+ elif self.text()==",":
599
+ values=[first]
600
+ while self.text()==",":
601
+ self.take(",")
602
+ if self.text()==")": break
603
+ values.append(self.expression())
604
+ self.take(")"); left=Node("tuple",children=values,line=line)
605
+ else: self.take(")"); left=first
606
+ elif self.current()[0] in (getattr(tokenize,"FSTRING_START",-1),getattr(tokenize,"TSTRING_START",-4)):
607
+ template=self.current()[0]==getattr(tokenize,"TSTRING_START",-4)
608
+ middle_type=getattr(tokenize,"TSTRING_MIDDLE",-5) if template else getattr(tokenize,"FSTRING_MIDDLE",-3)
609
+ end_type=getattr(tokenize,"TSTRING_END",-6) if template else getattr(tokenize,"FSTRING_END",-2)
610
+ self.take(); parts=[]
611
+ while self.current()[0] != end_type:
612
+ if self.current()[0] == middle_type:
613
+ parts.append(Node("constant",self.take()[1],line=line))
614
+ elif self.text()=="{":
615
+ opening=self.take("{"); expression_start=self.pos; value=self.expression()
616
+ expression_text=self.source[opening[4]:self.tokens[self.pos-1][4]]
617
+ conversion=None; spec=[]; debug_prefix=None; has_spec=False
618
+ if self.text()=="=":
619
+ self.take("=")
620
+ debug_prefix=self.source[opening[4]:self.current()[3]]
621
+ if self.text()=="!":
622
+ self.take("!"); conversion=self.take()[1]
623
+ if conversion not in ("s","r","a"): raise ParseError(f"잘못된 문자열 변환 '!{conversion}' (줄 {line})")
624
+ if self.text()==":":
625
+ self.take(":"); has_spec=True
626
+ while self.text()!="}":
627
+ if self.current()[0] == middle_type: spec.append(Node("constant",self.take()[1],line=line))
628
+ elif self.text()=="{":
629
+ nested_opening=self.take("{"); nested=self.expression(); nested_conversion=None; nested_prefix=None; nested_spec=[]; nested_has_spec=False
630
+ if self.text()=="=":
631
+ self.take("="); nested_prefix=self.source[nested_opening[4]:self.current()[3]]
632
+ if self.text()=="!":
633
+ self.take("!"); nested_conversion=self.take()[1]
634
+ if nested_conversion not in ("s","r","a"): raise ParseError(f"잘못된 문자열 변환 '!{nested_conversion}' (줄 {line})")
635
+ if self.text()==":":
636
+ self.take(":"); nested_has_spec=True
637
+ while self.text()!="}":
638
+ if self.current()[0] == middle_type: nested_spec.append(Node("constant",self.take()[1],line=line))
639
+ elif self.text()=="{":
640
+ deep_opening=self.take("{"); deep=self.expression(); deep_conversion=None; deep_prefix=None; deep_spec=[]; deep_has_spec=False
641
+ if self.text()=="=":
642
+ self.take("="); deep_prefix=self.source[deep_opening[4]:self.current()[3]]
643
+ if self.text()=="!":
644
+ self.take("!"); deep_conversion=self.take()[1]
645
+ if deep_conversion not in ("s","r","a"): raise ParseError(f"잘못된 문자열 변환 '!{deep_conversion}' (줄 {line})")
646
+ if self.text()==":":
647
+ self.take(":"); deep_has_spec=True
648
+ while self.text()!="}":
649
+ if self.current()[0] == middle_type: deep_spec.append(Node("constant",self.take()[1],line=line))
650
+ else: raise ParseError(f"형식 지정자 표현식이 너무 깊게 중첩되었습니다 (줄 {line})")
651
+ if deep_prefix is not None:
652
+ nested_spec.append(Node("constant",deep_prefix,line=line))
653
+ if deep_conversion is None and not deep_has_spec: deep_conversion="r"
654
+ self.take("}")
655
+ deep_children=[deep,*([Node("fstring",children=deep_spec,line=line)] if deep_has_spec else [])]
656
+ nested_spec.append(Node("format",deep_conversion,deep_children,line))
657
+ else: raise ParseError(f"형식 지정자 표현식이 너무 깊게 중첩되었습니다 (줄 {line})")
658
+ if nested_prefix is not None:
659
+ spec.append(Node("constant",nested_prefix,line=line))
660
+ if nested_conversion is None and not nested_has_spec: nested_conversion="r"
661
+ self.take("}")
662
+ nested_children=[nested,*([Node("fstring",children=nested_spec,line=line)] if nested_has_spec else [])]
663
+ spec.append(Node("format",nested_conversion,nested_children,line))
664
+ else: raise ParseError(f"지원하지 않는 f-string 형식 지정자 (줄 {line})")
665
+ if debug_prefix is not None:
666
+ parts.append(Node("constant",debug_prefix,line=line))
667
+ if conversion is None and not has_spec: conversion="r"
668
+ children=[value,*([Node("fstring",children=spec,line=line)] if has_spec else [])]
669
+ parts.append(Node("interpolation",{"conversion":conversion,"expression":expression_text},children,line) if template else Node("format",conversion,children,line)); self.take("}")
670
+ else: raise ParseError(f"지원하지 않는 f-string 형식 (줄 {line})")
671
+ self.take(); left=Node("template" if template else "fstring",children=parts,line=line)
672
+ elif word=="...":
673
+ self.take(); left=Node("constant",Ellipsis,line=line)
674
+ elif self.current()[0] in (tokenize.NUMBER,tokenize.STRING):
675
+ raw=self.take()[1]
676
+ import ast
677
+ value=ast.literal_eval(ast.parse(raw,filename=self.filename,mode="eval").body)
678
+ while self.current()[0]==tokenize.STRING:
679
+ adjacent_raw=self.take()[1]
680
+ adjacent=ast.literal_eval(ast.parse(adjacent_raw,filename=self.filename,mode="eval").body)
681
+ if type(value) is not type(adjacent) or not isinstance(value,(str,bytes)): raise ParseError(f"문자열과 바이트 리터럴은 이어 붙일 수 없습니다 (줄 {line})")
682
+ value+=adjacent
683
+ left=Node("constant",value,line=line)
684
+ elif word in ("True","False","None"):
685
+ self.take(); left=Node("constant",{"True":True,"False":False,"None":None}[word],line=line)
686
+ elif word=="[":
687
+ self.take(); values=[]
688
+ if self.text()!="]":
689
+ first=self.expression()
690
+ if self.text() in ("for","async"):
691
+ left=self.comprehension("listcomp",first,line,"]")
692
+ return self.postfix_and_binary(left,minimum)
693
+ values.append(first)
694
+ while self.text()==",":
695
+ self.take(",")
696
+ if self.text()=="]": break
697
+ values.append(self.expression())
698
+ self.take("]"); left=Node("list",children=values,line=line)
699
+ elif word=="{":
700
+ self.take(); pairs=[]
701
+ if self.text()!="}":
702
+ if self.text()=="**":
703
+ while True:
704
+ if self.text()=="**":
705
+ self.take("**"); unpack=self.expression(); self.require_grouped_namedexpr(unpack,line,"dict unpack"); pairs.append(Node("dict_unpack",children=[unpack],line=line))
706
+ else:
707
+ key=self.expression(); self.take(":"); value=self.expression()
708
+ self.require_grouped_namedexpr(key,line,"dict key"); self.require_grouped_namedexpr(value,line,"dict value")
709
+ pairs.append(Node("pair",children=[key,value],line=line))
710
+ if self.text()!=",": break
711
+ self.take(",")
712
+ if self.text()=="}": break
713
+ self.take("}"); left=Node("dict",children=pairs,line=line)
714
+ return self.postfix_and_binary(left,minimum)
715
+ first=self.expression()
716
+ if self.text()==":":
717
+ self.take(":"); value=self.expression()
718
+ self.require_grouped_namedexpr(first,line,"dict key"); self.require_grouped_namedexpr(value,line,"dict value")
719
+ if self.text() in ("for","async"):
720
+ pair=Node("pair",children=[first,value],line=line)
721
+ left=self.comprehension("dictcomp",pair,line,"}")
722
+ return self.postfix_and_binary(left,minimum)
723
+ pairs.append(Node("pair",children=[first,value],line=line))
724
+ while self.text()==",":
725
+ self.take(",")
726
+ if self.text()=="}": break
727
+ if self.text()=="**":
728
+ self.take("**"); unpack=self.expression(); self.require_grouped_namedexpr(unpack,line,"dict unpack"); pairs.append(Node("dict_unpack",children=[unpack],line=line))
729
+ else:
730
+ key=self.expression(); self.take(":"); value=self.expression()
731
+ self.require_grouped_namedexpr(key,line,"dict key"); self.require_grouped_namedexpr(value,line,"dict value")
732
+ pairs.append(Node("pair",children=[key,value],line=line))
733
+ self.take("}"); left=Node("dict",children=pairs,line=line)
734
+ else:
735
+ values=[first]
736
+ if self.text() in ("for","async"):
737
+ left=self.comprehension("setcomp",first,line,"}")
738
+ return self.postfix_and_binary(left,minimum)
739
+ while self.text()==",":
740
+ self.take(",")
741
+ if self.text()=="}": break
742
+ values.append(self.expression())
743
+ self.take("}"); left=Node("set",children=values,line=line)
744
+ else: self.take("}"); left=Node("dict",children=[],line=line)
745
+ else:
746
+ left=Node("name",self.take()[1],line=line)
747
+ string_starts=(tokenize.STRING,getattr(tokenize,"FSTRING_START",-1),getattr(tokenize,"TSTRING_START",-4))
748
+ def string_node(node): return node.kind in ("fstring","template") or (node.kind=="constant" and isinstance(node.value,(str,bytes)))
749
+ while string_node(left) and self.current()[0] in string_starts:
750
+ right=self.expression(100,True)
751
+ if not string_node(right): raise ParseError(f"잘못된 인접 문자열 리터럴 (줄 {line})")
752
+ if "template" in (left.kind,right.kind):
753
+ if left.kind!="template" or right.kind!="template": raise ParseError(f"t-string은 다른 문자열 리터럴과 섞을 수 없습니다 (줄 {line})")
754
+ left=Node("template",children=[*left.children,*right.children],line=line)
755
+ continue
756
+ if (left.kind=="constant" and isinstance(left.value,bytes)) or (right.kind=="constant" and isinstance(right.value,bytes)):
757
+ if left.kind!="constant" or right.kind!="constant" or not isinstance(left.value,bytes) or not isinstance(right.value,bytes): raise ParseError(f"바이트와 문자열 리터럴은 섞을 수 없습니다 (줄 {line})")
758
+ left=Node("constant",left.value+right.value,line=line); continue
759
+ left_parts=left.children if left.kind=="fstring" else [Node("constant",left.value,line=line)]
760
+ right_parts=right.children if right.kind=="fstring" else [Node("constant",right.value,line=line)]
761
+ left=Node("fstring",children=[*left_parts,*right_parts],line=line)
762
+ if atom_only: return left
763
+ return self.postfix_and_binary(left,minimum)
764
+
765
+ def comprehension(self,kind,element,line,closing):
766
+ clauses=[]
767
+ while self.text() in ("for","async"):
768
+ is_async=self.text()=="async"
769
+ if is_async: self.take("async")
770
+ self.take("for"); target=self.loop_target(); self.take("in"); iterable=self.expression(1)
771
+ filters=[]
772
+ while self.text()=="if": self.take(); filters.append(self.expression(1))
773
+ clauses.append(Node("comp_clause",(target,is_async),[iterable,*filters],line))
774
+ self.take(closing)
775
+ return Node(kind,None,[element,*clauses],line)
776
+
777
+ def loop_target(self):
778
+ line=self.current()[2]; first=self.expression(7); values=[first]; had_comma=False
779
+ while self.text()==",":
780
+ self.take(","); had_comma=True
781
+ if self.text()=="in": break
782
+ values.append(self.expression(7))
783
+ target=Node("tuple",children=values,line=line) if had_comma else first
784
+ if target.kind not in ("name","tuple","list","starred","attribute","subscript"): raise ParseError(f"잘못된 반복 대입 대상 (줄 {line})")
785
+ self.validate_unpack_target(target,line)
786
+ return target
787
+
788
+ def subscript_item(self,line):
789
+ start=Node("constant",None,line=line) if self.text()==":" else self.expression()
790
+ if self.text()!=":": return start
791
+ if start.kind=="starred": raise ParseError(f"slice 경계에는 별표 표현식을 사용할 수 없습니다 (줄 {line})")
792
+ self.take(":")
793
+ stop=Node("constant",None,line=line) if self.text() in (":",",","]") else self.expression()
794
+ if stop.kind=="starred": raise ParseError(f"slice 경계에는 별표 표현식을 사용할 수 없습니다 (줄 {line})")
795
+ step=Node("constant",None,line=line)
796
+ if self.text()==":":
797
+ self.take(":"); step=Node("constant",None,line=line) if self.text() in (",","]") else self.expression()
798
+ if step.kind=="starred": raise ParseError(f"slice 경계에는 별표 표현식을 사용할 수 없습니다 (줄 {line})")
799
+ return Node("slice",children=[start,stop,step],line=line)
800
+
801
+ def postfix_and_binary(self,left,minimum):
802
+ line=left.line
803
+ while self.text() in ("(", ".", "["):
804
+ if self.text()=="(":
805
+ self.take(); args=[]; descriptors=[]; closed=False; saw_keyword=False; saw_kwstar=False
806
+ if self.text()!=")":
807
+ while True:
808
+ if self.current()[0]==tokenize.NAME and self.pos+1<len(self.tokens) and self.tokens[self.pos+1][1]=="=":
809
+ keyword=self.take()[1]
810
+ if any(item[0]=="keyword" and item[1]==keyword for item in descriptors): raise ParseError(f"키워드 인자 중복: {keyword} (줄 {line})")
811
+ self.take("="); value=self.expression(); self.require_grouped_namedexpr(value,line,"키워드 인자"); args.append(value); descriptors.append(["keyword",keyword]); saw_keyword=True
812
+ elif self.text()=="*":
813
+ if saw_kwstar: raise ParseError(f"** 키워드 unpack 뒤에는 * 위치 unpack을 둘 수 없습니다 (줄 {line})")
814
+ self.take("*"); args.append(self.expression()); descriptors.append(["star",None])
815
+ elif self.text()=="**":
816
+ self.take("**"); args.append(self.expression()); descriptors.append(["kwstar",None]); saw_keyword=True; saw_kwstar=True
817
+ else:
818
+ if saw_keyword: raise ParseError(f"키워드 인자 뒤에는 일반 위치 인자를 둘 수 없습니다 (줄 {line})")
819
+ argument=self.expression()
820
+ if self.text() in ("for","async"):
821
+ argument=self.comprehension("generatorexpr",argument,line,")"); closed=True
822
+ args.append(argument); descriptors.append(["positional",None])
823
+ if closed: break
824
+ if self.text()!=",": break
825
+ self.take(",")
826
+ if self.text()==")": break
827
+ if not closed: self.take(")")
828
+ left=Node("call",descriptors,[left,*args],line=line)
829
+ elif self.text()==".":
830
+ self.take("."); left=Node("attribute",self.take()[1],[left],line)
831
+ else:
832
+ self.take("[")
833
+ if self.text() in ("]",","): raise ParseError(f"첨자에는 하나 이상의 항목이 필요합니다 (줄 {line})")
834
+ values=[self.subscript_item(line)]; had_comma=False
835
+ while self.text()==",":
836
+ self.take(","); had_comma=True
837
+ if self.text()=="]": break
838
+ values.append(self.subscript_item(line))
839
+ index=Node("tuple",children=values,line=line) if had_comma or values[0].kind=="starred" else values[0]
840
+ self.take("]")
841
+ left=Node("subscript",children=[left,index],line=line)
842
+ while True:
843
+ compound=None
844
+ if self.text()=="is" and self.pos+1<len(self.tokens) and self.tokens[self.pos+1][1]=="not": compound="is not"
845
+ if self.text()=="not" and self.pos+1<len(self.tokens) and self.tokens[self.pos+1][1]=="in": compound="not in"
846
+ candidate=compound or self.text()
847
+ if candidate not in self.PRECEDENCE or self.PRECEDENCE[candidate]<minimum: break
848
+ if candidate=="!=" and "barry_as_FLUFL" in self.future_features: raise ParseError(f"barry_as_FLUFL에서는 != 대신 <>를 사용해야 합니다 (줄 {line})")
849
+ if candidate=="<>" and "barry_as_FLUFL" not in self.future_features: break
850
+ if compound: self.take(); self.take(); op=compound
851
+ else: op=self.take()[1]
852
+ if op=="<>": op="!="
853
+ precedence=self.PRECEDENCE[op]
854
+ right=self.expression(precedence+(0 if op=="**" else 1))
855
+ comparisons={"==","!=","<","<=",">",">=","in","is","not in","is not"}
856
+ if op in comparisons:
857
+ if left.kind=="compare_chain": left.value.append(op); left.children.append(right)
858
+ else: left=Node("compare_chain",[op],[left,right],line)
859
+ else: left=Node("binary",op,[left,right],line)
860
+ if left.kind=="compare_chain" and len(left.value)==1:
861
+ left=Node("binary",left.value[0],left.children,line)
862
+ if minimum<=0 and self.text()==":=":
863
+ if left.kind!="name": raise ParseError(f"바다코끼리 대입 대상은 이름이어야 합니다 (줄 {line})")
864
+ self.take(":="); left=Node("namedexpr",left.value,[self.expression()],line)
865
+ if minimum<=0 and self.text()=="if":
866
+ self.take(); condition=self.expression(); self.take("else"); otherwise=self.expression()
867
+ left=Node("conditional",children=[condition,left,otherwise],line=line)
868
+ return left
869
+
870
+ def parse(source: str, filename: str = "<하이썬>") -> Node: return Parser(source,filename).parse()