lib/yard/parser/c/comment_parser.rb



# frozen_string_literal: true
module YARD
  module Parser
    module C
      module CommentParser
        protected

        def parse_comments(comments)
          @overrides = []
          spaces = nil
          comments = remove_private_comments(comments)
          comments = comments.split(/\r?\n/).map do |line|
            line.gsub!(%r{^\s*/?\*/?}, '')
            line.gsub!(%r{\*/\s*$}, '')
            if line =~ /^\s*$/
              next if spaces.nil?
              next ""
            end
            spaces = (line[/^(\s+)/, 1] || "").size if spaces.nil?
            line.gsub(/^\s{0,#{spaces}}/, '').rstrip
          end.compact

          comments = parse_overrides(comments)
          comments = parse_callseq(comments)
          comments.join("\n")
        end

        private

        def parse_overrides(comments)
          comments.map do |line|
            type, name = *line.scan(/^\s*Document-(class|module|method|attr|const):\s*(\S.*)\s*$/).first
            if type
              @overrides << [type.to_sym, name]
              nil
            else
              line
            end
          end.compact
        end

        def parse_callseq(comments)
          return comments unless comments[0] =~ /\Acall-seq:\s*(\S.+)?/
          if $1
            comments[0] = " #{$1}"
          else
            comments.shift
          end
          overloads = []
          seen_data = false
          while comments.first =~ /^\s+(\S.+)/ || comments.first =~ /^\s*$/
            line = comments.shift.strip
            break if line.empty? && seen_data
            next if line.empty?
            seen_data = true
            line.sub!(/^\w+[\.#]/, '')
            signature, types = *line.split(/ [-=]> /)
            types = parse_types(types)
            if signature.sub!(/\[?\s*(\{(?:\s*\|(.+?)\|)?.*\})\s*\]?\s*$/, '') && $1
              blk = $1
              blkparams = $2
            else
              blk = nil
              blkparams = nil
            end
            case signature
            when /^(\w+)\s*=\s+(\w+)/
              signature = "#{$1}=(#{$2})"
            when /^\w+\s+\S/
              signature = signature.split(/\s+/)
              signature = "#{signature[1]}#{signature[2] ? '(' + signature[2..-1].join(' ') + ')' : ''}"
            when /^\w+\[(.+?)\]\s*(=)?/
              signature = "[]#{$2}(#{$1})"
            when /^\w+\s+(#{CodeObjects::METHODMATCH})\s+(\w+)/
              signature = "#{$1}(#{$2})"
            end
            break unless signature =~ /^#{CodeObjects::METHODNAMEMATCH}/
            signature = signature.rstrip
            overloads << "@overload #{signature}"
            overloads << "  @yield [#{blkparams}]" if blk
            overloads << "  @return [#{types.join(', ')}]" unless types.empty?
          end

          comments + [""] + overloads
        end

        def parse_types(types)
          if types =~ /true or false/
            ["Boolean"]
          else
            (types || "").split(/,| or /).map do |t|
              case t.strip.gsub(/^an?_/, '')
              when "class"; "Class"
              when "obj", "object", "anObject"; "Object"
              when "arr", "array", "anArray", "ary", "new_ary", /^\[/; "Array"
              when /^char\s*\*/, "char", "str", "string", "new_str"; "String"
              when "enum", "anEnumerator"; "Enumerator"
              when "exc", "exception"; "Exception"
              when "proc", "proc_obj", "prc"; "Proc"
              when "binding"; "Binding"
              when "hsh", "hash", "aHash"; "Hash"
              when "ios", "io"; "IO"
              when "file"; "File"
              when "float"; "Float"
              when "time", "new_time"; "Time"
              when "dir", "aDir"; "Dir"
              when "regexp", "new_regexp"; "Regexp"
              when "matchdata"; "MatchData"
              when "encoding"; "Encoding"
              when "fixnum", "fix"; "Fixnum"
              when /^(?:un)?signed$/, /^(?:(?:un)?signed\s*)?(?:short|int|long|long\s+long)$/, "integer", "Integer"; "Integer"
              when "num", "numeric", "Numeric", "number"; "Numeric"
              when "aBignum"; "Bignum"
              when "nil"; "nil"
              when "true"; "true"
              when "false"; "false"
              when "bool", "boolean", "Boolean"; "Boolean"
              when "self"; "self"
              when /^[-+]?\d/; t
              when /[A-Z][_a-z0-9]+/; t
              end
            end.compact
          end
        end

        def remove_private_comments(comment)
          comment = comment.gsub(%r{/?\*--\n(.*?)/?\*\+\+}m, '')
          comment = comment.sub(%r{/?\*--\n.*}m, '')
          comment
        end
      end
    end
  end
end