Path: blob/main/Tools/peg_generator/pegen/c_generator.py
12 views
import ast1import os.path2import re3from dataclasses import dataclass, field4from enum import Enum5from typing import IO, Any, Dict, List, Optional, Set, Text, Tuple67from pegen import grammar8from pegen.grammar import (9Alt,10Cut,11Forced,12Gather,13GrammarVisitor,14Group,15Leaf,16Lookahead,17NamedItem,18NameLeaf,19NegativeLookahead,20Opt,21PositiveLookahead,22Repeat0,23Repeat1,24Rhs,25Rule,26StringLeaf,27)28from pegen.parser_generator import ParserGenerator2930EXTENSION_PREFIX = """\31#include "pegen.h"3233#if defined(Py_DEBUG) && defined(Py_BUILD_CORE)34# define D(x) if (p->debug) { x; }35#else36# define D(x)37#endif3839#ifdef __wasi__40# define MAXSTACK 400041#else42# define MAXSTACK 600043#endif4445"""464748EXTENSION_SUFFIX = """49void *50_PyPegen_parse(Parser *p)51{52// Initialize keywords53p->keywords = reserved_keywords;54p->n_keyword_lists = n_keyword_lists;55p->soft_keywords = soft_keywords;5657return start_rule(p);58}59"""606162class NodeTypes(Enum):63NAME_TOKEN = 064NUMBER_TOKEN = 165STRING_TOKEN = 266GENERIC_TOKEN = 367KEYWORD = 468SOFT_KEYWORD = 569CUT_OPERATOR = 670F_STRING_CHUNK = 7717273BASE_NODETYPES = {74"NAME": NodeTypes.NAME_TOKEN,75"NUMBER": NodeTypes.NUMBER_TOKEN,76"STRING": NodeTypes.STRING_TOKEN,77"SOFT_KEYWORD": NodeTypes.SOFT_KEYWORD,78}798081@dataclass82class FunctionCall:83function: str84arguments: List[Any] = field(default_factory=list)85assigned_variable: Optional[str] = None86assigned_variable_type: Optional[str] = None87return_type: Optional[str] = None88nodetype: Optional[NodeTypes] = None89force_true: bool = False90comment: Optional[str] = None9192def __str__(self) -> str:93parts = []94parts.append(self.function)95if self.arguments:96parts.append(f"({', '.join(map(str, self.arguments))})")97if self.force_true:98parts.append(", !p->error_indicator")99if self.assigned_variable:100if self.assigned_variable_type:101parts = [102"(",103self.assigned_variable,104" = ",105"(",106self.assigned_variable_type,107")",108*parts,109")",110]111else:112parts = ["(", self.assigned_variable, " = ", *parts, ")"]113if self.comment:114parts.append(f" // {self.comment}")115return "".join(parts)116117118class CCallMakerVisitor(GrammarVisitor):119def __init__(120self,121parser_generator: ParserGenerator,122exact_tokens: Dict[str, int],123non_exact_tokens: Set[str],124):125self.gen = parser_generator126self.exact_tokens = exact_tokens127self.non_exact_tokens = non_exact_tokens128self.cache: Dict[Any, FunctionCall] = {}129self.cleanup_statements: List[str] = []130131def keyword_helper(self, keyword: str) -> FunctionCall:132return FunctionCall(133assigned_variable="_keyword",134function="_PyPegen_expect_token",135arguments=["p", self.gen.keywords[keyword]],136return_type="Token *",137nodetype=NodeTypes.KEYWORD,138comment=f"token='{keyword}'",139)140141def soft_keyword_helper(self, value: str) -> FunctionCall:142return FunctionCall(143assigned_variable="_keyword",144function="_PyPegen_expect_soft_keyword",145arguments=["p", value],146return_type="expr_ty",147nodetype=NodeTypes.SOFT_KEYWORD,148comment=f"soft_keyword='{value}'",149)150151def visit_NameLeaf(self, node: NameLeaf) -> FunctionCall:152name = node.value153if name in self.non_exact_tokens:154if name in BASE_NODETYPES:155return FunctionCall(156assigned_variable=f"{name.lower()}_var",157function=f"_PyPegen_{name.lower()}_token",158arguments=["p"],159nodetype=BASE_NODETYPES[name],160return_type="expr_ty",161comment=name,162)163return FunctionCall(164assigned_variable=f"{name.lower()}_var",165function=f"_PyPegen_expect_token",166arguments=["p", name],167nodetype=NodeTypes.GENERIC_TOKEN,168return_type="Token *",169comment=f"token='{name}'",170)171172type = None173rule = self.gen.all_rules.get(name.lower())174if rule is not None:175type = "asdl_seq *" if rule.is_loop() or rule.is_gather() else rule.type176177return FunctionCall(178assigned_variable=f"{name}_var",179function=f"{name}_rule",180arguments=["p"],181return_type=type,182comment=f"{node}",183)184185def visit_StringLeaf(self, node: StringLeaf) -> FunctionCall:186val = ast.literal_eval(node.value)187if re.match(r"[a-zA-Z_]\w*\Z", val): # This is a keyword188if node.value.endswith("'"):189return self.keyword_helper(val)190else:191return self.soft_keyword_helper(node.value)192else:193assert val in self.exact_tokens, f"{node.value} is not a known literal"194type = self.exact_tokens[val]195return FunctionCall(196assigned_variable="_literal",197function=f"_PyPegen_expect_token",198arguments=["p", type],199nodetype=NodeTypes.GENERIC_TOKEN,200return_type="Token *",201comment=f"token='{val}'",202)203204def visit_Rhs(self, node: Rhs) -> FunctionCall:205if node in self.cache:206return self.cache[node]207if node.can_be_inlined:208self.cache[node] = self.generate_call(node.alts[0].items[0])209else:210name = self.gen.artifical_rule_from_rhs(node)211self.cache[node] = FunctionCall(212assigned_variable=f"{name}_var",213function=f"{name}_rule",214arguments=["p"],215comment=f"{node}",216)217return self.cache[node]218219def visit_NamedItem(self, node: NamedItem) -> FunctionCall:220call = self.generate_call(node.item)221if node.name:222call.assigned_variable = node.name223if node.type:224call.assigned_variable_type = node.type225return call226227def lookahead_call_helper(self, node: Lookahead, positive: int) -> FunctionCall:228call = self.generate_call(node.node)229if call.nodetype == NodeTypes.NAME_TOKEN:230return FunctionCall(231function=f"_PyPegen_lookahead_with_name",232arguments=[positive, call.function, *call.arguments],233return_type="int",234)235elif call.nodetype == NodeTypes.SOFT_KEYWORD:236return FunctionCall(237function=f"_PyPegen_lookahead_with_string",238arguments=[positive, call.function, *call.arguments],239return_type="int",240)241elif call.nodetype in {NodeTypes.GENERIC_TOKEN, NodeTypes.KEYWORD}:242return FunctionCall(243function=f"_PyPegen_lookahead_with_int",244arguments=[positive, call.function, *call.arguments],245return_type="int",246comment=f"token={node.node}",247)248else:249return FunctionCall(250function=f"_PyPegen_lookahead",251arguments=[positive, call.function, *call.arguments],252return_type="int",253)254255def visit_PositiveLookahead(self, node: PositiveLookahead) -> FunctionCall:256return self.lookahead_call_helper(node, 1)257258def visit_NegativeLookahead(self, node: NegativeLookahead) -> FunctionCall:259return self.lookahead_call_helper(node, 0)260261def visit_Forced(self, node: Forced) -> FunctionCall:262call = self.generate_call(node.node)263if isinstance(node.node, Leaf):264assert isinstance(node.node, Leaf)265val = ast.literal_eval(node.node.value)266assert val in self.exact_tokens, f"{node.node.value} is not a known literal"267type = self.exact_tokens[val]268return FunctionCall(269assigned_variable="_literal",270function=f"_PyPegen_expect_forced_token",271arguments=["p", type, f'"{val}"'],272nodetype=NodeTypes.GENERIC_TOKEN,273return_type="Token *",274comment=f"forced_token='{val}'",275)276if isinstance(node.node, Group):277call = self.visit(node.node.rhs)278call.assigned_variable = None279call.comment = None280return FunctionCall(281assigned_variable="_literal",282function=f"_PyPegen_expect_forced_result",283arguments=["p", str(call), f'"{node.node.rhs!s}"'],284return_type="void *",285comment=f"forced_token=({node.node.rhs!s})",286)287else:288raise NotImplementedError(f"Forced tokens don't work with {node.node} nodes")289290def visit_Opt(self, node: Opt) -> FunctionCall:291call = self.generate_call(node.node)292return FunctionCall(293assigned_variable="_opt_var",294function=call.function,295arguments=call.arguments,296force_true=True,297comment=f"{node}",298)299300def visit_Repeat0(self, node: Repeat0) -> FunctionCall:301if node in self.cache:302return self.cache[node]303name = self.gen.artificial_rule_from_repeat(node.node, False)304self.cache[node] = FunctionCall(305assigned_variable=f"{name}_var",306function=f"{name}_rule",307arguments=["p"],308return_type="asdl_seq *",309comment=f"{node}",310)311return self.cache[node]312313def visit_Repeat1(self, node: Repeat1) -> FunctionCall:314if node in self.cache:315return self.cache[node]316name = self.gen.artificial_rule_from_repeat(node.node, True)317self.cache[node] = FunctionCall(318assigned_variable=f"{name}_var",319function=f"{name}_rule",320arguments=["p"],321return_type="asdl_seq *",322comment=f"{node}",323)324return self.cache[node]325326def visit_Gather(self, node: Gather) -> FunctionCall:327if node in self.cache:328return self.cache[node]329name = self.gen.artifical_rule_from_gather(node)330self.cache[node] = FunctionCall(331assigned_variable=f"{name}_var",332function=f"{name}_rule",333arguments=["p"],334return_type="asdl_seq *",335comment=f"{node}",336)337return self.cache[node]338339def visit_Group(self, node: Group) -> FunctionCall:340return self.generate_call(node.rhs)341342def visit_Cut(self, node: Cut) -> FunctionCall:343return FunctionCall(344assigned_variable="_cut_var",345return_type="int",346function="1",347nodetype=NodeTypes.CUT_OPERATOR,348)349350def generate_call(self, node: Any) -> FunctionCall:351return super().visit(node)352353354class CParserGenerator(ParserGenerator, GrammarVisitor):355def __init__(356self,357grammar: grammar.Grammar,358tokens: Dict[int, str],359exact_tokens: Dict[str, int],360non_exact_tokens: Set[str],361file: Optional[IO[Text]],362debug: bool = False,363skip_actions: bool = False,364):365super().__init__(grammar, set(tokens.values()), file)366self.callmakervisitor: CCallMakerVisitor = CCallMakerVisitor(367self, exact_tokens, non_exact_tokens368)369self._varname_counter = 0370self.debug = debug371self.skip_actions = skip_actions372self.cleanup_statements: List[str] = []373374def add_level(self) -> None:375self.print("if (p->level++ == MAXSTACK) {")376with self.indent():377self.print("p->error_indicator = 1;")378self.print("PyErr_NoMemory();")379self.print("}")380381def remove_level(self) -> None:382self.print("p->level--;")383384def add_return(self, ret_val: str) -> None:385for stmt in self.cleanup_statements:386self.print(stmt)387self.remove_level()388self.print(f"return {ret_val};")389390def unique_varname(self, name: str = "tmpvar") -> str:391new_var = name + "_" + str(self._varname_counter)392self._varname_counter += 1393return new_var394395def call_with_errorcheck_return(self, call_text: str, returnval: str) -> None:396error_var = self.unique_varname()397self.print(f"int {error_var} = {call_text};")398self.print(f"if ({error_var}) {{")399with self.indent():400self.add_return(returnval)401self.print("}")402403def call_with_errorcheck_goto(self, call_text: str, goto_target: str) -> None:404error_var = self.unique_varname()405self.print(f"int {error_var} = {call_text};")406self.print(f"if ({error_var}) {{")407with self.indent():408self.print(f"goto {goto_target};")409self.print(f"}}")410411def out_of_memory_return(412self,413expr: str,414cleanup_code: Optional[str] = None,415) -> None:416self.print(f"if ({expr}) {{")417with self.indent():418if cleanup_code is not None:419self.print(cleanup_code)420self.print("p->error_indicator = 1;")421self.print("PyErr_NoMemory();")422self.add_return("NULL")423self.print(f"}}")424425def out_of_memory_goto(self, expr: str, goto_target: str) -> None:426self.print(f"if ({expr}) {{")427with self.indent():428self.print("PyErr_NoMemory();")429self.print(f"goto {goto_target};")430self.print(f"}}")431432def generate(self, filename: str) -> None:433self.collect_rules()434basename = os.path.basename(filename)435self.print(f"// @generated by pegen from {basename}")436header = self.grammar.metas.get("header", EXTENSION_PREFIX)437if header:438self.print(header.rstrip("\n"))439subheader = self.grammar.metas.get("subheader", "")440if subheader:441self.print(subheader)442self._setup_keywords()443self._setup_soft_keywords()444for i, (rulename, rule) in enumerate(self.all_rules.items(), 1000):445comment = " // Left-recursive" if rule.left_recursive else ""446self.print(f"#define {rulename}_type {i}{comment}")447self.print()448for rulename, rule in self.all_rules.items():449if rule.is_loop() or rule.is_gather():450type = "asdl_seq *"451elif rule.type:452type = rule.type + " "453else:454type = "void *"455self.print(f"static {type}{rulename}_rule(Parser *p);")456self.print()457for rulename, rule in list(self.all_rules.items()):458self.print()459if rule.left_recursive:460self.print("// Left-recursive")461self.visit(rule)462if self.skip_actions:463mode = 0464else:465mode = int(self.rules["start"].type == "mod_ty") if "start" in self.rules else 1466if mode == 1 and self.grammar.metas.get("bytecode"):467mode += 1468modulename = self.grammar.metas.get("modulename", "parse")469trailer = self.grammar.metas.get("trailer", EXTENSION_SUFFIX)470if trailer:471self.print(trailer.rstrip("\n") % dict(mode=mode, modulename=modulename))472473def _group_keywords_by_length(self) -> Dict[int, List[Tuple[str, int]]]:474groups: Dict[int, List[Tuple[str, int]]] = {}475for keyword_str, keyword_type in self.keywords.items():476length = len(keyword_str)477if length in groups:478groups[length].append((keyword_str, keyword_type))479else:480groups[length] = [(keyword_str, keyword_type)]481return groups482483def _setup_keywords(self) -> None:484n_keyword_lists = (485len(max(self.keywords.keys(), key=len)) + 1 if len(self.keywords) > 0 else 0486)487self.print(f"static const int n_keyword_lists = {n_keyword_lists};")488groups = self._group_keywords_by_length()489self.print("static KeywordToken *reserved_keywords[] = {")490with self.indent():491num_groups = max(groups) + 1 if groups else 1492for keywords_length in range(num_groups):493if keywords_length not in groups.keys():494self.print("(KeywordToken[]) {{NULL, -1}},")495else:496self.print("(KeywordToken[]) {")497with self.indent():498for keyword_str, keyword_type in groups[keywords_length]:499self.print(f'{{"{keyword_str}", {keyword_type}}},')500self.print("{NULL, -1},")501self.print("},")502self.print("};")503504def _setup_soft_keywords(self) -> None:505soft_keywords = sorted(self.soft_keywords)506self.print("static char *soft_keywords[] = {")507with self.indent():508for keyword in soft_keywords:509self.print(f'"{keyword}",')510self.print("NULL,")511self.print("};")512513def _set_up_token_start_metadata_extraction(self) -> None:514self.print("if (p->mark == p->fill && _PyPegen_fill_token(p) < 0) {")515with self.indent():516self.print("p->error_indicator = 1;")517self.add_return("NULL")518self.print("}")519self.print("int _start_lineno = p->tokens[_mark]->lineno;")520self.print("UNUSED(_start_lineno); // Only used by EXTRA macro")521self.print("int _start_col_offset = p->tokens[_mark]->col_offset;")522self.print("UNUSED(_start_col_offset); // Only used by EXTRA macro")523524def _set_up_token_end_metadata_extraction(self) -> None:525self.print("Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);")526self.print("if (_token == NULL) {")527with self.indent():528self.add_return("NULL")529self.print("}")530self.print("int _end_lineno = _token->end_lineno;")531self.print("UNUSED(_end_lineno); // Only used by EXTRA macro")532self.print("int _end_col_offset = _token->end_col_offset;")533self.print("UNUSED(_end_col_offset); // Only used by EXTRA macro")534535def _check_for_errors(self) -> None:536self.print("if (p->error_indicator) {")537with self.indent():538self.add_return("NULL")539self.print("}")540541def _set_up_rule_memoization(self, node: Rule, result_type: str) -> None:542self.print("{")543with self.indent():544self.add_level()545self.print(f"{result_type} _res = NULL;")546self.print(f"if (_PyPegen_is_memoized(p, {node.name}_type, &_res)) {{")547with self.indent():548self.add_return("_res")549self.print("}")550self.print("int _mark = p->mark;")551self.print("int _resmark = p->mark;")552self.print("while (1) {")553with self.indent():554self.call_with_errorcheck_return(555f"_PyPegen_update_memo(p, _mark, {node.name}_type, _res)", "_res"556)557self.print("p->mark = _mark;")558self.print(f"void *_raw = {node.name}_raw(p);")559self.print("if (p->error_indicator) {")560with self.indent():561self.add_return("NULL")562self.print("}")563self.print("if (_raw == NULL || p->mark <= _resmark)")564with self.indent():565self.print("break;")566self.print(f"_resmark = p->mark;")567self.print("_res = _raw;")568self.print("}")569self.print(f"p->mark = _resmark;")570self.add_return("_res")571self.print("}")572self.print(f"static {result_type}")573self.print(f"{node.name}_raw(Parser *p)")574575def _should_memoize(self, node: Rule) -> bool:576return node.memo and not node.left_recursive577578def _handle_default_rule_body(self, node: Rule, rhs: Rhs, result_type: str) -> None:579memoize = self._should_memoize(node)580581with self.indent():582self.add_level()583self._check_for_errors()584self.print(f"{result_type} _res = NULL;")585if memoize:586self.print(f"if (_PyPegen_is_memoized(p, {node.name}_type, &_res)) {{")587with self.indent():588self.add_return("_res")589self.print("}")590self.print("int _mark = p->mark;")591if any(alt.action and "EXTRA" in alt.action for alt in rhs.alts):592self._set_up_token_start_metadata_extraction()593self.visit(594rhs,595is_loop=False,596is_gather=node.is_gather(),597rulename=node.name,598)599if self.debug:600self.print(f'D(fprintf(stderr, "Fail at %d: {node.name}\\n", p->mark));')601self.print("_res = NULL;")602self.print(" done:")603with self.indent():604if memoize:605self.print(f"_PyPegen_insert_memo(p, _mark, {node.name}_type, _res);")606self.add_return("_res")607608def _handle_loop_rule_body(self, node: Rule, rhs: Rhs) -> None:609memoize = self._should_memoize(node)610is_repeat1 = node.name.startswith("_loop1")611612with self.indent():613self.add_level()614self._check_for_errors()615self.print("void *_res = NULL;")616if memoize:617self.print(f"if (_PyPegen_is_memoized(p, {node.name}_type, &_res)) {{")618with self.indent():619self.add_return("_res")620self.print("}")621self.print("int _mark = p->mark;")622if memoize:623self.print("int _start_mark = p->mark;")624self.print("void **_children = PyMem_Malloc(sizeof(void *));")625self.out_of_memory_return(f"!_children")626self.print("Py_ssize_t _children_capacity = 1;")627self.print("Py_ssize_t _n = 0;")628if any(alt.action and "EXTRA" in alt.action for alt in rhs.alts):629self._set_up_token_start_metadata_extraction()630self.visit(631rhs,632is_loop=True,633is_gather=node.is_gather(),634rulename=node.name,635)636if is_repeat1:637self.print("if (_n == 0 || p->error_indicator) {")638with self.indent():639self.print("PyMem_Free(_children);")640self.add_return("NULL")641self.print("}")642self.print("asdl_seq *_seq = (asdl_seq*)_Py_asdl_generic_seq_new(_n, p->arena);")643self.out_of_memory_return(f"!_seq", cleanup_code="PyMem_Free(_children);")644self.print("for (int i = 0; i < _n; i++) asdl_seq_SET_UNTYPED(_seq, i, _children[i]);")645self.print("PyMem_Free(_children);")646if memoize and node.name:647self.print(f"_PyPegen_insert_memo(p, _start_mark, {node.name}_type, _seq);")648self.add_return("_seq")649650def visit_Rule(self, node: Rule) -> None:651is_loop = node.is_loop()652is_gather = node.is_gather()653rhs = node.flatten()654if is_loop or is_gather:655result_type = "asdl_seq *"656elif node.type:657result_type = node.type658else:659result_type = "void *"660661for line in str(node).splitlines():662self.print(f"// {line}")663if node.left_recursive and node.leader:664self.print(f"static {result_type} {node.name}_raw(Parser *);")665666self.print(f"static {result_type}")667self.print(f"{node.name}_rule(Parser *p)")668669if node.left_recursive and node.leader:670self._set_up_rule_memoization(node, result_type)671672self.print("{")673674if node.name.endswith("without_invalid"):675with self.indent():676self.print("int _prev_call_invalid = p->call_invalid_rules;")677self.print("p->call_invalid_rules = 0;")678self.cleanup_statements.append("p->call_invalid_rules = _prev_call_invalid;")679680if is_loop:681self._handle_loop_rule_body(node, rhs)682else:683self._handle_default_rule_body(node, rhs, result_type)684685if node.name.endswith("without_invalid"):686self.cleanup_statements.pop()687688self.print("}")689690def visit_NamedItem(self, node: NamedItem) -> None:691call = self.callmakervisitor.generate_call(node)692if call.assigned_variable:693call.assigned_variable = self.dedupe(call.assigned_variable)694self.print(call)695696def visit_Rhs(697self, node: Rhs, is_loop: bool, is_gather: bool, rulename: Optional[str]698) -> None:699if is_loop:700assert len(node.alts) == 1701for alt in node.alts:702self.visit(alt, is_loop=is_loop, is_gather=is_gather, rulename=rulename)703704def join_conditions(self, keyword: str, node: Any) -> None:705self.print(f"{keyword} (")706with self.indent():707first = True708for item in node.items:709if first:710first = False711else:712self.print("&&")713self.visit(item)714self.print(")")715716def emit_action(self, node: Alt, cleanup_code: Optional[str] = None) -> None:717self.print(f"_res = {node.action};")718719self.print("if (_res == NULL && PyErr_Occurred()) {")720with self.indent():721self.print("p->error_indicator = 1;")722if cleanup_code:723self.print(cleanup_code)724self.add_return("NULL")725self.print("}")726727if self.debug:728self.print(729f'D(fprintf(stderr, "Hit with action [%d-%d]: %s\\n", _mark, p->mark, "{node}"));'730)731732def emit_default_action(self, is_gather: bool, node: Alt) -> None:733if len(self.local_variable_names) > 1:734if is_gather:735assert len(self.local_variable_names) == 2736self.print(737f"_res = _PyPegen_seq_insert_in_front(p, "738f"{self.local_variable_names[0]}, {self.local_variable_names[1]});"739)740else:741if self.debug:742self.print(743f'D(fprintf(stderr, "Hit without action [%d:%d]: %s\\n", _mark, p->mark, "{node}"));'744)745self.print(746f"_res = _PyPegen_dummy_name(p, {', '.join(self.local_variable_names)});"747)748else:749if self.debug:750self.print(751f'D(fprintf(stderr, "Hit with default action [%d:%d]: %s\\n", _mark, p->mark, "{node}"));'752)753self.print(f"_res = {self.local_variable_names[0]};")754755def emit_dummy_action(self) -> None:756self.print("_res = _PyPegen_dummy_name(p);")757758def handle_alt_normal(self, node: Alt, is_gather: bool, rulename: Optional[str]) -> None:759self.join_conditions(keyword="if", node=node)760self.print("{")761# We have parsed successfully all the conditions for the option.762with self.indent():763node_str = str(node).replace('"', '\\"')764self.print(765f'D(fprintf(stderr, "%*c+ {rulename}[%d-%d]: %s succeeded!\\n", p->level, \' \', _mark, p->mark, "{node_str}"));'766)767# Prepare to emit the rule action and do so768if node.action and "EXTRA" in node.action:769self._set_up_token_end_metadata_extraction()770if self.skip_actions:771self.emit_dummy_action()772elif node.action:773self.emit_action(node)774else:775self.emit_default_action(is_gather, node)776777# As the current option has parsed correctly, do not continue with the rest.778self.print(f"goto done;")779self.print("}")780781def handle_alt_loop(self, node: Alt, is_gather: bool, rulename: Optional[str]) -> None:782# Condition of the main body of the alternative783self.join_conditions(keyword="while", node=node)784self.print("{")785# We have parsed successfully one item!786with self.indent():787# Prepare to emit the rule action and do so788if node.action and "EXTRA" in node.action:789self._set_up_token_end_metadata_extraction()790if self.skip_actions:791self.emit_dummy_action()792elif node.action:793self.emit_action(node, cleanup_code="PyMem_Free(_children);")794else:795self.emit_default_action(is_gather, node)796797# Add the result of rule to the temporary buffer of children. This buffer798# will populate later an asdl_seq with all elements to return.799self.print("if (_n == _children_capacity) {")800with self.indent():801self.print("_children_capacity *= 2;")802self.print(803"void **_new_children = PyMem_Realloc(_children, _children_capacity*sizeof(void *));"804)805self.out_of_memory_return(f"!_new_children", cleanup_code="PyMem_Free(_children);")806self.print("_children = _new_children;")807self.print("}")808self.print("_children[_n++] = _res;")809self.print("_mark = p->mark;")810self.print("}")811812def visit_Alt(813self, node: Alt, is_loop: bool, is_gather: bool, rulename: Optional[str]814) -> None:815if len(node.items) == 1 and str(node.items[0]).startswith("invalid_"):816self.print(f"if (p->call_invalid_rules) {{ // {node}")817else:818self.print(f"{{ // {node}")819with self.indent():820self._check_for_errors()821node_str = str(node).replace('"', '\\"')822self.print(823f'D(fprintf(stderr, "%*c> {rulename}[%d-%d]: %s\\n", p->level, \' \', _mark, p->mark, "{node_str}"));'824)825# Prepare variable declarations for the alternative826vars = self.collect_vars(node)827for v, var_type in sorted(item for item in vars.items() if item[0] is not None):828if not var_type:829var_type = "void *"830else:831var_type += " "832if v == "_cut_var":833v += " = 0" # cut_var must be initialized834self.print(f"{var_type}{v};")835if v and v.startswith("_opt_var"):836self.print(f"UNUSED({v}); // Silence compiler warnings")837838with self.local_variable_context():839if is_loop:840self.handle_alt_loop(node, is_gather, rulename)841else:842self.handle_alt_normal(node, is_gather, rulename)843844self.print("p->mark = _mark;")845node_str = str(node).replace('"', '\\"')846self.print(847f"D(fprintf(stderr, \"%*c%s {rulename}[%d-%d]: %s failed!\\n\", p->level, ' ',\n"848f' p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "{node_str}"));'849)850if "_cut_var" in vars:851self.print("if (_cut_var) {")852with self.indent():853self.add_return("NULL")854self.print("}")855self.print("}")856857def collect_vars(self, node: Alt) -> Dict[Optional[str], Optional[str]]:858types = {}859with self.local_variable_context():860for item in node.items:861name, type = self.add_var(item)862types[name] = type863return types864865def add_var(self, node: NamedItem) -> Tuple[Optional[str], Optional[str]]:866call = self.callmakervisitor.generate_call(node.item)867name = node.name if node.name else call.assigned_variable868if name is not None:869name = self.dedupe(name)870return_type = call.return_type if node.type is None else node.type871return name, return_type872873874