diff --git a/lib/cmock_function_prototype_node_classes.rb b/lib/cmock_function_prototype_node_classes.rb index 302ecaf..fc7997e 100644 --- a/lib/cmock_function_prototype_node_classes.rb +++ b/lib/cmock_function_prototype_node_classes.rb @@ -2,6 +2,7 @@ module CMockFunctionPrototype module FunctionPrototypeUtils + # handles '*' and '[]' (they're both pointers after all) def normalize_ptr(ptr_string) ptr_string.gsub!(/\s+\*/, '*') ptr_string.gsub!(/\*(\w)/, '* \1') @@ -210,7 +211,7 @@ module CMockFunctionPrototype end def typedef(arg_list_index, function_name) - typename= make_function_pointer_param_typedef_name(arg_list_index, function_name) + typename = make_function_pointer_param_typedef_name(arg_list_index, function_name) # don't place 'const' in typedef no matter if it exists or not; # data types that comprise mock queues can't be const @@ -229,6 +230,9 @@ module CMockFunctionPrototype def type_and_smart_name_string(arg_list_index) if (name.text_value.blank?) + if (type.brackets?) + return "#{type.replace_brackets} #{make_cmock_arg_name(arg_list_index)}#{type.get_brackets}" + end return "#{type.text_value} #{make_cmock_arg_name(arg_list_index)}" end @@ -237,10 +241,36 @@ module CMockFunctionPrototype def type_and_smart_name_token_hash(arg_list_index) if (name.text_value.blank?) - return { :type => type.text_value, :name => make_cmock_arg_name(arg_list_index)} + if (type.brackets?) + return { :type => type.replace_brackets('*'), :name => make_cmock_arg_name(arg_list_index) } + end + return { :type => type.text_value, :name => make_cmock_arg_name(arg_list_index) } end - return { :type => type.text_value, :name => name.text_value} + if (name.brackets?) + return { :type => "#{type.text_value}*", :name => name.replace_brackets } + end + + return { :type => type.text_value, :name => name.text_value } + end + end + + + class NameWithBracketsNode < Treetop::Runtime::SyntaxNode + def text_value + return super.gsub(/\s+/, '') + end + + def brackets? + return !brackets.text_value.blank? + end + + def get_brackets + return brackets.text_value + end + + def replace_brackets(replace='') + return text_value.gsub(/(\s*\[\s*[0-9]*\])+/, replace) end end @@ -258,9 +288,28 @@ module CMockFunctionPrototype def text_value return normalize_ptr(super.gsub(/\s+/, ' ')).strip end + + def brackets? + return !brackets.text_value.blank? + end + + def get_brackets + return brackets.text_value + end + + def replace_brackets(replace='') + return text_value.gsub(/(\s*\[\s*[0-9]*\])+/, replace) + end end - + + class ArrayBracketsNode < Treetop::Runtime::SyntaxNode + def text_value + return super.gsub(/\s+/, '') + end + end + + class VoidNode < Treetop::Runtime::SyntaxNode def text_value return super.strip diff --git a/lib/cmock_function_prototype_parser.rb b/lib/cmock_function_prototype_parser.rb index bfcdd02..0ca08f8 100644 --- a/lib/cmock_function_prototype_parser.rb +++ b/lib/cmock_function_prototype_parser.rb @@ -451,7 +451,7 @@ module CMockFunctionPrototype r1 = _nt_type s0 << r1 if r1 - r3 = _nt_name + r3 = _nt_name_with_brackets if r3 r2 = r3 else @@ -501,6 +501,9 @@ module CMockFunctionPrototype end module Type0 + def brackets + elements[2] + end end def _nt_type @@ -554,6 +557,15 @@ module CMockFunctionPrototype end end s0 << r3 + if r3 + r11 = _nt_array_brackets + if r11 + r10 = r11 + else + r10 = instantiate_node(SyntaxNode,input, index...index) + end + s0 << r10 + end end if s0.last r0 = instantiate_node(TypeNode,input, i0...index, s0) @@ -1247,6 +1259,49 @@ module CMockFunctionPrototype return r0 end + module NameWithBrackets0 + def name + elements[0] + end + + def brackets + elements[1] + end + end + + def _nt_name_with_brackets + start_index = index + if node_cache[:name_with_brackets].has_key?(index) + cached = node_cache[:name_with_brackets][index] + @index = cached.interval.end if cached + return cached + end + + i0, s0 = index, [] + r1 = _nt_name + s0 << r1 + if r1 + r3 = _nt_array_brackets + if r3 + r2 = r3 + else + r2 = instantiate_node(SyntaxNode,input, index...index) + end + s0 << r2 + end + if s0.last + r0 = instantiate_node(NameWithBracketsNode,input, i0...index, s0) + r0.extend(NameWithBrackets0) + else + self.index = i0 + r0 = nil + end + + node_cache[:name_with_brackets][start_index] = r0 + + return r0 + end + module Void0 def space elements[1] @@ -1299,6 +1354,89 @@ module CMockFunctionPrototype return r0 end + module ArrayBrackets0 + def left_bracket + elements[0] + end + + def number + elements[1] + end + + def right_bracket + elements[2] + end + end + + module ArrayBrackets1 + def left_bracket + elements[0] + end + + def right_bracket + elements[1] + end + + end + + def _nt_array_brackets + start_index = index + if node_cache[:array_brackets].has_key?(index) + cached = node_cache[:array_brackets][index] + @index = cached.interval.end if cached + return cached + end + + i0, s0 = index, [] + r1 = _nt_left_bracket + s0 << r1 + if r1 + r2 = _nt_right_bracket + s0 << r2 + if r2 + s3, i3 = [], index + loop do + i4, s4 = index, [] + r5 = _nt_left_bracket + s4 << r5 + if r5 + r6 = _nt_number + s4 << r6 + if r6 + r7 = _nt_right_bracket + s4 << r7 + end + end + if s4.last + r4 = instantiate_node(SyntaxNode,input, i4...index, s4) + r4.extend(ArrayBrackets0) + else + self.index = i4 + r4 = nil + end + if r4 + s3 << r4 + else + break + end + end + r3 = instantiate_node(SyntaxNode,input, i3...index, s3) + s0 << r3 + end + end + if s0.last + r0 = instantiate_node(ArrayBracketsNode,input, i0...index, s0) + r0.extend(ArrayBrackets1) + else + self.index = i0 + r0 = nil + end + + node_cache[:array_brackets][start_index] = r0 + + return r0 + end + module Const0 def space elements[1] @@ -1459,6 +1597,86 @@ module CMockFunctionPrototype return r0 end + module LeftBracket0 + def space + elements[1] + end + end + + def _nt_left_bracket + start_index = index + if node_cache[:left_bracket].has_key?(index) + cached = node_cache[:left_bracket][index] + @index = cached.interval.end if cached + return cached + end + + i0, s0 = index, [] + if input.index('[', index) == index + r1 = instantiate_node(SyntaxNode,input, index...(index + 1)) + @index += 1 + else + terminal_parse_failure('[') + r1 = nil + end + s0 << r1 + if r1 + r2 = _nt_space + s0 << r2 + end + if s0.last + r0 = instantiate_node(SyntaxNode,input, i0...index, s0) + r0.extend(LeftBracket0) + else + self.index = i0 + r0 = nil + end + + node_cache[:left_bracket][start_index] = r0 + + return r0 + end + + module RightBracket0 + def space + elements[1] + end + end + + def _nt_right_bracket + start_index = index + if node_cache[:right_bracket].has_key?(index) + cached = node_cache[:right_bracket][index] + @index = cached.interval.end if cached + return cached + end + + i0, s0 = index, [] + if input.index(']', index) == index + r1 = instantiate_node(SyntaxNode,input, index...(index + 1)) + @index += 1 + else + terminal_parse_failure(']') + r1 = nil + end + s0 << r1 + if r1 + r2 = _nt_space + s0 << r2 + end + if s0.last + r0 = instantiate_node(SyntaxNode,input, i0...index, s0) + r0.extend(RightBracket0) + else + self.index = i0 + r0 = nil + end + + node_cache[:right_bracket][start_index] = r0 + + return r0 + end + module Comma0 def space elements[1] @@ -1499,6 +1717,45 @@ module CMockFunctionPrototype return r0 end + module Number0 + def space + elements[1] + end + end + + def _nt_number + start_index = index + if node_cache[:number].has_key?(index) + cached = node_cache[:number][index] + @index = cached.interval.end if cached + return cached + end + + i0, s0 = index, [] + if input.index(Regexp.new('[0-9]'), index) == index + r1 = instantiate_node(SyntaxNode,input, index...(index + 1)) + @index += 1 + else + r1 = nil + end + s0 << r1 + if r1 + r2 = _nt_space + s0 << r2 + end + if s0.last + r0 = instantiate_node(SyntaxNode,input, i0...index, s0) + r0.extend(Number0) + else + self.index = i0 + r0 = nil + end + + node_cache[:number][start_index] = r0 + + return r0 + end + def _nt_space start_index = index if node_cache[:space].has_key?(index) diff --git a/lib/cmock_function_prototype_parser.treetop b/lib/cmock_function_prototype_parser.treetop index c1a40b0..218df0a 100644 --- a/lib/cmock_function_prototype_parser.treetop +++ b/lib/cmock_function_prototype_parser.treetop @@ -45,7 +45,7 @@ grammar CMockFunctionPrototype # add name: tag to name? so we can always access it in programming even if blank. # tell parser that type and optional name will always be followed by ',' '(' or ')' but don't consume them; # this helps enforce the limits on what can be parsed in argument lists - type name:name? &( comma / left_paren / right_paren ) + type name:name_with_brackets? &( comma / left_paren / right_paren ) end rule type @@ -55,7 +55,8 @@ grammar CMockFunctionPrototype type_enum / type_void_ptr / type_primitive / - type_custom ) + type_custom ) + brackets:array_brackets? end rule type_struct @@ -87,7 +88,7 @@ grammar CMockFunctionPrototype end rule type_const_and_ptr_suffix - # "const *" or "* const" or "*" (asterisks can be multiples) + # "const *" or "* const" or "*" (more than one asterisk possible) ((const space asterisk+) / (asterisk+ (const space)?)) end @@ -95,11 +96,19 @@ grammar CMockFunctionPrototype [a-zA-Z0-9_]+ space end + rule name_with_brackets + name brackets:array_brackets? + end + rule void # in reality, 'void' is something different than the 'void*' type so limit to 'void' here and handle 'void*' in type rule; # recognizing the VoidNode uniquely in programming is helpful 'void' space !asterisk end + + rule array_brackets + left_bracket right_bracket (left_bracket number right_bracket)* + end rule const 'const' space @@ -117,10 +126,22 @@ grammar CMockFunctionPrototype ')' space end + rule left_bracket + '[' space + end + + rule right_bracket + ']' space + end + rule comma ',' space end + rule number + [0-9] space + end + rule space ' '* end diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 318dea7..30cf7d1 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -6,7 +6,7 @@ class CMockHeaderParser def initialize(parser, source, cfg, name) @src_lines = [] @prototypes = [] - @prototype_parse_matcher = /([\d\w\s\*\(\),]+??)\(([\d\w\s\*\(\),\.]*)\)$/m + @prototype_parse_matcher = /([\d\w\s\*\(\),\[\]]+??)\(([\d\w\s\*\(\),\.\[\]]*)\)$/m @c_attributes = cfg.attributes @parser = parser diff --git a/test/system/cases/basic_expect_and_return.yml b/test/system/cases/basic_expect_and_return.yml index 82eebab..294f205 100644 --- a/test/system/cases/basic_expect_and_return.yml +++ b/test/system/cases/basic_expect_and_return.yml @@ -13,12 +13,14 @@ UINT32 foo(custom_type a); UINT32 bar(custom_type b); UINT32 foo_varargs(custom_type a, ...); + char* foo_char_strings(char a[], char* b); :source: :header: | UINT32 function_a(int a, int b); void function_b(void); UINT32 function_c(int a); + char* function_d(char a[], char* b); :code: | UINT32 function_a(int a, int b) @@ -33,6 +35,11 @@ return foo_varargs((custom_type)a, "ignored", 5); } + char* function_d(char a[], char* b) + { + return foo_char_strings(a, b); + } + :tests: :common: | void setUp(void) {} @@ -76,4 +83,13 @@ TEST_ASSERT_EQUAL(10, function_c(3)); } + - :pass: TRUE + :should: 'handle char strings' + :code: | + test() + { + foo_char_strings_ExpectAndReturn("larry", "curly", "moe"); + TEST_ASSERT_EQUAL("moe", function_d("larry", "curly")); + } + ... diff --git a/test/unit/cmock_function_prototype_parser_test.rb b/test/unit/cmock_function_prototype_parser_test.rb index 43f25ee..778e196 100644 --- a/test/unit/cmock_function_prototype_parser_test.rb +++ b/test/unit/cmock_function_prototype_parser_test.rb @@ -146,6 +146,15 @@ class CMockFunctionPrototypeParserTest < Test::Unit::TestCase {:type => 'CUSTOM_TYPE*', :name => 'xyz_123'}], parsed.get_arguments) assert_nil(parsed.get_var_arg) + + parsed = @parser.parse("void foo_bar(CUSTOM_TYPE thing1[], int thing2 [ ], char thing3 [][2 ][ 3])") + assert_equal('CUSTOM_TYPE thing1[], int thing2[], char thing3[][2][3]', parsed.get_argument_list) + assert_equal([ + {:type => 'CUSTOM_TYPE*', :name => 'thing1'}, + {:type => 'int*', :name => 'thing2'}, + {:type => 'char*', :name => 'thing3'}], + parsed.get_arguments) + assert_nil(parsed.get_var_arg) end @@ -298,14 +307,18 @@ class CMockFunctionPrototypeParserTest < Test::Unit::TestCase should "insert unique names for top-level nameless arguments" do - parsed = @parser.parse("void foo_bar(int (*)(int, int), char* const, unsigned int c, CUSTOM_THING)") + parsed = @parser.parse("void foo_bar(int (*)(int, int), char* const, unsigned int c, CUSTOM_THING, int[], char[][2])") - assert_equal('int (*cmock_arg1)( int, int ), char* const cmock_arg2, unsigned int c, CUSTOM_THING cmock_arg4', parsed.get_argument_list) + assert_equal( + 'int (*cmock_arg1)( int, int ), char* const cmock_arg2, unsigned int c, CUSTOM_THING cmock_arg4, int cmock_arg5[], char cmock_arg6[][2]', + parsed.get_argument_list) assert_equal( [{:type => 'FUNC_PTR_FOO_BAR_PARAM_1_T', :name => 'cmock_arg1'}, {:type => 'char* const', :name => 'cmock_arg2'}, {:type => 'unsigned int', :name => 'c'}, - {:type => 'CUSTOM_THING', :name => 'cmock_arg4'}], + {:type => 'CUSTOM_THING', :name => 'cmock_arg4'}, + {:type => 'int*', :name => 'cmock_arg5'}, + {:type => 'char*', :name => 'cmock_arg6'}], parsed.get_arguments) assert_nil(parsed.get_var_arg) end diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index dbba4f6..251e5cc 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -204,7 +204,7 @@ class CMockHeaderParserTest < Test::Unit::TestCase end - should "raise upon prototype parsing failure" do + should "raise upon prototype parsing failure" do source = "int Foo(int a, unsigned int b);\n" + "void bar \n(uint la, int de, bool da) ; \n"