Extended prettyasserts.py to support unreachable statements

The main benefit is control over error reporting and avoiding the dive
into stdlib layers when debugging thanks to __builtin_trap().

This changes -p/--pattern -> -a/--assert

And adds -u/--unreachable
This commit is contained in:
Christopher Haster
2024-02-14 01:59:03 -06:00
parent 1422a61d16
commit 738dd86339
2 changed files with 54 additions and 16 deletions
+2 -2
View File
@@ -522,10 +522,10 @@ $(BUILDDIR)/%.s: %.c
$(CC) -S $(CFLAGS) $< -o $@ $(CC) -S $(CFLAGS) $< -o $@
$(BUILDDIR)/%.c: %.a.c $(BUILDDIR)/%.c: %.a.c
./scripts/prettyasserts.py -p LFS_ASSERT $< -o $@ ./scripts/prettyasserts.py -a LFS_ASSERT -u LFS_UNREACHABLE $< -o $@
$(BUILDDIR)/%.c: $(BUILDDIR)/%.a.c $(BUILDDIR)/%.c: $(BUILDDIR)/%.a.c
./scripts/prettyasserts.py -p LFS_ASSERT $< -o $@ ./scripts/prettyasserts.py -a LFS_ASSERT -u LFS_UNREACHABLE $< -o $@
$(BUILDDIR)/%.t.a.c: %.toml $(BUILDDIR)/%.t.a.c: %.toml
./scripts/test.py -c $< $(TESTCFLAGS) -o $@ ./scripts/test.py -c $< $(TESTCFLAGS) -o $@
+52 -14
View File
@@ -25,15 +25,17 @@ CMP = {
} }
LEXEMES = { LEXEMES = {
'ws': [r'(?:\s|\n|#.*?(?<!\\)\n|//.*?(?<!\\)\n|/\*.*?\*/)+'], 'ws': [r'(?:\s|\n|#.*?(?<!\\)\n|//.*?(?<!\\)\n|/\*.*?\*/)+'],
'assert': ['assert'], 'assert': ['__builtin_assert', 'assert'],
'arrow': ['=>'], 'unreachable': ['__builtin_unreachable', 'unreachable'],
'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"], 'arrow': ['=>'],
'paren': ['\(', '\)'], 'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
'cmp': CMP.keys(), 'paren': ['\(', '\)'],
'logic': ['\&\&', '\|\|'], 'cmp': CMP.keys(),
'sep': [':', ';', '\{', '\}', ','], 'logic': ['\&\&', '\|\|'],
'op': ['->', '>>', '<<'], # specifically ops that conflict with cmp 'sep': [':', ';', '\{', '\}', ','],
# specifically ops that conflict with cmp
'op': ['->', '>>', '<<'],
} }
@@ -117,6 +119,14 @@ def write_header(f, limit=LIMIT):
f.writeln(" fflush(NULL);") f.writeln(" fflush(NULL);")
f.writeln("}") f.writeln("}")
f.writeln() f.writeln()
f.writeln("__attribute__((unused))")
f.writeln("static void __pretty_assert_print_unreachable(")
f.writeln(" const char *file, int line) {")
f.writeln(" printf(\"%s:%d:unreachable: \"")
f.writeln(" \"unreachable statement reached\\n\", file, line);")
f.writeln(" fflush(NULL);")
f.writeln("}")
f.writeln()
# write assert macros # write assert macros
for op, cmp in sorted(CMP.items()): for op, cmp in sorted(CMP.items()):
@@ -183,6 +193,12 @@ def write_header(f, limit=LIMIT):
f.writeln(" } \\") f.writeln(" } \\")
f.writeln("} while (0)") f.writeln("} while (0)")
f.writeln() f.writeln()
f.writeln("#define __PRETTY_ASSERT_UNREACHABLE() do { \\")
f.writeln(" __pretty_assert_print_unreachable( \\")
f.writeln(" __FILE__, __LINE__); \\")
f.writeln(" __builtin_trap(); \\")
f.writeln("} while (0)")
f.writeln()
f.writeln() f.writeln()
def mkassert(type, cmp, lh, rh, size=None): def mkassert(type, cmp, lh, rh, size=None):
@@ -193,6 +209,9 @@ def mkassert(type, cmp, lh, rh, size=None):
return ("__PRETTY_ASSERT_%s_%s(%s, %s)" return ("__PRETTY_ASSERT_%s_%s(%s, %s)"
% (type.upper(), cmp.upper(), lh, rh)) % (type.upper(), cmp.upper(), lh, rh))
def mkunreachable():
return "__PRETTY_ASSERT_UNREACHABLE()"
# simple recursive descent parser # simple recursive descent parser
class ParseFailure(Exception): class ParseFailure(Exception):
@@ -311,6 +330,13 @@ def p_assert(p):
p.expect(')') p.expect(')')
return mkassert('bool', 'eq', lh, 'true') return mkassert('bool', 'eq', lh, 'true')
def p_unreachable(p):
# unreachable()?
p.expect('unreachable') ; p.accept('ws')
p.expect('(') ; p.accept('ws')
p.expect(')')
return mkunreachable()
def p_expr(p): def p_expr(p):
res = [] res = []
while True: while True:
@@ -330,6 +356,13 @@ def p_expr(p):
except ParseFailure: except ParseFailure:
p.pop(state) p.pop(state)
res.append(p.expect('assert')) res.append(p.expect('assert'))
elif p.lookahead('unreachable'):
state = p.push()
try:
res.append(p_unreachable(p))
except ParseFailure:
p.pop(state)
res.append(p.expect('unreachable'))
elif p.accept('string', 'op', 'ws', None): elif p.accept('string', 'op', 'ws', None):
res.append(p.m) res.append(p.m)
else: else:
@@ -388,11 +421,12 @@ def p_stmt(p):
else: else:
return ws + lh return ws + lh
def main(input=None, output=None, pattern=[], limit=LIMIT): def main(input=None, output=None, assert_=[], unreachable=[], limit=LIMIT):
with openio(input or '-', 'r') as in_f: with openio(input or '-', 'r') as in_f:
# create parser # create parser
lexemes = LEXEMES.copy() lexemes = LEXEMES.copy()
lexemes['assert'] += pattern lexemes['assert'] += assert_
lexemes['unreachable'] += unreachable
p = Parser(in_f, lexemes) p = Parser(in_f, lexemes)
with openio(output or '-', 'w') as f: with openio(output or '-', 'w') as f:
@@ -436,10 +470,14 @@ if __name__ == "__main__":
required=True, required=True,
help="Output C file.") help="Output C file.")
parser.add_argument( parser.add_argument(
'-p', '--pattern', '-a', '--assert',
dest='assert_',
action='append', action='append',
help="Regex patterns to search for starting an assert statement. This" help="Additional symbols for assert statements.")
" implicitly includes \"assert\" and \"=>\".") parser.add_argument(
'-u', '--unreachable',
action='append',
help="Additional symbols for unreachable statements.")
parser.add_argument( parser.add_argument(
'-l', '--limit', '-l', '--limit',
type=lambda x: int(x, 0), type=lambda x: int(x, 0),