123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- #!/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 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):
- inf = open(args.input, 'r') if args.input else sys.stdin
- outf = open(args.output, 'w') if args.output else sys.stdout
- 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())
|