| import os |
| import sys |
| import unittest |
| |
| # Run from the root dir |
| sys.path.insert(0, '.') |
| |
| from pycparser import c_parser, c_generator, c_ast, parse_file |
| from tests.test_util import cpp_supported, cpp_path, cpp_args |
| |
| _c_parser = c_parser.CParser( |
| lex_optimize=False, |
| yacc_debug=True, |
| yacc_optimize=False, |
| yacctab='yacctab') |
| |
| |
| def compare_asts(ast1, ast2): |
| """Compares two ASTs recursively just enough for the purpose of testing. |
| |
| Since this function is recursive it also accepts non-ast parameters, in |
| which case it compares them literally (with ==) or recursively (for tuples |
| or lists). |
| """ |
| # After the initial `if`, all the `elif` clauses assume that the types of |
| # ast1 and ast2 are the same. |
| if type(ast1) != type(ast2): |
| return False |
| elif isinstance(ast1, (list, tuple)): |
| if len(ast1) != len(ast2): |
| return False |
| for i in range(len(ast1)): |
| if not compare_asts(ast1[i], ast2[i]): |
| return False |
| return True |
| elif isinstance(ast1, c_ast.Node): |
| for attr in ast1.attr_names: |
| attr1 = getattr(ast1, attr) |
| attr2 = getattr(ast2, attr) |
| if not compare_asts(attr1, attr2): |
| return False |
| children1 = ast1.children() |
| children2 = ast2.children() |
| if len(children1) != len(children2): |
| return False |
| for i in range(len(children1)): |
| if not compare_asts(children1[i], children2[i]): |
| return False |
| return True |
| else: |
| return ast1 == ast2 |
| |
| |
| def parse_to_ast(src): |
| return _c_parser.parse(src) |
| |
| |
| class TestFunctionDeclGeneration(unittest.TestCase): |
| class _FuncDeclVisitor(c_ast.NodeVisitor): |
| def __init__(self): |
| self.stubs = [] |
| |
| def visit_FuncDecl(self, node): |
| gen = c_generator.CGenerator() |
| self.stubs.append(gen.visit(node)) |
| |
| def test_partial_funcdecl_generation(self): |
| src = r''' |
| void noop(void); |
| void *something(void *thing); |
| int add(int x, int y);''' |
| ast = parse_to_ast(src) |
| v = TestFunctionDeclGeneration._FuncDeclVisitor() |
| v.visit(ast) |
| self.assertEqual(len(v.stubs), 3) |
| self.assertTrue(r'void noop(void)' in v.stubs) |
| self.assertTrue(r'void *something(void *thing)' in v.stubs) |
| self.assertTrue(r'int add(int x, int y)' in v.stubs) |
| |
| |
| class TestCtoC(unittest.TestCase): |
| def _run_c_to_c(self, src, *args, **kwargs): |
| ast = parse_to_ast(src) |
| generator = c_generator.CGenerator(*args, **kwargs) |
| return generator.visit(ast) |
| |
| def _assert_ctoc_correct(self, src, *args, **kwargs): |
| """ Checks that the c2c translation was correct by parsing the code |
| generated by c2c for src and comparing the AST with the original |
| AST. |
| |
| Additional arguments are passed to CGenerator.__init__. |
| """ |
| src2 = self._run_c_to_c(src, *args, **kwargs) |
| self.assertTrue(compare_asts(parse_to_ast(src), parse_to_ast(src2)), |
| "{!r} != {!r}".format(src, src2)) |
| return src2 |
| |
| def test_trivial_decls(self): |
| self._assert_ctoc_correct('int a;') |
| self._assert_ctoc_correct('int b, a;') |
| self._assert_ctoc_correct('int c, b, a;') |
| self._assert_ctoc_correct('auto int a;') |
| self._assert_ctoc_correct('register int a;') |
| self._assert_ctoc_correct('_Thread_local int a;') |
| |
| def test_complex_decls(self): |
| self._assert_ctoc_correct('int** (*a)(void);') |
| self._assert_ctoc_correct('int** (*a)(void*, int);') |
| self._assert_ctoc_correct('int (*b)(char * restrict k, float);') |
| self._assert_ctoc_correct('int (*b)(char * _Atomic k, float);') |
| self._assert_ctoc_correct('int (*b)(char * _Atomic volatile k, float);') |
| self._assert_ctoc_correct('int test(const char* const* arg);') |
| self._assert_ctoc_correct('int test(const char** const arg);') |
| |
| def test_alignment(self): |
| self._assert_ctoc_correct('_Alignas(32) int b;') |
| self._assert_ctoc_correct('int _Alignas(32) a;') |
| self._assert_ctoc_correct('_Alignas(32) _Atomic(int) b;') |
| self._assert_ctoc_correct('_Atomic(int) _Alignas(32) b;') |
| self._assert_ctoc_correct('_Alignas(long long) int a;') |
| self._assert_ctoc_correct('int _Alignas(long long) a;') |
| self._assert_ctoc_correct(r''' |
| typedef struct node_t { |
| _Alignas(64) void* next; |
| int data; |
| } node; |
| ''') |
| self._assert_ctoc_correct(r''' |
| typedef struct node_t { |
| void _Alignas(64) * next; |
| int data; |
| } node; |
| ''') |
| |
| def test_ternary(self): |
| self._assert_ctoc_correct(''' |
| int main(void) |
| { |
| int a, b; |
| (a == 0) ? (b = 1) : (b = 2); |
| }''') |
| |
| def test_casts(self): |
| self._assert_ctoc_correct(r''' |
| int main() { |
| int b = (int) f; |
| int c = (int*) f; |
| }''') |
| self._assert_ctoc_correct(r''' |
| int main() { |
| int a = (int) b + 8; |
| int t = (int) c; |
| } |
| ''') |
| |
| def test_initlist(self): |
| self._assert_ctoc_correct('int arr[] = {1, 2, 3};') |
| |
| def test_exprs(self): |
| self._assert_ctoc_correct(''' |
| int main(void) |
| { |
| int a; |
| int b = a++; |
| int c = ++a; |
| int d = a--; |
| int e = --a; |
| }''') |
| |
| def test_statements(self): |
| # note two minuses here |
| self._assert_ctoc_correct(r''' |
| int main() { |
| int a; |
| a = 5; |
| ; |
| b = - - a; |
| return a; |
| }''') |
| |
| def test_struct_decl(self): |
| self._assert_ctoc_correct(r''' |
| typedef struct node_t { |
| struct node_t* next; |
| int data; |
| } node; |
| ''') |
| |
| def test_krstyle(self): |
| self._assert_ctoc_correct(r''' |
| int main(argc, argv) |
| int argc; |
| char** argv; |
| { |
| return 0; |
| } |
| ''') |
| |
| def test_switchcase(self): |
| self._assert_ctoc_correct(r''' |
| int main() { |
| switch (myvar) { |
| case 10: |
| { |
| k = 10; |
| p = k + 1; |
| break; |
| } |
| case 20: |
| case 30: |
| return 20; |
| default: |
| break; |
| } |
| } |
| ''') |
| |
| def test_nest_initializer_list(self): |
| self._assert_ctoc_correct(r''' |
| int main() |
| { |
| int i[1][1] = { { 1 } }; |
| }''') |
| |
| def test_nest_named_initializer(self): |
| self._assert_ctoc_correct(r'''struct test |
| { |
| int i; |
| struct test_i_t |
| { |
| int k; |
| } test_i; |
| int j; |
| }; |
| struct test test_var = {.i = 0, .test_i = {.k = 1}, .j = 2}; |
| ''') |
| |
| def test_expr_list_in_initializer_list(self): |
| self._assert_ctoc_correct(r''' |
| int main() |
| { |
| int i[1] = { (1, 2) }; |
| }''') |
| |
| def test_issue36(self): |
| self._assert_ctoc_correct(r''' |
| int main() { |
| }''') |
| |
| def test_issue37(self): |
| self._assert_ctoc_correct(r''' |
| int main(void) |
| { |
| unsigned size; |
| size = sizeof(size); |
| return 0; |
| }''') |
| |
| def test_issue66(self): |
| # A non-existing body must not be generated |
| # (previous valid behavior, still working) |
| self._assert_ctoc_correct(r''' |
| struct foo; |
| ''') |
| # An empty body must be generated |
| # (added behavior) |
| self._assert_ctoc_correct(r''' |
| struct foo {}; |
| ''') |
| |
| def test_issue83(self): |
| self._assert_ctoc_correct(r''' |
| void x(void) { |
| int i = (9, k); |
| } |
| ''') |
| |
| def test_issue84(self): |
| self._assert_ctoc_correct(r''' |
| void x(void) { |
| for (int i = 0;;) |
| i; |
| } |
| ''') |
| |
| def test_issue246(self): |
| self._assert_ctoc_correct(r''' |
| int array[3] = {[0] = 0, [1] = 1, [1+1] = 2}; |
| ''') |
| |
| def test_noreturn(self): |
| self._assert_ctoc_correct(r''' |
| _Noreturn int x(void) { |
| abort(); |
| } |
| ''') |
| |
| def test_exprlist_with_semi(self): |
| self._assert_ctoc_correct(r''' |
| void x() { |
| if (i < j) |
| tmp = C[i], C[i] = C[j], C[j] = tmp; |
| if (i <= j) |
| i++, j--; |
| } |
| ''') |
| |
| def test_exprlist_with_subexprlist(self): |
| self._assert_ctoc_correct(r''' |
| void x() { |
| (a = b, (b = c, c = a)); |
| } |
| ''') |
| |
| def test_comma_operator_funcarg(self): |
| self._assert_ctoc_correct(r''' |
| void f(int x) { return x; } |
| int main(void) { f((1, 2)); return 0; } |
| ''') |
| |
| def test_comma_op_in_ternary(self): |
| self._assert_ctoc_correct(r''' |
| void f() { |
| (0, 0) ? (0, 0) : (0, 0); |
| } |
| ''') |
| |
| def test_comma_op_assignment(self): |
| self._assert_ctoc_correct(r''' |
| void f() { |
| i = (a, b, c); |
| } |
| ''') |
| |
| def test_pragma(self): |
| self._assert_ctoc_correct(r''' |
| #pragma foo |
| void f() { |
| #pragma bar |
| i = (a, b, c); |
| if (d) |
| #pragma qux |
| j = e; |
| if (d) |
| #pragma qux |
| #pragma quux |
| j = e; |
| } |
| typedef struct s { |
| #pragma baz |
| } s; |
| ''') |
| |
| def test_compound_literal(self): |
| self._assert_ctoc_correct('char **foo = (char *[]){ "x", "y", "z" };') |
| self._assert_ctoc_correct('int i = ++(int){ 1 };') |
| self._assert_ctoc_correct('struct foo_s foo = (struct foo_s){ 1, 2 };') |
| |
| def test_enum(self): |
| self._assert_ctoc_correct(r''' |
| enum e |
| { |
| a, |
| b = 2, |
| c = 3 |
| }; |
| ''') |
| self._assert_ctoc_correct(r''' |
| enum f |
| { |
| g = 4, |
| h, |
| i |
| }; |
| ''') |
| |
| def test_enum_typedef(self): |
| self._assert_ctoc_correct('typedef enum EnumName EnumTypedefName;') |
| |
| def test_generate_struct_union_enum_exception(self): |
| generator = c_generator.CGenerator() |
| self.assertRaises( |
| AssertionError, |
| generator._generate_struct_union_enum, |
| n=c_ast.Struct( |
| name='TestStruct', |
| decls=[], |
| ), |
| name='', |
| ) |
| |
| def test_array_decl(self): |
| self._assert_ctoc_correct('int g(const int a[const 20]){}') |
| ast = parse_to_ast('const int a[const 20];') |
| generator = c_generator.CGenerator() |
| self.assertEqual(generator.visit(ast.ext[0].type), |
| 'const int [const 20]') |
| self.assertEqual(generator.visit(ast.ext[0].type.type), |
| 'const int') |
| |
| def test_ptr_decl(self): |
| src = 'const int ** const x;' |
| self._assert_ctoc_correct(src) |
| ast = parse_to_ast(src) |
| generator = c_generator.CGenerator() |
| self.assertEqual(generator.visit(ast.ext[0].type), |
| 'const int ** const') |
| self.assertEqual(generator.visit(ast.ext[0].type.type), |
| 'const int *') |
| self.assertEqual(generator.visit(ast.ext[0].type.type.type), |
| 'const int') |
| |
| def test_atomic_qual(self): |
| self._assert_ctoc_correct('_Atomic int x;') |
| self._assert_ctoc_correct('_Atomic int* x;') |
| self._assert_ctoc_correct('int* _Atomic x;') |
| |
| # _Atomic specifier gets turned into qualifier. |
| s1 = '_Atomic(int) x;' |
| c1 = self._run_c_to_c(s1) |
| self.assertEqual(c1, '_Atomic int x;\n') |
| self._assert_ctoc_correct(s1) |
| |
| s2 = '_Atomic(int*) x;' |
| c2 = self._run_c_to_c(s2) |
| self.assertEqual(c2, 'int * _Atomic x;\n') |
| self._assert_ctoc_correct(s2) |
| |
| s3 = '_Atomic(_Atomic(int)*) x;' |
| c3 = self._run_c_to_c(s3) |
| self.assertEqual(c3, '_Atomic int * _Atomic x;\n') |
| self._assert_ctoc_correct(s3) |
| |
| # TODO: Regeneration with multiple qualifiers is not fully supported. |
| # REF: https://github.com/eliben/pycparser/issues/433 |
| # self._assert_ctoc_correct('auto const _Atomic(int *) a;') |
| |
| s4 = 'typedef _Atomic(int) atomic_int;' |
| c4 = self._run_c_to_c(s4) |
| self.assertEqual(c4, 'typedef _Atomic int atomic_int;\n') |
| self._assert_ctoc_correct(s4) |
| |
| s5 = 'typedef _Atomic(_Atomic(_Atomic(int (*)(void)) *) *) t;' |
| c5 = self._run_c_to_c(s5) |
| self.assertEqual(c5, 'typedef int (* _Atomic * _Atomic * _Atomic t)(void);\n') |
| self._assert_ctoc_correct(s5) |
| |
| self._assert_ctoc_correct(r''' |
| typedef struct node_t { |
| _Atomic(void*) a; |
| _Atomic(void) *b; |
| _Atomic void *c; |
| } node; |
| ''') |
| |
| def test_nested_sizeof(self): |
| src = '1' |
| for _ in range(30): |
| src = 'sizeof(' + src + ')' |
| src = 'int x = ' + src + ';' |
| self._assert_ctoc_correct(src) |
| |
| def test_static_assert(self): |
| self._assert_ctoc_correct('_Static_assert(sizeof(int) == sizeof(int), "123");') |
| self._assert_ctoc_correct('int main() { _Static_assert(sizeof(int) == sizeof(int), "123"); } ') |
| self._assert_ctoc_correct('_Static_assert(sizeof(int) == sizeof(int));') |
| |
| def test_reduce_parentheses_binaryops(self): |
| c1 = 'int x = a + b + c + d;'; |
| self.assertEqual(self._run_c_to_c(c1), 'int x = ((a + b) + c) + d;\n') |
| self.assertEqual( |
| self._run_c_to_c(c1, reduce_parentheses=True), |
| 'int x = a + b + c + d;\n') |
| |
| # codes with minimum number of (necessary) parenthesis: |
| test_snippets = [ |
| 'int x = a*b*c*d;', |
| 'int x = a+b*c*d;', |
| 'int x = a*b+c*d;', |
| 'int x = a*b*c+d;', |
| 'int x = (a+b)*c*d;', |
| 'int x = (a+b)*(c+d);', |
| 'int x = (a+b)/(c-d);', |
| 'int x = a+b-c-d;', |
| 'int x = a+(b-c)-d;' |
| ] |
| for src in test_snippets: |
| src2 = self._assert_ctoc_correct(src, reduce_parentheses=True) |
| self.assertTrue( |
| src2.count('(') == src.count('('), |
| msg="{!r} did not have minimum number of parenthesis, should be like {!r}.".format( |
| src2, src)) |
| |
| |
| class TestCasttoC(unittest.TestCase): |
| def _find_file(self, name): |
| test_dir = os.path.dirname(__file__) |
| name = os.path.join(test_dir, 'c_files', name) |
| assert os.path.exists(name) |
| return name |
| |
| def test_to_type(self): |
| src = 'int *x;' |
| generator = c_generator.CGenerator() |
| test_fun = c_ast.FuncCall(c_ast.ID('test_fun'), c_ast.ExprList([])) |
| |
| ast1 = parse_to_ast(src) |
| int_ptr_type = ast1.ext[0].type |
| int_type = int_ptr_type.type |
| self.assertEqual(generator.visit(c_ast.Cast(int_ptr_type, test_fun)), |
| '(int *) test_fun()') |
| self.assertEqual(generator.visit(c_ast.Cast(int_type, test_fun)), |
| '(int) test_fun()') |
| |
| @unittest.skipUnless(cpp_supported(), 'cpp only works on Unix') |
| def test_to_type_with_cpp(self): |
| generator = c_generator.CGenerator() |
| test_fun = c_ast.FuncCall(c_ast.ID('test_fun'), c_ast.ExprList([])) |
| memmgr_path = self._find_file('memmgr.h') |
| |
| ast2 = parse_file(memmgr_path, use_cpp=True, |
| cpp_path=cpp_path(), cpp_args=cpp_args()) |
| void_ptr_type = ast2.ext[-3].type.type |
| void_type = void_ptr_type.type |
| self.assertEqual(generator.visit(c_ast.Cast(void_ptr_type, test_fun)), |
| '(void *) test_fun()') |
| self.assertEqual(generator.visit(c_ast.Cast(void_type, test_fun)), |
| '(void) test_fun()') |
| |
| def test_nested_else_if_line_breaks(self): |
| generator = c_generator.CGenerator() |
| test_ast1 = c_ast.If(None, None, None) |
| test_ast2 = c_ast.If(None, None, c_ast.If(None, None, None)) |
| test_ast3 = c_ast.If(None, None, c_ast.If(None, None, c_ast.If(None, None, None))) |
| test_ast4 = c_ast.If(None, c_ast.Compound([]), c_ast.If(None, c_ast.Compound([]), c_ast.If(None, c_ast.Compound([]), None))) |
| |
| self.assertEqual(generator.visit(test_ast1), |
| 'if ()\n \n') |
| self.assertEqual(generator.visit(test_ast2), |
| 'if ()\n \nelse\n if ()\n \n') |
| self.assertEqual(generator.visit(test_ast3), |
| 'if ()\n \nelse\n if ()\n \nelse\n if ()\n \n') |
| self.assertEqual(generator.visit(test_ast4), |
| 'if ()\n{\n}\nelse\n if ()\n{\n}\nelse\n if ()\n{\n}\n') |
| |
| if __name__ == "__main__": |
| unittest.main() |