mirror of https://github.com/python/cpython
279 lines
9.2 KiB
Python
279 lines
9.2 KiB
Python
import re
|
|
|
|
from ._regexes import (
|
|
LOCAL as _LOCAL,
|
|
LOCAL_STATICS as _LOCAL_STATICS,
|
|
)
|
|
from ._common import (
|
|
log_match,
|
|
parse_var_decl,
|
|
set_capture_groups,
|
|
match_paren,
|
|
)
|
|
from ._compound_decl_body import DECL_BODY_PARSERS
|
|
|
|
|
|
LOCAL = set_capture_groups(_LOCAL, (
|
|
'EMPTY',
|
|
'INLINE_LEADING',
|
|
'INLINE_PRE',
|
|
'INLINE_KIND',
|
|
'INLINE_NAME',
|
|
'STORAGE',
|
|
'VAR_DECL',
|
|
'VAR_INIT',
|
|
'VAR_ENDING',
|
|
'COMPOUND_BARE',
|
|
'COMPOUND_LABELED',
|
|
'COMPOUND_PAREN',
|
|
'BLOCK_LEADING',
|
|
'BLOCK_OPEN',
|
|
'SIMPLE_STMT',
|
|
'SIMPLE_ENDING',
|
|
'BLOCK_CLOSE',
|
|
))
|
|
LOCAL_RE = re.compile(rf'^ \s* {LOCAL}', re.VERBOSE)
|
|
|
|
|
|
# Note that parse_function_body() still has trouble with a few files
|
|
# in the CPython codebase.
|
|
|
|
def parse_function_body(source, name, anon_name):
|
|
# XXX
|
|
raise NotImplementedError
|
|
|
|
|
|
def parse_function_body(name, text, resolve, source, anon_name, parent):
|
|
raise NotImplementedError
|
|
# For now we do not worry about locals declared in for loop "headers".
|
|
depth = 1;
|
|
while depth > 0:
|
|
m = LOCAL_RE.match(text)
|
|
while not m:
|
|
text, resolve = continue_text(source, text or '{', resolve)
|
|
m = LOCAL_RE.match(text)
|
|
text = text[m.end():]
|
|
(
|
|
empty,
|
|
inline_leading, inline_pre, inline_kind, inline_name,
|
|
storage, decl,
|
|
var_init, var_ending,
|
|
compound_bare, compound_labeled, compound_paren,
|
|
block_leading, block_open,
|
|
simple_stmt, simple_ending,
|
|
block_close,
|
|
) = m.groups()
|
|
|
|
if empty:
|
|
log_match('', m)
|
|
resolve(None, None, None, text)
|
|
yield None, text
|
|
elif inline_kind:
|
|
log_match('', m)
|
|
kind = inline_kind
|
|
name = inline_name or anon_name('inline-')
|
|
data = [] # members
|
|
# We must set the internal "text" from _iter_source() to the
|
|
# start of the inline compound body,
|
|
# Note that this is effectively like a forward reference that
|
|
# we do not emit.
|
|
resolve(kind, None, name, text, None)
|
|
_parse_body = DECL_BODY_PARSERS[kind]
|
|
before = []
|
|
ident = f'{kind} {name}'
|
|
for member, inline, text in _parse_body(text, resolve, source, anon_name, ident):
|
|
if member:
|
|
data.append(member)
|
|
if inline:
|
|
yield from inline
|
|
# un-inline the decl. Note that it might not actually be inline.
|
|
# We handle the case in the "maybe_inline_actual" branch.
|
|
text = f'{inline_leading or ""} {inline_pre or ""} {kind} {name} {text}'
|
|
# XXX Should "parent" really be None for inline type decls?
|
|
yield resolve(kind, data, name, text, None), text
|
|
elif block_close:
|
|
log_match('', m)
|
|
depth -= 1
|
|
resolve(None, None, None, text)
|
|
# XXX This isn't great. Calling resolve() should have
|
|
# cleared the closing bracket. However, some code relies
|
|
# on the yielded value instead of the resolved one. That
|
|
# needs to be fixed.
|
|
yield None, text
|
|
elif compound_bare:
|
|
log_match('', m)
|
|
yield resolve('statement', compound_bare, None, text, parent), text
|
|
elif compound_labeled:
|
|
log_match('', m)
|
|
yield resolve('statement', compound_labeled, None, text, parent), text
|
|
elif compound_paren:
|
|
log_match('', m)
|
|
try:
|
|
pos = match_paren(text)
|
|
except ValueError:
|
|
text = f'{compound_paren} {text}'
|
|
#resolve(None, None, None, text)
|
|
text, resolve = continue_text(source, text, resolve)
|
|
yield None, text
|
|
else:
|
|
head = text[:pos]
|
|
text = text[pos:]
|
|
if compound_paren == 'for':
|
|
# XXX Parse "head" as a compound statement.
|
|
stmt1, stmt2, stmt3 = head.split(';', 2)
|
|
data = {
|
|
'compound': compound_paren,
|
|
'statements': (stmt1, stmt2, stmt3),
|
|
}
|
|
else:
|
|
data = {
|
|
'compound': compound_paren,
|
|
'statement': head,
|
|
}
|
|
yield resolve('statement', data, None, text, parent), text
|
|
elif block_open:
|
|
log_match('', m)
|
|
depth += 1
|
|
if block_leading:
|
|
# An inline block: the last evaluated expression is used
|
|
# in place of the block.
|
|
# XXX Combine it with the remainder after the block close.
|
|
stmt = f'{block_open}{{<expr>}}...;'
|
|
yield resolve('statement', stmt, None, text, parent), text
|
|
else:
|
|
resolve(None, None, None, text)
|
|
yield None, text
|
|
elif simple_ending:
|
|
log_match('', m)
|
|
yield resolve('statement', simple_stmt, None, text, parent), text
|
|
elif var_ending:
|
|
log_match('', m)
|
|
kind = 'variable'
|
|
_, name, vartype = parse_var_decl(decl)
|
|
data = {
|
|
'storage': storage,
|
|
'vartype': vartype,
|
|
}
|
|
after = ()
|
|
if var_ending == ',':
|
|
# It was a multi-declaration, so queue up the next one.
|
|
_, qual, typespec, _ = vartype.values()
|
|
text = f'{storage or ""} {qual or ""} {typespec} {text}'
|
|
yield resolve(kind, data, name, text, parent), text
|
|
if var_init:
|
|
_data = f'{name} = {var_init.strip()}'
|
|
yield resolve('statement', _data, None, text, parent), text
|
|
else:
|
|
# This should be unreachable.
|
|
raise NotImplementedError
|
|
|
|
|
|
#############################
|
|
# static local variables
|
|
|
|
LOCAL_STATICS = set_capture_groups(_LOCAL_STATICS, (
|
|
'INLINE_LEADING',
|
|
'INLINE_PRE',
|
|
'INLINE_KIND',
|
|
'INLINE_NAME',
|
|
'STATIC_DECL',
|
|
'STATIC_INIT',
|
|
'STATIC_ENDING',
|
|
'DELIM_LEADING',
|
|
'BLOCK_OPEN',
|
|
'BLOCK_CLOSE',
|
|
'STMT_END',
|
|
))
|
|
LOCAL_STATICS_RE = re.compile(rf'^ \s* {LOCAL_STATICS}', re.VERBOSE)
|
|
|
|
|
|
def parse_function_statics(source, func, anon_name):
|
|
# For now we do not worry about locals declared in for loop "headers".
|
|
depth = 1;
|
|
while depth > 0:
|
|
for srcinfo in source:
|
|
m = LOCAL_STATICS_RE.match(srcinfo.text)
|
|
if m:
|
|
break
|
|
else:
|
|
# We ran out of lines.
|
|
if srcinfo is not None:
|
|
srcinfo.done()
|
|
return
|
|
for item, depth in _parse_next_local_static(m, srcinfo,
|
|
anon_name, func, depth):
|
|
if callable(item):
|
|
parse_body = item
|
|
yield from parse_body(source)
|
|
elif item is not None:
|
|
yield item
|
|
|
|
|
|
def _parse_next_local_static(m, srcinfo, anon_name, func, depth):
|
|
(inline_leading, inline_pre, inline_kind, inline_name,
|
|
static_decl, static_init, static_ending,
|
|
_delim_leading,
|
|
block_open,
|
|
block_close,
|
|
stmt_end,
|
|
) = m.groups()
|
|
remainder = srcinfo.text[m.end():]
|
|
|
|
if inline_kind:
|
|
log_match('func inline', m)
|
|
kind = inline_kind
|
|
name = inline_name or anon_name('inline-')
|
|
# Immediately emit a forward declaration.
|
|
yield srcinfo.resolve(kind, name=name, data=None), depth
|
|
|
|
# un-inline the decl. Note that it might not actually be inline.
|
|
# We handle the case in the "maybe_inline_actual" branch.
|
|
srcinfo.nest(
|
|
remainder,
|
|
f'{inline_leading or ""} {inline_pre or ""} {kind} {name}'
|
|
)
|
|
def parse_body(source):
|
|
_parse_body = DECL_BODY_PARSERS[kind]
|
|
|
|
data = [] # members
|
|
ident = f'{kind} {name}'
|
|
for item in _parse_body(source, anon_name, ident):
|
|
if item.kind == 'field':
|
|
data.append(item)
|
|
else:
|
|
yield item
|
|
# XXX Should "parent" really be None for inline type decls?
|
|
yield srcinfo.resolve(kind, data, name, parent=None)
|
|
|
|
srcinfo.resume()
|
|
yield parse_body, depth
|
|
|
|
elif static_decl:
|
|
log_match('local variable', m)
|
|
_, name, data = parse_var_decl(static_decl)
|
|
|
|
yield srcinfo.resolve('variable', data, name, parent=func), depth
|
|
|
|
if static_init:
|
|
srcinfo.advance(f'{name} {static_init} {remainder}')
|
|
elif static_ending == ',':
|
|
# It was a multi-declaration, so queue up the next one.
|
|
_, qual, typespec, _ = data.values()
|
|
srcinfo.advance(f'static {qual or ""} {typespec} {remainder}')
|
|
else:
|
|
srcinfo.advance('')
|
|
|
|
else:
|
|
log_match('func other', m)
|
|
if block_open:
|
|
depth += 1
|
|
elif block_close:
|
|
depth -= 1
|
|
elif stmt_end:
|
|
pass
|
|
else:
|
|
# This should be unreachable.
|
|
raise NotImplementedError
|
|
srcinfo.advance(remainder)
|
|
yield None, depth
|