blob: 532fe26c441ff5d9e8ccbf01387a104c46956254 [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']]]]])
197
198 self.assertEqual(self.get_decl('unsigned ar[];'),
199 ['Decl', 'ar',
200 ['ArrayDecl', '',
201 ['TypeDecl', ['IdentifierType', ['unsigned']]]]])
202
203 self.assertEqual(self.get_decl('int strlen(char* s);'),
204 ['Decl', 'strlen',
205 ['FuncDecl',
206 [['Decl', 's',
207 ['PtrDecl',
208 ['TypeDecl', ['IdentifierType', ['char']]]]]],
209 ['TypeDecl', ['IdentifierType', ['int']]]]])
210
211 self.assertEqual(self.get_decl('int strcmp(char* s1, char* s2);'),
212 ['Decl', 'strcmp',
213 ['FuncDecl',
214 [ ['Decl', 's1',
215 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
216 ['Decl', 's2',
217 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]
218 ],
219 ['TypeDecl', ['IdentifierType', ['int']]]]])
220
221 def test_nested_decls(self): # the fun begins
222 self.assertEqual(self.get_decl('char** ar2D;'),
223 ['Decl', 'ar2D',
224 ['PtrDecl', ['PtrDecl',
225 ['TypeDecl', ['IdentifierType', ['char']]]]]])
226
227 self.assertEqual(self.get_decl('int (*a)[1][2];'),
228 ['Decl', 'a',
229 ['PtrDecl',
230 ['ArrayDecl', '1',
231 ['ArrayDecl', '2',
232 ['TypeDecl', ['IdentifierType', ['int']]]]]]])
233
234 self.assertEqual(self.get_decl('int *a[1][2];'),
235 ['Decl', 'a',
236 ['ArrayDecl', '1',
237 ['ArrayDecl', '2',
238 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['int']]]]]]])
239
240 self.assertEqual(self.get_decl('char ***ar3D[40];'),
241 ['Decl', 'ar3D',
242 ['ArrayDecl', '40',
243 ['PtrDecl', ['PtrDecl', ['PtrDecl',
244 ['TypeDecl', ['IdentifierType', ['char']]]]]]]])
245
246 self.assertEqual(self.get_decl('char (***ar3D)[40];'),
247 ['Decl', 'ar3D',
248 ['PtrDecl', ['PtrDecl', ['PtrDecl',
249 ['ArrayDecl', '40', ['TypeDecl', ['IdentifierType', ['char']]]]]]]])
250
251 self.assertEqual(self.get_decl('int (*x[4])(char, int);'),
252 ['Decl', 'x',
253 ['ArrayDecl', '4',
254 ['PtrDecl',
255 ['FuncDecl',
256 [ ['Typename', ['TypeDecl', ['IdentifierType', ['char']]]],
257 ['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
258 ['TypeDecl', ['IdentifierType', ['int']]]]]]])
259
260 self.assertEqual(self.get_decl('char *(*(**foo [][8])())[];'),
261 ['Decl', 'foo',
262 ['ArrayDecl', '',
263 ['ArrayDecl', '8',
264 ['PtrDecl', ['PtrDecl',
265 ['FuncDecl',
266 [],
267 ['PtrDecl',
268 ['ArrayDecl', '',
269 ['PtrDecl',
270 ['TypeDecl',
271 ['IdentifierType', ['char']]]]]]]]]]]])
272
273 # explore named and unnamed function pointer parameters,
274 # with and without qualifiers
275 #
276
277 # unnamed w/o quals
278 self.assertEqual(self.get_decl('int (*k)(int);'),
279 ['Decl', 'k',
280 ['PtrDecl',
281 ['FuncDecl',
282 [['Typename', ['TypeDecl', ['IdentifierType', ['int']]]]],
283 ['TypeDecl', ['IdentifierType', ['int']]]]]])
284
285 # unnamed w/ quals
286 self.assertEqual(self.get_decl('int (*k)(const int);'),
287 ['Decl', 'k',
288 ['PtrDecl',
289 ['FuncDecl',
290 [['Typename', ['const'], ['TypeDecl', ['IdentifierType', ['int']]]]],
291 ['TypeDecl', ['IdentifierType', ['int']]]]]])
292
293 # named w/o quals
294 self.assertEqual(self.get_decl('int (*k)(int q);'),
295 ['Decl', 'k',
296 ['PtrDecl',
297 ['FuncDecl',
298 [['Decl', 'q', ['TypeDecl', ['IdentifierType', ['int']]]]],
299 ['TypeDecl', ['IdentifierType', ['int']]]]]])
300
301 # named w/ quals
302 self.assertEqual(self.get_decl('int (*k)(const volatile int q);'),
303 ['Decl', 'k',
304 ['PtrDecl',
305 ['FuncDecl',
306 [['Decl', ['volatile', 'const'], 'q',
307 ['TypeDecl', ['IdentifierType', ['int']]]]],
308 ['TypeDecl', ['IdentifierType', ['int']]]]]])
309
eli.bendersky79d5cf62010-10-29 13:33:52 +0200310 # restrict qualifier
311 self.assertEqual(self.get_decl('int (*k)(restrict int* q);'),
312 ['Decl', 'k',
313 ['PtrDecl',
314 ['FuncDecl',
315 [['Decl', ['restrict'], 'q',
316 ['PtrDecl',
317 ['TypeDecl', ['IdentifierType', ['int']]]]]],
318 ['TypeDecl', ['IdentifierType', ['int']]]]]])
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300319
320 def test_qualifiers_storage_specifiers(self):
321 def assert_qs(txt, index, quals, storage):
322 d = self.parse(txt).ext[index]
323 self.assertEqual(d.quals, quals)
324 self.assertEqual(d.storage, storage)
325
326 assert_qs("extern int p;", 0, [], ['extern'])
327 assert_qs("const long p = 6;", 0, ['const'], [])
328
329 d1 = "static const int p, q, r;"
330 for i in range(3):
331 assert_qs(d1, i, ['const'], ['static'])
332
333 d2 = "static char * const p;"
334 assert_qs(d2, 0, [], ['static'])
335 pdecl = self.parse(d2).ext[0].type
336 self.failUnless(isinstance(pdecl, PtrDecl))
337 self.assertEqual(pdecl.quals, ['const'])
338
339 def test_sizeof(self):
340 e = """
341 void foo()
342 {
343 int a = sizeof k;
344 int b = sizeof(int);
345 int c = sizeof(int**);
346
347 char* p = "just to make sure this parses w/o error...";
348 int d = sizeof(int());
349 }
350 """
351 compound = self.parse(e).ext[0].body
352
eli.benderskyef29ff92010-10-29 16:25:43 +0200353 s1 = compound.block_items[0].init
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300354 self.assertTrue(isinstance(s1, UnaryOp))
355 self.assertEqual(s1.op, 'sizeof')
356 self.assertTrue(isinstance(s1.expr, ID))
357 self.assertEqual(s1.expr.name, 'k')
358
eli.benderskyef29ff92010-10-29 16:25:43 +0200359 s2 = compound.block_items[1].init
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300360 self.assertEqual(expand_decl(s2.expr),
361 ['Typename', ['TypeDecl', ['IdentifierType', ['int']]]])
362
eli.benderskyef29ff92010-10-29 16:25:43 +0200363 s3 = compound.block_items[2].init
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300364 self.assertEqual(expand_decl(s3.expr),
365 ['Typename',
366 ['PtrDecl',
367 ['PtrDecl',
368 ['TypeDecl',
369 ['IdentifierType', ['int']]]]]])
370
371 def test_enums(self):
372 e1 = "enum mycolor op;"
373 e1_type = self.parse(e1).ext[0].type.type
374
375 self.assertTrue(isinstance(e1_type, Enum))
376 self.assertEqual(e1_type.name, 'mycolor')
377 self.assertEqual(e1_type.values, None)
378
379 e2 = "enum mysize {large=20, small, medium} shoes;"
380 e2_type = self.parse(e2).ext[0].type.type
381
382 self.assertTrue(isinstance(e2_type, Enum))
383 self.assertEqual(e2_type.name, 'mysize')
384
385 e2_elist = e2_type.values
386 self.assertTrue(isinstance(e2_elist, EnumeratorList))
387
388 for e2_eval in e2_elist.enumerators:
389 self.assertTrue(isinstance(e2_eval, Enumerator))
390
391 self.assertEqual(e2_elist.enumerators[0].name, 'large')
392 self.assertEqual(e2_elist.enumerators[0].value.value, '20')
393 self.assertEqual(e2_elist.enumerators[2].name, 'medium')
394 self.assertEqual(e2_elist.enumerators[2].value, None)
395
396 # enum with trailing comma (C99 feature)
397 e3 = """
398 enum
399 {
400 red,
401 blue,
402 green,
403 } color;
404 """
405
406 e3_type = self.parse(e3).ext[0].type.type
407 self.assertTrue(isinstance(e3_type, Enum))
408 e3_elist = e3_type.values
409 self.assertTrue(isinstance(e3_elist, EnumeratorList))
410
411 for e3_eval in e3_elist.enumerators:
412 self.assertTrue(isinstance(e3_eval, Enumerator))
413
414 self.assertEqual(e3_elist.enumerators[0].name, 'red')
415 self.assertEqual(e3_elist.enumerators[0].value, None)
416 self.assertEqual(e3_elist.enumerators[1].name, 'blue')
417 self.assertEqual(e3_elist.enumerators[2].name, 'green')
418
419 def test_typedef(self):
420 # without typedef, error
421 s1 = """
422 node k;
423 """
424 self.assertRaises(ParseError, self.parse, s1)
425
426 # now with typedef, works
427 s2 = """
428 typedef void* node;
429 node k;
430 """
431 ps2 = self.parse(s2)
432 self.assertEqual(expand_decl(ps2.ext[0]),
433 ['Typedef', 'node',
434 ['PtrDecl',
435 ['TypeDecl', ['IdentifierType', ['void']]]]])
436
437 self.assertEqual(expand_decl(ps2.ext[1]),
438 ['Decl', 'k',
439 ['TypeDecl', ['IdentifierType', ['node']]]])
440
441 s3 = """
442 typedef int T;
443 typedef T *pT;
444
445 pT aa, bb;
446 """
447 ps3 = self.parse(s3)
448 self.assertEqual(expand_decl(ps3.ext[3]),
449 ['Decl', 'bb',
450 ['TypeDecl', ['IdentifierType', ['pT']]]])
451
452 s4 = '''
453 typedef char* __builtin_va_list;
454 typedef __builtin_va_list __gnuc_va_list;
455 '''
456 ps4 = self.parse(s4)
457 self.assertEqual(expand_decl(ps4.ext[1]),
458 ['Typedef', '__gnuc_va_list',
459 ['TypeDecl',
460 ['IdentifierType', ['__builtin_va_list']]]])
461
462 s5 = '''typedef struct tagHash Hash;'''
463 ps5 = self.parse(s5)
464 self.assertEqual(expand_decl(ps5.ext[0]),
465 ['Typedef', 'Hash', ['TypeDecl', ['Struct', 'tagHash', []]]])
466
467 def test_struct_union(self):
468 s1 = """
469 struct {
470 int id;
471 char* name;
472 } joe;
473 """
474
475 self.assertEqual(expand_decl(self.parse(s1).ext[0]),
476 ['Decl', 'joe',
477 ['TypeDecl', ['Struct', None,
478 [ ['Decl', 'id',
479 ['TypeDecl',
480 ['IdentifierType', ['int']]]],
481 ['Decl', 'name',
482 ['PtrDecl',
483 ['TypeDecl',
484 ['IdentifierType', ['char']]]]]]]]])
485
486 s2 = """
487 struct node p;
488 """
489 self.assertEqual(expand_decl(self.parse(s2).ext[0]),
490 ['Decl', 'p',
491 ['TypeDecl', ['Struct', 'node', []]]])
492
493 s21 = """
494 union pri ra;
495 """
496 self.assertEqual(expand_decl(self.parse(s21).ext[0]),
497 ['Decl', 'ra',
498 ['TypeDecl', ['Union', 'pri', []]]])
499
500 s3 = """
501 struct node* p;
502 """
503 self.assertEqual(expand_decl(self.parse(s3).ext[0]),
504 ['Decl', 'p',
505 ['PtrDecl',
506 ['TypeDecl', ['Struct', 'node', []]]]])
507
508 s4 = """
509 struct node;
510 """
511 self.assertEqual(expand_decl(self.parse(s4).ext[0]),
512 ['Decl', None,
513 ['Struct', 'node', []]])
514
515 s5 = """
516 union
517 {
518 struct
519 {
520 int type;
521 } n;
522
523 struct
524 {
525 int type;
526 int intnode;
527 } ni;
528 } u;
529 """
530 self.assertEqual(expand_decl(self.parse(s5).ext[0]),
531 ['Decl', 'u',
532 ['TypeDecl',
533 ['Union', None,
534 [['Decl', 'n',
535 ['TypeDecl',
536 ['Struct', None,
537 [['Decl', 'type',
538 ['TypeDecl', ['IdentifierType', ['int']]]]]]]],
539 ['Decl', 'ni',
540 ['TypeDecl',
541 ['Struct', None,
542 [['Decl', 'type',
543 ['TypeDecl', ['IdentifierType', ['int']]]],
544 ['Decl', 'intnode',
545 ['TypeDecl', ['IdentifierType', ['int']]]]]]]]]]]])
546
547 s6 = """
548 typedef struct foo_tag
549 {
550 void* data;
551 } foo, *pfoo;
552 """
553 s6_ast = self.parse(s6)
554
555 self.assertEqual(expand_decl(s6_ast.ext[0]),
556 ['Typedef', 'foo',
557 ['TypeDecl',
558 ['Struct', 'foo_tag',
559 [['Decl', 'data',
560 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]])
561
562 self.assertEqual(expand_decl(s6_ast.ext[1]),
563 ['Typedef', 'pfoo',
564 ['PtrDecl',
565 ['TypeDecl',
566 ['Struct', 'foo_tag',
567 [['Decl', 'data',
568 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['void']]]]]]]]]])
569
570 s7 = r"""
571 struct _on_exit_args {
572 void * _fnargs[32];
573 void * _dso_handle[32];
574
575 long _fntypes;
576 #line 77 "D:\eli\cpp_stuff\libc_include/sys/reent.h"
577
578 long _is_cxa;
579 };
580 """
581
582 s7_ast = self.parse(s7, filename='test.c')
583 self.assert_coord(s7_ast.ext[0].type.decls[2], 6, 'test.c')
584 self.assert_coord(s7_ast.ext[0].type.decls[3], 78,
585 r'D:\eli\cpp_stuff\libc_include/sys/reent.h')
586
587 s8 = """
588 typedef enum tagReturnCode {SUCCESS, FAIL} ReturnCode;
589
590 typedef struct tagEntry
591 {
592 char* key;
593 char* value;
594 } Entry;
595
596
597 typedef struct tagNode
598 {
599 Entry* entry;
600
601 struct tagNode* next;
602 } Node;
603
604 typedef struct tagHash
605 {
606 unsigned int table_size;
607
608 Node** heads;
609
610 } Hash;
611 """
612 s8_ast = self.parse(s8)
613 self.assertEqual(expand_decl(s8_ast.ext[3]),
614 ['Typedef', 'Hash',
615 ['TypeDecl', ['Struct', 'tagHash',
616 [['Decl', 'table_size',
617 ['TypeDecl', ['IdentifierType', ['int', 'unsigned']]]],
618 ['Decl', 'heads',
619 ['PtrDecl', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['Node']]]]]]]]]])
620
eli.bendersky0e0a71f2010-10-09 08:32:00 +0200621 def test_struct_bitfields(self):
eli.bendersky38ed9a92010-10-09 09:29:59 +0200622 # a struct with two bitfields, one unnamed
eli.bendersky0e0a71f2010-10-09 08:32:00 +0200623 s1 = """
624 struct {
625 int k:6;
626 int :2;
627 } joe;
628 """
629
630 parsed_struct = self.parse(s1).ext[0]
631
eli.bendersky38ed9a92010-10-09 09:29:59 +0200632 # We can see here the name of the decl for the unnamed bitfield is
eli.bendersky0e0a71f2010-10-09 08:32:00 +0200633 # None, but expand_decl doesn't show bitfield widths
634 # ...
635 self.assertEqual(expand_decl(parsed_struct),
636 ['Decl', 'joe',
637 ['TypeDecl', ['Struct', None,
638 [ ['Decl', 'k',
639 ['TypeDecl',
640 ['IdentifierType', ['int']]]],
641 ['Decl', None,
642 ['TypeDecl',
643 ['IdentifierType', ['int']]]]]]]])
644
645 # ...
646 # so we test them manually
647 self.assertEqual(parsed_struct.type.type.decls[0].bitsize.value, '6')
648 self.assertEqual(parsed_struct.type.type.decls[1].bitsize.value, '2')
649
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300650 def test_tags_namespace(self):
651 """ Tests that the tags of structs/unions/enums reside in a separate namespace and
652 can be named after existing types.
653 """
654 s1 = """
655 typedef int tagEntry;
656
657 struct tagEntry
658 {
659 char* key;
660 char* value;
661 } Entry;
662 """
663
664 s1_ast = self.parse(s1)
665 self.assertEqual(expand_decl(s1_ast.ext[1]),
666 ['Decl', 'Entry',
667 ['TypeDecl', ['Struct', 'tagEntry',
668 [['Decl', 'key',
669 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
670 ['Decl', 'value',
671 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
672
673 s2 = """
674 struct tagEntry;
675
676 typedef struct tagEntry tagEntry;
677
678 struct tagEntry
679 {
680 char* key;
681 char* value;
682 } Entry;
683 """
684
685 s2_ast = self.parse(s2)
686 self.assertEqual(expand_decl(s2_ast.ext[2]),
687 ['Decl', 'Entry',
688 ['TypeDecl', ['Struct', 'tagEntry',
689 [['Decl', 'key',
690 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]],
691 ['Decl', 'value',
692 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]]]]])
693
694 s3 = """
695 typedef int mytag;
696
697 enum mytag {ABC, CDE};
698 enum mytag joe;
699 """
700
701 s3_type = self.parse(s3).ext[1].type
702
703 self.assertTrue(isinstance(s3_type, Enum))
704 self.assertEqual(s3_type.name, 'mytag')
705
706 def test_multi_decls(self):
707 d1 = 'int a, b;'
708
709 self.assertEqual(self.get_decl(d1, 0),
710 ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
711 self.assertEqual(self.get_decl(d1, 1),
712 ['Decl', 'b', ['TypeDecl', ['IdentifierType', ['int']]]])
713
714 d2 = 'char* p, notp, ar[4];'
715 self.assertEqual(self.get_decl(d2, 0),
716 ['Decl', 'p',
717 ['PtrDecl',
718 ['TypeDecl', ['IdentifierType', ['char']]]]])
719 self.assertEqual(self.get_decl(d2, 1),
720 ['Decl', 'notp', ['TypeDecl', ['IdentifierType', ['char']]]])
721 self.assertEqual(self.get_decl(d2, 2),
722 ['Decl', 'ar',
723 ['ArrayDecl', '4',
724 ['TypeDecl', ['IdentifierType', ['char']]]]])
725
726 def test_invalid_multiple_types_error(self):
727 bad = [
728 'int enum {ab, cd} fubr;',
729 'enum kid char brbr;']
730
731 for b in bad:
732 self.assertRaises(ParseError, self.parse, b)
733
734 def test_decl_inits(self):
735 d1 = 'int a = 16;'
736 self.assertEqual(self.get_decl(d1),
737 ['Decl', 'a', ['TypeDecl', ['IdentifierType', ['int']]]])
738 self.assertEqual(self.get_decl_init(d1),
739 ['Constant', 'int', '16'])
740
741 d2 = 'long ar[] = {7, 8, 9};'
742 self.assertEqual(self.get_decl(d2),
743 ['Decl', 'ar',
744 ['ArrayDecl', '',
745 ['TypeDecl', ['IdentifierType', ['long']]]]])
746 self.assertEqual(self.get_decl_init(d2),
747 [ ['Constant', 'int', '7'],
748 ['Constant', 'int', '8'],
749 ['Constant', 'int', '9'],])
750
751 d3 = 'char p = j;'
752 self.assertEqual(self.get_decl(d3),
753 ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['char']]]])
754 self.assertEqual(self.get_decl_init(d3),
755 ['ID', 'j'])
756
757 d4 = "char x = 'c', *p = {0, 1, 2, {4, 5}, 6};"
758 self.assertEqual(self.get_decl(d4, 0),
759 ['Decl', 'x', ['TypeDecl', ['IdentifierType', ['char']]]])
760 self.assertEqual(self.get_decl_init(d4, 0),
761 ['Constant', 'char', "'c'"])
762 self.assertEqual(self.get_decl(d4, 1),
763 ['Decl', 'p',
764 ['PtrDecl',
765 ['TypeDecl', ['IdentifierType', ['char']]]]])
766 self.assertEqual(self.get_decl_init(d4, 1),
767 [ ['Constant', 'int', '0'],
768 ['Constant', 'int', '1'],
769 ['Constant', 'int', '2'],
770 [ ['Constant', 'int', '4'],
771 ['Constant', 'int', '5']],
772 ['Constant', 'int', '6']])
773
774 def test_function_definitions(self):
775 def parse_fdef(str):
776 return self.parse(str).ext[0]
777
778 def fdef_decl(fdef):
779 return expand_decl(fdef.decl)
780
781 f1 = parse_fdef('''
782 int factorial(int p)
783 {
784 return 3;
785 }
786 ''')
787
788 self.assertEqual(fdef_decl(f1),
789 ['Decl', 'factorial',
790 ['FuncDecl',
791 [['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]]],
792 ['TypeDecl', ['IdentifierType', ['int']]]]])
793
eli.benderskyef29ff92010-10-29 16:25:43 +0200794 self.assertEqual(type(f1.body.block_items[0]), Return)
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300795
796 f2 = parse_fdef('''
797 char* zzz(int p, char* c)
798 {
799 int a;
800 char b;
801
802 a = b + 2;
803 return 3;
804 }
805 ''')
806
807 self.assertEqual(fdef_decl(f2),
808 ['Decl', 'zzz',
809 ['FuncDecl',
810 [ ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['int']]]],
811 ['Decl', 'c', ['PtrDecl',
812 ['TypeDecl', ['IdentifierType', ['char']]]]]],
813 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
814
eli.benderskyef29ff92010-10-29 16:25:43 +0200815 self.assertEqual(list(map(type, f2.body.block_items)),
816 [Decl, Decl, Assignment, Return])
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300817
818 f3 = parse_fdef('''
819 char* zzz(p, c)
820 long p, *c;
821 {
822 int a;
823 char b;
824
825 a = b + 2;
826 return 3;
827 }
828 ''')
829
830 self.assertEqual(fdef_decl(f3),
831 ['Decl', 'zzz',
832 ['FuncDecl',
833 [ ['ID', 'p'],
834 ['ID', 'c']],
835 ['PtrDecl', ['TypeDecl', ['IdentifierType', ['char']]]]]])
836
eli.benderskyef29ff92010-10-29 16:25:43 +0200837 self.assertEqual(list(map(type, f3.body.block_items)),
838 [Decl, Decl, Assignment, Return])
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300839
840 self.assertEqual(expand_decl(f3.param_decls[0]),
841 ['Decl', 'p', ['TypeDecl', ['IdentifierType', ['long']]]])
842 self.assertEqual(expand_decl(f3.param_decls[1]),
843 ['Decl', 'c', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['long']]]]])
844
eli.bendersky71540662010-07-03 12:58:52 +0200845 def test_unified_string_literals(self):
846 # simple string, for reference
847 d1 = self.get_decl_init('char* s = "hello";')
848 self.assertEqual(d1, ['Constant', 'string', '"hello"'])
849
850 d2 = self.get_decl_init('char* s = "hello" " world";')
851 self.assertEqual(d2, ['Constant', 'string', '"hello world"'])
852
853 # the test case from issue 6
854 d3 = self.parse(r'''
855 int main() {
856 fprintf(stderr,
857 "Wrong Params?\n"
858 "Usage:\n"
859 "%s <binary_file_path>\n",
860 argv[0]
861 );
862 }
863 ''')
864
865 self.assertEqual(
eli.benderskyef29ff92010-10-29 16:25:43 +0200866 d3.ext[0].body.block_items[0].args.exprs[1].value,
eli.bendersky71540662010-07-03 12:58:52 +0200867 r'"Wrong Params?\nUsage:\n%s <binary_file_path>\n"')
eli.bendersky4a89f112010-07-05 06:02:03 +0200868
869 d4 = self.get_decl_init('char* s = "" "foobar";')
870 self.assertEqual(d4, ['Constant', 'string', '"foobar"'])
871
872 d5 = self.get_decl_init(r'char* s = "foo\"" "bar";')
873 self.assertEqual(d5, ['Constant', 'string', r'"foo\"bar"'])
eli.bendersky71540662010-07-03 12:58:52 +0200874
eli.bendersky79d5cf62010-10-29 13:33:52 +0200875 def test_inline_specifier(self):
876 ps2 = self.parse('static inline void inlinefoo(void);')
877 self.assertEqual(ps2.ext[0].funcspec, ['inline'])
eli.bendersky2e907fa2010-10-29 15:51:07 +0200878
879 # variable length array
880 def test_vla(self):
881 ps2 = self.parse(r'''
882 int main() {
883 int size;
884 int var[size = 5];
885
886 int var2[*];
887 }
888 ''')
eli.benderskyef29ff92010-10-29 16:25:43 +0200889 self.failUnless(isinstance(ps2.ext[0].body.block_items[1].type.dim, Assignment))
890 self.failUnless(isinstance(ps2.ext[0].body.block_items[2].type.dim, ID))
eli.bendersky79d5cf62010-10-29 13:33:52 +0200891
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300892
893class TestCParser_whole_code(unittest.TestCase):
894 """ Testing of parsing whole chunks of code.
895
896 Since I don't want to rely on the structure of ASTs too
897 much, most of these tests are implemented with visitors.
898 """
899 def parse(self, txt, filename=''):
900 return self.cparser.parse(txt, filename)
901
902 def setUp(self):
903 self.cparser = _c_parser
904
905 # A simple helper visitor that lists the values of all the
906 # Constant nodes it sees.
907 #
908 class ConstantVisitor(NodeVisitor):
909 def __init__(self):
910 self.values = []
911
912 def visit_Constant(self, node):
913 self.values.append(node.value)
914
915 # This visitor counts the amount of references to the ID
916 # with the name provided to it in the constructor.
917 #
918 class IDNameCounter(NodeVisitor):
919 def __init__(self, name):
920 self.name = name
921 self.nrefs = 0
922
923 def visit_ID(self, node):
924 if node.name == self.name:
925 self.nrefs += 1
926
927 # Counts the amount of nodes of a given class
928 #
929 class NodeKlassCounter(NodeVisitor):
930 def __init__(self, node_klass):
931 self.klass = node_klass
932 self.n = 0
933
934 def generic_visit(self, node):
935 if node.__class__ == self.klass:
936 self.n += 1
937
938 NodeVisitor.generic_visit(self, node)
939
940 def assert_all_Constants(self, code, constants):
941 """ Asserts that the list of all Constant values (by
942 'preorder' appearance) in the chunk of code is as
943 given.
944 """
eli.benderskyed890492010-06-25 08:25:55 +0300945 if isinstance(code, str):
946 parsed = self.parse(code)
947 else:
948 parsed = code
949
Eli Bendersky3921e8e2010-05-21 09:05:39 +0300950 cv = self.ConstantVisitor()
951 cv.visit(parsed)
952 self.assertEqual(cv.values, constants)
953
954 def assert_num_ID_refs(self, code, name, num):
955 """ Asserts the number of references to the ID with
956 the given name.
957 """
958 if isinstance(code, str):
959 parsed = self.parse(code)
960 else:
961 parsed = code
962
963 iv = self.IDNameCounter(name)
964 iv.visit(parsed)
965 self.assertEqual(iv.nrefs, num)
966
967 def assert_num_klass_nodes(self, code, klass, num):
968 """ Asserts the amount of klass nodes in the code.
969 """
970 if isinstance(code, str):
971 parsed = self.parse(code)
972 else:
973 parsed = code
974
975 cv = self.NodeKlassCounter(klass)
976 cv.visit(parsed)
977 self.assertEqual(cv.n, num)
978
979 def test_expressions(self):
980 e1 = '''int k = (r + 10.0) >> 6 + 8 << (3 & 0x14);'''
981 self.assert_all_Constants(e1, ['10.0', '6', '8', '3', '0x14'])
982
983 e2 = r'''char n = '\n', *prefix = "st_";'''
984 self.assert_all_Constants(e2, [r"'\n'", '"st_"'])
985
986 def test_statements(self):
987 s1 = r'''
988 void foo(){
989 if (sp == 1)
990 if (optind >= argc ||
991 argv[optind][0] != '-' || argv[optind][1] == '\0')
992 return -1;
993 else if (strcmp(argv[optind], "--") == 0) {
994 optind++;
995 return -1;
996 }
997 }
998 '''
999
1000 self.assert_all_Constants(s1,
1001 ['1', '0', r"'-'", '1', r"'\0'", '1', r'"--"', '0', '1'])
1002
1003 ps1 = self.parse(s1)
1004 self.assert_num_ID_refs(ps1, 'argv', 3)
1005 self.assert_num_ID_refs(ps1, 'optind', 5)
1006
1007 self.assert_num_klass_nodes(ps1, If, 3)
1008 self.assert_num_klass_nodes(ps1, Return, 2)
1009 self.assert_num_klass_nodes(ps1, FuncCall, 1) # strcmp
1010 self.assert_num_klass_nodes(ps1, BinaryOp, 7)
1011
1012 # In the following code, Hash and Node were defined as
1013 # int to pacify the parser that sees they're used as
1014 # types
1015 #
1016 s2 = r'''
1017 typedef int Hash, Node;
1018
1019 void HashDestroy(Hash* hash)
1020 {
1021 unsigned int i;
1022
1023 if (hash == NULL)
1024 return;
1025
1026 for (i = 0; i < hash->table_size; ++i)
1027 {
1028 Node* temp = hash->heads[i];
1029
1030 while (temp != NULL)
1031 {
1032 Node* temp2 = temp;
1033
1034 free(temp->entry->key);
1035 free(temp->entry->value);
1036 free(temp->entry);
1037
1038 temp = temp->next;
1039
1040 free(temp2);
1041 }
1042 }
1043
1044 free(hash->heads);
1045 hash->heads = NULL;
1046
1047 free(hash);
1048 }
1049 '''
1050
1051 ps2 = self.parse(s2)
1052 self.assert_num_klass_nodes(ps2, FuncCall, 6)
1053 self.assert_num_klass_nodes(ps2, FuncDef, 1)
1054 self.assert_num_klass_nodes(ps2, For, 1)
1055 self.assert_num_klass_nodes(ps2, While, 1)
1056 self.assert_num_klass_nodes(ps2, StructRef, 10)
1057
1058 # declarations don't count
1059 self.assert_num_ID_refs(ps2, 'hash', 6)
1060 self.assert_num_ID_refs(ps2, 'i', 4)
eli.benderskyed890492010-06-25 08:25:55 +03001061
1062 s3 = r'''
1063 void x(void) {
1064 int a, b;
1065 if (a < b)
1066 do {
1067 a = 0;
1068 } while (0);
1069 else if (a == b) {
1070 a = 1;
1071 }
1072 }
1073 '''
1074
1075 ps3 = self.parse(s3)
1076 self.assert_num_klass_nodes(ps3, DoWhile, 1)
1077 self.assert_num_ID_refs(ps3, 'a', 4)
1078 self.assert_all_Constants(ps3, ['0', '0', '1'])
eli.bendersky145890d2010-10-29 12:02:32 +02001079
1080 def test_for_statement(self):
1081 s2 = r'''
1082 void x(void)
1083 {
1084 int i;
1085 for (i = 0; i < 5; ++i) {
1086 x = 50;
1087 }
1088 }
1089 '''
1090 ps2 = self.parse(s2)
1091 self.assert_num_klass_nodes(ps2, For, 1)
1092 # here there are 3 refs to 'i' since the declaration doesn't count as
1093 # a ref in the visitor
1094 #
1095 self.assert_num_ID_refs(ps2, 'i', 3)
eli.benderskyed890492010-06-25 08:25:55 +03001096
eli.bendersky145890d2010-10-29 12:02:32 +02001097 s3 = r'''
1098 void x(void)
1099 {
1100 for (int i = 0; i < 5; ++i) {
1101 x = 50;
1102 }
1103 }
1104 '''
1105 ps3 = self.parse(s3)
eli.bendersky145890d2010-10-29 12:02:32 +02001106 self.assert_num_klass_nodes(ps3, For, 1)
1107 # here there are 2 refs to 'i' since the declaration doesn't count as
1108 # a ref in the visitor
1109 #
1110 self.assert_num_ID_refs(ps3, 'i', 2)
Eli Bendersky3921e8e2010-05-21 09:05:39 +03001111
1112 def test_whole_file(self):
1113 # See how pycparser handles a whole, real C file.
1114 #
1115 filename = 'c_files/memmgr_with_h.c'
1116 code = open(filename, 'rU').read()
1117 p = self.parse(code)
1118
1119 self.assert_num_klass_nodes(p, FuncDef, 5)
1120
1121 # each FuncDef also has a FuncDecl. 4 declarations
1122 # + 5 definitions, overall 9
1123 self.assert_num_klass_nodes(p, FuncDecl, 9)
1124
1125 self.assert_num_klass_nodes(p, Typedef, 4)
1126
1127 self.assertEqual(p.ext[4].coord.line, 88)
1128 self.assertEqual(p.ext[4].coord.file, "./memmgr.h")
1129
1130 self.assertEqual(p.ext[6].coord.line, 10)
1131 self.assertEqual(p.ext[6].coord.file, "memmgr.c")
1132
1133 def test_whole_file_with_stdio(self):
1134 # Parse a whole file with stdio.h included by cpp
1135 #
1136 filename = 'c_files/cppd_with_stdio_h.c'
1137 code = open(filename, 'rU').read()
1138 p = self.parse(code)
1139
1140 self.failUnless(isinstance(p.ext[0], Typedef))
1141 self.assertEqual(p.ext[0].coord.line, 213)
1142 self.assertEqual(p.ext[0].coord.file, "D:\eli\cpp_stuff\libc_include/stddef.h")
1143
1144 self.failUnless(isinstance(p.ext[-1], FuncDef))
1145 self.assertEqual(p.ext[-1].coord.line, 15)
1146 self.assertEqual(p.ext[-1].coord.file, "example_c_file.c")
1147
1148 self.failUnless(isinstance(p.ext[-8], Typedef))
1149 self.failUnless(isinstance(p.ext[-8].type, TypeDecl))
1150 self.assertEqual(p.ext[-8].name, 'cookie_io_functions_t')
1151
1152
1153if __name__ == '__main__':
1154 #~ suite = unittest.TestLoader().loadTestsFromNames(
1155 #~ ['test_c_parser.TestCParser_fundamentals.test_typedef'])
1156
1157 #~ suite = unittest.TestLoader().loadTestsFromNames(
1158 #~ ['test_c_parser.TestCParser_whole_code.test_whole_file_with_stdio'])
1159
1160 #~ suite = unittest.TestLoader().loadTestsFromTestCase(
1161 #~ TestCParser_whole_code)
1162
1163 #~ unittest.TextTestRunner(verbosity=2).run(suite)
1164 unittest.main()