Path: blob/main/Tools/c-analyzer/c_parser/parser/_func_body.py
12 views
import re12from ._regexes import (3LOCAL as _LOCAL,4LOCAL_STATICS as _LOCAL_STATICS,5)6from ._common import (7log_match,8parse_var_decl,9set_capture_groups,10match_paren,11)12from ._compound_decl_body import DECL_BODY_PARSERS131415LOCAL = set_capture_groups(_LOCAL, (16'EMPTY',17'INLINE_LEADING',18'INLINE_PRE',19'INLINE_KIND',20'INLINE_NAME',21'STORAGE',22'VAR_DECL',23'VAR_INIT',24'VAR_ENDING',25'COMPOUND_BARE',26'COMPOUND_LABELED',27'COMPOUND_PAREN',28'BLOCK_LEADING',29'BLOCK_OPEN',30'SIMPLE_STMT',31'SIMPLE_ENDING',32'BLOCK_CLOSE',33))34LOCAL_RE = re.compile(rf'^ \s* {LOCAL}', re.VERBOSE)353637# Note that parse_function_body() still has trouble with a few files38# in the CPython codebase.3940def parse_function_body(source, name, anon_name):41# XXX42raise NotImplementedError434445def parse_function_body(name, text, resolve, source, anon_name, parent):46raise NotImplementedError47# For now we do not worry about locals declared in for loop "headers".48depth = 1;49while depth > 0:50m = LOCAL_RE.match(text)51while not m:52text, resolve = continue_text(source, text or '{', resolve)53m = LOCAL_RE.match(text)54text = text[m.end():]55(56empty,57inline_leading, inline_pre, inline_kind, inline_name,58storage, decl,59var_init, var_ending,60compound_bare, compound_labeled, compound_paren,61block_leading, block_open,62simple_stmt, simple_ending,63block_close,64) = m.groups()6566if empty:67log_match('', m, depth)68resolve(None, None, None, text)69yield None, text70elif inline_kind:71log_match('', m, depth)72kind = inline_kind73name = inline_name or anon_name('inline-')74data = [] # members75# We must set the internal "text" from _iter_source() to the76# start of the inline compound body,77# Note that this is effectively like a forward reference that78# we do not emit.79resolve(kind, None, name, text, None)80_parse_body = DECL_BODY_PARSERS[kind]81before = []82ident = f'{kind} {name}'83for member, inline, text in _parse_body(text, resolve, source, anon_name, ident):84if member:85data.append(member)86if inline:87yield from inline88# un-inline the decl. Note that it might not actually be inline.89# We handle the case in the "maybe_inline_actual" branch.90text = f'{inline_leading or ""} {inline_pre or ""} {kind} {name} {text}'91# XXX Should "parent" really be None for inline type decls?92yield resolve(kind, data, name, text, None), text93elif block_close:94log_match('', m, depth)95depth -= 196resolve(None, None, None, text)97# XXX This isn't great. Calling resolve() should have98# cleared the closing bracket. However, some code relies99# on the yielded value instead of the resolved one. That100# needs to be fixed.101yield None, text102elif compound_bare:103log_match('', m, depth)104yield resolve('statement', compound_bare, None, text, parent), text105elif compound_labeled:106log_match('', m, depth)107yield resolve('statement', compound_labeled, None, text, parent), text108elif compound_paren:109log_match('', m, depth)110try:111pos = match_paren(text)112except ValueError:113text = f'{compound_paren} {text}'114#resolve(None, None, None, text)115text, resolve = continue_text(source, text, resolve)116yield None, text117else:118head = text[:pos]119text = text[pos:]120if compound_paren == 'for':121# XXX Parse "head" as a compound statement.122stmt1, stmt2, stmt3 = head.split(';', 2)123data = {124'compound': compound_paren,125'statements': (stmt1, stmt2, stmt3),126}127else:128data = {129'compound': compound_paren,130'statement': head,131}132yield resolve('statement', data, None, text, parent), text133elif block_open:134log_match('', m, depth)135depth += 1136if block_leading:137# An inline block: the last evaluated expression is used138# in place of the block.139# XXX Combine it with the remainder after the block close.140stmt = f'{block_open}{{<expr>}}...;'141yield resolve('statement', stmt, None, text, parent), text142else:143resolve(None, None, None, text)144yield None, text145elif simple_ending:146log_match('', m, depth)147yield resolve('statement', simple_stmt, None, text, parent), text148elif var_ending:149log_match('', m, depth)150kind = 'variable'151_, name, vartype = parse_var_decl(decl)152data = {153'storage': storage,154'vartype': vartype,155}156after = ()157if var_ending == ',':158# It was a multi-declaration, so queue up the next one.159_, qual, typespec, _ = vartype.values()160text = f'{storage or ""} {qual or ""} {typespec} {text}'161yield resolve(kind, data, name, text, parent), text162if var_init:163_data = f'{name} = {var_init.strip()}'164yield resolve('statement', _data, None, text, parent), text165else:166# This should be unreachable.167raise NotImplementedError168169170#############################171# static local variables172173LOCAL_STATICS = set_capture_groups(_LOCAL_STATICS, (174'INLINE_LEADING',175'INLINE_PRE',176'INLINE_KIND',177'INLINE_NAME',178'STATIC_DECL',179'STATIC_INIT',180'STATIC_ENDING',181'DELIM_LEADING',182'BLOCK_OPEN',183'BLOCK_CLOSE',184'STMT_END',185))186LOCAL_STATICS_RE = re.compile(rf'^ \s* {LOCAL_STATICS}', re.VERBOSE)187188189def parse_function_statics(source, func, anon_name):190# For now we do not worry about locals declared in for loop "headers".191depth = 1;192while depth > 0:193for srcinfo in source:194m = LOCAL_STATICS_RE.match(srcinfo.text)195if m:196break197else:198# We ran out of lines.199if srcinfo is not None:200srcinfo.done()201return202for item, depth in _parse_next_local_static(m, srcinfo,203anon_name, func, depth):204if callable(item):205parse_body = item206yield from parse_body(source)207elif item is not None:208yield item209210211def _parse_next_local_static(m, srcinfo, anon_name, func, depth):212(inline_leading, inline_pre, inline_kind, inline_name,213static_decl, static_init, static_ending,214_delim_leading,215block_open,216block_close,217stmt_end,218) = m.groups()219remainder = srcinfo.text[m.end():]220221if inline_kind:222log_match('func inline', m, depth, depth)223kind = inline_kind224name = inline_name or anon_name('inline-')225# Immediately emit a forward declaration.226yield srcinfo.resolve(kind, name=name, data=None), depth227228# un-inline the decl. Note that it might not actually be inline.229# We handle the case in the "maybe_inline_actual" branch.230srcinfo.nest(231remainder,232f'{inline_leading or ""} {inline_pre or ""} {kind} {name}'233)234def parse_body(source):235_parse_body = DECL_BODY_PARSERS[kind]236237data = [] # members238ident = f'{kind} {name}'239for item in _parse_body(source, anon_name, ident):240if item.kind == 'field':241data.append(item)242else:243yield item244# XXX Should "parent" really be None for inline type decls?245yield srcinfo.resolve(kind, data, name, parent=None)246247srcinfo.resume()248yield parse_body, depth249250elif static_decl:251log_match('local variable', m, depth, depth)252_, name, data = parse_var_decl(static_decl)253254yield srcinfo.resolve('variable', data, name, parent=func), depth255256if static_init:257srcinfo.advance(f'{name} {static_init} {remainder}')258elif static_ending == ',':259# It was a multi-declaration, so queue up the next one.260_, qual, typespec, _ = data.values()261srcinfo.advance(f'static {qual or ""} {typespec} {remainder}')262else:263srcinfo.advance('')264265else:266log_match('func other', m)267if block_open:268log_match('func other', None, depth, depth + 1)269depth += 1270elif block_close:271log_match('func other', None, depth, depth - 1)272depth -= 1273elif stmt_end:274log_match('func other', None, depth, depth)275pass276else:277# This should be unreachable.278raise NotImplementedError279srcinfo.advance(remainder)280yield None, depth281282283