Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/littlefs-project/littlefs.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Haster <geky@geky.net>2022-08-16 19:41:46 +0300
committerChristopher Haster <geky@geky.net>2022-08-16 19:41:46 +0300
commitb08463f8de7ec7d698a046ede60b293b3cb4d6f7 (patch)
tree5596667559621658bf9a86587f3f8dfccfca11f9 /scripts
parent92eee8e6cd04328bcf2aaf88f17c418dd2a0545f (diff)
Reworked scripts/pretty_asserts.py a bit
- Renamed explode_asserts.py -> pretty_asserts.py, this name is hopefully a bit more descriptive - Small cleanup of the parser rules - Added recognization of memcmp/strcmp => 0 statements and generate the relevant memory inspecting assert messages I attempted to fix the incorrect column numbers for the generated asserts, but unfortunately this didn't go anywhere and I don't think it's actually possible. There is no column control analogous to the #line directive. I thought you might be able to intermix #line directives to put arguments at the right column like so: assert(a == b); __PRETTY_ASSERT_INT_EQ( #line 1 a, #line 1 b); But this doesn't work as preprocessor directives are not allowed in macros arguments in standard C. Unfortunately this is probably not possible to fix without better support in the language.
Diffstat (limited to 'scripts')
-rwxr-xr-xscripts/explode_asserts.py391
-rwxr-xr-xscripts/pretty_asserts.py426
2 files changed, 426 insertions, 391 deletions
diff --git a/scripts/explode_asserts.py b/scripts/explode_asserts.py
deleted file mode 100755
index e49dbc0..0000000
--- a/scripts/explode_asserts.py
+++ /dev/null
@@ -1,391 +0,0 @@
-#!/usr/bin/env python3
-
-import re
-import sys
-
-PATTERN = ['LFS_ASSERT', 'assert']
-PREFIX = 'LFS'
-MAXWIDTH = 16
-
-ASSERT = "__{PREFIX}_ASSERT_{TYPE}_{COMP}"
-FAIL = """
-__attribute__((unused))
-static void __{prefix}_assert_fail_{type}(
- const char *file, int line, const char *comp,
- {ctype} lh, size_t lsize,
- {ctype} rh, size_t rsize) {{
- printf("%s:%d:assert: assert failed with ", file, line);
- __{prefix}_assert_print_{type}(lh, lsize);
- printf(", expected %s ", comp);
- __{prefix}_assert_print_{type}(rh, rsize);
- printf("\\n");
- fflush(NULL);
- raise(SIGABRT);
-}}
-"""
-
-COMP = {
- '==': 'eq',
- '!=': 'ne',
- '<=': 'le',
- '>=': 'ge',
- '<': 'lt',
- '>': 'gt',
-}
-
-TYPE = {
- 'int': {
- 'ctype': 'intmax_t',
- 'fail': FAIL,
- 'print': """
- __attribute__((unused))
- static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
- (void)size;
- printf("%"PRIiMAX, v);
- }}
- """,
- 'assert': """
- #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
- do {{
- __typeof__(lh) _lh = lh;
- __typeof__(lh) _rh = (__typeof__(lh))rh;
- if (!(_lh {op} _rh)) {{
- __{prefix}_assert_fail_{type}(file, line, "{comp}",
- (intmax_t)_lh, 0, (intmax_t)_rh, 0);
- }}
- }} while (0)
- """
- },
- 'bool': {
- 'ctype': 'bool',
- 'fail': FAIL,
- 'print': """
- __attribute__((unused))
- static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
- (void)size;
- printf("%s", v ? "true" : "false");
- }}
- """,
- 'assert': """
- #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
- do {{
- bool _lh = !!(lh);
- bool _rh = !!(rh);
- if (!(_lh {op} _rh)) {{
- __{prefix}_assert_fail_{type}(file, line, "{comp}",
- _lh, 0, _rh, 0);
- }}
- }} while (0)
- """
- },
- 'mem': {
- 'ctype': 'const void *',
- 'fail': FAIL,
- 'print': """
- __attribute__((unused))
- static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
- const uint8_t *s = v;
- printf("\\\"");
- for (size_t i = 0; i < size && i < {maxwidth}; i++) {{
- if (s[i] >= ' ' && s[i] <= '~') {{
- printf("%c", s[i]);
- }} else {{
- printf("\\\\x%02x", s[i]);
- }}
- }}
- if (size > {maxwidth}) {{
- printf("...");
- }}
- printf("\\\"");
- }}
- """,
- 'assert': """
- #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh, size)
- do {{
- const void *_lh = lh;
- const void *_rh = rh;
- if (!(memcmp(_lh, _rh, size) {op} 0)) {{
- __{prefix}_assert_fail_{type}(file, line, "{comp}",
- _lh, size, _rh, size);
- }}
- }} while (0)
- """
- },
- 'str': {
- 'ctype': 'const char *',
- 'fail': FAIL,
- 'print': """
- __attribute__((unused))
- static void __{prefix}_assert_print_{type}({ctype} v, size_t size) {{
- __{prefix}_assert_print_mem(v, size);
- }}
- """,
- 'assert': """
- #define __{PREFIX}_ASSERT_{TYPE}_{COMP}(file, line, lh, rh)
- do {{
- const char *_lh = lh;
- const char *_rh = rh;
- if (!(strcmp(_lh, _rh) {op} 0)) {{
- __{prefix}_assert_fail_{type}(file, line, "{comp}",
- _lh, strlen(_lh), _rh, strlen(_rh));
- }}
- }} while (0)
- """
- }
-}
-
-def openio(path, mode='r'):
- if path == '-':
- if 'r' in mode:
- return os.fdopen(os.dup(sys.stdin.fileno()), 'r')
- else:
- return os.fdopen(os.dup(sys.stdout.fileno()), 'w')
- else:
- return open(path, mode)
-
-def mkdecls(outf, maxwidth=16):
- outf.write("#include <stdio.h>\n")
- outf.write("#include <stdbool.h>\n")
- outf.write("#include <stdint.h>\n")
- outf.write("#include <inttypes.h>\n")
- outf.write("#include <signal.h>\n")
-
- for type, desc in sorted(TYPE.items()):
- format = {
- 'type': type.lower(), 'TYPE': type.upper(),
- 'ctype': desc['ctype'],
- 'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(),
- 'maxwidth': maxwidth,
- }
- outf.write(re.sub('\s+', ' ',
- desc['print'].strip().format(**format))+'\n')
- outf.write(re.sub('\s+', ' ',
- desc['fail'].strip().format(**format))+'\n')
-
- for op, comp in sorted(COMP.items()):
- format.update({
- 'comp': comp.lower(), 'COMP': comp.upper(),
- 'op': op,
- })
- outf.write(re.sub('\s+', ' ',
- desc['assert'].strip().format(**format))+'\n')
-
-def mkassert(type, comp, lh, rh, size=None):
- format = {
- 'type': type.lower(), 'TYPE': type.upper(),
- 'comp': comp.lower(), 'COMP': comp.upper(),
- 'prefix': PREFIX.lower(), 'PREFIX': PREFIX.upper(),
- 'lh': lh.strip(' '),
- 'rh': rh.strip(' '),
- 'size': size,
- }
- if size:
- return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh}, {size})')
- .format(**format))
- else:
- return ((ASSERT + '(__FILE__, __LINE__, {lh}, {rh})')
- .format(**format))
-
-
-# simple recursive descent parser
-LEX = {
- 'ws': [r'(?:\s|\n|#.*?\n|//.*?\n|/\*.*?\*/)+'],
- 'assert': PATTERN,
- 'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
- 'arrow': ['=>'],
- 'paren': ['\(', '\)'],
- 'op': ['strcmp', 'memcmp', '->'],
- 'comp': ['==', '!=', '<=', '>=', '<', '>'],
- 'logic': ['\&\&', '\|\|'],
- 'sep': [':', ';', '\{', '\}', ','],
-}
-
-class ParseFailure(Exception):
- def __init__(self, expected, found):
- self.expected = expected
- self.found = found
-
- def __str__(self):
- return "expected %r, found %s..." % (
- self.expected, repr(self.found)[:70])
-
-class Parse:
- def __init__(self, inf, lexemes):
- p = '|'.join('(?P<%s>%s)' % (n, '|'.join(l))
- for n, l in lexemes.items())
- p = re.compile(p, re.DOTALL)
- data = inf.read()
- tokens = []
- while True:
- m = p.search(data)
- if m:
- if m.start() > 0:
- tokens.append((None, data[:m.start()]))
- tokens.append((m.lastgroup, m.group()))
- data = data[m.end():]
- else:
- tokens.append((None, data))
- break
- self.tokens = tokens
- self.off = 0
-
- def lookahead(self, *pattern):
- if self.off < len(self.tokens):
- token = self.tokens[self.off]
- if token[0] in pattern or token[1] in pattern:
- self.m = token[1]
- return self.m
- self.m = None
- return self.m
-
- def accept(self, *patterns):
- m = self.lookahead(*patterns)
- if m is not None:
- self.off += 1
- return m
-
- def expect(self, *patterns):
- m = self.accept(*patterns)
- if not m:
- raise ParseFailure(patterns, self.tokens[self.off:])
- return m
-
- def push(self):
- return self.off
-
- def pop(self, state):
- self.off = state
-
-def passert(p):
- def pastr(p):
- p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
- p.expect('strcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
- lh = pexpr(p) ; p.accept('ws')
- p.expect(',') ; p.accept('ws')
- rh = pexpr(p) ; p.accept('ws')
- p.expect(')') ; p.accept('ws')
- comp = p.expect('comp') ; p.accept('ws')
- p.expect('0') ; p.accept('ws')
- p.expect(')')
- return mkassert('str', COMP[comp], lh, rh)
-
- def pamem(p):
- p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
- p.expect('memcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
- lh = pexpr(p) ; p.accept('ws')
- p.expect(',') ; p.accept('ws')
- rh = pexpr(p) ; p.accept('ws')
- p.expect(',') ; p.accept('ws')
- size = pexpr(p) ; p.accept('ws')
- p.expect(')') ; p.accept('ws')
- comp = p.expect('comp') ; p.accept('ws')
- p.expect('0') ; p.accept('ws')
- p.expect(')')
- return mkassert('mem', COMP[comp], lh, rh, size)
-
- def paint(p):
- p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
- lh = pexpr(p) ; p.accept('ws')
- comp = p.expect('comp') ; p.accept('ws')
- rh = pexpr(p) ; p.accept('ws')
- p.expect(')')
- return mkassert('int', COMP[comp], lh, rh)
-
- def pabool(p):
- p.expect('assert') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
- lh = pexprs(p) ; p.accept('ws')
- p.expect(')')
- return mkassert('bool', 'eq', lh, 'true')
-
- def pa(p):
- return p.expect('assert')
-
- state = p.push()
- lastf = None
- for pa in [pastr, pamem, paint, pabool, pa]:
- try:
- return pa(p)
- except ParseFailure as f:
- p.pop(state)
- lastf = f
- else:
- raise lastf
-
-def pexpr(p):
- res = []
- while True:
- if p.accept('('):
- res.append(p.m)
- while True:
- res.append(pexprs(p))
- if p.accept('sep'):
- res.append(p.m)
- else:
- break
- res.append(p.expect(')'))
- elif p.lookahead('assert'):
- res.append(passert(p))
- elif p.accept('assert', 'ws', 'string', 'op', None):
- res.append(p.m)
- else:
- return ''.join(res)
-
-def pexprs(p):
- res = []
- while True:
- res.append(pexpr(p))
- if p.accept('comp', 'logic', ','):
- res.append(p.m)
- else:
- return ''.join(res)
-
-def pstmt(p):
- ws = p.accept('ws') or ''
- lh = pexprs(p)
- if p.accept('=>'):
- rh = pexprs(p)
- return ws + mkassert('int', 'eq', lh, rh)
- else:
- return ws + lh
-
-
-def main(args):
- with openio(args.input or '-', 'r') as inf:
- with openio(args.output or '-', 'w') as outf:
- lexemes = LEX.copy()
- if args.pattern:
- lexemes['assert'] = args.pattern
- p = Parse(inf, lexemes)
-
- # write extra verbose asserts
- mkdecls(outf, maxwidth=args.maxwidth)
- if args.input:
- outf.write("#line %d \"%s\"\n" % (1, args.input))
-
- # parse and write out stmt at a time
- try:
- while True:
- outf.write(pstmt(p))
- if p.accept('sep'):
- outf.write(p.m)
- else:
- break
- except ParseFailure as f:
- pass
-
- for i in range(p.off, len(p.tokens)):
- outf.write(p.tokens[i][1])
-
-if __name__ == "__main__":
- import argparse
- parser = argparse.ArgumentParser(
- description="Cpp step that increases assert verbosity")
- parser.add_argument('input', nargs='?',
- help="Input C file after cpp.")
- parser.add_argument('-o', '--output', required=True,
- help="Output C file.")
- parser.add_argument('-p', '--pattern', action='append',
- help="Patterns to search for starting an assert statement.")
- parser.add_argument('--maxwidth', default=MAXWIDTH, type=int,
- help="Maximum number of characters to display for strcmp and memcmp.")
- main(parser.parse_args())
diff --git a/scripts/pretty_asserts.py b/scripts/pretty_asserts.py
new file mode 100755
index 0000000..ceb206c
--- /dev/null
+++ b/scripts/pretty_asserts.py
@@ -0,0 +1,426 @@
+#!/usr/bin/env python3
+
+import re
+import sys
+
+# NOTE the use of macros here helps keep a consistent stack depth which
+# tools may rely on.
+#
+# If compilation errors are noisy consider using -ftrack-macro-expansion=0.
+#
+
+LIMIT = 16
+
+CMP = {
+ '==': 'eq',
+ '!=': 'ne',
+ '<=': 'le',
+ '>=': 'ge',
+ '<': 'lt',
+ '>': 'gt',
+}
+
+LEXEMES = {
+ 'ws': [r'(?:\s|\n|#.*?\n|//.*?\n|/\*.*?\*/)+'],
+ 'assert': ['assert'],
+ 'arrow': ['=>'],
+ 'string': [r'"(?:\\.|[^"])*"', r"'(?:\\.|[^'])\'"],
+ 'paren': ['\(', '\)'],
+ 'cmp': CMP.keys(),
+ 'logic': ['\&\&', '\|\|'],
+ 'sep': [':', ';', '\{', '\}', ','],
+}
+
+
+def openio(path, mode='r'):
+ if path == '-':
+ if 'r' in mode:
+ return os.fdopen(os.dup(sys.stdin.fileno()), 'r')
+ else:
+ return os.fdopen(os.dup(sys.stdout.fileno()), 'w')
+ else:
+ return open(path, mode)
+
+def write_header(f, limit=LIMIT):
+ f.writeln("// Generated by %s:" % sys.argv[0])
+ f.writeln("//")
+ f.writeln("// %s" % ' '.join(sys.argv))
+ f.writeln("//")
+ f.writeln()
+
+ f.writeln("#include <stdio.h>")
+ f.writeln("#include <stdbool.h>")
+ f.writeln("#include <stdint.h>")
+ f.writeln("#include <inttypes.h>")
+ f.writeln("#include <signal.h>")
+ f.writeln()
+
+ # write print macros
+ f.writeln("__attribute__((unused))")
+ f.writeln("static void __pretty_assert_print_bool(")
+ f.writeln(" const void *v, size_t size) {")
+ f.writeln(" (void)size;")
+ f.writeln(" printf(\"%s\", *(const bool*)v ? \"true\" : \"false\");")
+ f.writeln("}")
+ f.writeln()
+ f.writeln("__attribute__((unused))")
+ f.writeln("static void __pretty_assert_print_int(")
+ f.writeln(" const void *v, size_t size) {")
+ f.writeln(" (void)size;")
+ f.writeln(" printf(\"%\"PRIiMAX, *(const intmax_t*)v);")
+ f.writeln("}")
+ f.writeln()
+ f.writeln("__attribute__((unused))")
+ f.writeln("static void __pretty_assert_print_mem(")
+ f.writeln(" const void *v, size_t size) {")
+ f.writeln(" const uint8_t *v_ = v;")
+ f.writeln(" printf(\"\\\"\");")
+ f.writeln(" for (size_t i = 0; i < size && i < %d; i++) {" % limit)
+ f.writeln(" if (v_[i] >= ' ' && v_[i] <= '~') {")
+ f.writeln(" printf(\"%c\", v_[i]);")
+ f.writeln(" } else {")
+ f.writeln(" printf(\"\\\\x%02x\", v_[i]);")
+ f.writeln(" }")
+ f.writeln(" }")
+ f.writeln(" if (size > %d) {" % limit)
+ f.writeln(" printf(\"...\");")
+ f.writeln(" }")
+ f.writeln(" printf(\"\\\"\");")
+ f.writeln("}")
+ f.writeln()
+ f.writeln("__attribute__((unused))")
+ f.writeln("static void __pretty_assert_print_str(")
+ f.writeln(" const void *v, size_t size) {")
+ f.writeln(" __pretty_assert_print_mem(v, size);")
+ f.writeln("}")
+ f.writeln()
+ f.writeln("__attribute__((unused, noinline))")
+ f.writeln("static void __pretty_assert_fail(")
+ f.writeln(" const char *file, int line,")
+ f.writeln(" void (*type_print_cb)(const void*, size_t),")
+ f.writeln(" const char *cmp,")
+ f.writeln(" const void *lh, size_t lsize,")
+ f.writeln(" const void *rh, size_t rsize) {")
+ f.writeln(" printf(\"%s:%d:assert: assert failed with \", file, line);")
+ f.writeln(" type_print_cb(lh, lsize);")
+ f.writeln(" printf(\", expected %s \", cmp);")
+ f.writeln(" type_print_cb(rh, rsize);")
+ f.writeln(" printf(\"\\n\");")
+ f.writeln(" fflush(NULL);")
+ f.writeln(" raise(SIGABRT);")
+ f.writeln("}")
+ f.writeln()
+
+ # write assert macros
+ for op, cmp in sorted(CMP.items()):
+ f.writeln("#define __PRETTY_ASSERT_BOOL_%s(lh, rh) do { \\"
+ % cmp.upper())
+ f.writeln(" bool _lh = !!(lh); \\")
+ f.writeln(" bool _rh = !!(rh); \\")
+ f.writeln(" if (!(_lh %s _rh)) { \\" % op)
+ f.writeln(" __pretty_assert_fail( \\")
+ f.writeln(" __FILE__, __LINE__, \\")
+ f.writeln(" __pretty_assert_print_bool, \"%s\", \\"
+ % cmp)
+ f.writeln(" &_lh, 0, \\")
+ f.writeln(" &_rh, 0); \\")
+ f.writeln(" } \\")
+ f.writeln("} while (0)")
+ for op, cmp in sorted(CMP.items()):
+ f.writeln("#define __PRETTY_ASSERT_INT_%s(lh, rh) do { \\"
+ % cmp.upper())
+ f.writeln(" __typeof__(lh) _lh = lh; \\")
+ f.writeln(" __typeof__(lh) _rh = rh; \\")
+ f.writeln(" if (!(_lh %s _rh)) { \\" % op)
+ f.writeln(" __pretty_assert_fail( \\")
+ f.writeln(" __FILE__, __LINE__, \\")
+ f.writeln(" __pretty_assert_print_int, \"%s\", \\"
+ % cmp)
+ f.writeln(" &(intmax_t){_lh}, 0, \\")
+ f.writeln(" &(intmax_t){_rh}, 0); \\")
+ f.writeln(" } \\")
+ f.writeln("} while (0)")
+ for op, cmp in sorted(CMP.items()):
+ f.writeln("#define __PRETTY_ASSERT_MEM_%s(lh, rh, size) do { \\"
+ % cmp.upper())
+ f.writeln(" const void *_lh = lh; \\")
+ f.writeln(" const void *_rh = rh; \\")
+ f.writeln(" if (!(memcmp(_lh, _rh, size) %s 0)) { \\" % op)
+ f.writeln(" __pretty_assert_fail( \\")
+ f.writeln(" __FILE__, __LINE__, \\")
+ f.writeln(" __pretty_assert_print_mem, \"%s\", \\"
+ % cmp)
+ f.writeln(" _lh, size, \\")
+ f.writeln(" _rh, size); \\")
+ f.writeln(" } \\")
+ f.writeln("} while (0)")
+ for op, cmp in sorted(CMP.items()):
+ f.writeln("#define __PRETTY_ASSERT_STR_%s(lh, rh) do { \\"
+ % cmp.upper())
+ f.writeln(" const char *_lh = lh; \\")
+ f.writeln(" const char *_rh = rh; \\")
+ f.writeln(" if (!(strcmp(_lh, _rh) %s 0)) { \\" % op)
+ f.writeln(" __pretty_assert_fail( \\")
+ f.writeln(" __FILE__, __LINE__, \\")
+ f.writeln(" __pretty_assert_print_str, \"%s\", \\"
+ % cmp)
+ f.writeln(" _lh, strlen(_lh), \\")
+ f.writeln(" _rh, strlen(_rh)); \\")
+ f.writeln(" } \\")
+ f.writeln("} while (0)")
+ f.writeln()
+ f.writeln()
+
+def mkassert(type, cmp, lh, rh, size=None):
+ if size is not None:
+ return ("__PRETTY_ASSERT_%s_%s(%s, %s, %s)"
+ % (type.upper(), cmp.upper(), lh, rh, size))
+ else:
+ return ("__PRETTY_ASSERT_%s_%s(%s, %s)"
+ % (type.upper(), cmp.upper(), lh, rh))
+
+
+# simple recursive descent parser
+class ParseFailure(Exception):
+ def __init__(self, expected, found):
+ self.expected = expected
+ self.found = found
+
+ def __str__(self):
+ return "expected %r, found %s..." % (
+ self.expected, repr(self.found)[:70])
+
+class Parser:
+ def __init__(self, in_f, lexemes=LEXEMES):
+ p = '|'.join('(?P<%s>%s)' % (n, '|'.join(l))
+ for n, l in lexemes.items())
+ p = re.compile(p, re.DOTALL)
+ data = in_f.read()
+ tokens = []
+ line = 1
+ col = 0
+ while True:
+ m = p.search(data)
+ if m:
+ if m.start() > 0:
+ tokens.append((None, data[:m.start()], line, col))
+ tokens.append((m.lastgroup, m.group(), line, col))
+ data = data[m.end():]
+ else:
+ tokens.append((None, data, line, col))
+ break
+ self.tokens = tokens
+ self.off = 0
+
+ def lookahead(self, *pattern):
+ if self.off < len(self.tokens):
+ token = self.tokens[self.off]
+ if token[0] in pattern or token[1] in pattern:
+ self.m = token[1]
+ return self.m
+ self.m = None
+ return self.m
+
+ def accept(self, *patterns):
+ m = self.lookahead(*patterns)
+ if m is not None:
+ self.off += 1
+ return m
+
+ def expect(self, *patterns):
+ m = self.accept(*patterns)
+ if not m:
+ raise ParseFailure(patterns, self.tokens[self.off:])
+ return m
+
+ def push(self):
+ return self.off
+
+ def pop(self, state):
+ self.off = state
+
+def p_assert(p):
+ state = p.push()
+
+ # assert(memcmp(a,b,size) cmp 0)?
+ try:
+ p.expect('assert') ; p.accept('ws')
+ p.expect('(') ; p.accept('ws')
+ p.expect('memcmp') ; p.accept('ws')
+ p.expect('(') ; p.accept('ws')
+ lh = p_expr(p) ; p.accept('ws')
+ p.expect(',') ; p.accept('ws')
+ rh = p_expr(p) ; p.accept('ws')
+ p.expect(',') ; p.accept('ws')
+ size = p_expr(p) ; p.accept('ws')
+ p.expect(')') ; p.accept('ws')
+ cmp = p.expect('cmp') ; p.accept('ws')
+ p.expect('0') ; p.accept('ws')
+ p.expect(')')
+ return mkassert('mem', CMP[cmp], lh, rh, size)
+ except ParseFailure:
+ p.pop(state)
+
+ # assert(strcmp(a,b) cmp 0)?
+ try:
+ p.expect('assert') ; p.accept('ws')
+ p.expect('(') ; p.accept('ws')
+ p.expect('strcmp') ; p.accept('ws')
+ p.expect('(') ; p.accept('ws')
+ lh = p_expr(p) ; p.accept('ws')
+ p.expect(',') ; p.accept('ws')
+ rh = p_expr(p) ; p.accept('ws')
+ p.expect(')') ; p.accept('ws')
+ cmp = p.expect('cmp') ; p.accept('ws')
+ p.expect('0') ; p.accept('ws')
+ p.expect(')')
+ return mkassert('str', CMP[cmp], lh, rh)
+ except ParseFailure:
+ p.pop(state)
+
+ # assert(a cmp b)?
+ try:
+ p.expect('assert') ; p.accept('ws')
+ p.expect('(') ; p.accept('ws')
+ lh = p_expr(p) ; p.accept('ws')
+ cmp = p.expect('cmp') ; p.accept('ws')
+ rh = p_expr(p) ; p.accept('ws')
+ p.expect(')')
+ return mkassert('int', CMP[cmp], lh, rh)
+ except ParseFailure:
+ p.pop(state)
+
+ # assert(a)?
+ p.expect('assert') ; p.accept('ws')
+ p.expect('(') ; p.accept('ws')
+ lh = p_exprs(p) ; p.accept('ws')
+ p.expect(')')
+ return mkassert('bool', 'eq', lh, 'true')
+
+def p_expr(p):
+ res = []
+ while True:
+ if p.accept('('):
+ res.append(p.m)
+ while True:
+ res.append(p_exprs(p))
+ if p.accept('sep'):
+ res.append(p.m)
+ else:
+ break
+ res.append(p.expect(')'))
+ elif p.lookahead('assert'):
+ state = p.push()
+ try:
+ res.append(p_assert(p))
+ except ParseFailure:
+ p.pop(state)
+ res.append(p.expect('assert'))
+ elif p.accept('string', None, 'ws'):
+ res.append(p.m)
+ else:
+ return ''.join(res)
+
+def p_exprs(p):
+ res = []
+ while True:
+ res.append(p_expr(p))
+ if p.accept('cmp', 'logic', ','):
+ res.append(p.m)
+ else:
+ return ''.join(res)
+
+def p_stmt(p):
+ ws = p.accept('ws') or ''
+
+ # memcmp(lh,rh,size) => 0?
+ if p.lookahead('memcmp'):
+ state = p.push()
+ try:
+ p.expect('memcmp') ; p.accept('ws')
+ p.expect('(') ; p.accept('ws')
+ lh = p_expr(p) ; p.accept('ws')
+ p.expect(',') ; p.accept('ws')
+ rh = p_expr(p) ; p.accept('ws')
+ p.expect(',') ; p.accept('ws')
+ size = p_expr(p) ; p.accept('ws')
+ p.expect(')') ; p.accept('ws')
+ p.expect('=>') ; p.accept('ws')
+ p.expect('0') ; p.accept('ws')
+ return ws + mkassert('mem', 'eq', lh, rh, size)
+ except ParseFailure:
+ p.pop(state)
+
+ # strcmp(lh,rh) => 0?
+ if p.lookahead('strcmp'):
+ state = p.push()
+ try:
+ p.expect('strcmp') ; p.accept('ws') ; p.expect('(') ; p.accept('ws')
+ lh = p_expr(p) ; p.accept('ws')
+ p.expect(',') ; p.accept('ws')
+ rh = p_expr(p) ; p.accept('ws')
+ p.expect(')') ; p.accept('ws')
+ p.expect('=>') ; p.accept('ws')
+ p.expect('0') ; p.accept('ws')
+ return ws + mkassert('str', 'eq', lh, rh)
+ except ParseFailure:
+ p.pop(state)
+
+ # lh => rh?
+ lh = p_exprs(p)
+ if p.accept('=>'):
+ rh = p_exprs(p)
+ return ws + mkassert('int', 'eq', lh, rh)
+ else:
+ return ws + lh
+
+def main(input=None, output=None, pattern=[], limit=LIMIT):
+ with openio(input or '-', 'r') as in_f:
+ # create parser
+ lexemes = LEXEMES.copy()
+ lexemes['assert'] += pattern
+ p = Parser(in_f, lexemes)
+
+ with openio(output or '-', 'w') as f:
+ def writeln(s=''):
+ f.write(s)
+ f.write('\n')
+ f.writeln = writeln
+
+ # write extra verbose asserts
+ write_header(f, limit=limit)
+ if input is not None:
+ f.writeln("#line %d \"%s\"" % (1, input))
+
+ # parse and write out stmt at a time
+ try:
+ while True:
+ f.write(p_stmt(p))
+ if p.accept('sep'):
+ f.write(p.m)
+ else:
+ break
+ except ParseFailure as f:
+ pass
+
+ for i in range(p.off, len(p.tokens)):
+ f.write(p.tokens[i][1])
+
+
+if __name__ == "__main__":
+ import argparse
+ import sys
+ parser = argparse.ArgumentParser(
+ description="Preprocessor that makes asserts easy to debug.")
+ parser.add_argument('input',
+ help="Input C file.")
+ parser.add_argument('-o', '--output', required=True,
+ help="Output C file.")
+ parser.add_argument('-p', '--pattern', action='append',
+ help="Regex patterns to search for starting an assert statement. This"
+ " implicitly includes \"assert\" and \"=>\".")
+ parser.add_argument('-l', '--limit', default=LIMIT, type=int,
+ help="Maximum number of characters to display in strcmp and memcmp.")
+ sys.exit(main(**{k: v
+ for k, v in vars(parser.parse_args()).items()
+ if v is not None}))