1import re 2 3from ._regexes import ( 4 LOCAL as _LOCAL, 5 LOCAL_STATICS as _LOCAL_STATICS, 6) 7from ._common import ( 8 log_match, 9 parse_var_decl, 10 set_capture_groups, 11 match_paren, 12) 13from ._compound_decl_body import DECL_BODY_PARSERS 14 15 16LOCAL = set_capture_groups(_LOCAL, ( 17 'EMPTY', 18 'INLINE_LEADING', 19 'INLINE_PRE', 20 'INLINE_KIND', 21 'INLINE_NAME', 22 'STORAGE', 23 'VAR_DECL', 24 'VAR_INIT', 25 'VAR_ENDING', 26 'COMPOUND_BARE', 27 'COMPOUND_LABELED', 28 'COMPOUND_PAREN', 29 'BLOCK_LEADING', 30 'BLOCK_OPEN', 31 'SIMPLE_STMT', 32 'SIMPLE_ENDING', 33 'BLOCK_CLOSE', 34)) 35LOCAL_RE = re.compile(rf'^ \s* {LOCAL}', re.VERBOSE) 36 37 38# Note that parse_function_body() still has trouble with a few files 39# in the CPython codebase. 40 41def parse_function_body(source, name, anon_name): 42 # XXX 43 raise NotImplementedError 44 45 46def parse_function_body(name, text, resolve, source, anon_name, parent): 47 raise NotImplementedError 48 # For now we do not worry about locals declared in for loop "headers". 49 depth = 1; 50 while depth > 0: 51 m = LOCAL_RE.match(text) 52 while not m: 53 text, resolve = continue_text(source, text or '{', resolve) 54 m = LOCAL_RE.match(text) 55 text = text[m.end():] 56 ( 57 empty, 58 inline_leading, inline_pre, inline_kind, inline_name, 59 storage, decl, 60 var_init, var_ending, 61 compound_bare, compound_labeled, compound_paren, 62 block_leading, block_open, 63 simple_stmt, simple_ending, 64 block_close, 65 ) = m.groups() 66 67 if empty: 68 log_match('', m) 69 resolve(None, None, None, text) 70 yield None, text 71 elif inline_kind: 72 log_match('', m) 73 kind = inline_kind 74 name = inline_name or anon_name('inline-') 75 data = [] # members 76 # We must set the internal "text" from _iter_source() to the 77 # start of the inline compound body, 78 # Note that this is effectively like a forward reference that 79 # we do not emit. 80 resolve(kind, None, name, text, None) 81 _parse_body = DECL_BODY_PARSERS[kind] 82 before = [] 83 ident = f'{kind} {name}' 84 for member, inline, text in _parse_body(text, resolve, source, anon_name, ident): 85 if member: 86 data.append(member) 87 if inline: 88 yield from inline 89 # un-inline the decl. Note that it might not actually be inline. 90 # We handle the case in the "maybe_inline_actual" branch. 91 text = f'{inline_leading or ""} {inline_pre or ""} {kind} {name} {text}' 92 # XXX Should "parent" really be None for inline type decls? 93 yield resolve(kind, data, name, text, None), text 94 elif block_close: 95 log_match('', m) 96 depth -= 1 97 resolve(None, None, None, text) 98 # XXX This isn't great. Calling resolve() should have 99 # cleared the closing bracket. However, some code relies 100 # on the yielded value instead of the resolved one. That 101 # needs to be fixed. 102 yield None, text 103 elif compound_bare: 104 log_match('', m) 105 yield resolve('statement', compound_bare, None, text, parent), text 106 elif compound_labeled: 107 log_match('', m) 108 yield resolve('statement', compound_labeled, None, text, parent), text 109 elif compound_paren: 110 log_match('', m) 111 try: 112 pos = match_paren(text) 113 except ValueError: 114 text = f'{compound_paren} {text}' 115 #resolve(None, None, None, text) 116 text, resolve = continue_text(source, text, resolve) 117 yield None, text 118 else: 119 head = text[:pos] 120 text = text[pos:] 121 if compound_paren == 'for': 122 # XXX Parse "head" as a compound statement. 123 stmt1, stmt2, stmt3 = head.split(';', 2) 124 data = { 125 'compound': compound_paren, 126 'statements': (stmt1, stmt2, stmt3), 127 } 128 else: 129 data = { 130 'compound': compound_paren, 131 'statement': head, 132 } 133 yield resolve('statement', data, None, text, parent), text 134 elif block_open: 135 log_match('', m) 136 depth += 1 137 if block_leading: 138 # An inline block: the last evaluated expression is used 139 # in place of the block. 140 # XXX Combine it with the remainder after the block close. 141 stmt = f'{block_open}{{<expr>}}...;' 142 yield resolve('statement', stmt, None, text, parent), text 143 else: 144 resolve(None, None, None, text) 145 yield None, text 146 elif simple_ending: 147 log_match('', m) 148 yield resolve('statement', simple_stmt, None, text, parent), text 149 elif var_ending: 150 log_match('', m) 151 kind = 'variable' 152 _, name, vartype = parse_var_decl(decl) 153 data = { 154 'storage': storage, 155 'vartype': vartype, 156 } 157 after = () 158 if var_ending == ',': 159 # It was a multi-declaration, so queue up the next one. 160 _, qual, typespec, _ = vartype.values() 161 text = f'{storage or ""} {qual or ""} {typespec} {text}' 162 yield resolve(kind, data, name, text, parent), text 163 if var_init: 164 _data = f'{name} = {var_init.strip()}' 165 yield resolve('statement', _data, None, text, parent), text 166 else: 167 # This should be unreachable. 168 raise NotImplementedError 169 170 171############################# 172# static local variables 173 174LOCAL_STATICS = set_capture_groups(_LOCAL_STATICS, ( 175 'INLINE_LEADING', 176 'INLINE_PRE', 177 'INLINE_KIND', 178 'INLINE_NAME', 179 'STATIC_DECL', 180 'STATIC_INIT', 181 'STATIC_ENDING', 182 'DELIM_LEADING', 183 'BLOCK_OPEN', 184 'BLOCK_CLOSE', 185 'STMT_END', 186)) 187LOCAL_STATICS_RE = re.compile(rf'^ \s* {LOCAL_STATICS}', re.VERBOSE) 188 189 190def parse_function_statics(source, func, anon_name): 191 # For now we do not worry about locals declared in for loop "headers". 192 depth = 1; 193 while depth > 0: 194 for srcinfo in source: 195 m = LOCAL_STATICS_RE.match(srcinfo.text) 196 if m: 197 break 198 else: 199 # We ran out of lines. 200 if srcinfo is not None: 201 srcinfo.done() 202 return 203 for item, depth in _parse_next_local_static(m, srcinfo, 204 anon_name, func, depth): 205 if callable(item): 206 parse_body = item 207 yield from parse_body(source) 208 elif item is not None: 209 yield item 210 211 212def _parse_next_local_static(m, srcinfo, anon_name, func, depth): 213 (inline_leading, inline_pre, inline_kind, inline_name, 214 static_decl, static_init, static_ending, 215 _delim_leading, 216 block_open, 217 block_close, 218 stmt_end, 219 ) = m.groups() 220 remainder = srcinfo.text[m.end():] 221 222 if inline_kind: 223 log_match('func inline', m) 224 kind = inline_kind 225 name = inline_name or anon_name('inline-') 226 # Immediately emit a forward declaration. 227 yield srcinfo.resolve(kind, name=name, data=None), depth 228 229 # un-inline the decl. Note that it might not actually be inline. 230 # We handle the case in the "maybe_inline_actual" branch. 231 srcinfo.nest( 232 remainder, 233 f'{inline_leading or ""} {inline_pre or ""} {kind} {name}' 234 ) 235 def parse_body(source): 236 _parse_body = DECL_BODY_PARSERS[kind] 237 238 data = [] # members 239 ident = f'{kind} {name}' 240 for item in _parse_body(source, anon_name, ident): 241 if item.kind == 'field': 242 data.append(item) 243 else: 244 yield item 245 # XXX Should "parent" really be None for inline type decls? 246 yield srcinfo.resolve(kind, data, name, parent=None) 247 248 srcinfo.resume() 249 yield parse_body, depth 250 251 elif static_decl: 252 log_match('local variable', m) 253 _, name, data = parse_var_decl(static_decl) 254 255 yield srcinfo.resolve('variable', data, name, parent=func), depth 256 257 if static_init: 258 srcinfo.advance(f'{name} {static_init} {remainder}') 259 elif static_ending == ',': 260 # It was a multi-declaration, so queue up the next one. 261 _, qual, typespec, _ = data.values() 262 srcinfo.advance(f'static {qual or ""} {typespec} {remainder}') 263 else: 264 srcinfo.advance('') 265 266 else: 267 log_match('func other', m) 268 if block_open: 269 depth += 1 270 elif block_close: 271 depth -= 1 272 elif stmt_end: 273 pass 274 else: 275 # This should be unreachable. 276 raise NotImplementedError 277 srcinfo.advance(remainder) 278 yield None, depth 279