2020-10-22 21:42:51 -03:00
|
|
|
import re
|
|
|
|
|
|
|
|
from ._regexes import (
|
|
|
|
_ind,
|
|
|
|
STRING_LITERAL,
|
|
|
|
VAR_DECL as _VAR_DECL,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2023-03-06 22:40:09 -04:00
|
|
|
def log_match(group, m, depth_before=None, depth_after=None):
|
2020-10-22 21:42:51 -03:00
|
|
|
from . import _logger
|
2023-03-06 22:40:09 -04:00
|
|
|
|
|
|
|
if m is not None:
|
|
|
|
text = m.group(0)
|
|
|
|
if text.startswith(('(', ')')) or text.endswith(('(', ')')):
|
|
|
|
_logger.debug(f'matched <{group}> ({text!r})')
|
|
|
|
else:
|
|
|
|
_logger.debug(f'matched <{group}> ({text})')
|
|
|
|
|
|
|
|
elif depth_before is not None or depth_after is not None:
|
|
|
|
if depth_before is None:
|
|
|
|
depth_before = '???'
|
|
|
|
elif depth_after is None:
|
|
|
|
depth_after = '???'
|
|
|
|
_logger.log(1, f'depth: %s -> %s', depth_before, depth_after)
|
|
|
|
|
2022-08-17 19:54:59 -03:00
|
|
|
else:
|
2023-03-06 22:40:09 -04:00
|
|
|
raise NotImplementedError('this should not have been hit')
|
2020-10-22 21:42:51 -03:00
|
|
|
|
|
|
|
|
|
|
|
#############################
|
|
|
|
# regex utils
|
|
|
|
|
|
|
|
def set_capture_group(pattern, group, *, strict=True):
|
|
|
|
old = f'(?: # <{group}>'
|
|
|
|
if strict and f'(?: # <{group}>' not in pattern:
|
|
|
|
raise ValueError(f'{old!r} not found in pattern')
|
|
|
|
return pattern.replace(old, f'( # <{group}>', 1)
|
|
|
|
|
|
|
|
|
|
|
|
def set_capture_groups(pattern, groups, *, strict=True):
|
|
|
|
for group in groups:
|
|
|
|
pattern = set_capture_group(pattern, group, strict=strict)
|
|
|
|
return pattern
|
|
|
|
|
|
|
|
|
|
|
|
#############################
|
|
|
|
# syntax-related utils
|
|
|
|
|
|
|
|
_PAREN_RE = re.compile(rf'''
|
|
|
|
(?:
|
|
|
|
(?:
|
|
|
|
[^'"()]*
|
|
|
|
{_ind(STRING_LITERAL, 3)}
|
|
|
|
)*
|
|
|
|
[^'"()]*
|
|
|
|
(?:
|
|
|
|
( [(] )
|
|
|
|
|
|
|
|
|
( [)] )
|
|
|
|
)
|
|
|
|
)
|
|
|
|
''', re.VERBOSE)
|
|
|
|
|
|
|
|
|
|
|
|
def match_paren(text, depth=0):
|
|
|
|
pos = 0
|
|
|
|
while (m := _PAREN_RE.match(text, pos)):
|
|
|
|
pos = m.end()
|
|
|
|
_open, _close = m.groups()
|
|
|
|
if _open:
|
|
|
|
depth += 1
|
|
|
|
else: # _close
|
|
|
|
depth -= 1
|
|
|
|
if depth == 0:
|
|
|
|
return pos
|
|
|
|
else:
|
|
|
|
raise ValueError(f'could not find matching parens for {text!r}')
|
|
|
|
|
|
|
|
|
|
|
|
VAR_DECL = set_capture_groups(_VAR_DECL, (
|
|
|
|
'STORAGE',
|
|
|
|
'TYPE_QUAL',
|
|
|
|
'TYPE_SPEC',
|
|
|
|
'DECLARATOR',
|
|
|
|
'IDENTIFIER',
|
|
|
|
'WRAPPED_IDENTIFIER',
|
|
|
|
'FUNC_IDENTIFIER',
|
|
|
|
))
|
|
|
|
|
|
|
|
|
|
|
|
def parse_var_decl(decl):
|
|
|
|
m = re.match(VAR_DECL, decl, re.VERBOSE)
|
|
|
|
(storage, typequal, typespec, declarator,
|
|
|
|
name,
|
|
|
|
wrappedname,
|
|
|
|
funcptrname,
|
|
|
|
) = m.groups()
|
|
|
|
if name:
|
|
|
|
kind = 'simple'
|
|
|
|
elif wrappedname:
|
|
|
|
kind = 'wrapped'
|
|
|
|
name = wrappedname
|
|
|
|
elif funcptrname:
|
|
|
|
kind = 'funcptr'
|
|
|
|
name = funcptrname
|
|
|
|
else:
|
|
|
|
raise NotImplementedError
|
|
|
|
abstract = declarator.replace(name, '')
|
|
|
|
vartype = {
|
|
|
|
'storage': storage,
|
|
|
|
'typequal': typequal,
|
|
|
|
'typespec': typespec,
|
|
|
|
'abstract': abstract,
|
|
|
|
}
|
|
|
|
return (kind, name, vartype)
|
|
|
|
|
|
|
|
|
|
|
|
#############################
|
|
|
|
# parser state utils
|
|
|
|
|
|
|
|
# XXX Drop this or use it!
|
|
|
|
def iter_results(results):
|
|
|
|
if not results:
|
|
|
|
return
|
|
|
|
if callable(results):
|
|
|
|
results = results()
|
|
|
|
|
|
|
|
for result, text in results():
|
|
|
|
if result:
|
|
|
|
yield result, text
|