diff --git a/lib/CLexer.rb b/lib/CLexer.rb index 06c5dcd4..13beacea 100644 --- a/lib/CLexer.rb +++ b/lib/CLexer.rb @@ -1,9 +1,17 @@ # -# This is a simple lexer for the C programming language. +# This is a simple lexer for the C programming language. # MIT license. (c) 2023 Pascal Bourguignon # class CLexer + # + # CLexer is a simple C lexer. It is used to tokenize a C source file. + # + # Usage: + # lexer = CLexer.new(pre_processed_c_source) + # tokens = lexer.tokenize + # + # The tokenize method returns an array of tokens. KEYWORDS = %w[auto break case char const continue default do double else enum extern float for goto if int long register return short signed @@ -67,14 +75,13 @@ class CLexer '{' => :open_brace, '|' => :logical_or_op, '}' => :close_brace, - '~' => :bitwise_not_op, + '~' => :bitwise_not_op }.freeze - OPERATOR_REGEX = Regexp.new('\A(' + OPERATOR_SYMBOLS.keys.map { |op| Regexp.escape(op) }.join('|') + ')') OPERATOR_SYMS = OPERATOR_SYMBOLS.values.freeze - KEYWORDS_SYMS = KEYWORDS.map{ |n| n.to_sym }.freeze + KEYWORDS_SYMS = KEYWORDS.map(&:to_sym).freeze def initialize(input) @input = input @@ -82,7 +89,7 @@ def initialize(input) end def tokenize - while @input.size > 0 + while @input.size.positive? case @input when /\A[[:space:]]+/m @input = $' @@ -91,7 +98,7 @@ def tokenize when /\A\/\*/ consume_multiline_comment when /\A[_a-zA-Z][_a-zA-Z0-9]*/ - identifier_or_keyword = $& ; + identifier_or_keyword = $& @input = $' if KEYWORDS.include?(identifier_or_keyword) @tokens << identifier_or_keyword.to_sym @@ -99,27 +106,27 @@ def tokenize @tokens << [:identifier, identifier_or_keyword] end when /\A\d+\.\d*([eE][+-]?\d+)?[fFlL]?|\.\d+([eE][+-]?\d+)?[fFlL]?|\d+[eE][+-]?\d+[fFlL]?/ - float_constant = $& ; + float_constant = $& @input = $' @tokens << [:float_literal, float_constant] when /\A\d+/ - integer_constant = $& ; + integer_constant = $& @input = $' @tokens << [:integer_literal, integer_constant] when /\A0[xX][0-9a-fA-F]+/ - hex_constant = $& ; + hex_constant = $& @input = $' @tokens << [:hex_literal, hex_constant] when /\A'((\\.|[^\\'])*)'/ - char_literal = $& ; + char_literal = $& @input = $' @tokens << [:char_literal, char_literal] when /\A"((\\.|[^\\"])*)"/ - string_literal = $& ; + string_literal = $& @input = $' @tokens << [:string_literal, string_literal] when OPERATOR_REGEX - operator = $& ; + operator = $& @input = $' @tokens << OPERATOR_SYMBOLS[operator] else @@ -133,7 +140,7 @@ def tokenize private def consume_multiline_comment - while @input.size > 0 + while @input.size.positive? case @input when /\A\*\// @input = $' @@ -145,8 +152,8 @@ def consume_multiline_comment end end -def example - input = File.read("/home/pbourguignon/src/c-tidbits/pipes/tee.out.c") +def example + input = File.read('tee.c') lexer = CLexer.new(input) tokens = lexer.tokenize puts tokens.inspect diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 56790328..973a467e 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -16,7 +16,7 @@ def initialize(cfg) @c_strippables = cfg.strippables @process_gcc_attributes = cfg.process_gcc_attributes @process_cpp_attributes = cfg.process_cpp_attributes - @noreturn_attributes = cfg.noreturn_attributes.uniq + @noreturn_attributes = cfg.noreturn_attributes.uniq @c_attr_noconst = cfg.attributes.uniq - ['const'] @c_attributes = ['const'] + @c_attr_noconst @c_calling_conventions = cfg.c_calling_conventions.uniq @@ -25,7 +25,7 @@ def initialize(cfg) attribute_regexp = '((?:\s*__attribute__\s*\(\s*\(.*?\)\s*\))*)' type_and_name_regexp = '([\w\s\*\(\),\[\]]*?\w[\w\s\*\(\),\[\]]*?)' args_regexp = '([\w\s\*\(\),\.\[\]+\-\/]*)' - @function_declaration_parse_base_match = type_and_name_regexp+'\('+ args_regexp + '\)' + attribute_regexp + @function_declaration_parse_base_match = type_and_name_regexp + '\(' + args_regexp + '\)' + attribute_regexp @declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m @standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq @array_size_name = cfg.array_size_name @@ -44,14 +44,14 @@ def initialize(cfg) def raise_parse_error(message) # TODO: keep track of line number to be able to insert it in the error message. if @parse_project[:source_path].nil? - raise "Failed Parsing Declaration Prototype!" + "\n" + message + raise 'Failed Parsing Declaration Prototype!' + "\n" + message else raise "#{@parse_project[:source_path]}:1: Failed Parsing Declaration Prototype!" + "\n" + message end end def parse(name, source, src_path = nil) - $stderr.puts "Parsing #{src_path}" if !src_path.nil? and @verbosity >= 1 + $stderr.puts "Parsing #{src_path}" if !src_path.nil? && (@verbosity >= 1) @parse_project = { :source_path => src_path, :module_name => name.gsub(/\W/, ''), @@ -73,10 +73,10 @@ def parse(name, source, src_path = nil) end @parse_project[:normalized_source] = if @treat_inlines == :include - transform_inline_functions(source) - else - '' - end + transform_inline_functions(source) + else + '' + end { :includes => nil, :functions => @parse_project[:functions], @@ -96,9 +96,12 @@ def remove_comments_from_source(source) end def remove_nested_pairs_of_braces(source) - # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection) + # remove nested pairs of braces because no function declarations + # will be inside of them (leave outer pair for function definition + # detection) if RUBY_VERSION.split('.')[0].to_i > 1 - # we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash. + # we assign a string first because (no joke) if Ruby 1.9.3 sees + # this line as a regex, it will crash. r = '\\{([^\\{\\}]*|\\g<0>)*\\}' source.gsub!(/#{r}/m, '{ }') else @@ -125,7 +128,7 @@ def count_number_of_pairs_of_braces_in_function(source) curr_level -= 1 end - break if is_function_start_found && curr_level == 0 # We reached the end of the inline function body + break if is_function_start_found && curr_level.zero? # We reached the end of the inline function body end if curr_level != 0 @@ -200,10 +203,13 @@ def transform_inline_functions(source) end # 2. Determine if we are dealing with an inline function declaration iso function definition - # If the start of the post-match string is a function-declaration-like string (something ending with semicolon after the function arguments), - # we are dealing with a inline function declaration + # If the start of the post-match string is a + # function-declaration-like string (something ending with + # semicolon after the function arguments), we are dealing with + # a inline function declaration if /\A#{@function_declaration_parse_base_match}\s*;/m =~ inline_function_match.post_match - # Only remove the inline part from the function declaration, leaving the function declaration won't do any harm + # Only remove the inline part from the function declaration, + # leaving the function declaration won't do any harm inspected_source += inline_function_match.pre_match source = inline_function_match.post_match next @@ -214,7 +220,7 @@ def transform_inline_functions(source) if /\A#{@function_declaration_parse_base_match}\s*\{/m =~ inline_function_match.post_match total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match) - break if total_pairs_to_remove == 0 # Bad source? + break if total_pairs_to_remove.zero? # Bad source? inline_function_stripped = inline_function_match.post_match @@ -226,7 +232,8 @@ def transform_inline_functions(source) next end - # 4. If we get here, it means the regex match, but it is not related to the function (ex. static variable in header) + # 4. If we get here, it means the regex match, but it is not + # related to the function (ex. static variable in header) # Leave this code as it is. inspected_source += inline_function_match.pre_match + inline_function_match[0] source = inline_function_match.post_match @@ -237,20 +244,23 @@ def transform_inline_functions(source) end def import_source(source, cpp = false) - # let's clean up the encoding in case they've done anything weird with the characters we might find source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) - # void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void - # to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void + # void must be void for cmock _ExpectAndReturn calls to process + # properly, not some weird typedef which equates to void to a + # certain extent, this action assumes we're chewing on + # pre-processed header files, otherwise we'll most likely just get + # stuff from @treat_as_void @local_as_void = @treat_as_void void_types = source.scan(/typedef\s+(?:\(\s*)?void(?:\s*\))?\s+([\w]+)\s*;/) if void_types @local_as_void += void_types.flatten.uniq.compact end - # If user wants to mock inline functions, - # remove the (user specific) inline keywords before removing anything else to avoid missing an inline function + # If user wants to mock inline functions, remove the (user + # specific) inline keywords before removing anything else to avoid + # missing an inline function if @treat_inlines == :include @inline_function_patterns.each do |user_format_string| source.gsub!(/#{user_format_string}/, '') # remove user defined inline function patterns @@ -274,8 +284,11 @@ def import_source(source, cpp = false) source.gsub!(/extern\s+\"C\"\s*\{/, '') source.gsub!(/^\s*#.*/, '') - # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them - # forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes + # enums, unions, structs, and typedefs can all contain things + # (e.g. function pointers) that parse like function prototypes, so + # yank them forward declared structs are removed before struct + # definitions so they don't mess up real thing later. we leave + # structs keywords in function prototypes source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces # remove problem keywords @@ -332,7 +345,7 @@ def import_source(source, cpp = false) end src_lines.delete_if(&:empty?) # drop empty lines - src_lines + src_lines end # Rudimentary C++ parser - does not handle all situations - e.g.: @@ -405,15 +418,15 @@ def parse_functions(source) # type : stuffs ; # # stuffs : | stuff stuffs ; - # + # # stuff : token # | :open_paren stuffs :close_paren # | :open_bracket stuffs :close_bracket # | :open_brace stuffs :close_brace # | :lt stuffs :gt' -- angle brackets # ; - # -- Note: we will also scan char_literal and string_literal - # -- because they could appear in constant expressions (eg. enums) + # -- Note: we will also scan char_literal and string_literal + # -- because they could appear in constant expressions (eg. enums) # -- and contain parentheses. # -- Note: angle brackets for templates are very ambiguous, because # -- we may also have '<' tokens in constant expressions (eg. in a enum). @@ -430,7 +443,7 @@ def parse_functions(source) # # # Therefore we will parse in two phases: - # Phase 1: + # Phase 1: # we parse fun_declaration_1 : { stuff } ; # -- this takes care of parentheses, et al. # Phase 2: @@ -440,11 +453,11 @@ def parse_functions(source) # then name identifier, # then the rest is type. - def eos?(src,pos) - src.length<=pos + def eos?(src, pos) + src.length <= pos end - def validate_identifier(token,what) + def validate_identifier(token, what) if token[0] == :identifier token else @@ -452,22 +465,22 @@ def validate_identifier(token,what) end end - def parse_token(src,pos) - if eos?(src,pos) + def parse_token(src, pos) + if eos?(src, pos) raise_parse_error "Expected a token, not end of source at position #{pos}" end - [ src[pos], pos+1 ] + [src[pos], pos + 1] end - def parse_stuff(src,pos) + def parse_stuff(src, pos) # stuff : token - # | '(' stuffs ')' - # | '[' stuffs ']' + # | '(' stuffs ')' + # | '[' stuffs ']' # | '{' stuffs '}' - # | '<' stuffs '>' + # | '<' stuffs '>' # ; stuff = nil - if not eos?(src,pos) + if !eos?(src, pos) case src[pos] when :open_paren then stuff, pos = parse_delimited_stuffs(src, pos, :close_paren) when :open_bracket then stuff, pos = parse_delimited_stuffs(src, pos, :close_bracket) @@ -479,14 +492,14 @@ def parse_stuff(src,pos) [stuff, pos] end - def parse_delimited_stuffs(src,pos,closing) - pos += 1 # eat the opening tokenn + def parse_delimited_stuffs(src, pos, closing) + pos += 1 # eat the opening token stuffs = [] - while not eos?(src, pos) and src[pos] != closing + while !eos?(src, pos) && (src[pos] != closing) item, pos = parse_stuff(src, pos) stuffs << item end - if not eos?(src, pos) + if !eos?(src, pos) pos += 1 # skip closing token end op = case closing @@ -495,41 +508,41 @@ def parse_delimited_stuffs(src,pos,closing) when :close_brace then :braces when :gt then :angle_brackets end - [ [op, stuffs], pos ] + [[op, stuffs], pos] end - def parse_stuffs(src,pos) + def parse_stuffs(src, pos) # stuffs : | stuff stuffs ; stuffs = [] - while not eos?(src, pos) + while !eos?(src, pos) stuff, pos = parse_stuff(src, pos) stuffs << stuff unless stuff.nil? end - [ stuffs, pos ] + [stuffs, pos] end - def is_parens(stuff) - stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :parens) + def parens?(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :parens) end def parens_list(stuff) - stuff[1] if is_parens(stuff) + stuff[1] if parens?(stuff) end - def is_brackets(stuff) - stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :brackets) + def brackets?(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :brackets) end def brackets_list(stuff) - stuff[1] if is_brackets(stuff) + stuff[1] if brackets?(stuff) end - def is_token(token) + def token?(token) token.is_a?(Symbol) and (CLexer::OPERATOR_SYMS.index(token) or CLexer::KEYWORDS_SYMS.index(token)) end - def is_identifier(token,name=nil) - if token.is_a?(Array) and (token.length == 2) + def identifier?(token, name = nil) + if token.is_a?(Array) && (token.length == 2) if name.nil? (token[0] == :identifier) else @@ -541,11 +554,11 @@ def is_identifier(token,name=nil) end def identifier_name(token) - token[1] if token.is_a?(Array) and (token[0] == :identifier) + token[1] if token.is_a?(Array) && (token[0] == :identifier) end def token_name(token) - if is_identifier(token) + if identifier?(token) identifier_name(token) elsif token.is_a?(Symbol) token.to_s @@ -556,106 +569,106 @@ def token_name(token) end end - def is_c_calling_convention(stuff) + def c_calling_convention?(stuff) # whether stuff is a C calling convention (listed in @c_calling_conventions). # note: stuff may be either a symbol, a string or an :identifier array. - res = if stuff.is_a?(Symbol) or is_token(stuff) or is_identifier(stuff) - not @c_calling_conventions.index(token_name(stuff)).nil? + res = if stuff.is_a?(Symbol) || token?(stuff) || identifier?(stuff) + !@c_calling_conventions.index(token_name(stuff)).nil? else false end res end - def is_c_attribute(token) + def c_attribute?(token) # whether the token is a C attribute (listed in @c_attributes or in @noreturn_attributes). # note: token may be either a symbol, a string or an :identifier array. if token.is_a?(String) name = token elsif token.is_a?(Symbol) name = token.to_s - elsif is_token(token) or is_identifier(token) + elsif token?(token) || identifier?(token) name = token_name(token) elsif is_attribute(token) name = attribute_name(token) - else + else return false end - res = (@c_attributes.index(name) or - ((not @noreturn_attributes.nil?) and + res = (@c_attributes.index(name) || + (!@noreturn_attributes.nil? && (@noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) res end - def make_attribute(namespace,name,arguments,kind) + def make_attribute(namespace, name, arguments, kind) if name.nil? - raise_parse_error "Attribute name should not be nil! #{namespace.inspect}, #{name.inspect}, #{arguments.inspect}" + raise_parse_error "Attribute name should not be nil! #{namespace.inspect}, #{name.inspect}, #{arguments.inspect}" end - [:attribute,namespace,name,arguments,kind] + [:attribute, namespace, name, arguments, kind] end - def is_attribute(object) - (object.is_a?(Array)) and (object.length == 5) and (object[0] == :attribute) + def attribute?(object) + object.is_a?(Array) && (object.length == 5) && (object[0] == :attribute) end def attribute_namespace(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[1] end def attribute_name(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[2] end def attribute_qualified_name(attribute) if attribute_namespace(attribute) - attribute_namespace(attribute) + "::" + attribute_name(attribute) + attribute_namespace(attribute) + '::' + attribute_name(attribute) else attribute_name(attribute) end end def attribute_arguments(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[3] end def attribute_kind(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[4] end - def is_noreturn(attribute) - if is_identifier(attribute) + def noreturn?(attribute) + if identifier?(attribute) @noreturn_attributes.include?(identifier_name(attribute)) - elsif is_attribute(attribute) + elsif attribute?(attribute) @noreturn_attributes.include?(attribute_qualified_name(attribute)) else false end end - def has_noreturn_attribute(attributes) + def noreturn_attribute?(attributes) attributes.any? do |attribute| - is_noreturn(attribute) + noreturn?(attribute) end end - def is_gcc_attribute_syntax(operator, parameters) + def gcc_attribute_syntax?(operator, parameters) # gcc atributes are all of the syntax __attribute__ (...) # where ... is a list of stuffs. - # so is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,stuff_list]) - is_identifier(operator,'__attribute__') and is_parens(parameters) + # so gcc_attribute_syntax?([:identifier,"__attribute__"],[:parens,stuff_list]) + identifier?(operator, '__attribute__') && parens?(parameters) # see parse_gcc_attribute end - def is_processable_gcc_attribute(name) - is_c_attribute(name) + def processable_gcc_attribute?(name) + c_attribute?(name) end - def parse_gcc_attribute(op,stuff) + def parse_gcc_attribute(op, stuff) # gcc atributes are all of the syntax __attribute__ (...) # where ... is a list of stuffs. # Here, attribute = [:attribute, [:parens,stuff_list]] @@ -674,69 +687,70 @@ def parse_gcc_attribute(op,stuff) # --> [[:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_write"],[:integer_literal,"1"]]]], # [:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_only"],[:integer_literal,"2"]]]]] - if not (is_identifier(op,'__attribute__') and is_parens(stuff) and is_parens(parens_list(stuff)[0])) - raise_parse_error "Unexpected attribute syntax #{[op,stuff].inspect}" + unless identifier?(op, '__attribute__') && parens?(stuff) && parens?(parens_list(stuff)[0]) + raise_parse_error "Unexpected attribute syntax #{[op, stuff].inspect}" end normalized = [] - j=0 + j = 0 chunks = parens_list(stuff) - while j '::' ] [ '(' ')' ] ; attributes = [] - if not (is_brackets(stuff) and (1 == brackets_list(stuff).length) and is_brackets(brackets_list(stuff)[0])) + unless brackets?(stuff) && (brackets_list(stuff).length == 1) && brackets?(brackets_list(stuff)[0]) raise_parse_error "Unexpected C++ attribute syntax #{stuff.inspect}" + - "\nis_brackets(stuff) = #{is_brackets(stuff)}" + - "\nbrackets_list(stuff).length = #{is_brackets(stuff) ? brackets_list(stuff).length : nil}" + - "\nis_brackets(brackets_list(stuff)[0]) = #{is_brackets(stuff) and brackets_list(stuff).length>1 ? is_brackets(brackets_list(stuff)[0]) : nil}" + "\nbrackets?(stuff) = #{brackets?(stuff)}" + + "\nbrackets_list(stuff).length = #{brackets?(stuff) ? brackets_list(stuff).length : nil}" + + "\nbrackets?(brackets_list(stuff)[0]) = #{ (brackets?(stuff) && (brackets_list(stuff).length > 1)) ? (brackets?(brackets_list(stuff)[0])) : nil}" end - + stuff = brackets_list(brackets_list(stuff)[0]) # Note: for better support for C++, we'd have to update CLexer for C++ tokens. @@ -758,9 +772,9 @@ def parse_cpp_attributes(stuff) # etc (but C++ lexers must be context-sensitive). default_namespace = nil - start=0 - if 30 + guess[:ptr?] = (starc > 0) else # char* are "strings", not "pointers". - guess[:ptr?] = starc>1 + guess[:ptr?] = (starc > 1) end if first_const.nil? # no const: guess[:const?] = false elsif starc == 0 - # const, no star + # const, no star guess[:const?] = true else # const, some star: - before_last_star = type[0..last_star-1].rindex(:mul_op) - + before_last_star = type[0 .. (last_star - 1)].rindex(:mul_op) + if before_last_star.nil? - # a single star: - guess[:const?] = (first_const0) and (not last_const.nil?) and (last_star < last_const)) + guess[:const_ptr?] = ((starc > 0) && !last_const.nil? && (last_star < last_const)) guess end - def parse_function_signature(src,pos) - - # Phase 1: + def parse_function_signature(src, pos) + # Phase 1: # we parse fun_declaration_1 : { stuff } ; # -- this takes care of parentheses, et al. items, pos = parse_stuffs(src, pos) - raise_parse_error "Unparsed characters from position #{pos}" unless pos == src.length + raise_parse_error "Unparsed characters from position #{pos}" unless pos == src.length # Phase 2: # then match from the end of the list of stuffs, @@ -864,25 +876,25 @@ def parse_function_signature(src,pos) # match from the end of the list of stuffs, # for c_calling_conventions, __attributes__ (...) - i = items.length-1 - while is_c_calling_convention(items[i]) or ((3<=i) and is_gcc_attribute_syntax(items[i-1],items[i])) - if is_c_calling_convention(items[i]) + i = items.length - 1 + while c_calling_convention?(items[i]) || ((3 <= i) && gcc_attribute_syntax?(items[i - 1], items[i])) + if c_calling_convention?(items[i]) ccc << [:c_calling_convention, token_name(items[i])] i -= 1 else - attributes += parse_gcc_attribute(items[i-1],items[i]) + attributes += parse_gcc_attribute(items[i - 1], items[i]) i -= 2 end end # then '(' parameters ')' = '(' stuffs ')' - if is_parens(items[i]) + if parens?(items[i]) parameters = parens_list(items[i]) i -= 1 end # then name identifier, - if not is_identifier(items[i]) + unless identifier?(items[i]) raise_parse_error "Expected an identifier but got #{items[i].inspect} as function name in #{items.inspect}" end name = identifier_name(items[i]) @@ -899,28 +911,27 @@ def parse_type(stuff) # to remove 'const' only when it applies to the pointer itself, not when it # applies to the type pointed to. For non-pointer types, remove any # occurrence of 'const'. - arg_info = guess_ptr_and_const(stuff) - @attributes = (stuff.any?{|item| item == :mul_op}) ? @c_attr_noconst : @c_attributes + @attributes = (stuff.any? { |item| item == :mul_op }) ? @c_attr_noconst : @c_attributes type = [] attributes = [] ccc = [] i = 0 - while i foo_t* foo # foo_t* foo[][][] --> foo_t** foo if parameters == [[]] parameters else parameters.map do |parameter| - if is_parens(parameter) - [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(parameter)]) - elsif parameter.is_a?(Array) - i = parameter.rindex{|item| not is_brackets(item)} - if i.nil? then - # all items are brackets + if parens?(parameter) + [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(parameter)]) + elsif parameter.is_a?(Array) + i = parameter.rindex { |item| !brackets?(item) } + if i.nil? + # all items are brackets raise_parse_error "All items are brackets parameter=#{parameter.inspect}" - elsif i == parameter.length-1 then + elsif i == parameter.length-1 # no item is a brackets - parameter + parameter else - # some brackets, remove them and insert * before the name - # Note: int foo[3][4][5] --> int* foo - parameter[0,i] + [:mul_op] + [parameter[i]] + # some brackets, remove them and insert * before the name + # Note: int foo[3][4][5] --> int* foo + parameter[0, i] + [:mul_op] + [parameter[i]] end.map do |item| # recurse into parens groups: - if is_parens(item) + if parens?(item) [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(item)]) - else - item + else + item end end else @@ -1033,7 +1043,7 @@ def replace_arrays_by_pointers_in_parameters(parameters) # [:brackets, [[:integer_literal, "4"]]]]], # [:parens, [:int, [:identifier, "x"]]] ], # [ :int, [:identifier, "a"] ] ]) -# ==> +# ==> # [[:int, :mul_op, [:identifier, "b"]], # [:int, :mul_op, [:identifier, "c"]], # [:int, @@ -1041,19 +1051,18 @@ def replace_arrays_by_pointers_in_parameters(parameters) # [:parens, [:int, [:identifier, "x"]]]], # [:int, [:identifier, "a"]]] - def replace_function_pointers_by_custom_types(parameters) parameters.map do |parameter| plen = parameter.length - if 20) ? spec[0..ptrindex-1] : [] - funcname = spec[ptrindex+1..-1] + funcdecl = (ptrindex > 0) ? spec[0 .. ptrindex - 1] : [] + funcname = spec[ptrindex + 1 .. -1] constindex = funcname.index(:const) if constindex funcname.delete_at(constindex) @@ -1066,7 +1075,7 @@ def replace_function_pointers_by_custom_types(parameters) raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" end - elsif is_identifier(parameter[-2]) # ...foo() + elsif identifier?(parameter[-2]) # ...foo() funcdecl = [] funcname = [parameter[-2]] @@ -1076,12 +1085,12 @@ def replace_function_pointers_by_custom_types(parameters) raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" end - functype = [:identifier,"cmock_#{@parse_project[:module_name]}_func_ptr#{@parse_project[:typedefs].size + 1}"] + functype = [:identifier, "cmock_#{@parse_project[:module_name]}_func_ptr#{@parse_project[:typedefs].size + 1}"] funcret = parameter[0..-3] funcargs = parameter[-1] # add typedef for function pointer - @parse_project[:typedefs] << "typedef #{unparse(funcret)}(#{unparse(funcdecl+[:mul_op]+[functype])})#{unparse(funcargs)};".gsub(/\(\*\s+/,'(*').gsub(/\s+\*/,'*').gsub(/\s+,/,',') + @parse_project[:typedefs] << "typedef #{unparse(funcret)}(#{unparse(funcdecl + [:mul_op] + [functype])})#{unparse(funcargs)};".gsub(/\(\*\s+/,'(*').gsub(/\s+\*/, '*').gsub(/\s+,/, ',') funcname = [[:identifier, "cmock_arg#{@c += 1}"]] if funcname.empty? [functype] + funcconst + funcname else @@ -1090,14 +1099,14 @@ def replace_function_pointers_by_custom_types(parameters) end end - def is_anonymous_parameter(parameter) + def anonymous_parameter?(parameter) parameter = parameter.reject { |token| [:struct, :union, :enum, :const, :mul_op].include?(token) } - if (parameter.length == 0) + if parameter.length == 0 true - elsif (parameter.length == 1) - not (parameter[0] == :ellipsis) + elsif parameter.length == 1 + !(parameter[0] == :ellipsis) else - not is_identifier(parameter[-1]) + !identifier?(parameter[-1]) end end @@ -1105,7 +1114,7 @@ def add_names_to_anonymous_parameters(parameters) parameters.map do |parameter| if parameter.nil? nil - elsif is_anonymous_parameter(parameter) + elsif anonymous_parameter?(parameter) parameter << [:identifier, "cmock_arg#{@c += 1}"] else parameter @@ -1114,23 +1123,23 @@ def add_names_to_anonymous_parameters(parameters) end def parameter_unwrap_superfluous_parentheses(parameter) - pc = parameter.count { |item| is_parens(item) } - if (pc == 1) and is_parens(parameter[-1]) and - (parens_list(parameter[-1]).length == 1) and - is_parens(parens_list(parameter[-1])[0]) - # ... ((...)) --> unwrap ... (...) + pc = parameter.count { |item| parens?(item) } + if (pc == 1) && parens?(parameter[-1]) && + (parens_list(parameter[-1]).length == 1) && + parens?(parens_list(parameter[-1])[0]) + # ... ((...)) --> unwrap ... (...) parameter_unwrap_superfluous_parentheses(parameter[0..-2] + parens_list(parameter[-1])) - elsif (pc == 1) and is_parens(parameter[-1]) and - (parens_list(parameter[-1]).length == 2) and - is_parens(parens_list(parameter[-1])[0]) and - is_parens(parens_list(parameter[-1])[1]) + elsif (pc == 1) && parens?(parameter[-1]) && + (parens_list(parameter[-1]).length == 2) && + parens?(parens_list(parameter[-1])[0]) && + parens?(parens_list(parameter[-1])[1]) # ... ((...)(...)) --> unwrap ... (...)(...) parameter_unwrap_superfluous_parentheses(parameter[0..-2] + [parens_list(parameter[-1])[0]] + [parens_list(parameter[-1])[1]]) - elsif (pc == 2) and is_parens(parameter[-2]) and is_parens(parameter[-1]) and - (parens_list(parameter[-2]).length == 1) and - is_parens(parens_list(parameter[-2])[0]) + elsif (pc == 2) && parens?(parameter[-2])&& parens?(parameter[-1]) && + (parens_list(parameter[-2]).length == 1) && + parens?(parens_list(parameter[-2])[0]) # ... ((...)) (...) --> unwrap ... (...) (...) parameter_unwrap_superfluous_parentheses(parameter[0..-3] + parens_list(parameter[-2]) + parameter[-1]) else @@ -1145,7 +1154,7 @@ def clean_args(parameters) # [ :int, [:identifier, "c"], [:brackets, [:integer_literal, "5"]] ], # [ :int, [:identifier, "a"] ] ] - if parameters.empty? or ((parameters.length == 1) and @local_as_void.include?(unparse(parameters[0]))) + if parameters.empty? || ((parameters.length == 1) && @local_as_void.include?(unparse(parameters[0]))) [:void] else @c = 0 @@ -1163,11 +1172,11 @@ def clean_args(parameters) # magically turn brackets into asterisks, also match for parentheses that come from macros parameters = replace_arrays_by_pointers_in_parameters(parameters) - # scan argument list for function pointers and replace them with custom types + # scan argument list for function pointers and replace them with custom types # scan argument list for function pointers with shorthand notation and replace them with custom types # Note: if I'm not wrong, this new code using tokens handles both cases, with and without funcdecl. # parameters=[[:unsigned, :int, [:parens, [:mul_op, [:identifier, "func_ptr"]]], [:parens, [:int, :comma, :char]]]] - parameters=replace_function_pointers_by_custom_types(parameters) + parameters = replace_function_pointers_by_custom_types(parameters) # automatically name unnamed arguments (those that only had a type) parameters = add_names_to_anonymous_parameters(parameters) @@ -1179,8 +1188,8 @@ def clean_args(parameters) end end - def is_string_type(type) - (type.length>=2) and (type[0]==:char) and (type[1]==:mul_op) + def string_type?(type) + (type.length >= 2) && (type[0] == :char) && (type[1] == :mul_op) end def parse_args(parameters) @@ -1188,15 +1197,15 @@ def parse_args(parameters) # so they're each of the form :void, :ellipsis, or [type... name] args = [] parameters.each do |parameter| - return args if (parameter == :void) or (parameter == [:ellipsis]) + return args if (parameter == :void) || (parameter == [:ellipsis]) - if parameter.nil? or (parameter.length<2) + if parameter.nil? || (parameter.length < 2) raise_parse_error "Invalid parameter #{parameter.inspect} in #{parameters.inspect}" else - type=parameter[0..-2] - name=parameter[-1] + type = parameter[0..-2] + name = parameter[-1] type, _, _, arg_info = parse_type(type) - arg_info[:name]=identifier_name(name) + arg_info[:name] = identifier_name(name) arg_info.delete(:modifier) # don't care about this arg_info.delete(:noreturn) # don't care about this arg_info.delete(:c_calling_convention) # don't care about this @@ -1207,8 +1216,8 @@ def parse_args(parameters) arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" arg_info[:ptr?] = true arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] - elsif arg_info[:ptr?] or is_string_type(type) - if arg_info[:const?] + elsif arg_info[:ptr?] || string_type?(type) + if arg_info[:const?] arg_info[:type] = "const #{arg_info[:type]}" end end @@ -1230,7 +1239,7 @@ def parse_args(parameters) args end - + def parse_declaration(declaration, namespace = [], classname = nil) decl = {} decl[:namespace] = namespace @@ -1241,14 +1250,14 @@ def parse_declaration(declaration, namespace = [], classname = nil) # Split declaration into type, name, parameters, attributes, and calling convention - type, name, parameters, p_attributes, p_ccc = parse_function_signature(decl_tokens, 0) + type, name, parameters, p_attributes, _ = parse_function_signature(decl_tokens, 0) # Process function attributes, return type, and name # Some attributes may be written after the parameter list, so we need to # check for them and move them to the front of the declaration. - type, attributes, ccc, parsed = parse_type(type) + type, attributes, _, parsed = parse_type(type) attributes += p_attributes - ccc += p_ccc + # ccc += p_ccc # Record original name without scope prefix decl[:unscoped_name] = name @@ -1263,28 +1272,28 @@ def parse_declaration(declaration, namespace = [], classname = nil) decl[:name] << '_' unless decl[:name].empty? decl[:name] << decl[:unscoped_name] - decl[:noreturn] = has_noreturn_attribute(attributes) + decl[:noreturn] = noreturn_attribute?(attributes) if decl[:noreturn] - attributes.delete_if{|attribute| is_noreturn(attribute)} + attributes.delete_if { |attribute| noreturn?(attribute) } end if parsed[:ptr?] - if parsed[:const?] - type = [:const] + type unless type[0]==:const - attributes.delete_if{|attr| attribute_name(attr) == "const"} - end + if parsed[:const?] + type = [:const] + type unless type[0] ==:const + attributes.delete_if { |attr| attribute_name(attr) == 'const' } + end end # TODO: perhaps we need a specific configuration, or use strippable to select the __attributes__ to remove. - attributes.delete_if{|attribute| is_attribute(attribute) and (attribute_kind(attribute) == :gcc)} + attributes.delete_if { |attribute| attribute?(attribute) && (attribute_kind(attribute) == :gcc) } decl[:modifier] = unparse(attributes.uniq) - if not (parsed[:c_calling_convention].nil? or parsed[:c_calling_convention].empty?) - decl[:c_calling_convention] = parsed[:c_calling_convention] + if !(parsed[:c_calling_convention].nil? || parsed[:c_calling_convention].empty?) + decl[:c_calling_convention] = parsed[:c_calling_convention] end - rettype = unparse(type).gsub(/\s+\*/,'*') + rettype = unparse(type).gsub(/\s+\*/, '*') rettype = 'void' if @local_as_void.include?(rettype.strip) decl[:return] = { :type => rettype, :name => 'cmock_to_return', @@ -1308,12 +1317,12 @@ def parse_declaration(declaration, namespace = [], classname = nil) if passign.nil? parameter else - parameter[0..passign-1] + parameter[0 .. passign - 1] end } # check for var args if parameters[-1] == [:ellipsis] - decl[:var_arg] = "..." + decl[:var_arg] = '...' parameters.pop if parameters.empty? parameters = [:void] @@ -1326,7 +1335,7 @@ def parse_declaration(declaration, namespace = [], classname = nil) raise_parse_error "Invalid parameters #{parameters.inspect}" end parameters = clean_args(parameters) - decl[:args_string] = parameters.map{|parameter| unparse(parameter)}.join(', ').gsub(/\s+\*/, '*') + decl[:args_string] = parameters.map { |parameter| unparse(parameter) }.join(', ').gsub(/\s+\*/, '*') decl[:args] = parse_args(parameters) decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ') decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr } @@ -1355,11 +1364,11 @@ def prototype_inspect_array_of_hashes(array) array.each { |hash| hashes << prototype_inspect_hash(hash) } case array.size when 0 - return '[]' + '[]' when 1 - return "[#{hashes[0]}]" + "[#{hashes[0]}]" else - return "[\n #{hashes.join("\n ")}\n ]\n" + "[\n #{hashes.join("\n ")}\n ]\n" end end diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 4cd66e52..822d7cb5 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -2956,53 +2956,45 @@ class Classy { it "builds parser on sound bases" do - assert(@parser.is_parens([:parens,[[:identifier,"a"]]]),"is_parens identifies parens") - refute(@parser.is_parens([:brackets,[[:identifier,"a"]]]),"is_parens rejects brackets") - refute(@parser.is_parens([:identifier,"Foo"]),"is_parens rejects identifier") + assert(@parser.parens?([:parens,[[:identifier,"a"]]]),"parens? identifies parens") + refute(@parser.parens?([:brackets,[[:identifier,"a"]]]),"parens? rejects brackets") + refute(@parser.parens?([:identifier,"Foo"]),"parens? rejects identifier") assert_equal([[:identifier,"a"],[:integer_literal,"42"]], @parser.parens_list([:parens,[[:identifier,"a"],[:integer_literal,"42"]]]), "parens_list returns list of elements in parens") - assert(@parser.is_brackets([:brackets,[[:identifier,"a"]]]),"is_brackets identifies brackets") - refute(@parser.is_brackets([:parens,[[:identifier,"a"]]]),"is_brackets rejects parens") - refute(@parser.is_brackets([:identifier,"Foo"]),"is_brackets rejects identifier") + assert(@parser.brackets?([:brackets,[[:identifier,"a"]]]),"brackets? identifies brackets") + refute(@parser.brackets?([:parens,[[:identifier,"a"]]]),"brackets? rejects parens") + refute(@parser.brackets?([:identifier,"Foo"]),"brackets? rejects identifier") assert_equal([[:identifier,"a"],[:integer_literal,"42"]], @parser.brackets_list([:brackets,[[:identifier,"a"],[:integer_literal,"42"]]]), "brackets_list returns list of elements in brackets") - - # assert(@parser.is_braces([:braces,[[:identifier,"a"]]]),"is_braces identifies braces") - # refute(@parser.is_braces([:brackets,[[:identifier,"a"]]]),"is_braces rejects brackets") - # refute(@parser.is_braces([:identifier,"Foo"]),"is_braces rejects identifier") - # assert_equal([[:identifier,"a"],[:integer_literal,"42"]], - # @parser.braces_list([:braces,[[:identifier,"a"],[:integer_literal,"42"]]]), - # "braces_list returns list of elements in braces") - - assert(@parser.is_identifier([:identifier,"Foo"]),"is_identifier identifies identifier") - assert(@parser.is_identifier([:identifier,"Foo"],"Foo"),"is_identifier identifies identifier with name") - refute(@parser.is_identifier([:identifier,"Foo"],"Bar"),"is_identifier rejects identifier with wrong name") - refute(@parser.is_identifier(:bar,"Bar"),"is_identifier rejects non-identifier") - refute(@parser.is_identifier(:bar),"is_identifier rejects non-identifier") + assert(@parser.identifier?([:identifier,"Foo"]),"identifier? identifies identifier") + assert(@parser.identifier?([:identifier,"Foo"],"Foo"),"identifier? identifies identifier with name") + refute(@parser.identifier?([:identifier,"Foo"],"Bar"),"identifier? rejects identifier with wrong name") + refute(@parser.identifier?(:bar,"Bar"),"identifier? rejects non-identifier") + refute(@parser.identifier?(:bar),"identifier? rejects non-identifier") assert_equal("Foo",@parser.identifier_name([:identifier,"Foo"]),"identifier_name returns name of identifier") - assert(@parser.is_c_calling_convention([:identifier,"__stdcall"]),"is_c_calling_convention should identify __stdcall") - refute(@parser.is_c_calling_convention([:identifier,"callfoo"]),"is_c_calling_convention should refute callfoo") - assert(@parser.is_c_calling_convention(:__stdcall),"is_c_calling_convention should accept :__stdcall") + assert(@parser.c_calling_convention?([:identifier,"__stdcall"]),"c_calling_convention? should identify __stdcall") + refute(@parser.c_calling_convention?([:identifier,"callfoo"]),"c_calling_convention? should refute callfoo") + assert(@parser.c_calling_convention?(:__stdcall),"c_calling_convention? should accept :__stdcall") - assert(@parser.is_c_attribute(:const),"is_c_attribute should identify :const") - assert(@parser.is_c_attribute([:identifier,"const"]),"is_c_attribute should identify [:identifier, 'const']") - assert(@parser.is_c_attribute([:identifier,"__ramfunc"]),"is_c_attribute should identify [:identifier, '__ramfunc']") - refute(@parser.is_c_attribute([:identifier,"__attribute__"]),"is_c_attribute should refute [:identifier, '__attribute__']") - refute(@parser.is_c_attribute(:conste),"is_c_attribute should refute :constes") - assert(@parser.is_c_attribute([:identifier,"noreturn"]),"is_c_attribute should identify [:identifier, 'noreturn']") - assert(@parser.is_c_attribute(:noreturn),"is_c_attribute should identify :noreturn") + assert(@parser.c_attribute?(:const),"c_attribute? should identify :const") + assert(@parser.c_attribute?([:identifier,"const"]),"c_attribute? should identify [:identifier, 'const']") + assert(@parser.c_attribute?([:identifier,"__ramfunc"]),"c_attribute? should identify [:identifier, '__ramfunc']") + refute(@parser.c_attribute?([:identifier,"__attribute__"]),"c_attribute? should refute [:identifier, '__attribute__']") + refute(@parser.c_attribute?(:conste),"c_attribute? should refute :constes") + assert(@parser.c_attribute?([:identifier,"noreturn"]),"c_attribute? should identify [:identifier, 'noreturn']") + assert(@parser.c_attribute?(:noreturn),"c_attribute? should identify :noreturn") - assert(@parser.is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,[[:parens,[[:identifier,"noreturn"]]]]]), - "is_gcc_attribute_syntax identifies parsed __attribute__((noreturn))") + assert(@parser.gcc_attribute_syntax?([:identifier,"__attribute__"],[:parens,[[:parens,[[:identifier,"noreturn"]]]]]), + "gcc_attribute_syntax? identifies parsed __attribute__((noreturn))") - assert(@parser.is_attribute([:attribute,nil,"noreturn",nil,:gcc]), - "is_attribute identifies [:attribute,nil,\"noreturn\",nil,:gcc]") + assert(@parser.attribute?([:attribute,nil,"noreturn",nil,:gcc]), + "attribute? identifies [:attribute,nil,\"noreturn\",nil,:gcc]") end