def test_ifdef(self): parsed_pre_ast = self.parser.parse(""" #define BAZ 1024 int Some(text); # if BAZ > 1000 #define BOO 2 int more_Text(); # else #define BOO 4 #endif """) # Learn about the macros defined above. actual = self.visitor.preprocess(parsed_pre_ast) expected = pre_ast.CompositeBlock([ pre_ast.TextBlock(content='int Some(text);'), pre_ast.CompositeBlock( [pre_ast.TextBlock(content='int more_Text();')]) ]) self.assertASTEqual(actual, expected) # Make sure that the right macro was defined. self.assertASTEqual( self.macros.object_likes["BOO"], pre_ast.DefineObjectLike(name="BOO", replacement="2"))
def test_parse_with_empty_if_elif_and_else_blocks(self): source = """ #if CONFIG_SOMETHING #elif defined(CONFIG_SOMETHING_ELSE) #else #endif """ actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.If(conditional_blocks=[ pre_ast.ConditionalBlock( conditional_expression='CONFIG_SOMETHING', content=[], ), pre_ast.ConditionalBlock( conditional_expression='defined(CONFIG_SOMETHING_ELSE)', content=[], ), pre_ast.ConditionalBlock( conditional_expression='1', content=[], ), ]), ]) self.assertASTEqual(actual, expected)
def test_parse_pragma_with_string_argument(self): source = '#pragma "-foo"' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.Pragma('"-foo"'), ]) self.assertASTEqual(actual, expected)
def test_parse_with_nested_if_blocks(self): source = """ #if CONFIG_SOMETHING #if CONFIG_SOMETHING_ELSE #define FOO #endif #else #define BAR #endif """ actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.If(conditional_blocks=[ pre_ast.ConditionalBlock( conditional_expression='CONFIG_SOMETHING', content=[ pre_ast.If(conditional_blocks=[ pre_ast.ConditionalBlock( conditional_expression="CONFIG_SOMETHING_ELSE", content=[ pre_ast.DefineObjectLike(name="FOO", replacement="") ], ) ]) ]), pre_ast.ConditionalBlock( conditional_expression='1', content=[ pre_ast.DefineObjectLike(name="BAR", replacement="") ], ), ]), ]) self.assertASTEqual(actual, expected)
def test_parse_with_top_level_ifdef_and_else_blocks(self): source = """ int a; #ifdef CONFIG_SOMETHING struct s { int x; } y; struct s t, u; #else int z; #endif int b; """ actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.TextBlock('int a;'), pre_ast.If(conditional_blocks=[ pre_ast.ConditionalBlock( conditional_expression='defined(CONFIG_SOMETHING)', content=[ pre_ast.TextBlock( '\n'.join(( 'struct s {', ' int x;', '} y;', 'struct s t, u;', )), ), ]), pre_ast.ConditionalBlock(conditional_expression="1", content=[pre_ast.TextBlock('int z;')]) ]), pre_ast.TextBlock('int b;') ]) self.assertASTEqual(actual, expected)
def visit_composite_block(self, composite_block): preprocessed_content = [] for element in composite_block.content: maybe_preprocessed_element = element.accept(self) if maybe_preprocessed_element: preprocessed_content.append(maybe_preprocessed_element) return pre_ast.CompositeBlock(preprocessed_content)
def test_parse_error(self): source = '#error foo bar 42 baz' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.Error('foo bar 42 baz'), ]) self.assertASTEqual(actual, expected)
def test_parse_text_block(self): source = 'int x;' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.TextBlock('int x;'), ]) self.assertASTEqual(actual, expected)
def test_parse_pragma(self): source = '#pragma foo' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.Pragma('foo'), ]) self.assertASTEqual(actual, expected)
def test_parse_undef(self): source = '#undef foo' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.Undef('foo'), ]) self.assertASTEqual(actual, expected)
def test_parse_include_with_double_quotes(self): source = '#include "some/path/to/file_2.h"' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock( [pre_ast.Include( path='some/path/to/file_2.h', quotes_type='"', )]) self.assertASTEqual(actual, expected)
def test_parse_include_with_angle_brackets(self): source = '# include <some/path/to/file_1.h>' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock(content=[ pre_ast.Include( path='some/path/to/file_1.h', quotes_type="<", ) ]) self.assertASTEqual(actual, expected)
def test_parse_define_object_like_as_numeric_constant(self): source = '#define foo 42' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.DefineObjectLike( name='foo', replacement='42', ), ]) self.assertASTEqual(actual, expected)
def test_parse_define_empty_object_like(self): source = '#define foo' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.DefineObjectLike( name='foo', replacement="", ), ]) self.assertASTEqual(actual, expected)
def test_parse_pragma_and_text_block(self): source = '\n'.join(( '#pragma foo bar', 'int x;', )) actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.Pragma("foo bar"), pre_ast.TextBlock('int x;'), ]) self.assertASTEqual(actual, expected)
def test_parse_define_empty_funcion_like(self): source = '#define foo()' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.DefineFunctionLike( name='foo', arguments=[], replacement="", ), ]) self.assertASTEqual(actual, expected)
def test_parse_define_empty_object_like_looks_like_func(self): # https://gcc.gnu.org/onlinedocs/cpp/Function-like-Macros.html # This is not a function like macro. source = '#define foo (bar)' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.DefineObjectLike( name='foo', replacement="(bar)", ), ]) self.assertASTEqual(actual, expected)
def test_parse_define_with_function_like_expression_and_concatenation( self): source = '#define __DECL_REG(name) uint64_t r ## name;' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.DefineFunctionLike( name='__DECL_REG', arguments=["name"], replacement='uint64_t r ## name;', ), ]) self.assertASTEqual(actual, expected)
def test_parse_function_call_with_multiword_argument(self): source = 'f(union u)' expected = c_ast.CFunctionCall( function_name='f', arguments=[ pre_ast.CompositeBlock([ c_ast.CVariable('union'), c_ast.CVariable('u'), ]), ], ) actual = self.parser.parse(source) self.assertASTEqual(actual, expected)
def test_parse_with_empty_ifndef_block(self): source = """ #ifndef CONFIG_SOMETHING #endif """ actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.If([ pre_ast.ConditionalBlock( conditional_expression="!defined(CONFIG_SOMETHING)", content=[]), ]) ]) self.assertASTEqual(actual, expected)
def test_parse_multiline_define(self): source = '\n'.join([ '#define foo bar\\', ' baz', ' 42', ]) actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.DefineObjectLike( name='foo', replacement='bar baz', ), pre_ast.TextBlock('42'), ]) self.assertASTEqual(actual, expected)
def test_collect_includes_with_composite_block(self): mock_conditiona_block_1 = mock.MagicMock() mock_conditiona_block_2 = mock.MagicMock() mock_conditiona_block_3 = mock.MagicMock() node = pre_ast.CompositeBlock([ mock_conditiona_block_1, mock_conditiona_block_2, mock_conditiona_block_3, ]) mock_conditiona_block_1.accept.return_value = [33] mock_conditiona_block_2.accept.return_value = [] mock_conditiona_block_3.accept.return_value = ['foo', 42] actual = self.include_collector.collect_includes(node) expected = [33, 'foo', 42] self.assertEqual(actual, expected)
def test_to_string_with_composite_block(self): mock_node_1 = mock.MagicMock() mock_node_2 = mock.MagicMock() mock_node_3 = mock.MagicMock() node = pre_ast.CompositeBlock([ mock_node_1, mock_node_2, mock_node_3, ]) mock_node_1.accept.side_effect = (lambda visitor, parts: parts.extend( ('foo', 'bar', '42'))) mock_node_2.accept.side_effect = (lambda visitor, parts: parts.extend( ())) mock_node_3.accept.side_effect = (lambda visitor, parts: parts.extend( ('33 24', ))) actual = self.to_string_visitor.to_string(node) expected = 'foo bar 42 33 24' self.assertEqual(actual, expected)
def test_resolve_with_composite_block(self): mock_conditional_block_1 = mock.MagicMock() mock_conditional_block_2 = mock.MagicMock() mock_conditional_block_3 = mock.MagicMock() node = pre_ast.CompositeBlock([ mock_conditional_block_1, mock_conditional_block_2, mock_conditional_block_3, ]) self.include_linking_visitor.resolve(node, self.files) for mock_node in ( mock_conditional_block_1, mock_conditional_block_2, mock_conditional_block_3, ): mock_node.accept.assert_called_once_with( self.include_linking_visitor, self.files, )
def test_parse_with_empty_ifndef_header_guard(self): source = """ #ifndef _SOMETHING_H #define _SOMETHING_H #endif """ actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([ pre_ast.If([ pre_ast.ConditionalBlock( conditional_expression="!defined(_SOMETHING_H)", content=[ pre_ast.DefineObjectLike( name='_SOMETHING_H', replacement="", ), ], ), ]), ]) self.assertASTEqual(actual, expected)
def parse(self, source): line_continuation = pyparsing.Literal('\\\n') ignorable = ( line_continuation | pyparsing.cppStyleComment.addParseAction( pyparsing.ParserElement.resetCache) # Removed for now because it is too expensive at this point. We can # eliminate those later in the trimming phase. # | parser.extern_field() | self.static_function()).suppress() source = ignorable.transformString(source) source = Sanitizer().transform(source) # Start parsing: Top level node will be a pre_ast.File self.push_node(pre_ast.CompositeBlock(content=[])) last_block_end = 0 scanner = self._preprocessor_directive().parseWithTabs() for tokens, start, end in scanner.scanString(source): text = source[last_block_end:start].strip() if text: # We skipped over a text block - push it on the current node. self.current_node.content.append(pre_ast.TextBlock(text)) last_block_end = end # Now process the different directives. directive, rest_of_line = tokens if directive == "include": self._process_include(rest_of_line) # Im not really sure what #pragma is supposed to do here. elif directive == "pragma": self.current_node.content.append(pre_ast.Pragma(rest_of_line)) elif directive == "error": self.current_node.content.append(pre_ast.Error(rest_of_line)) elif directive == "define": self._define_parser.parseString(rest_of_line) elif directive == "undef": self.current_node.content.append(pre_ast.Undef(rest_of_line)) elif directive == "if": self._add_conditional_block(rest_of_line) elif directive == "ifdef": self._add_conditional_block("defined(%s)" % rest_of_line) elif directive == "ifndef": self._add_conditional_block("!defined(%s)" % rest_of_line) elif directive == "else": self._add_elif_block("1") elif directive == "elif": self._add_elif_block(rest_of_line) elif directive == "endif": # Pop the stack. self.pop_node() # ConditionalBlock self.pop_node() # If block. # Last text node. text = source[last_block_end:].strip() if text: self.current_node.content.append(pre_ast.TextBlock(text)) return self.pop_node()
def test_parse_empty_program(self): source = '' actual = self.parser.parse(source) expected = pre_ast.CompositeBlock([]) self.assertASTEqual(actual, expected)