class Steep::AnnotationParser

def initialize(factory:)

def initialize(factory:)
  @factory = factory
end

def keyword_and_type(keyword)

def keyword_and_type(keyword)
  /@type\s+#{keyword}#{COLON}#{TYPE}/
end

def keyword_subject_type(keyword, name)

def keyword_subject_type(keyword, name)
  /@type\s+#{keyword}\s+(?<name>#{name})#{COLON}#{TYPE}/
end

def parse(src, location:)

def parse(src, location:)
  case src
  when keyword_subject_type("var", VAR_NAME)
    Regexp.last_match.yield_self do |match|
      match or raise
      name = match[:name] or raise
      AST::Annotation::VarType.new(name: name.to_sym,
                                   type: parse_type(match, location: location),
                                   location: location)
    end
  when keyword_subject_type("method", METHOD_NAME)
    Regexp.last_match.yield_self do |match|
      match or raise
      name = match[:name] or raise
      type = match[:type] or raise
      method_type = factory.method_type(RBS::Parser.parse_method_type(type) || raise)
      AST::Annotation::MethodType.new(name: name.to_sym,
                                      type: method_type,
                                      location: location)
    end
  when keyword_subject_type("const", CONST_NAME)
    Regexp.last_match.yield_self do |match|
      match or raise
      name = match[:name] or raise
      type = parse_type(match, location: location)
      AST::Annotation::ConstType.new(name: RBS::TypeName.parse(name), type: type, location: location)
    end
  when keyword_subject_type("ivar", IVAR_NAME)
    Regexp.last_match.yield_self do |match|
      match or raise
      name = match[:name] or raise
      type = parse_type(match, location: location)
      AST::Annotation::IvarType.new(name: name.to_sym,
                                     type: type,
                                     location: location)
    end
  when keyword_and_type("return")
    Regexp.last_match.yield_self do |match|
      match or raise
      type = parse_type(match, location: location)
      AST::Annotation::ReturnType.new(type: type, location: location)
    end
  when keyword_and_type("block")
    Regexp.last_match.yield_self do |match|
      match or raise
      type = parse_type(match, location: location)
      AST::Annotation::BlockType.new(type: type, location: location)
    end
  when keyword_and_type("self")
    Regexp.last_match.yield_self do |match|
      match or raise
      type = parse_type(match, location: location)
      AST::Annotation::SelfType.new(type: type, location: location)
    end
  when keyword_and_type("instance")
    Regexp.last_match.yield_self do |match|
      match or raise
      type = parse_type(match, location: location)
      AST::Annotation::InstanceType.new(type: type, location: location)
    end
  when keyword_and_type("module")
    Regexp.last_match.yield_self do |match|
      match or raise
      type = parse_type(match, location: location)
      AST::Annotation::ModuleType.new(type: type, location: location)
    end
  when keyword_and_type("break")
    Regexp.last_match.yield_self do |match|
      match or raise
      type = parse_type(match, location: location)
      AST::Annotation::BreakType.new(type: type, location: location)
    end
  when /@dynamic\s+(?<names>(#{DYNAMIC_NAME}\s*,\s*)*#{DYNAMIC_NAME})/
    Regexp.last_match.yield_self do |match|
      match or raise
      names = (match[:names] || raise).split(/\s*,\s*/)
      AST::Annotation::Dynamic.new(
        names: names.map {|name|
          case
          when name.delete_prefix!("self.")
            AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :module)
          when name.delete_prefix!("self?.")
            AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :module_instance)
          else
            AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :instance)
          end
        },
        location: location
      )
    end
  when /@implements\s+(?<name>#{CONST_NAME})#{TYPE_PARAMS}$/
    Regexp.last_match.yield_self do |match|
      match or raise
      type_name = RBS::TypeName.parse(match[:name] || raise)
      params = match[:params]&.yield_self {|params| params.split(/,/).map {|param| param.strip.to_sym } } || []
      name = AST::Annotation::Implements::Module.new(name: type_name, args: params)
      AST::Annotation::Implements.new(name: name, location: location)
    end
  end
rescue RBS::ParsingError => exn
  raise SyntaxError.new(source: src, location: location, exn: exn)
end

def parse_type(match, name = :type, location:)

def parse_type(match, name = :type, location:)
  string = match[name] or raise
  st, en = match.offset(name)
  st or raise
  en or raise
  loc = RBS::Location.new(location.buffer, location.start_pos + st, location.start_pos + en)
  type =
    begin
      RBS::Parser.parse_type(string)
    rescue RBS::ParsingError => exn
      raise SyntaxError.new(source: string, location: loc, exn: exn)
    end or raise
  unless (type.location || raise).source == string.strip
    raise SyntaxError.new(source: string, location: loc, message: "Failed to parse a type in annotation")
  end
  factory.type(type)
end