initial import from SVN
diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py
new file mode 100644
index 0000000..d57d8ee
--- /dev/null
+++ b/tests/test_c_parser.py
@@ -0,0 +1,1028 @@
+#!/usr/bin/env python
+
+import pprint
+import re
+import sys
+import unittest
+
+sys.path.insert(0, '..')
+
+from pycparser import c_parser
+from pycparser.c_ast import *
+from pycparser.c_parser import CParser, Coord, ParseError
+
+
+_c_parser = c_parser.CParser(
+ lex_optimize=False,
+ yacc_debug=True,
+ yacc_optimize=False,
+ yacctab='yacctab')
+
+
+def expand_decl(decl):
+ """ Converts the declaration into a nested list.
+ """
+ typ = type(decl)
+
+ if typ == TypeDecl:
+ return ['TypeDecl', expand_decl(decl.type)]
+ elif typ == IdentifierType:
+ return ['IdentifierType', decl.names]
+ elif typ == ID:
+ return ['ID', decl.name]
+ elif typ in [Struct, Union]:
+ decls = [expand_decl(d) for d in decl.decls or []]
+ return [typ.__name__, decl.name, decls]
+ else:
+ nested = expand_decl(decl.type)
+
+ if typ == Decl:
+ if decl.quals:
+ return ['Decl', decl.quals, decl.name, nested]
+ else:
+ return ['Decl', decl.name, nested]
+ elif typ == Typename: # for function parameters
+ if decl.quals:
+ return ['Typename', decl.quals, nested]
+ else:
+ return ['Typename', nested]
+ elif typ == ArrayDecl:
+ dimval = decl.dim.value if decl.dim else ''
+ return ['ArrayDecl', dimval, nested]
+ elif typ == PtrDecl:
+ return ['PtrDecl', nested]
+ elif typ == Typedef:
+ return ['Typedef', decl.name, nested]
+ elif typ == FuncDecl:
+ if decl.args:
+ params = [expand_decl(param) for param in decl.args.params]
+ else:
+ params = []
+ return ['FuncDecl', params, nested]
+
+
+def expand_init(init):
+ """ Converts an initialization into a nested list
+ """
+ typ = type(init)
+
+ if typ == Constant:
+ return ['Constant', init.type, init.value]
+ elif typ == ID:
+ return ['ID', init.name]
+ elif typ == ExprList:
+ return [expand_init(expr) for expr in init.exprs]
+
+
+class TestCParser_fundamentals(unittest.TestCase):
+ """ Tests for "fundamental features" of CParser. Testing
+ (mostly) each feature in a detailed manner.
+ """
+ def parse(self, txt, filename=''):
+ return self.cparser.parse(txt, filename)
+
+ def setUp(self):
+ self.cparser = _c_parser
+
+ def get_decl(self, txt, index=0):
+ """ Given a source and an index returns the expanded
+ declaration at that index.
+
+ FileAST holds a list of 'external declarations'.
+ index is the offset of the desired declaration in that
+ list.
+ """
+ t = self.parse(txt).ext[index]
+ return expand_decl(t)
+
+ def get_decl_init(self, txt, index=0):
+ """ Returns the expanded initializer of the declaration
+ at index.
+ """
+ t = self.parse(txt).ext[index]
+ return expand_init(t.init)
+
+ def test_FileAST(self):
+ t = self.parse('int a; char c;')
+ self.failUnless(isinstance(t, FileAST))
+ self.assertEqual(len(t.ext), 2)
+
+ """ Tests the "coordinates" of parsed elements - file
+ name and line numbers, with modification insterted by
+ #line directives.
+ """
+ def assert_coord(self, node, line, file=None):
+ self.assertEqual(node.coord.line, line)
+ if file:
+ self.assertEqual(node.coord.file, file)
+
+ def test_coords(self):
+ self.assert_coord(self.parse('int a;').ext[0], 1)
+
+ t1 = """
+ int a;
+ int b;\n\n
+ int c;
+ """
+ f1 = self.parse(t1, filename='test.c')
+ self.assert_coord(f1.ext[0], 2, 'test.c')
+ self.assert_coord(f1.ext[1], 3, 'test.c')
+ self.assert_coord(f1.ext[2], 6, 'test.c')
+
+ t1_1 = '''
+ int main() {
+ k = p;
+ printf("%d", b);
+ return 0;
+ }'''
+ f1_1 = self.parse(t1_1, filename='test.c')
+ self.assert_coord(f1_1.ext[0].body.stmts[0], 3, 'test.c')
+ self.assert_coord(f1_1.ext[0].body.stmts[1], 4, 'test.c')
+
+ t2 = """
+ #line 99
+ int c;
+ """
+ self.assert_coord(self.parse(t2).ext[0], 99)
+
+ t3 = """
+ int dsf;
+ char p;
+ #line 3000 "in.h"
+ char d;
+ """
+ f3 = self.parse(t3, filename='test.c')
+ self.assert_coord(f3.ext[0], 2, 'test.c')
+ self.assert_coord(f3.ext[1], 3, 'test.c')
+ self.assert_coord(f3.ext[2], 3000, 'in.h')
+
+ t4 = """
+ #line 20 "restore.h"
+ int maydler(char);
+
+ #line 30 "includes/daween.ph"
+ long j, k;
+
+ #line 50000
+ char* ro;
+ """
+ f4 = self.parse(t4, filename='myb.c')
+ self.assert_coord(f4.ext[0], 20, 'restore.h')
+ self.assert_coord(f4.ext[1], 30, 'includes/daween.ph')
+ self.assert_coord(f4.ext[2], 30, 'includes/daween.ph')
+ self.assert_coord(f4.ext[3], 50000, 'includes/daween.ph')
+
+ t5 = """
+ int
+ #line 99
+ c;
+ """
+ self.assert_coord(self.parse(t5).ext[0], 99)
+
+ def test_simple_decls(self):
+ self.assertEqual(self.get_decl('int a;'),
+ ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
+
+ self.assertEqual(self.get_decl('unsigned int a;'),
+ ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int', 'unsigned']]]])
+
+ self.assertEqual(self.get_decl('char* string;'),
+ ['Decl', 'string',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]])
+
+ self.assertEqual(self.get_decl('long ar[15];'),
+ ['Decl', 'ar',
+ ['ArrayDecl', '15',
+ ['TypeDecl', ['IdentifierType', ['long']]]]])
+
+ self.assertEqual(self.get_decl('unsigned ar[];'),
+ ['Decl', 'ar',
+ ['ArrayDecl', '',
+ ['TypeDecl', ['IdentifierType', ['unsigned']]]]])
+
+ self.assertEqual(self.get_decl('int strlen(char* s);'),
+ ['Decl', 'strlen',
+ ['FuncDecl',
+ [['Decl', 's',
+ ['PtrDecl',
+ ['TypeDecl', ['IdentifierType', ['char']]]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]])
+
+ self.assertEqual(self.get_decl('int strcmp(char* s1, char* s2);'),
+ ['Decl', 'strcmp',
+ ['FuncDecl',
+ [ ['Decl', 's1',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
+ ['Decl', 's2',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]
+ ],
+ ['TypeDecl', ['IdentifierType', ['int']]]]])
+
+ def test_nested_decls(self): # the fun begins
+ self.assertEqual(self.get_decl('char** ar2D;'),
+ ['Decl', 'ar2D',
+ ['PtrDecl', ['PtrDecl',
+ ['TypeDecl', ['IdentifierType', ['char']]]]]])
+
+ self.assertEqual(self.get_decl('int (*a)[1][2];'),
+ ['Decl', 'a',
+ ['PtrDecl',
+ ['ArrayDecl', '1',
+ ['ArrayDecl', '2',
+ ['TypeDecl', ['IdentifierType', ['int']]]]]]])
+
+ self.assertEqual(self.get_decl('int *a[1][2];'),
+ ['Decl', 'a',
+ ['ArrayDecl', '1',
+ ['ArrayDecl', '2',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['int']]]]]]])
+
+ self.assertEqual(self.get_decl('char ***ar3D[40];'),
+ ['Decl', 'ar3D',
+ ['ArrayDecl', '40',
+ ['PtrDecl', ['PtrDecl', ['PtrDecl',
+ ['TypeDecl', ['IdentifierType', ['char']]]]]]]])
+
+ self.assertEqual(self.get_decl('char (***ar3D)[40];'),
+ ['Decl', 'ar3D',
+ ['PtrDecl', ['PtrDecl', ['PtrDecl',
+ ['ArrayDecl', '40', ['TypeDecl', ['IdentifierType', ['char']]]]]]]])
+
+ self.assertEqual(self.get_decl('int (*x[4])(char, int);'),
+ ['Decl', 'x',
+ ['ArrayDecl', '4',
+ ['PtrDecl',
+ ['FuncDecl',
+ [ ['Typename', ['TypeDecl', ['IdentifierType', ['char']]]],
+ ['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]]]])
+
+ self.assertEqual(self.get_decl('char *(*(**foo [][8])())[];'),
+ ['Decl', 'foo',
+ ['ArrayDecl', '',
+ ['ArrayDecl', '8',
+ ['PtrDecl', ['PtrDecl',
+ ['FuncDecl',
+ [],
+ ['PtrDecl',
+ ['ArrayDecl', '',
+ ['PtrDecl',
+ ['TypeDecl',
+ ['IdentifierType', ['char']]]]]]]]]]]])
+
+ # explore named and unnamed function pointer parameters,
+ # with and without qualifiers
+ #
+
+ # unnamed w/o quals
+ self.assertEqual(self.get_decl('int (*k)(int);'),
+ ['Decl', 'k',
+ ['PtrDecl',
+ ['FuncDecl',
+ [['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]]])
+
+ # unnamed w/ quals
+ self.assertEqual(self.get_decl('int (*k)(const int);'),
+ ['Decl', 'k',
+ ['PtrDecl',
+ ['FuncDecl',
+ [['Typename', ['const'], ['TypeDecl', ['IdentifierType', ['int']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]]])
+
+ # named w/o quals
+ self.assertEqual(self.get_decl('int (*k)(int q);'),
+ ['Decl', 'k',
+ ['PtrDecl',
+ ['FuncDecl',
+ [['Decl', 'q', ['TypeDecl', ['IdentifierType', ['int']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]]])
+
+ # named w/ quals
+ self.assertEqual(self.get_decl('int (*k)(const volatile int q);'),
+ ['Decl', 'k',
+ ['PtrDecl',
+ ['FuncDecl',
+ [['Decl', ['volatile', 'const'], 'q',
+ ['TypeDecl', ['IdentifierType', ['int']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]]])
+
+
+ def test_qualifiers_storage_specifiers(self):
+ def assert_qs(txt, index, quals, storage):
+ d = self.parse(txt).ext[index]
+ self.assertEqual(d.quals, quals)
+ self.assertEqual(d.storage, storage)
+
+ assert_qs("extern int p;", 0, [], ['extern'])
+ assert_qs("const long p = 6;", 0, ['const'], [])
+
+ d1 = "static const int p, q, r;"
+ for i in range(3):
+ assert_qs(d1, i, ['const'], ['static'])
+
+ d2 = "static char * const p;"
+ assert_qs(d2, 0, [], ['static'])
+ pdecl = self.parse(d2).ext[0].type
+ self.failUnless(isinstance(pdecl, PtrDecl))
+ self.assertEqual(pdecl.quals, ['const'])
+
+ def test_sizeof(self):
+ e = """
+ void foo()
+ {
+ int a = sizeof k;
+ int b = sizeof(int);
+ int c = sizeof(int**);
+
+ char* p = "just to make sure this parses w/o error...";
+ int d = sizeof(int());
+ }
+ """
+ compound = self.parse(e).ext[0].body
+
+ s1 = compound.decls[0].init
+ self.assertTrue(isinstance(s1, UnaryOp))
+ self.assertEqual(s1.op, 'sizeof')
+ self.assertTrue(isinstance(s1.expr, ID))
+ self.assertEqual(s1.expr.name, 'k')
+
+ s2 = compound.decls[1].init
+ self.assertEqual(expand_decl(s2.expr),
+ ['Typename', ['TypeDecl', ['IdentifierType', ['int']]]])
+
+ s3 = compound.decls[2].init
+ self.assertEqual(expand_decl(s3.expr),
+ ['Typename',
+ ['PtrDecl',
+ ['PtrDecl',
+ ['TypeDecl',
+ ['IdentifierType', ['int']]]]]])
+
+ def test_enums(self):
+ e1 = "enum mycolor op;"
+ e1_type = self.parse(e1).ext[0].type.type
+
+ self.assertTrue(isinstance(e1_type, Enum))
+ self.assertEqual(e1_type.name, 'mycolor')
+ self.assertEqual(e1_type.values, None)
+
+ e2 = "enum mysize {large=20, small, medium} shoes;"
+ e2_type = self.parse(e2).ext[0].type.type
+
+ self.assertTrue(isinstance(e2_type, Enum))
+ self.assertEqual(e2_type.name, 'mysize')
+
+ e2_elist = e2_type.values
+ self.assertTrue(isinstance(e2_elist, EnumeratorList))
+
+ for e2_eval in e2_elist.enumerators:
+ self.assertTrue(isinstance(e2_eval, Enumerator))
+
+ self.assertEqual(e2_elist.enumerators[0].name, 'large')
+ self.assertEqual(e2_elist.enumerators[0].value.value, '20')
+ self.assertEqual(e2_elist.enumerators[2].name, 'medium')
+ self.assertEqual(e2_elist.enumerators[2].value, None)
+
+ # enum with trailing comma (C99 feature)
+ e3 = """
+ enum
+ {
+ red,
+ blue,
+ green,
+ } color;
+ """
+
+ e3_type = self.parse(e3).ext[0].type.type
+ self.assertTrue(isinstance(e3_type, Enum))
+ e3_elist = e3_type.values
+ self.assertTrue(isinstance(e3_elist, EnumeratorList))
+
+ for e3_eval in e3_elist.enumerators:
+ self.assertTrue(isinstance(e3_eval, Enumerator))
+
+ self.assertEqual(e3_elist.enumerators[0].name, 'red')
+ self.assertEqual(e3_elist.enumerators[0].value, None)
+ self.assertEqual(e3_elist.enumerators[1].name, 'blue')
+ self.assertEqual(e3_elist.enumerators[2].name, 'green')
+
+ def test_typedef(self):
+ # without typedef, error
+ s1 = """
+ node k;
+ """
+ self.assertRaises(ParseError, self.parse, s1)
+
+ # now with typedef, works
+ s2 = """
+ typedef void* node;
+ node k;
+ """
+ ps2 = self.parse(s2)
+ self.assertEqual(expand_decl(ps2.ext[0]),
+ ['Typedef', 'node',
+ ['PtrDecl',
+ ['TypeDecl', ['IdentifierType', ['void']]]]])
+
+ self.assertEqual(expand_decl(ps2.ext[1]),
+ ['Decl', 'k',
+ ['TypeDecl', ['IdentifierType', ['node']]]])
+
+ s3 = """
+ typedef int T;
+ typedef T *pT;
+
+ pT aa, bb;
+ """
+ ps3 = self.parse(s3)
+ self.assertEqual(expand_decl(ps3.ext[3]),
+ ['Decl', 'bb',
+ ['TypeDecl', ['IdentifierType', ['pT']]]])
+
+ s4 = '''
+ typedef char* __builtin_va_list;
+ typedef __builtin_va_list __gnuc_va_list;
+ '''
+ ps4 = self.parse(s4)
+ self.assertEqual(expand_decl(ps4.ext[1]),
+ ['Typedef', '__gnuc_va_list',
+ ['TypeDecl',
+ ['IdentifierType', ['__builtin_va_list']]]])
+
+ s5 = '''typedef struct tagHash Hash;'''
+ ps5 = self.parse(s5)
+ self.assertEqual(expand_decl(ps5.ext[0]),
+ ['Typedef', 'Hash', ['TypeDecl', ['Struct', 'tagHash', []]]])
+
+ def test_struct_union(self):
+ s1 = """
+ struct {
+ int id;
+ char* name;
+ } joe;
+ """
+
+ self.assertEqual(expand_decl(self.parse(s1).ext[0]),
+ ['Decl', 'joe',
+ ['TypeDecl', ['Struct', None,
+ [ ['Decl', 'id',
+ ['TypeDecl',
+ ['IdentifierType', ['int']]]],
+ ['Decl', 'name',
+ ['PtrDecl',
+ ['TypeDecl',
+ ['IdentifierType', ['char']]]]]]]]])
+
+ s2 = """
+ struct node p;
+ """
+ self.assertEqual(expand_decl(self.parse(s2).ext[0]),
+ ['Decl', 'p',
+ ['TypeDecl', ['Struct', 'node', []]]])
+
+ s21 = """
+ union pri ra;
+ """
+ self.assertEqual(expand_decl(self.parse(s21).ext[0]),
+ ['Decl', 'ra',
+ ['TypeDecl', ['Union', 'pri', []]]])
+
+ s3 = """
+ struct node* p;
+ """
+ self.assertEqual(expand_decl(self.parse(s3).ext[0]),
+ ['Decl', 'p',
+ ['PtrDecl',
+ ['TypeDecl', ['Struct', 'node', []]]]])
+
+ s4 = """
+ struct node;
+ """
+ self.assertEqual(expand_decl(self.parse(s4).ext[0]),
+ ['Decl', None,
+ ['Struct', 'node', []]])
+
+ s5 = """
+ union
+ {
+ struct
+ {
+ int type;
+ } n;
+
+ struct
+ {
+ int type;
+ int intnode;
+ } ni;
+ } u;
+ """
+ self.assertEqual(expand_decl(self.parse(s5).ext[0]),
+ ['Decl', 'u',
+ ['TypeDecl',
+ ['Union', None,
+ [['Decl', 'n',
+ ['TypeDecl',
+ ['Struct', None,
+ [['Decl', 'type',
+ ['TypeDecl', ['IdentifierType', ['int']]]]]]]],
+ ['Decl', 'ni',
+ ['TypeDecl',
+ ['Struct', None,
+ [['Decl', 'type',
+ ['TypeDecl', ['IdentifierType', ['int']]]],
+ ['Decl', 'intnode',
+ ['TypeDecl', ['IdentifierType', ['int']]]]]]]]]]]])
+
+ s6 = """
+ typedef struct foo_tag
+ {
+ void* data;
+ } foo, *pfoo;
+ """
+ s6_ast = self.parse(s6)
+
+ self.assertEqual(expand_decl(s6_ast.ext[0]),
+ ['Typedef', 'foo',
+ ['TypeDecl',
+ ['Struct', 'foo_tag',
+ [['Decl', 'data',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]])
+
+ self.assertEqual(expand_decl(s6_ast.ext[1]),
+ ['Typedef', 'pfoo',
+ ['PtrDecl',
+ ['TypeDecl',
+ ['Struct', 'foo_tag',
+ [['Decl', 'data',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]]])
+
+ s7 = r"""
+ struct _on_exit_args {
+ void * _fnargs[32];
+ void * _dso_handle[32];
+
+ long _fntypes;
+ #line 77 "D:\eli\cpp_stuff\libc_include/sys/reent.h"
+
+ long _is_cxa;
+ };
+ """
+
+ s7_ast = self.parse(s7, filename='test.c')
+ self.assert_coord(s7_ast.ext[0].type.decls[2], 6, 'test.c')
+ self.assert_coord(s7_ast.ext[0].type.decls[3], 78,
+ r'D:\eli\cpp_stuff\libc_include/sys/reent.h')
+
+ s8 = """
+ typedef enum tagReturnCode {SUCCESS, FAIL} ReturnCode;
+
+ typedef struct tagEntry
+ {
+ char* key;
+ char* value;
+ } Entry;
+
+
+ typedef struct tagNode
+ {
+ Entry* entry;
+
+ struct tagNode* next;
+ } Node;
+
+ typedef struct tagHash
+ {
+ unsigned int table_size;
+
+ Node** heads;
+
+ } Hash;
+ """
+ s8_ast = self.parse(s8)
+ self.assertEqual(expand_decl(s8_ast.ext[3]),
+ ['Typedef', 'Hash',
+ ['TypeDecl', ['Struct', 'tagHash',
+ [['Decl', 'table_size',
+ ['TypeDecl', ['IdentifierType', ['int', 'unsigned']]]],
+ ['Decl', 'heads',
+ ['PtrDecl', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['Node']]]]]]]]]])
+
+ def test_tags_namespace(self):
+ """ Tests that the tags of structs/unions/enums reside in a separate namespace and
+ can be named after existing types.
+ """
+ s1 = """
+ typedef int tagEntry;
+
+ struct tagEntry
+ {
+ char* key;
+ char* value;
+ } Entry;
+ """
+
+ s1_ast = self.parse(s1)
+ self.assertEqual(expand_decl(s1_ast.ext[1]),
+ ['Decl', 'Entry',
+ ['TypeDecl', ['Struct', 'tagEntry',
+ [['Decl', 'key',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
+ ['Decl', 'value',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
+
+ s2 = """
+ struct tagEntry;
+
+ typedef struct tagEntry tagEntry;
+
+ struct tagEntry
+ {
+ char* key;
+ char* value;
+ } Entry;
+ """
+
+ s2_ast = self.parse(s2)
+ self.assertEqual(expand_decl(s2_ast.ext[2]),
+ ['Decl', 'Entry',
+ ['TypeDecl', ['Struct', 'tagEntry',
+ [['Decl', 'key',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
+ ['Decl', 'value',
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
+
+ s3 = """
+ typedef int mytag;
+
+ enum mytag {ABC, CDE};
+ enum mytag joe;
+ """
+
+ s3_type = self.parse(s3).ext[1].type
+
+ self.assertTrue(isinstance(s3_type, Enum))
+ self.assertEqual(s3_type.name, 'mytag')
+
+ def test_multi_decls(self):
+ d1 = 'int a, b;'
+
+ self.assertEqual(self.get_decl(d1, 0),
+ ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
+ self.assertEqual(self.get_decl(d1, 1),
+ ['Decl', 'b', ['TypeDecl', ['IdentifierType', ['int']]]])
+
+ d2 = 'char* p, notp, ar[4];'
+ self.assertEqual(self.get_decl(d2, 0),
+ ['Decl', 'p',
+ ['PtrDecl',
+ ['TypeDecl', ['IdentifierType', ['char']]]]])
+ self.assertEqual(self.get_decl(d2, 1),
+ ['Decl', 'notp', ['TypeDecl', ['IdentifierType', ['char']]]])
+ self.assertEqual(self.get_decl(d2, 2),
+ ['Decl', 'ar',
+ ['ArrayDecl', '4',
+ ['TypeDecl', ['IdentifierType', ['char']]]]])
+
+ def test_invalid_multiple_types_error(self):
+ bad = [
+ 'int enum {ab, cd} fubr;',
+ 'enum kid char brbr;']
+
+ for b in bad:
+ self.assertRaises(ParseError, self.parse, b)
+
+ def test_decl_inits(self):
+ d1 = 'int a = 16;'
+ self.assertEqual(self.get_decl(d1),
+ ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
+ self.assertEqual(self.get_decl_init(d1),
+ ['Constant', 'int', '16'])
+
+ d2 = 'long ar[] = {7, 8, 9};'
+ self.assertEqual(self.get_decl(d2),
+ ['Decl', 'ar',
+ ['ArrayDecl', '',
+ ['TypeDecl', ['IdentifierType', ['long']]]]])
+ self.assertEqual(self.get_decl_init(d2),
+ [ ['Constant', 'int', '7'],
+ ['Constant', 'int', '8'],
+ ['Constant', 'int', '9'],])
+
+ d3 = 'char p = j;'
+ self.assertEqual(self.get_decl(d3),
+ ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['char']]]])
+ self.assertEqual(self.get_decl_init(d3),
+ ['ID', 'j'])
+
+ d4 = "char x = 'c', *p = {0, 1, 2, {4, 5}, 6};"
+ self.assertEqual(self.get_decl(d4, 0),
+ ['Decl', 'x', ['TypeDecl', ['IdentifierType', ['char']]]])
+ self.assertEqual(self.get_decl_init(d4, 0),
+ ['Constant', 'char', "'c'"])
+ self.assertEqual(self.get_decl(d4, 1),
+ ['Decl', 'p',
+ ['PtrDecl',
+ ['TypeDecl', ['IdentifierType', ['char']]]]])
+ self.assertEqual(self.get_decl_init(d4, 1),
+ [ ['Constant', 'int', '0'],
+ ['Constant', 'int', '1'],
+ ['Constant', 'int', '2'],
+ [ ['Constant', 'int', '4'],
+ ['Constant', 'int', '5']],
+ ['Constant', 'int', '6']])
+
+ def test_function_definitions(self):
+ def parse_fdef(str):
+ return self.parse(str).ext[0]
+
+ def fdef_decl(fdef):
+ return expand_decl(fdef.decl)
+
+ f1 = parse_fdef('''
+ int factorial(int p)
+ {
+ return 3;
+ }
+ ''')
+
+ self.assertEqual(fdef_decl(f1),
+ ['Decl', 'factorial',
+ ['FuncDecl',
+ [['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]]],
+ ['TypeDecl', ['IdentifierType', ['int']]]]])
+
+ self.assertEqual(type(f1.body.stmts[0]), Return)
+ self.assertEqual(f1.body.decls, None)
+
+ f2 = parse_fdef('''
+ char* zzz(int p, char* c)
+ {
+ int a;
+ char b;
+
+ a = b + 2;
+ return 3;
+ }
+ ''')
+
+ self.assertEqual(fdef_decl(f2),
+ ['Decl', 'zzz',
+ ['FuncDecl',
+ [ ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]],
+ ['Decl', 'c', ['PtrDecl',
+ ['TypeDecl', ['IdentifierType', ['char']]]]]],
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
+
+ self.assertEqual(list(map(type, f2.body.stmts)),
+ [Assignment, Return])
+ self.assertEqual(len(f2.body.decls), 2)
+
+ f3 = parse_fdef('''
+ char* zzz(p, c)
+ long p, *c;
+ {
+ int a;
+ char b;
+
+ a = b + 2;
+ return 3;
+ }
+ ''')
+
+ self.assertEqual(fdef_decl(f3),
+ ['Decl', 'zzz',
+ ['FuncDecl',
+ [ ['ID', 'p'],
+ ['ID', 'c']],
+ ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
+
+ self.assertEqual(list(map(type, f3.body.stmts)),
+ [Assignment, Return])
+ self.assertEqual(len(f3.body.decls), 2)
+
+ self.assertEqual(expand_decl(f3.param_decls[0]),
+ ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['long']]]])
+ self.assertEqual(expand_decl(f3.param_decls[1]),
+ ['Decl', 'c', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['long']]]]])
+
+
+class TestCParser_whole_code(unittest.TestCase):
+ """ Testing of parsing whole chunks of code.
+
+ Since I don't want to rely on the structure of ASTs too
+ much, most of these tests are implemented with visitors.
+ """
+ def parse(self, txt, filename=''):
+ return self.cparser.parse(txt, filename)
+
+ def setUp(self):
+ self.cparser = _c_parser
+
+ # A simple helper visitor that lists the values of all the
+ # Constant nodes it sees.
+ #
+ class ConstantVisitor(NodeVisitor):
+ def __init__(self):
+ self.values = []
+
+ def visit_Constant(self, node):
+ self.values.append(node.value)
+
+ # This visitor counts the amount of references to the ID
+ # with the name provided to it in the constructor.
+ #
+ class IDNameCounter(NodeVisitor):
+ def __init__(self, name):
+ self.name = name
+ self.nrefs = 0
+
+ def visit_ID(self, node):
+ if node.name == self.name:
+ self.nrefs += 1
+
+ # Counts the amount of nodes of a given class
+ #
+ class NodeKlassCounter(NodeVisitor):
+ def __init__(self, node_klass):
+ self.klass = node_klass
+ self.n = 0
+
+ def generic_visit(self, node):
+ if node.__class__ == self.klass:
+ self.n += 1
+
+ NodeVisitor.generic_visit(self, node)
+
+ def assert_all_Constants(self, code, constants):
+ """ Asserts that the list of all Constant values (by
+ 'preorder' appearance) in the chunk of code is as
+ given.
+ """
+ parsed = self.parse(code)
+ cv = self.ConstantVisitor()
+ cv.visit(parsed)
+ self.assertEqual(cv.values, constants)
+
+ def assert_num_ID_refs(self, code, name, num):
+ """ Asserts the number of references to the ID with
+ the given name.
+ """
+ if isinstance(code, str):
+ parsed = self.parse(code)
+ else:
+ parsed = code
+
+ iv = self.IDNameCounter(name)
+ iv.visit(parsed)
+ self.assertEqual(iv.nrefs, num)
+
+ def assert_num_klass_nodes(self, code, klass, num):
+ """ Asserts the amount of klass nodes in the code.
+ """
+ if isinstance(code, str):
+ parsed = self.parse(code)
+ else:
+ parsed = code
+
+ cv = self.NodeKlassCounter(klass)
+ cv.visit(parsed)
+ self.assertEqual(cv.n, num)
+
+ def test_expressions(self):
+ e1 = '''int k = (r + 10.0) >> 6 + 8 << (3 & 0x14);'''
+ self.assert_all_Constants(e1, ['10.0', '6', '8', '3', '0x14'])
+
+ e2 = r'''char n = '\n', *prefix = "st_";'''
+ self.assert_all_Constants(e2, [r"'\n'", '"st_"'])
+
+ def test_statements(self):
+ s1 = r'''
+ void foo(){
+ if (sp == 1)
+ if (optind >= argc ||
+ argv[optind][0] != '-' || argv[optind][1] == '\0')
+ return -1;
+ else if (strcmp(argv[optind], "--") == 0) {
+ optind++;
+ return -1;
+ }
+ }
+ '''
+
+ self.assert_all_Constants(s1,
+ ['1', '0', r"'-'", '1', r"'\0'", '1', r'"--"', '0', '1'])
+
+ ps1 = self.parse(s1)
+ self.assert_num_ID_refs(ps1, 'argv', 3)
+ self.assert_num_ID_refs(ps1, 'optind', 5)
+
+ self.assert_num_klass_nodes(ps1, If, 3)
+ self.assert_num_klass_nodes(ps1, Return, 2)
+ self.assert_num_klass_nodes(ps1, FuncCall, 1) # strcmp
+ self.assert_num_klass_nodes(ps1, BinaryOp, 7)
+
+ # In the following code, Hash and Node were defined as
+ # int to pacify the parser that sees they're used as
+ # types
+ #
+ s2 = r'''
+ typedef int Hash, Node;
+
+ void HashDestroy(Hash* hash)
+ {
+ unsigned int i;
+
+ if (hash == NULL)
+ return;
+
+ for (i = 0; i < hash->table_size; ++i)
+ {
+ Node* temp = hash->heads[i];
+
+ while (temp != NULL)
+ {
+ Node* temp2 = temp;
+
+ free(temp->entry->key);
+ free(temp->entry->value);
+ free(temp->entry);
+
+ temp = temp->next;
+
+ free(temp2);
+ }
+ }
+
+ free(hash->heads);
+ hash->heads = NULL;
+
+ free(hash);
+ }
+ '''
+
+ ps2 = self.parse(s2)
+ self.assert_num_klass_nodes(ps2, FuncCall, 6)
+ self.assert_num_klass_nodes(ps2, FuncDef, 1)
+ self.assert_num_klass_nodes(ps2, For, 1)
+ self.assert_num_klass_nodes(ps2, While, 1)
+ self.assert_num_klass_nodes(ps2, StructRef, 10)
+
+ # declarations don't count
+ self.assert_num_ID_refs(ps2, 'hash', 6)
+ self.assert_num_ID_refs(ps2, 'i', 4)
+
+ def test_whole_file(self):
+ # See how pycparser handles a whole, real C file.
+ #
+ filename = 'c_files/memmgr_with_h.c'
+ code = open(filename, 'rU').read()
+ p = self.parse(code)
+
+ self.assert_num_klass_nodes(p, FuncDef, 5)
+
+ # each FuncDef also has a FuncDecl. 4 declarations
+ # + 5 definitions, overall 9
+ self.assert_num_klass_nodes(p, FuncDecl, 9)
+
+ self.assert_num_klass_nodes(p, Typedef, 4)
+
+ self.assertEqual(p.ext[4].coord.line, 88)
+ self.assertEqual(p.ext[4].coord.file, "./memmgr.h")
+
+ self.assertEqual(p.ext[6].coord.line, 10)
+ self.assertEqual(p.ext[6].coord.file, "memmgr.c")
+
+ def test_whole_file_with_stdio(self):
+ # Parse a whole file with stdio.h included by cpp
+ #
+ filename = 'c_files/cppd_with_stdio_h.c'
+ code = open(filename, 'rU').read()
+ p = self.parse(code)
+
+ self.failUnless(isinstance(p.ext[0], Typedef))
+ self.assertEqual(p.ext[0].coord.line, 213)
+ self.assertEqual(p.ext[0].coord.file, "D:\eli\cpp_stuff\libc_include/stddef.h")
+
+ self.failUnless(isinstance(p.ext[-1], FuncDef))
+ self.assertEqual(p.ext[-1].coord.line, 15)
+ self.assertEqual(p.ext[-1].coord.file, "example_c_file.c")
+
+ self.failUnless(isinstance(p.ext[-8], Typedef))
+ self.failUnless(isinstance(p.ext[-8].type, TypeDecl))
+ self.assertEqual(p.ext[-8].name, 'cookie_io_functions_t')
+
+
+if __name__ == '__main__':
+ #~ suite = unittest.TestLoader().loadTestsFromNames(
+ #~ ['test_c_parser.TestCParser_fundamentals.test_typedef'])
+
+ #~ suite = unittest.TestLoader().loadTestsFromNames(
+ #~ ['test_c_parser.TestCParser_whole_code.test_whole_file_with_stdio'])
+
+ #~ suite = unittest.TestLoader().loadTestsFromTestCase(
+ #~ TestCParser_whole_code)
+
+ #~ unittest.TextTestRunner(verbosity=2).run(suite)
+ unittest.main()