blob: 30f445fccc47fc08ed1cb32beb0765e963ea5bbd [file] [log] [blame]
Eli Bendersky3921e8e2010-05-21 09:05:39 +03001#!/usr/bin/env python
2
3import pprint
4import re
5import sys
6import unittest
7
8sys.path.insert(0, '..')
9
10from pycparser import c_parser
11from pycparser.c_ast import *
12from pycparser.c_parser import CParser, Coord, ParseError
13
14
15_c_parser = c_parser.CParser(
16 lex_optimize=False,
17 yacc_debug=True,
18 yacc_optimize=False,
19 yacctab='yacctab')
20
21
22def expand_decl(decl):
23 """ Converts the declaration into a nested list.
24 """
25 typ = type(decl)
26
27 if typ == TypeDecl:
28 return ['TypeDecl', expand_decl(decl.type)]
29 elif typ == IdentifierType:
30 return ['IdentifierType', decl.names]
31 elif typ == ID:
32 return ['ID', decl.name]
33 elif typ in [Struct, Union]:
34 decls = [expand_decl(d) for d in decl.decls or []]
35 return [typ.__name__, decl.name, decls]
36 else:
37 nested = expand_decl(decl.type)
38
39 if typ == Decl:
40 if decl.quals:
41 return ['Decl', decl.quals, decl.name, nested]
42 else:
43 return ['Decl', decl.name, nested]
44 elif typ == Typename: # for function parameters
45 if decl.quals:
46 return ['Typename', decl.quals, nested]
47 else:
48 return ['Typename', nested]
49 elif typ == ArrayDecl:
50 dimval = decl.dim.value if decl.dim else ''
51 return ['ArrayDecl', dimval, nested]
52 elif typ == PtrDecl:
53 return ['PtrDecl', nested]
54 elif typ == Typedef:
55 return ['Typedef', decl.name, nested]
56 elif typ == FuncDecl:
57 if decl.args:
58 params = [expand_decl(param) for param in decl.args.params]
59 else:
60 params = []
61 return ['FuncDecl', params, nested]
62
63
64def expand_init(init):
65 """ Converts an initialization into a nested list
66 """
67 typ = type(init)
68
69 if typ == Constant:
70 return ['Constant', init.type, init.value]
71 elif typ == ID:
72 return ['ID', init.name]
73 elif typ == ExprList:
74 return [expand_init(expr) for expr in init.exprs]
75
76
77class TestCParser_fundamentals(unittest.TestCase):
78 """ Tests for "fundamental features" of CParser. Testing
79 (mostly) each feature in a detailed manner.
80 """
81 def parse(self, txt, filename=''):
82 return self.cparser.parse(txt, filename)
83
84 def setUp(self):
85 self.cparser = _c_parser
86
87 def get_decl(self, txt, index=0):
88 """ Given a source and an index returns the expanded
89 declaration at that index.
90
91 FileAST holds a list of 'external declarations'.
92 index is the offset of the desired declaration in that
93 list.
94 """
95 t = self.parse(txt).ext[index]
96 return expand_decl(t)
97
98 def get_decl_init(self, txt, index=0):
99 """ Returns the expanded initializer of the declaration
100 at index.
101 """
102 t = self.parse(txt).ext[index]
103 return expand_init(t.init)
104
105 def test_FileAST(self):
106 t = self.parse('int a; char c;')
107 self.failUnless(isinstance(t, FileAST))
108 self.assertEqual(len(t.ext), 2)
109
110 """ Tests the "coordinates" of parsed elements - file
111 name and line numbers, with modification insterted by
112 #line directives.
113 """
114 def assert_coord(self, node, line, file=None):
115 self.assertEqual(node.coord.line, line)
116 if file:
117 self.assertEqual(node.coord.file, file)
118
119 def test_coords(self):
120 self.assert_coord(self.parse('int a;').ext[0], 1)
121
122 t1 = """
123 int a;
124 int b;\n\n
125 int c;
126 """
127 f1 = self.parse(t1, filename='test.c')
128 self.assert_coord(f1.ext[0], 2, 'test.c')
129 self.assert_coord(f1.ext[1], 3, 'test.c')
130 self.assert_coord(f1.ext[2], 6, 'test.c')
131
132 t1_1 = '''
133 int main() {
134 k = p;
135 printf("%d", b);
136 return 0;
137 }'''
138 f1_1 = self.parse(t1_1, filename='test.c')
eli.benderskyef29ff92010-10-29 16:25:43 +0200139 self.assert_coord(f1_1.ext[0].body.block_items[0], 3, 'test.c')
140 self.assert_coord(f1_1.ext[0].body.block_items[1], 4, 'test.c')
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300141
142 t2 = """
143 #line 99
144 int c;
145 """
146 self.assert_coord(self.parse(t2).ext[0], 99)
147
148 t3 = """
149 int dsf;
150 char p;
151 #line 3000 "in.h"
152 char d;
153 """
154 f3 = self.parse(t3, filename='test.c')
155 self.assert_coord(f3.ext[0], 2, 'test.c')
156 self.assert_coord(f3.ext[1], 3, 'test.c')
157 self.assert_coord(f3.ext[2], 3000, 'in.h')
158
159 t4 = """
160 #line 20 "restore.h"
161 int maydler(char);
162
163 #line 30 "includes/daween.ph"
164 long j, k;
165
166 #line 50000
167 char* ro;
168 """
169 f4 = self.parse(t4, filename='myb.c')
170 self.assert_coord(f4.ext[0], 20, 'restore.h')
171 self.assert_coord(f4.ext[1], 30, 'includes/daween.ph')
172 self.assert_coord(f4.ext[2], 30, 'includes/daween.ph')
173 self.assert_coord(f4.ext[3], 50000, 'includes/daween.ph')
174
175 t5 = """
176 int
177 #line 99
178 c;
179 """
180 self.assert_coord(self.parse(t5).ext[0], 99)
181
182 def test_simple_decls(self):
183 self.assertEqual(self.get_decl('int a;'),
184 ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
185
186 self.assertEqual(self.get_decl('unsigned int a;'),
187 ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int', 'unsigned']]]])
188
189 self.assertEqual(self.get_decl('char* string;'),
190 ['Decl', 'string',
191 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]])
192
193 self.assertEqual(self.get_decl('long ar[15];'),
194 ['Decl', 'ar',
195 ['ArrayDecl', '15',
196 ['TypeDecl', ['IdentifierType', ['long']]]]])
eli.bendersky98f45372010-10-30 09:46:29 +0200197
198 self.assertEqual(self.get_decl('long long ar[15];'),
199 ['Decl', 'ar',
200 ['ArrayDecl', '15',
201 ['TypeDecl', ['IdentifierType', ['long long']]]]])
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300202
203 self.assertEqual(self.get_decl('unsigned ar[];'),
204 ['Decl', 'ar',
205 ['ArrayDecl', '',
206 ['TypeDecl', ['IdentifierType', ['unsigned']]]]])
207
208 self.assertEqual(self.get_decl('int strlen(char* s);'),
209 ['Decl', 'strlen',
210 ['FuncDecl',
211 [['Decl', 's',
212 ['PtrDecl',
213 ['TypeDecl', ['IdentifierType', ['char']]]]]],
214 ['TypeDecl', ['IdentifierType', ['int']]]]])
215
216 self.assertEqual(self.get_decl('int strcmp(char* s1, char* s2);'),
217 ['Decl', 'strcmp',
218 ['FuncDecl',
219 [ ['Decl', 's1',
220 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
221 ['Decl', 's2',
222 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]
223 ],
224 ['TypeDecl', ['IdentifierType', ['int']]]]])
225
226 def test_nested_decls(self): # the fun begins
227 self.assertEqual(self.get_decl('char** ar2D;'),
228 ['Decl', 'ar2D',
229 ['PtrDecl', ['PtrDecl',
230 ['TypeDecl', ['IdentifierType', ['char']]]]]])
231
232 self.assertEqual(self.get_decl('int (*a)[1][2];'),
233 ['Decl', 'a',
234 ['PtrDecl',
235 ['ArrayDecl', '1',
236 ['ArrayDecl', '2',
237 ['TypeDecl', ['IdentifierType', ['int']]]]]]])
238
239 self.assertEqual(self.get_decl('int *a[1][2];'),
240 ['Decl', 'a',
241 ['ArrayDecl', '1',
242 ['ArrayDecl', '2',
243 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['int']]]]]]])
244
245 self.assertEqual(self.get_decl('char ***ar3D[40];'),
246 ['Decl', 'ar3D',
247 ['ArrayDecl', '40',
248 ['PtrDecl', ['PtrDecl', ['PtrDecl',
249 ['TypeDecl', ['IdentifierType', ['char']]]]]]]])
250
251 self.assertEqual(self.get_decl('char (***ar3D)[40];'),
252 ['Decl', 'ar3D',
253 ['PtrDecl', ['PtrDecl', ['PtrDecl',
254 ['ArrayDecl', '40', ['TypeDecl', ['IdentifierType', ['char']]]]]]]])
255
256 self.assertEqual(self.get_decl('int (*x[4])(char, int);'),
257 ['Decl', 'x',
258 ['ArrayDecl', '4',
259 ['PtrDecl',
260 ['FuncDecl',
261 [ ['Typename', ['TypeDecl', ['IdentifierType', ['char']]]],
262 ['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
263 ['TypeDecl', ['IdentifierType', ['int']]]]]]])
264
265 self.assertEqual(self.get_decl('char *(*(**foo [][8])())[];'),
266 ['Decl', 'foo',
267 ['ArrayDecl', '',
268 ['ArrayDecl', '8',
269 ['PtrDecl', ['PtrDecl',
270 ['FuncDecl',
271 [],
272 ['PtrDecl',
273 ['ArrayDecl', '',
274 ['PtrDecl',
275 ['TypeDecl',
276 ['IdentifierType', ['char']]]]]]]]]]]])
277
278 # explore named and unnamed function pointer parameters,
279 # with and without qualifiers
280 #
281
282 # unnamed w/o quals
283 self.assertEqual(self.get_decl('int (*k)(int);'),
284 ['Decl', 'k',
285 ['PtrDecl',
286 ['FuncDecl',
287 [['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
288 ['TypeDecl', ['IdentifierType', ['int']]]]]])
289
290 # unnamed w/ quals
291 self.assertEqual(self.get_decl('int (*k)(const int);'),
292 ['Decl', 'k',
293 ['PtrDecl',
294 ['FuncDecl',
295 [['Typename', ['const'], ['TypeDecl', ['IdentifierType', ['int']]]]],
296 ['TypeDecl', ['IdentifierType', ['int']]]]]])
297
298 # named w/o quals
299 self.assertEqual(self.get_decl('int (*k)(int q);'),
300 ['Decl', 'k',
301 ['PtrDecl',
302 ['FuncDecl',
303 [['Decl', 'q', ['TypeDecl', ['IdentifierType', ['int']]]]],
304 ['TypeDecl', ['IdentifierType', ['int']]]]]])
305
306 # named w/ quals
307 self.assertEqual(self.get_decl('int (*k)(const volatile int q);'),
308 ['Decl', 'k',
309 ['PtrDecl',
310 ['FuncDecl',
311 [['Decl', ['volatile', 'const'], 'q',
312 ['TypeDecl', ['IdentifierType', ['int']]]]],
313 ['TypeDecl', ['IdentifierType', ['int']]]]]])
314
eli.bendersky79d5cf62010-10-29 13:33:52 +0200315 # restrict qualifier
316 self.assertEqual(self.get_decl('int (*k)(restrict int* q);'),
317 ['Decl', 'k',
318 ['PtrDecl',
319 ['FuncDecl',
320 [['Decl', ['restrict'], 'q',
321 ['PtrDecl',
322 ['TypeDecl', ['IdentifierType', ['int']]]]]],
323 ['TypeDecl', ['IdentifierType', ['int']]]]]])
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300324
325 def test_qualifiers_storage_specifiers(self):
326 def assert_qs(txt, index, quals, storage):
327 d = self.parse(txt).ext[index]
328 self.assertEqual(d.quals, quals)
329 self.assertEqual(d.storage, storage)
330
331 assert_qs("extern int p;", 0, [], ['extern'])
332 assert_qs("const long p = 6;", 0, ['const'], [])
333
334 d1 = "static const int p, q, r;"
335 for i in range(3):
336 assert_qs(d1, i, ['const'], ['static'])
337
338 d2 = "static char * const p;"
339 assert_qs(d2, 0, [], ['static'])
340 pdecl = self.parse(d2).ext[0].type
341 self.failUnless(isinstance(pdecl, PtrDecl))
342 self.assertEqual(pdecl.quals, ['const'])
343
344 def test_sizeof(self):
345 e = """
346 void foo()
347 {
348 int a = sizeof k;
349 int b = sizeof(int);
350 int c = sizeof(int**);
351
352 char* p = "just to make sure this parses w/o error...";
353 int d = sizeof(int());
354 }
355 """
356 compound = self.parse(e).ext[0].body
357
eli.benderskyef29ff92010-10-29 16:25:43 +0200358 s1 = compound.block_items[0].init
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300359 self.assertTrue(isinstance(s1, UnaryOp))
360 self.assertEqual(s1.op, 'sizeof')
361 self.assertTrue(isinstance(s1.expr, ID))
362 self.assertEqual(s1.expr.name, 'k')
363
eli.benderskyef29ff92010-10-29 16:25:43 +0200364 s2 = compound.block_items[1].init
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300365 self.assertEqual(expand_decl(s2.expr),
366 ['Typename', ['TypeDecl', ['IdentifierType', ['int']]]])
367
eli.benderskyef29ff92010-10-29 16:25:43 +0200368 s3 = compound.block_items[2].init
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300369 self.assertEqual(expand_decl(s3.expr),
370 ['Typename',
371 ['PtrDecl',
372 ['PtrDecl',
373 ['TypeDecl',
374 ['IdentifierType', ['int']]]]]])
375
376 def test_enums(self):
377 e1 = "enum mycolor op;"
378 e1_type = self.parse(e1).ext[0].type.type
379
380 self.assertTrue(isinstance(e1_type, Enum))
381 self.assertEqual(e1_type.name, 'mycolor')
382 self.assertEqual(e1_type.values, None)
383
384 e2 = "enum mysize {large=20, small, medium} shoes;"
385 e2_type = self.parse(e2).ext[0].type.type
386
387 self.assertTrue(isinstance(e2_type, Enum))
388 self.assertEqual(e2_type.name, 'mysize')
389
390 e2_elist = e2_type.values
391 self.assertTrue(isinstance(e2_elist, EnumeratorList))
392
393 for e2_eval in e2_elist.enumerators:
394 self.assertTrue(isinstance(e2_eval, Enumerator))
395
396 self.assertEqual(e2_elist.enumerators[0].name, 'large')
397 self.assertEqual(e2_elist.enumerators[0].value.value, '20')
398 self.assertEqual(e2_elist.enumerators[2].name, 'medium')
399 self.assertEqual(e2_elist.enumerators[2].value, None)
400
401 # enum with trailing comma (C99 feature)
402 e3 = """
403 enum
404 {
405 red,
406 blue,
407 green,
408 } color;
409 """
410
411 e3_type = self.parse(e3).ext[0].type.type
412 self.assertTrue(isinstance(e3_type, Enum))
413 e3_elist = e3_type.values
414 self.assertTrue(isinstance(e3_elist, EnumeratorList))
415
416 for e3_eval in e3_elist.enumerators:
417 self.assertTrue(isinstance(e3_eval, Enumerator))
418
419 self.assertEqual(e3_elist.enumerators[0].name, 'red')
420 self.assertEqual(e3_elist.enumerators[0].value, None)
421 self.assertEqual(e3_elist.enumerators[1].name, 'blue')
422 self.assertEqual(e3_elist.enumerators[2].name, 'green')
423
424 def test_typedef(self):
425 # without typedef, error
426 s1 = """
427 node k;
428 """
429 self.assertRaises(ParseError, self.parse, s1)
430
431 # now with typedef, works
432 s2 = """
433 typedef void* node;
434 node k;
435 """
436 ps2 = self.parse(s2)
437 self.assertEqual(expand_decl(ps2.ext[0]),
438 ['Typedef', 'node',
439 ['PtrDecl',
440 ['TypeDecl', ['IdentifierType', ['void']]]]])
441
442 self.assertEqual(expand_decl(ps2.ext[1]),
443 ['Decl', 'k',
444 ['TypeDecl', ['IdentifierType', ['node']]]])
445
446 s3 = """
447 typedef int T;
448 typedef T *pT;
449
450 pT aa, bb;
451 """
452 ps3 = self.parse(s3)
453 self.assertEqual(expand_decl(ps3.ext[3]),
454 ['Decl', 'bb',
455 ['TypeDecl', ['IdentifierType', ['pT']]]])
456
457 s4 = '''
458 typedef char* __builtin_va_list;
459 typedef __builtin_va_list __gnuc_va_list;
460 '''
461 ps4 = self.parse(s4)
462 self.assertEqual(expand_decl(ps4.ext[1]),
463 ['Typedef', '__gnuc_va_list',
464 ['TypeDecl',
465 ['IdentifierType', ['__builtin_va_list']]]])
466
467 s5 = '''typedef struct tagHash Hash;'''
468 ps5 = self.parse(s5)
469 self.assertEqual(expand_decl(ps5.ext[0]),
470 ['Typedef', 'Hash', ['TypeDecl', ['Struct', 'tagHash', []]]])
471
472 def test_struct_union(self):
473 s1 = """
474 struct {
475 int id;
476 char* name;
477 } joe;
478 """
479
480 self.assertEqual(expand_decl(self.parse(s1).ext[0]),
481 ['Decl', 'joe',
482 ['TypeDecl', ['Struct', None,
483 [ ['Decl', 'id',
484 ['TypeDecl',
485 ['IdentifierType', ['int']]]],
486 ['Decl', 'name',
487 ['PtrDecl',
488 ['TypeDecl',
489 ['IdentifierType', ['char']]]]]]]]])
490
491 s2 = """
492 struct node p;
493 """
494 self.assertEqual(expand_decl(self.parse(s2).ext[0]),
495 ['Decl', 'p',
496 ['TypeDecl', ['Struct', 'node', []]]])
497
498 s21 = """
499 union pri ra;
500 """
501 self.assertEqual(expand_decl(self.parse(s21).ext[0]),
502 ['Decl', 'ra',
503 ['TypeDecl', ['Union', 'pri', []]]])
504
505 s3 = """
506 struct node* p;
507 """
508 self.assertEqual(expand_decl(self.parse(s3).ext[0]),
509 ['Decl', 'p',
510 ['PtrDecl',
511 ['TypeDecl', ['Struct', 'node', []]]]])
512
513 s4 = """
514 struct node;
515 """
516 self.assertEqual(expand_decl(self.parse(s4).ext[0]),
517 ['Decl', None,
518 ['Struct', 'node', []]])
519
520 s5 = """
521 union
522 {
523 struct
524 {
525 int type;
526 } n;
527
528 struct
529 {
530 int type;
531 int intnode;
532 } ni;
533 } u;
534 """
535 self.assertEqual(expand_decl(self.parse(s5).ext[0]),
536 ['Decl', 'u',
537 ['TypeDecl',
538 ['Union', None,
539 [['Decl', 'n',
540 ['TypeDecl',
541 ['Struct', None,
542 [['Decl', 'type',
543 ['TypeDecl', ['IdentifierType', ['int']]]]]]]],
544 ['Decl', 'ni',
545 ['TypeDecl',
546 ['Struct', None,
547 [['Decl', 'type',
548 ['TypeDecl', ['IdentifierType', ['int']]]],
549 ['Decl', 'intnode',
550 ['TypeDecl', ['IdentifierType', ['int']]]]]]]]]]]])
551
552 s6 = """
553 typedef struct foo_tag
554 {
555 void* data;
556 } foo, *pfoo;
557 """
558 s6_ast = self.parse(s6)
559
560 self.assertEqual(expand_decl(s6_ast.ext[0]),
561 ['Typedef', 'foo',
562 ['TypeDecl',
563 ['Struct', 'foo_tag',
564 [['Decl', 'data',
565 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]])
566
567 self.assertEqual(expand_decl(s6_ast.ext[1]),
568 ['Typedef', 'pfoo',
569 ['PtrDecl',
570 ['TypeDecl',
571 ['Struct', 'foo_tag',
572 [['Decl', 'data',
573 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]]])
574
575 s7 = r"""
576 struct _on_exit_args {
577 void * _fnargs[32];
578 void * _dso_handle[32];
579
580 long _fntypes;
581 #line 77 "D:\eli\cpp_stuff\libc_include/sys/reent.h"
582
583 long _is_cxa;
584 };
585 """
586
587 s7_ast = self.parse(s7, filename='test.c')
588 self.assert_coord(s7_ast.ext[0].type.decls[2], 6, 'test.c')
589 self.assert_coord(s7_ast.ext[0].type.decls[3], 78,
590 r'D:\eli\cpp_stuff\libc_include/sys/reent.h')
591
592 s8 = """
593 typedef enum tagReturnCode {SUCCESS, FAIL} ReturnCode;
594
595 typedef struct tagEntry
596 {
597 char* key;
598 char* value;
599 } Entry;
600
601
602 typedef struct tagNode
603 {
604 Entry* entry;
605
606 struct tagNode* next;
607 } Node;
608
609 typedef struct tagHash
610 {
611 unsigned int table_size;
612
613 Node** heads;
614
615 } Hash;
616 """
617 s8_ast = self.parse(s8)
618 self.assertEqual(expand_decl(s8_ast.ext[3]),
619 ['Typedef', 'Hash',
620 ['TypeDecl', ['Struct', 'tagHash',
621 [['Decl', 'table_size',
622 ['TypeDecl', ['IdentifierType', ['int', 'unsigned']]]],
623 ['Decl', 'heads',
624 ['PtrDecl', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['Node']]]]]]]]]])
625
eli.bendersky0e0a71f2010-10-09 08:32:00 +0200626 def test_struct_bitfields(self):
eli.bendersky38ed9a92010-10-09 09:29:59 +0200627 # a struct with two bitfields, one unnamed
eli.bendersky0e0a71f2010-10-09 08:32:00 +0200628 s1 = """
629 struct {
630 int k:6;
631 int :2;
632 } joe;
633 """
634
635 parsed_struct = self.parse(s1).ext[0]
636
eli.bendersky38ed9a92010-10-09 09:29:59 +0200637 # We can see here the name of the decl for the unnamed bitfield is
eli.bendersky0e0a71f2010-10-09 08:32:00 +0200638 # None, but expand_decl doesn't show bitfield widths
639 # ...
640 self.assertEqual(expand_decl(parsed_struct),
641 ['Decl', 'joe',
642 ['TypeDecl', ['Struct', None,
643 [ ['Decl', 'k',
644 ['TypeDecl',
645 ['IdentifierType', ['int']]]],
646 ['Decl', None,
647 ['TypeDecl',
648 ['IdentifierType', ['int']]]]]]]])
649
650 # ...
651 # so we test them manually
652 self.assertEqual(parsed_struct.type.type.decls[0].bitsize.value, '6')
653 self.assertEqual(parsed_struct.type.type.decls[1].bitsize.value, '2')
654
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300655 def test_tags_namespace(self):
656 """ Tests that the tags of structs/unions/enums reside in a separate namespace and
657 can be named after existing types.
658 """
659 s1 = """
660 typedef int tagEntry;
661
662 struct tagEntry
663 {
664 char* key;
665 char* value;
666 } Entry;
667 """
668
669 s1_ast = self.parse(s1)
670 self.assertEqual(expand_decl(s1_ast.ext[1]),
671 ['Decl', 'Entry',
672 ['TypeDecl', ['Struct', 'tagEntry',
673 [['Decl', 'key',
674 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
675 ['Decl', 'value',
676 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
677
678 s2 = """
679 struct tagEntry;
680
681 typedef struct tagEntry tagEntry;
682
683 struct tagEntry
684 {
685 char* key;
686 char* value;
687 } Entry;
688 """
689
690 s2_ast = self.parse(s2)
691 self.assertEqual(expand_decl(s2_ast.ext[2]),
692 ['Decl', 'Entry',
693 ['TypeDecl', ['Struct', 'tagEntry',
694 [['Decl', 'key',
695 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
696 ['Decl', 'value',
697 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
698
699 s3 = """
700 typedef int mytag;
701
702 enum mytag {ABC, CDE};
703 enum mytag joe;
704 """
705
706 s3_type = self.parse(s3).ext[1].type
707
708 self.assertTrue(isinstance(s3_type, Enum))
709 self.assertEqual(s3_type.name, 'mytag')
710
711 def test_multi_decls(self):
712 d1 = 'int a, b;'
713
714 self.assertEqual(self.get_decl(d1, 0),
715 ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
716 self.assertEqual(self.get_decl(d1, 1),
717 ['Decl', 'b', ['TypeDecl', ['IdentifierType', ['int']]]])
718
719 d2 = 'char* p, notp, ar[4];'
720 self.assertEqual(self.get_decl(d2, 0),
721 ['Decl', 'p',
722 ['PtrDecl',
723 ['TypeDecl', ['IdentifierType', ['char']]]]])
724 self.assertEqual(self.get_decl(d2, 1),
725 ['Decl', 'notp', ['TypeDecl', ['IdentifierType', ['char']]]])
726 self.assertEqual(self.get_decl(d2, 2),
727 ['Decl', 'ar',
728 ['ArrayDecl', '4',
729 ['TypeDecl', ['IdentifierType', ['char']]]]])
730
731 def test_invalid_multiple_types_error(self):
732 bad = [
733 'int enum {ab, cd} fubr;',
734 'enum kid char brbr;']
735
736 for b in bad:
737 self.assertRaises(ParseError, self.parse, b)
738
739 def test_decl_inits(self):
740 d1 = 'int a = 16;'
741 self.assertEqual(self.get_decl(d1),
742 ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
743 self.assertEqual(self.get_decl_init(d1),
744 ['Constant', 'int', '16'])
745
746 d2 = 'long ar[] = {7, 8, 9};'
747 self.assertEqual(self.get_decl(d2),
748 ['Decl', 'ar',
749 ['ArrayDecl', '',
750 ['TypeDecl', ['IdentifierType', ['long']]]]])
751 self.assertEqual(self.get_decl_init(d2),
752 [ ['Constant', 'int', '7'],
753 ['Constant', 'int', '8'],
754 ['Constant', 'int', '9'],])
755
756 d3 = 'char p = j;'
757 self.assertEqual(self.get_decl(d3),
758 ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['char']]]])
759 self.assertEqual(self.get_decl_init(d3),
760 ['ID', 'j'])
761
762 d4 = "char x = 'c', *p = {0, 1, 2, {4, 5}, 6};"
763 self.assertEqual(self.get_decl(d4, 0),
764 ['Decl', 'x', ['TypeDecl', ['IdentifierType', ['char']]]])
765 self.assertEqual(self.get_decl_init(d4, 0),
766 ['Constant', 'char', "'c'"])
767 self.assertEqual(self.get_decl(d4, 1),
768 ['Decl', 'p',
769 ['PtrDecl',
770 ['TypeDecl', ['IdentifierType', ['char']]]]])
771 self.assertEqual(self.get_decl_init(d4, 1),
772 [ ['Constant', 'int', '0'],
773 ['Constant', 'int', '1'],
774 ['Constant', 'int', '2'],
775 [ ['Constant', 'int', '4'],
776 ['Constant', 'int', '5']],
777 ['Constant', 'int', '6']])
778
779 def test_function_definitions(self):
780 def parse_fdef(str):
781 return self.parse(str).ext[0]
782
783 def fdef_decl(fdef):
784 return expand_decl(fdef.decl)
785
786 f1 = parse_fdef('''
787 int factorial(int p)
788 {
789 return 3;
790 }
791 ''')
792
793 self.assertEqual(fdef_decl(f1),
794 ['Decl', 'factorial',
795 ['FuncDecl',
796 [['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]]],
797 ['TypeDecl', ['IdentifierType', ['int']]]]])
798
eli.benderskyef29ff92010-10-29 16:25:43 +0200799 self.assertEqual(type(f1.body.block_items[0]), Return)
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300800
801 f2 = parse_fdef('''
802 char* zzz(int p, char* c)
803 {
804 int a;
805 char b;
806
807 a = b + 2;
808 return 3;
809 }
810 ''')
811
812 self.assertEqual(fdef_decl(f2),
813 ['Decl', 'zzz',
814 ['FuncDecl',
815 [ ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]],
816 ['Decl', 'c', ['PtrDecl',
817 ['TypeDecl', ['IdentifierType', ['char']]]]]],
818 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
819
eli.benderskyef29ff92010-10-29 16:25:43 +0200820 self.assertEqual(list(map(type, f2.body.block_items)),
821 [Decl, Decl, Assignment, Return])
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300822
823 f3 = parse_fdef('''
824 char* zzz(p, c)
825 long p, *c;
826 {
827 int a;
828 char b;
829
830 a = b + 2;
831 return 3;
832 }
833 ''')
834
835 self.assertEqual(fdef_decl(f3),
836 ['Decl', 'zzz',
837 ['FuncDecl',
838 [ ['ID', 'p'],
839 ['ID', 'c']],
840 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
841
eli.benderskyef29ff92010-10-29 16:25:43 +0200842 self.assertEqual(list(map(type, f3.body.block_items)),
843 [Decl, Decl, Assignment, Return])
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300844
845 self.assertEqual(expand_decl(f3.param_decls[0]),
846 ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['long']]]])
847 self.assertEqual(expand_decl(f3.param_decls[1]),
848 ['Decl', 'c', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['long']]]]])
849
eli.bendersky71540662010-07-03 12:58:52 +0200850 def test_unified_string_literals(self):
851 # simple string, for reference
852 d1 = self.get_decl_init('char* s = "hello";')
853 self.assertEqual(d1, ['Constant', 'string', '"hello"'])
854
855 d2 = self.get_decl_init('char* s = "hello" " world";')
856 self.assertEqual(d2, ['Constant', 'string', '"hello world"'])
857
858 # the test case from issue 6
859 d3 = self.parse(r'''
860 int main() {
861 fprintf(stderr,
862 "Wrong Params?\n"
863 "Usage:\n"
864 "%s <binary_file_path>\n",
865 argv[0]
866 );
867 }
868 ''')
869
870 self.assertEqual(
eli.benderskyef29ff92010-10-29 16:25:43 +0200871 d3.ext[0].body.block_items[0].args.exprs[1].value,
eli.bendersky71540662010-07-03 12:58:52 +0200872 r'"Wrong Params?\nUsage:\n%s <binary_file_path>\n"')
eli.bendersky4a89f112010-07-05 06:02:03 +0200873
874 d4 = self.get_decl_init('char* s = "" "foobar";')
875 self.assertEqual(d4, ['Constant', 'string', '"foobar"'])
876
877 d5 = self.get_decl_init(r'char* s = "foo\"" "bar";')
878 self.assertEqual(d5, ['Constant', 'string', r'"foo\"bar"'])
eli.bendersky71540662010-07-03 12:58:52 +0200879
eli.bendersky79d5cf62010-10-29 13:33:52 +0200880 def test_inline_specifier(self):
881 ps2 = self.parse('static inline void inlinefoo(void);')
882 self.assertEqual(ps2.ext[0].funcspec, ['inline'])
eli.bendersky2e907fa2010-10-29 15:51:07 +0200883
884 # variable length array
885 def test_vla(self):
886 ps2 = self.parse(r'''
887 int main() {
888 int size;
889 int var[size = 5];
890
891 int var2[*];
892 }
893 ''')
eli.benderskyef29ff92010-10-29 16:25:43 +0200894 self.failUnless(isinstance(ps2.ext[0].body.block_items[1].type.dim, Assignment))
895 self.failUnless(isinstance(ps2.ext[0].body.block_items[2].type.dim, ID))
eli.bendersky79d5cf62010-10-29 13:33:52 +0200896
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300897
898class TestCParser_whole_code(unittest.TestCase):
899 """ Testing of parsing whole chunks of code.
900
901 Since I don't want to rely on the structure of ASTs too
902 much, most of these tests are implemented with visitors.
903 """
904 def parse(self, txt, filename=''):
905 return self.cparser.parse(txt, filename)
906
907 def setUp(self):
908 self.cparser = _c_parser
909
910 # A simple helper visitor that lists the values of all the
911 # Constant nodes it sees.
912 #
913 class ConstantVisitor(NodeVisitor):
914 def __init__(self):
915 self.values = []
916
917 def visit_Constant(self, node):
918 self.values.append(node.value)
919
920 # This visitor counts the amount of references to the ID
921 # with the name provided to it in the constructor.
922 #
923 class IDNameCounter(NodeVisitor):
924 def __init__(self, name):
925 self.name = name
926 self.nrefs = 0
927
928 def visit_ID(self, node):
929 if node.name == self.name:
930 self.nrefs += 1
931
932 # Counts the amount of nodes of a given class
933 #
934 class NodeKlassCounter(NodeVisitor):
935 def __init__(self, node_klass):
936 self.klass = node_klass
937 self.n = 0
938
939 def generic_visit(self, node):
940 if node.__class__ == self.klass:
941 self.n += 1
942
943 NodeVisitor.generic_visit(self, node)
944
945 def assert_all_Constants(self, code, constants):
946 """ Asserts that the list of all Constant values (by
947 'preorder' appearance) in the chunk of code is as
948 given.
949 """
eli.benderskyed890492010-06-25 08:25:55 +0300950 if isinstance(code, str):
951 parsed = self.parse(code)
952 else:
953 parsed = code
954
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300955 cv = self.ConstantVisitor()
956 cv.visit(parsed)
957 self.assertEqual(cv.values, constants)
958
959 def assert_num_ID_refs(self, code, name, num):
960 """ Asserts the number of references to the ID with
961 the given name.
962 """
963 if isinstance(code, str):
964 parsed = self.parse(code)
965 else:
966 parsed = code
967
968 iv = self.IDNameCounter(name)
969 iv.visit(parsed)
970 self.assertEqual(iv.nrefs, num)
971
972 def assert_num_klass_nodes(self, code, klass, num):
973 """ Asserts the amount of klass nodes in the code.
974 """
975 if isinstance(code, str):
976 parsed = self.parse(code)
977 else:
978 parsed = code
979
980 cv = self.NodeKlassCounter(klass)
981 cv.visit(parsed)
982 self.assertEqual(cv.n, num)
983
984 def test_expressions(self):
985 e1 = '''int k = (r + 10.0) >> 6 + 8 << (3 & 0x14);'''
986 self.assert_all_Constants(e1, ['10.0', '6', '8', '3', '0x14'])
987
988 e2 = r'''char n = '\n', *prefix = "st_";'''
989 self.assert_all_Constants(e2, [r"'\n'", '"st_"'])
990
991 def test_statements(self):
992 s1 = r'''
993 void foo(){
994 if (sp == 1)
995 if (optind >= argc ||
996 argv[optind][0] != '-' || argv[optind][1] == '\0')
997 return -1;
998 else if (strcmp(argv[optind], "--") == 0) {
999 optind++;
1000 return -1;
1001 }
1002 }
1003 '''
1004
1005 self.assert_all_Constants(s1,
1006 ['1', '0', r"'-'", '1', r"'\0'", '1', r'"--"', '0', '1'])
1007
1008 ps1 = self.parse(s1)
1009 self.assert_num_ID_refs(ps1, 'argv', 3)
1010 self.assert_num_ID_refs(ps1, 'optind', 5)
1011
1012 self.assert_num_klass_nodes(ps1, If, 3)
1013 self.assert_num_klass_nodes(ps1, Return, 2)
1014 self.assert_num_klass_nodes(ps1, FuncCall, 1) # strcmp
1015 self.assert_num_klass_nodes(ps1, BinaryOp, 7)
1016
1017 # In the following code, Hash and Node were defined as
1018 # int to pacify the parser that sees they're used as
1019 # types
1020 #
1021 s2 = r'''
1022 typedef int Hash, Node;
1023
1024 void HashDestroy(Hash* hash)
1025 {
1026 unsigned int i;
1027
1028 if (hash == NULL)
1029 return;
1030
1031 for (i = 0; i < hash->table_size; ++i)
1032 {
1033 Node* temp = hash->heads[i];
1034
1035 while (temp != NULL)
1036 {
1037 Node* temp2 = temp;
1038
1039 free(temp->entry->key);
1040 free(temp->entry->value);
1041 free(temp->entry);
1042
1043 temp = temp->next;
1044
1045 free(temp2);
1046 }
1047 }
1048
1049 free(hash->heads);
1050 hash->heads = NULL;
1051
1052 free(hash);
1053 }
1054 '''
1055
1056 ps2 = self.parse(s2)
1057 self.assert_num_klass_nodes(ps2, FuncCall, 6)
1058 self.assert_num_klass_nodes(ps2, FuncDef, 1)
1059 self.assert_num_klass_nodes(ps2, For, 1)
1060 self.assert_num_klass_nodes(ps2, While, 1)
1061 self.assert_num_klass_nodes(ps2, StructRef, 10)
1062
1063 # declarations don't count
1064 self.assert_num_ID_refs(ps2, 'hash', 6)
1065 self.assert_num_ID_refs(ps2, 'i', 4)
eli.benderskyed890492010-06-25 08:25:55 +03001066
1067 s3 = r'''
1068 void x(void) {
1069 int a, b;
1070 if (a < b)
1071 do {
1072 a = 0;
1073 } while (0);
1074 else if (a == b) {
1075 a = 1;
1076 }
1077 }
1078 '''
1079
1080 ps3 = self.parse(s3)
1081 self.assert_num_klass_nodes(ps3, DoWhile, 1)
1082 self.assert_num_ID_refs(ps3, 'a', 4)
1083 self.assert_all_Constants(ps3, ['0', '0', '1'])
eli.bendersky145890d2010-10-29 12:02:32 +02001084
1085 def test_for_statement(self):
1086 s2 = r'''
1087 void x(void)
1088 {
1089 int i;
1090 for (i = 0; i < 5; ++i) {
1091 x = 50;
1092 }
1093 }
1094 '''
1095 ps2 = self.parse(s2)
1096 self.assert_num_klass_nodes(ps2, For, 1)
1097 # here there are 3 refs to 'i' since the declaration doesn't count as
1098 # a ref in the visitor
1099 #
1100 self.assert_num_ID_refs(ps2, 'i', 3)
eli.benderskyed890492010-06-25 08:25:55 +03001101
eli.bendersky145890d2010-10-29 12:02:32 +02001102 s3 = r'''
1103 void x(void)
1104 {
1105 for (int i = 0; i < 5; ++i) {
1106 x = 50;
1107 }
1108 }
1109 '''
1110 ps3 = self.parse(s3)
eli.bendersky145890d2010-10-29 12:02:32 +02001111 self.assert_num_klass_nodes(ps3, For, 1)
1112 # here there are 2 refs to 'i' since the declaration doesn't count as
1113 # a ref in the visitor
1114 #
1115 self.assert_num_ID_refs(ps3, 'i', 2)
Eli Bendersky3921e8e2010-05-21 09:05:39 +03001116
1117 def test_whole_file(self):
1118 # See how pycparser handles a whole, real C file.
1119 #
1120 filename = 'c_files/memmgr_with_h.c'
1121 code = open(filename, 'rU').read()
1122 p = self.parse(code)
1123
1124 self.assert_num_klass_nodes(p, FuncDef, 5)
1125
1126 # each FuncDef also has a FuncDecl. 4 declarations
1127 # + 5 definitions, overall 9
1128 self.assert_num_klass_nodes(p, FuncDecl, 9)
1129
1130 self.assert_num_klass_nodes(p, Typedef, 4)
1131
1132 self.assertEqual(p.ext[4].coord.line, 88)
1133 self.assertEqual(p.ext[4].coord.file, "./memmgr.h")
1134
1135 self.assertEqual(p.ext[6].coord.line, 10)
1136 self.assertEqual(p.ext[6].coord.file, "memmgr.c")
1137
1138 def test_whole_file_with_stdio(self):
1139 # Parse a whole file with stdio.h included by cpp
1140 #
1141 filename = 'c_files/cppd_with_stdio_h.c'
1142 code = open(filename, 'rU').read()
1143 p = self.parse(code)
1144
1145 self.failUnless(isinstance(p.ext[0], Typedef))
1146 self.assertEqual(p.ext[0].coord.line, 213)
1147 self.assertEqual(p.ext[0].coord.file, "D:\eli\cpp_stuff\libc_include/stddef.h")
1148
1149 self.failUnless(isinstance(p.ext[-1], FuncDef))
1150 self.assertEqual(p.ext[-1].coord.line, 15)
1151 self.assertEqual(p.ext[-1].coord.file, "example_c_file.c")
1152
1153 self.failUnless(isinstance(p.ext[-8], Typedef))
1154 self.failUnless(isinstance(p.ext[-8].type, TypeDecl))
1155 self.assertEqual(p.ext[-8].name, 'cookie_io_functions_t')
1156
1157
1158if __name__ == '__main__':
1159 #~ suite = unittest.TestLoader().loadTestsFromNames(
1160 #~ ['test_c_parser.TestCParser_fundamentals.test_typedef'])
1161
1162 #~ suite = unittest.TestLoader().loadTestsFromNames(
1163 #~ ['test_c_parser.TestCParser_whole_code.test_whole_file_with_stdio'])
1164
1165 #~ suite = unittest.TestLoader().loadTestsFromTestCase(
1166 #~ TestCParser_whole_code)
1167
1168 #~ unittest.TextTestRunner(verbosity=2).run(suite)
1169 unittest.main()