From 44036f7e1c713f5bf6202c8d8df61b15e8f64591 Mon Sep 17 00:00:00 2001 From: t11230 Date: Sat, 8 Aug 2020 14:19:38 -0400 Subject: [PATCH] Added support for designated range initializers - Also added ability to round-trip test cases --- pycparserext/ext_c_generator.py | 3 ++ pycparserext/ext_c_parser.py | 28 +++++++++++++++ test/test_pycparserext.py | 64 +++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/pycparserext/ext_c_generator.py b/pycparserext/ext_c_generator.py index 751905a..3ecccad 100644 --- a/pycparserext/ext_c_generator.py +++ b/pycparserext/ext_c_generator.py @@ -138,6 +138,9 @@ def visit_TypeOfExpression(self, n): def visit_TypeList(self, n): return ', '.join(self.visit(ch) for ch in n.types) + def visit_RangeExpression(self, n): + return '%s ... %s' % (self.visit(n.first), self.visit(n.last)) + class GNUCGenerator(GnuCGenerator): def __init__(self): diff --git a/pycparserext/ext_c_parser.py b/pycparserext/ext_c_parser.py index 51d9b64..00fad2a 100644 --- a/pycparserext/ext_c_parser.py +++ b/pycparserext/ext_c_parser.py @@ -158,6 +158,29 @@ def __iter__(self): attr_names = () +class RangeExpression(c_ast.Node): + def __init__(self, first, last, coord=None): + self.first = first + self.last = last + self.coord = coord + + def children(self): + nodelist = [] + if self.first is not None: + nodelist.append(("first", self.first)) + if self.last is not None: + nodelist.append(("last", self.last)) + return tuple(nodelist) + + def __iter__(self): + if self.first is not None: + yield self.first + if self.last is not None: + yield self.last + + attr_names = () + + # These are the same as pycparser's, but it does *not* declare __slots__-- # so we can poke in attributes at our leisure. class TypeDeclExt(c_ast.TypeDecl): @@ -512,6 +535,11 @@ def p_struct_declaration_list_1(self, p): """ struct_declaration_list : empty """ p[0] = None + def p_range_designator(self, p): + """ designator : LBRACKET constant_expression ELLIPSIS constant_expression RBRACKET + """ + p[0] = RangeExpression(p[2], p[4], coord=self._coord(p.lineno(1))) + # }}} diff --git a/test/test_pycparserext.py b/test/test_pycparserext.py index f415930..0ab21f8 100644 --- a/test/test_pycparserext.py +++ b/test/test_pycparserext.py @@ -2,6 +2,53 @@ import pytest +# Inspired from pycparser's compare_asts test function +def _compare_asts(first, second): + if type(first) is not type(second): + return False + + if isinstance(first, tuple): + if first[0] != second[0]: + return False + + return _compare_asts(first[1], second[1]) + + for attr in first.attr_names: + if getattr(first, attr) != getattr(second, attr): + return False + + for i, c1 in enumerate(first.children()): + if not _compare_asts(c1, second.children()[i]): + return False + return True + + +def _round_trip_matches(src): + from pycparserext.ext_c_parser import GnuCParser + from pycparserext.ext_c_generator import GnuCGenerator + + p = GnuCParser() + + first_ast = p.parse(src) + + gen = GnuCGenerator().visit(first_ast) + + second_ast = p.parse(gen) + + if not _compare_asts(first_ast, second_ast): + print('First AST:') + first_ast.show() + + print('Generated code:') + print(gen) + + print('Second AST:') + second_ast.show() + + return False + return True + + def test_asm_volatile_1(): src = """ void read_tsc(void) { @@ -374,6 +421,23 @@ def test_double_pointer(): assert gen.visit(ast).find("func_with_p2pp(const char *, Error **)") != -1 +def test_designated_initializers(): + src = """ + int a[6] = { [4] = 29, [2] = 15 }; + + int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 }; + + int v1 = 5, v2 = 6; + int b[] = { [1] = v1, v2, [4] = v4 }; + + struct foo { int x; int y; }; + struct foo bar[10] = { [1].y = 5, [2].x = 1, [0].x = 3 }; + + char char_map[256] = { [0 ... 255] = '?', ['0' ... '9'] = 'X' }; + """ + assert _round_trip_matches(src) + + def test_node_visitor(): from pycparser.c_ast import NodeVisitor