class RBS::Parser
  token tUIDENT tLIDENT tUNDERSCOREIDENT tNAMESPACE tINTERFACEIDENT tGLOBALIDENT
        tLKEYWORD tUKEYWORD tLKEYWORD_Q_E tUKEYWORD_Q_E
        tIVAR tCLASSVAR
        tANNOTATION
        tSTRING tSYMBOL tINTEGER tWRITE_ATTR
        kLPAREN kRPAREN kLBRACKET kRBRACKET kLBRACE kRBRACE
        kVOID kNIL kTRUE kFALSE kANY kUNTYPED kTOP kBOT kSELF kSELFQ kINSTANCE kCLASS kBOOL kSINGLETON kTYPE kDEF kMODULE
        kPRIVATE kPUBLIC kALIAS
        kCOLON kCOLON2 kCOMMA kBAR kAMP kHAT kARROW kQUESTION kEXCLAMATION kSTAR kSTAR2 kFATARROW kEQ kDOT kDOT3 kLT
        kINTERFACE kEND kINCLUDE kEXTEND kATTRREADER kATTRWRITER kATTRACCESSOR tOPERATOR tQUOTEDMETHOD tQUOTEDIDENT
        kPREPEND kEXTENSION kINCOMPATIBLE
        type_TYPE type_SIGNATURE type_METHODTYPE tEOF
        kOUT kIN kUNCHECKED kOVERLOAD
        tPARAMNAME

  prechigh
  nonassoc kQUESTION
  left kAMP
  left kBAR
  nonassoc kARROW
  preclow

  expect 5

rule

  target:
      type_TYPE type eof {
        result = val[1]
      }
    | type_SIGNATURE signatures eof {
        result = val[1]
      }
    | type_METHODTYPE method_type eof {
        result = val[1]
      }

  eof: | tEOF

  signatures:
      { result = [] }
    | signatures signature {
        result = val[0].push(val[1])
      }

  signature:
      type_decl
    | const_decl
    | global_decl
    | interface_decl
    | module_decl
    | class_decl

  start_new_scope: { start_new_variables_scope }
  start_merged_scope: { start_merged_variables_scope }

  annotations:
      { result = [] }
    | tANNOTATION annotations {
        result = val[1].unshift(Annotation.new(string: val[0].value, location: val[0].location))
      }

  class_decl:
      annotations kCLASS start_new_scope class_name module_type_params super_class class_members kEND {
        reset_variable_scope

        location = val[1].location + val[7].location
        location = location.with_children(
          required: {
            keyword: val[1].location,
            name: val[3].location,
            end: val[7].location
          },
          optional: {
            type_params: val[4]&.location,
            lt: val[5]&.location
          }
        )
        result = Declarations::Class.new(
          name: val[3].value,
          type_params: val[4]&.value || Declarations::ModuleTypeParams.empty,
          super_class: val[5]&.value,
          members: val[6],
          annotations: val[0],
          location: location,
          comment: leading_comment(val[0].first&.location || location)
        )
      }

  super_class:
      { result = nil }
    | kLT class_name {
        loc = val[1].location.with_children(
          required: { name: val[1].location },
          optional: { args: nil }
        )
        sup = Declarations::Class::Super.new(name: val[1].value, args: [], location: loc)
        result = LocatedValue.new(value: sup, location: val[0].location)
      }
    | kLT class_name kLBRACKET type_list kRBRACKET {
        loc = (val[1].location + val[4].location).with_children(
          required: { name: val[1].location },
          optional: { args: val[2].location + val[4].location }
        )
        sup = Declarations::Class::Super.new(name: val[1].value, args: val[3], location: loc)
        result = LocatedValue.new(value: sup, location: val[0].location)
      }

  module_decl:
      annotations kMODULE start_new_scope class_name module_type_params colon_module_self_types class_members kEND {
        reset_variable_scope

        colon_loc = val[5].location
        self_loc = val[5].value.yield_self do |params|
          case params.size
          when 0
            nil
          when 1
            params[0].location
          else
            params.first.location + params.last.location
          end
        end

        location = val[1].location + val[7].location
        location = location.with_children(
          required: {
            keyword: val[1].location,
            name: val[3].location,
            end: val[7].location
          },
          optional: {
            type_params: val[4]&.location,
            colon: colon_loc,
            self_types: self_loc
          }
        )
        result = Declarations::Module.new(
          name: val[3].value,
          type_params: val[4]&.value || Declarations::ModuleTypeParams.empty,
          self_types: val[5].value,
          members: val[6],
          annotations: val[0],
          location: location,
          comment: leading_comment(val[0].first&.location || location)
        )
      }
    | annotations kMODULE start_new_scope namespace tUKEYWORD module_self_types class_members kEND {
        reset_variable_scope

        location = val[1].location + val[7].location
        name_loc, colon_loc = split_kw_loc(val[4].location)
        self_loc = case val[5].size
                   when 0
                     nil
                   when 1
                     val[5][0].location
                   else
                     val[5].first.location + val[5].last.location
                   end
        location = location.with_children(
          required: { keyword: val[1].location, name: name_loc, end: val[7].location },
          optional: { colon: colon_loc, type_params: nil, self_types: self_loc }
        )

        result = Declarations::Module.new(
          name: RBS::TypeName.new(name: val[4].value, namespace: val[3]&.value || RBS::Namespace.empty),
          type_params: Declarations::ModuleTypeParams.empty,
          self_types: val[5],
          members: val[6],
          annotations: val[0],
          location: location,
          comment: leading_comment(val[0].first&.location || location)
        )
      }

  colon_module_self_types:
      { result = LocatedValue.new(value: [], location: nil) }
    | kCOLON module_self_types {
        result = LocatedValue.new(value: val[1], location: val[0].location)
      }

  module_self_types:
      module_self_type {
        result = [val[0]]
      }
    | module_self_types kCOMMA module_self_type {
        result = val[0].push(val[2])
      }

  module_self_type:
      qualified_name kLBRACKET type_list kRBRACKET {
        name = val[0].value
        args = val[2]
        location = val[0].location + val[3].location
        location = location.with_children(
          required: { name: val[0].location },
          optional: { args: val[1].location + val[3].location }
        )

        case
        when name.class?
          result = Declarations::Module::Self.new(name: name, args: args, location: location)
        when name.interface?
          result = Declarations::Module::Self.new(name: name, args: args, location: location)
        else
          raise SemanticsError.new("Module self type should be instance or interface", subject: val[0], location: val[0].location)
        end
      }
    | qualified_name {
        name = val[0].value
        args = []
        location = val[0].location.with_children(
          required: { name: val[0].location },
          optional: { args: nil }
        )

        case
        when name.class?
          result = Declarations::Module::Self.new(name: name, args: args, location: location)
        when name.interface?
          result = Declarations::Module::Self.new(name: name, args: args, location: location)
        else
          raise SemanticsError.new("Module self type should be instance or interface", subject: val[0], location: val[0].location)
        end
      }

  class_members:
      { result = [] }
    | class_members class_member {
        result = val[0].push(val[1])
      }

  class_member:
      method_member
    | include_member
    | extend_member
    | prepend_member
    | var_type_member
    | attribute_member
    | kPUBLIC {
        result = Members::Public.new(location: val[0].location)
      }
    | kPRIVATE {
        result = Members::Private.new(location: val[0].location)
      }
    | alias_member
    | signature

  attribute_kind:
      { result = LocatedValue.new(value: :instance, location: nil) }
    | kSELF kDOT { result = LocatedValue.new(value: :singleton, location: val[0].location + val[1].location) }

  attribute_member:
      annotations kATTRREADER attribute_kind keyword type {
        location = val[1].location + val[4].location
        name_loc, colon_loc = split_kw_loc(val[3].location)
        location = location.with_children(
          required: { keyword: val[1].location, name: name_loc, colon: colon_loc },
          optional: { ivar: nil, ivar_name: nil, kind: val[2].location }
        )
        result = Members::AttrReader.new(name: val[3].value,
                                         ivar_name: nil,
                                         type: val[4],
                                         kind: val[2].value,
                                         annotations: val[0],
                                         location: location,
                                         comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kATTRREADER attribute_kind method_name attr_var_opt kCOLON type {
        location = val[1].location + val[6].location
        ivar_loc = val[4]&.location
        case name_value = val[4]&.value
        when LocatedValue
          ivar_name = name_value.value
          ivar_name_loc = name_value.location
        when false
          ivar_name = false
          ivar_name_loc = nil
        else
          ivar_name = nil
          ivar_loc = nil
        end
        location = location.with_children(
          required: { keyword: val[1].location, name: val[3].location, colon: val[5].location },
          optional: { ivar: ivar_loc, ivar_name: ivar_name_loc, kind: val[2].location }
        )
        result = Members::AttrReader.new(name: val[3].value.to_sym,
                                         ivar_name: ivar_name,
                                         type: val[6],
                                         kind: val[2].value,
                                         annotations: val[0],
                                         location: location,
                                         comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kATTRWRITER attribute_kind keyword type {
        location = val[1].location + val[4].location
        name_loc, colon_loc = split_kw_loc(val[3].location)
        location = location.with_children(
          required: { keyword: val[1].location, name: name_loc, colon: colon_loc },
          optional: { ivar: nil, ivar_name: nil, kind: val[2].location }
        )
        result = Members::AttrWriter.new(name: val[3].value,
                                         ivar_name: nil,
                                         kind: val[2].value,
                                         type: val[4],
                                         annotations: val[0],
                                         location: location,
                                         comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kATTRWRITER attribute_kind method_name attr_var_opt kCOLON type {
        location = val[1].location + val[6].location
        ivar_loc = val[4]&.location
        case name_value = val[4]&.value
        when LocatedValue
          ivar_name = name_value.value
          ivar_name_loc = name_value.location
        when false
          ivar_name = false
          ivar_name_loc = nil
        else
          ivar_name = nil
          ivar_loc = nil
        end
        location = location.with_children(
          required: { keyword: val[1].location, name: val[3].location, colon: val[5].location },
          optional: { ivar: ivar_loc, ivar_name: ivar_name_loc, kind: val[2].location }
        )

        result = Members::AttrWriter.new(name: val[3].value.to_sym,
                                         ivar_name: ivar_name,
                                         kind: val[2].value,
                                         type: val[6],
                                         annotations: val[0],
                                         location: location,
                                         comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kATTRACCESSOR attribute_kind keyword type {
        location = val[1].location + val[4].location
        name_loc, colon_loc = split_kw_loc(val[3].location)
        location = location.with_children(
          required: { keyword: val[1].location, name: name_loc, colon: colon_loc },
          optional: { ivar: nil, ivar_name: nil, kind: val[2].location }
        )

        result = Members::AttrAccessor.new(name: val[3].value,
                                           ivar_name: nil,
                                           kind: val[2].value,
                                           type: val[4],
                                           annotations: val[0],
                                           location: location,
                                           comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kATTRACCESSOR attribute_kind method_name attr_var_opt kCOLON type {
        location = val[1].location + val[6].location
        ivar_loc = val[4]&.location
        case name_value = val[4]&.value
        when LocatedValue
          ivar_name = name_value.value
          ivar_name_loc = name_value.location
        when false
          ivar_name = false
          ivar_name_loc = nil
        else
          ivar_name = nil
          ivar_loc = nil
        end
        location = location.with_children(
          required: { keyword: val[1].location, name: val[3].location, colon: val[5].location },
          optional: { ivar: ivar_loc, ivar_name: ivar_name_loc, kind: val[2].location }
        )

        result = Members::AttrAccessor.new(name: val[3].value.to_sym,
                                           ivar_name: ivar_name,
                                           kind: val[2].value,
                                           type: val[6],
                                           annotations: val[0],
                                           location: location,
                                           comment: leading_comment(val[0].first&.location || location))
      }

  attr_var_opt:
      { result = nil }
    | kLPAREN kRPAREN { result = LocatedValue.new(value: false, location: val[0].location + val[1].location) }
    | kLPAREN tIVAR kRPAREN {
        result = LocatedValue.new(
          value: val[1],
          location: val[0].location + val[2].location
        )
      }

  var_type_member:
      tIVAR kCOLON type {
        location = (val[0].location + val[2].location).with_children(
          required: { name: val[0].location, colon: val[1].location },
          optional: { kind: nil }
        )

        result = Members::InstanceVariable.new(
          name: val[0].value,
          type: val[2],
          location: location,
          comment: leading_comment(location)
        )
      }
    | tCLASSVAR kCOLON type {
        type = val[2]

        if type.is_a?(Types::Variable)
          type = Types::ClassInstance.new(
            name: TypeName.new(name: type.name, namespace: Namespace.empty),
            args: [],
            location: type.location
          )
        end

        location = (val[0].location + val[2].location).with_children(
          required: { name: val[0].location, colon: val[1].location },
          optional: { kind: nil }
        )

        result = Members::ClassVariable.new(
          name: val[0].value,
          type: type,
          location: location,
          comment: leading_comment(location)
        )
      }
    | kSELF kDOT tIVAR kCOLON type {
      type = val[4]

      if type.is_a?(Types::Variable)
        type = Types::ClassInstance.new(
          name: TypeName.new(name: type.name, namespace: Namespace.empty),
          args: [],
          location: type.location
        )
      end

      location = (val[0].location + val[4].location).with_children(
        required: { name: val[2].location, colon: val[3].location },
        optional: { kind: val[0].location + val[1].location }
      )

      result = Members::ClassInstanceVariable.new(
        name: val[2].value,
        type: type,
        location: location,
        comment: leading_comment(location)
      )
    }

  interface_decl:
      annotations kINTERFACE start_new_scope interface_name module_type_params interface_members kEND {
        reset_variable_scope

        location = val[1].location + val[6].location
        location = location.with_children(
          required: { keyword: val[1].location, name: val[3].location, end: val[6].location },
          optional: { type_params: val[4]&.location }
        )
        result = Declarations::Interface.new(
          name: val[3].value,
          type_params: val[4]&.value || Declarations::ModuleTypeParams.empty,
          members: val[5],
          annotations: val[0],
          location: location,
          comment: leading_comment(val[0].first&.location || location)
        )
      }

  interface_members:
      { result = [] }
    | interface_members interface_member {
        result = val[0].push(val[1])
      }

  interface_member:
      method_member {
        unless val[0].kind == :instance
          raise SemanticsError.new("Interface cannot have singleton method", subject: val[0], location: val[0].location)
        end

        if val[0].types.last == :super
          raise SemanticsError.new("Interface method cannot have `super` type", subject: val[0], location: val[0].location)
        end

        result = val[0]
      }
    | include_member {
        unless val[0].name.interface?
          raise SemanticsError.new("Interface should include an interface", subject: val[0], location: val[0].location)
        end

        result = val[0]
      }
    | alias_member

  include_member:
      annotations kINCLUDE qualified_name {
        if val[2].value.alias?
          raise SemanticsError.new("Should include module or interface", subject: val[2].value, location: val[2].location)
        end

        location = (val[1].location + val[2].location).with_children(
          required: { keyword: val[1].location, name: val[2].location },
          optional: { args: nil }
        )

        result = Members::Include.new(name: val[2].value,
                                      args: [],
                                      annotations: val[0],
                                      location: location,
                                      comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kINCLUDE qualified_name kLBRACKET type_list kRBRACKET {
        if val[2].value.alias?
          raise SemanticsError.new("Should include module or interface", subject: val[2].value, location: val[2].location)
        end

        location = (val[1].location + val[5].location).with_children(
          required: { keyword: val[1].location, name: val[2].location },
          optional: { args: val[3].location + val[5].location }
        )

        result = Members::Include.new(name: val[2].value,
                                      args: val[4],
                                      annotations: val[0],
                                      location: location,
                                      comment: leading_comment(val[0].first&.location || location))
      }

  extend_member:
      annotations kEXTEND qualified_name {
        if val[2].value.alias?
          raise SemanticsError.new("Should extend module or interface", subject: val[2].value, location: val[2].location)
        end

        location = (val[1].location + val[2].location).with_children(
          required: { keyword: val[1].location, name: val[2].location },
          optional: { args: nil }
        )

        result = Members::Extend.new(name: val[2].value,
                                     args: [],
                                     annotations: val[0],
                                     location: location,
                                     comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kEXTEND qualified_name kLBRACKET type_list kRBRACKET {
        if val[2].value.alias?
          raise SemanticsError.new("Should extend module or interface", subject: val[2].value, location: val[2].location)
        end

        location = (val[1].location + val[5].location).with_children(
          required: { keyword: val[1].location, name: val[2].location },
          optional: { args: val[3].location + val[5].location }
        )

        result = Members::Extend.new(name: val[2].value,
                                     args: val[4],
                                     annotations: val[0],
                                     location: location,
                                     comment: leading_comment(val[0].first&.location || location))
    }

  prepend_member:
      annotations kPREPEND qualified_name {
        unless val[2].value.class?
          raise SemanticsError.new("Should prepend module", subject: val[2].value, location: val[2].location)
        end

        location = (val[1].location + val[2].location).with_children(
          required: { keyword: val[1].location, name: val[2].location },
          optional: { args: nil }
        )

        result = Members::Prepend.new(name: val[2].value,
                                      args: [],
                                      annotations: val[0],
                                      location: location,
                                      comment: leading_comment(val[0].first&.location || location))
      }
    | annotations kPREPEND qualified_name kLBRACKET type_list kRBRACKET {
        unless val[2].value.class?
          raise SemanticsError.new("Should prepend module", subject: val[2].value, location: val[2].location)
        end

        location = (val[1].location + val[5].location).with_children(
          required: { keyword: val[1].location, name: val[2].location },
          optional: { args: val[3].location + val[5].location }
        )

        result = Members::Prepend.new(name: val[2].value,
                                      args: val[4],
                                      annotations: val[0],
                                      location: location,
                                      comment: leading_comment(val[0].first&.location || location))
      }

  overload:
        { result = nil }
    | kOVERLOAD {
        RBS.logger.warn "`overload def` syntax is deprecated. Use `...` syntax instead."
        result = val[0]
      }

  method_member:
      annotations attributes overload kDEF method_kind def_name method_types {
        location = val[3].location + val[6].last.location

        required_children = { keyword: val[3].location, name: val[5].location }
        optional_children = { kind: nil, overload: nil }

        if val[4]
          kind = val[4].value
          optional_children[:kind] = val[4].location
        else
          kind = :instance
        end

        last_type = val[6].last
        if last_type.is_a?(LocatedValue) && last_type.value == :dot3
          overload = true
          optional_children[:overload] = last_type.location
          val[6].pop
        else
          overload = false
        end

        result = Members::MethodDefinition.new(
          name: val[5].value,
          kind: kind,
          types: val[6],
          annotations: val[0],
          location: location.with_children(required: required_children, optional: optional_children),
          comment: leading_comment(val[0].first&.location || val[2]&.location || val[3].location),
          overload: overload || !!val[2]
        )
      }

  attributes:
    | attributes kINCOMPATIBLE {
        RBS.logger.warn "`incompatible` method attribute is deprecated and ignored."
      }

  method_kind:
      { result = nil }
    | kSELF kDOT { result = LocatedValue.new(value: :singleton, location: val[0].location + val[1].location) }
    | kSELFQ kDOT { result = LocatedValue.new(value: :singleton_instance, location: val[0].location + val[1].location) }

  method_types:
      method_type { result = [val[0]] }
    | kDOT3 { result = [LocatedValue.new(value: :dot3, location: val[0].location)] }
    | method_type kBAR method_types {
        result = val[2].unshift(val[0])
      }

  method_type:
      start_merged_scope type_params proc_type {
        reset_variable_scope

        location = (val[1] || val[2]).location + val[2].location
        type_params = val[1]&.value || []

        type, block = val[2].value

        result = MethodType.new(type_params: type_params,
                                type: type,
                                block: block,
                                location: location)
      }

  params_opt:
      { result = nil }
    | kLPAREN params kRPAREN {
        result = LocatedValue.new(value: val[1], location: val[0].location + val[2].location)
      }

  block:
      kLBRACE simple_function_type kRBRACE {
        block = Types::Block.new(type: val[1].value, required: true)
        result = LocatedValue.new(value: block, location: val[0].location + val[2].location)
      }
    | kQUESTION kLBRACE simple_function_type kRBRACE {
        block = Types::Block.new(type: val[2].value, required: false)
        result = LocatedValue.new(value: block, location: val[0].location + val[3].location)
      }

  def_name:
      keyword {
        loc = val[0].location

        result = LocatedValue.new(
          value: val[0].value,
          location: Location.new(buffer: loc.buffer, start_pos: loc.start_pos, end_pos: loc.end_pos - 1)
        )
      }
    | method_name kCOLON {
        result = LocatedValue.new(value: val[0].value.to_sym,
                                  location: val[0].location + val[1].location)
      }

  method_name:
      tOPERATOR
    | kAMP | kHAT | kSTAR | kLT | kEXCLAMATION | kSTAR2 | kBAR
    | method_name0
    | method_name0 kQUESTION {
        unless val[0].location.pred?(val[1].location)
          raise SyntaxError.new(token_str: "kQUESTION", error_value: val[1])
        end

        result = LocatedValue.new(value: "#{val[0].value}?",
                                  location: val[0].location + val[1].location)
      }
    | method_name0 kEXCLAMATION {
        unless val[0].location.pred?(val[1].location)
          raise SyntaxError.new(token_str: "kEXCLAMATION", error_value: val[1])
        end

        result = LocatedValue.new(value: "#{val[0].value}!",
                                  location: val[0].location + val[1].location)
      }
    | tQUOTEDMETHOD
    | tQUOTEDIDENT
    | tWRITE_ATTR

  method_name0: tUIDENT | tLIDENT | tINTERFACEIDENT | tUNDERSCOREIDENT | identifier_keywords

  identifier_keywords:
      kCLASS | kVOID | kNIL | kTRUE | kFALSE | kANY | kUNTYPED | kTOP | kBOT | kINSTANCE | kBOOL | kSINGLETON
    | kTYPE | kMODULE | kPRIVATE | kPUBLIC | kEND | kINCLUDE | kEXTEND | kPREPEND
    | kATTRREADER | kATTRACCESSOR | kATTRWRITER | kDEF | kEXTENSION | kSELF | kINCOMPATIBLE
    | kUNCHECKED | kINTERFACE | kALIAS | kOUT | kIN | kOVERLOAD

  module_type_params:
      { result = nil }
    | kLBRACKET module_type_params0 kRBRACKET {
        val[1].each {|p| insert_bound_variable(p.name) }

        result = LocatedValue.new(value: val[1], location: val[0].location + val[2].location)
      }

  module_type_params0:
      module_type_param {
        result = Declarations::ModuleTypeParams.new()
        result.add(val[0])
      }
    | module_type_params0 kCOMMA module_type_param {
        result = val[0].add(val[2])
      }

  module_type_param:
      type_param_check type_param_variance tUIDENT {
        loc = case
              when l0 = val[0].location
                l0 + val[2].location
              when l1 = val[1].location
                l1 + val[2].location
              else
                val[2].location
              end
        loc = loc.with_children(
          required: { name: val[2].location },
          optional: { variance: val[1].location, unchecked: val[0].location }
        )
        result = Declarations::ModuleTypeParams::TypeParam.new(
          name: val[2].value.to_sym,
          variance: val[1].value,
          skip_validation: val[0].value,
          location: loc
        )
      }

  type_param_variance:
      { result = LocatedValue.new(value: :invariant, location: nil) }
    | kOUT { result = LocatedValue.new(value: :covariant, location: val[0].location) }
    | kIN { result = LocatedValue.new(value: :contravariant, location: val[0].location) }

  type_param_check:
      { result = LocatedValue.new(value: false, location: nil) }
    | kUNCHECKED { result = LocatedValue.new(value: true, location: val[0].location) }

  type_params:
      { result = nil }
    | kLBRACKET type_params0 kRBRACKET {
        val[1].each {|var| insert_bound_variable(var) }

        result = LocatedValue.new(value: val[1],
                                  location: val[0].location + val[2].location)
      }

  type_params0:
      tUIDENT {
        result = [val[0].value.to_sym]
      }
    | type_params0 kCOMMA tUIDENT {
        result = val[0].push(val[2].value.to_sym)
      }

  alias_member:
      annotations kALIAS method_name method_name {
        location = val[1].location + val[3].location
        location = location.with_children(
          required: { keyword: val[1].location, new_name: val[2].location, old_name: val[3].location },
          optional: { new_kind: nil, old_kind: nil }
        )
        result = Members::Alias.new(
          new_name: val[2].value.to_sym,
          old_name: val[3].value.to_sym,
          kind: :instance,
          annotations: val[0],
          location: location,
          comment: leading_comment(val[0].first&.location || location)
        )
      }
    | annotations kALIAS kSELF kDOT method_name kSELF kDOT method_name {
        location = val[1].location + val[7].location
        location = location.with_children(
          required: { keyword: val[1].location, new_name: val[4].location, old_name: val[7].location },
          optional: {
            new_kind: val[2].location + val[3].location,
            old_kind: val[5].location + val[6].location
          }
        )
        result = Members::Alias.new(
          new_name: val[4].value.to_sym,
          old_name: val[7].value.to_sym,
          kind: :singleton,
          annotations: val[0],
          location: location,
          comment: leading_comment(val[0].first&.location || location)
        )
      }

  type_decl:
      annotations kTYPE type_alias_name kEQ type {
        location = val[1].location + val[4].location
        location = location.with_children(
          required: { keyword: val[1].location, name: val[2].location, eq: val[3].location }
        )
        result = Declarations::Alias.new(
          name: val[2].value,
          type: val[4],
          annotations: val[0],
          location: location,
          comment: leading_comment(val[0].first&.location || location)
        )
      }

  const_decl:
      class_name kCOLON type {
        location = val[0].location + val[2].location
        location = location.with_children(
          required: { name: val[0].location, colon: val[1].location }
        )
        result = Declarations::Constant.new(name: val[0].value,
                                            type: val[2],
                                            location: location,
                                            comment: leading_comment(location))
      }
    | namespace tUKEYWORD type {
        location = (val[0] || val[1]).location + val[2].location

        lhs_loc = (val[0] || val[1]).location + val[1].location
        name_loc, colon_loc = split_kw_loc(lhs_loc)
        location = location.with_children(
          required: { name: name_loc, colon: colon_loc }
        )

        name = TypeName.new(name: val[1].value, namespace: val[0]&.value || Namespace.empty)
        result = Declarations::Constant.new(name: name,
                                            type: val[2],
                                            location: location,
                                            comment: leading_comment(location))
      }

  global_decl:
      tGLOBALIDENT kCOLON type {
        location = val[0].location + val[2].location
        location = location.with_children(
          required: { name: val[0].location, colon: val[1].location }
        )
        result = Declarations::Global.new(name: val[0].value.to_sym,
                                          type: val[2],
                                          location: location,
                                          comment: leading_comment(location))
      }

  type:
      simple_type
    | type kBAR type {
        types = case l = val[0]
                when Types::Union
                  l.types + [val[2]]
                else
                  [l, val[2]]
                end

        result = Types::Union.new(types: types, location: val[0].location + val[2].location)
      }
    | type kAMP type {
        types = case l = val[0]
                when Types::Intersection
                  l.types + [val[2]]
                else
                  [l, val[2]]
                end

        result = Types::Intersection.new(types: types,
                                         location: val[0].location + val[2].location)
      }

  simple_type:
      kVOID {
        result = Types::Bases::Void.new(location: val[0].location)
      }
    | kANY {
        RBS.logger.warn "`any` type is deprecated. Use `untyped` instead. (#{val[0].location.to_s})"
        result = Types::Bases::Any.new(location: val[0].location)
      }
    | kUNTYPED {
        result = Types::Bases::Any.new(location: val[0].location)
      }
    | kBOOL {
        result = Types::Bases::Bool.new(location: val[0].location)
      }
    | kNIL {
        result = Types::Bases::Nil.new(location: val[0].location)
      }
    | kTOP {
        result = Types::Bases::Top.new(location: val[0].location)
      }
    | kBOT {
        result = Types::Bases::Bottom.new(location: val[0].location)
      }
    | kSELF {
        result = Types::Bases::Self.new(location: val[0].location)
      }
    | kSELFQ {
        result = Types::Optional.new(type: Types::Bases::Self.new(location: val[0].location),
                                     location: val[0].location)
      }
    | kINSTANCE {
        result = Types::Bases::Instance.new(location: val[0].location)
      }
    | kCLASS {
        result = Types::Bases::Class.new(location: val[0].location)
      }
    | kTRUE {
        result = Types::Literal.new(literal: true, location: val[0].location)
      }
    | kFALSE {
        result = Types::Literal.new(literal: false, location: val[0].location)
      }
    | tINTEGER {
        result = Types::Literal.new(literal: val[0].value, location: val[0].location)
      }
    | tSTRING {
        result = Types::Literal.new(literal: val[0].value, location: val[0].location)
      }
    | tSYMBOL {
        result = Types::Literal.new(literal: val[0].value, location: val[0].location)
      }
    | qualified_name {
        name = val[0].value
        args = []
        location = val[0].location

        case
        when name.class?
          if is_bound_variable?(name.name)
            result = Types::Variable.new(name: name.name, location: location)
          else
            location = location.with_children(
              required: { name: val[0].location },
              optional: { args: nil }
            )
            result = Types::ClassInstance.new(name: name, args: args, location: location)
          end
        when name.alias?
          location = location.with_children(
            required: { name: val[0].location },
            optional: { args: nil }
          )
          result = Types::Alias.new(name: name, location: location)
        when name.interface?
          location = location.with_children(
            required: { name: val[0].location },
            optional: { args: nil }
          )
          result = Types::Interface.new(name: name, args: args, location: location)
        end
      }
    | qualified_name kLBRACKET type_list kRBRACKET {
        name = val[0].value
        args = val[2]
        location = val[0].location + val[3].location

        case
        when name.class?
          if is_bound_variable?(name.name)
            raise SemanticsError.new("#{name.name} is type variable and cannot be applied", subject: name, location: location)
          end
          location = location.with_children(
            required: { name: val[0].location },
            optional: { args: val[1].location + val[3].location }
          )
          result = Types::ClassInstance.new(name: name, args: args, location: location)
        when name.interface?
          location = location.with_children(
            required: { name: val[0].location },
            optional: { args: val[1].location + val[3].location }
          )
          result = Types::Interface.new(name: name, args: args, location: location)
        else
          raise SyntaxError.new(token_str: "kLBRACKET", error_value: val[1])
        end
      }
    | kLBRACKET kRBRACKET {
        location = val[0].location + val[1].location
        result = Types::Tuple.new(types: [], location: location)
      }
    | kLBRACKET type_list comma_opt kRBRACKET {
        location = val[0].location + val[3].location
        types = val[1]
        result = Types::Tuple.new(types: types, location: location)
      }
    | kLPAREN type kRPAREN {
        type = val[1].dup
        type.instance_eval do
          @location = val[0].location + val[2].location
        end
        result = type
      }
    | kSINGLETON kLPAREN class_name kRPAREN {
        location = val[0].location + val[3].location
        location = location.with_children(
          required: { name: val[2].location }
        )
        result = Types::ClassSingleton.new(name: val[2].value, location: location)
      }
    | kHAT proc_type {
        type, block = val[1].value
        result = Types::Proc.new(type: type, block: block, location: val[0].location + val[1].location)
      }
    | simple_type kQUESTION {
        result = Types::Optional.new(type: val[0], location: val[0].location + val[1].location)
      }
    | record_type

  type_list:
      type {
        result = [val[0]]
      }
    | type_list kCOMMA type {
        result = val[0] + [val[2]]
      }

  record_type:
      kLBRACE record_fields comma_opt kRBRACE {
        result = Types::Record.new(
          fields: val[1],
          location: val[0].location + val[3].location
        )
      }

  record_fields:
      record_field {
        result = val[0]
      }
    | record_fields kCOMMA record_field {
        result = val[0].merge!(val[2])
      }

  record_field:
      tSYMBOL kFATARROW type {
        result = { val[0].value => val[2] }
      }
    | tSTRING kFATARROW type {
        result = { val[0].value => val[2] }
      }
    | tINTEGER kFATARROW type {
        result = { val[0].value => val[2] }
      }
    | keyword type {
        result = { val[0].value => val[1] }
      }
    | identifier_keywords kCOLON type {
        result = { val[0].value => val[2] }
      }
    | tQUOTEDIDENT kCOLON type {
        result = { val[0].value => val[2] }
      }
    | tQUOTEDMETHOD kCOLON type {
        result = { val[0].value => val[2] }
      }

  keyword_name:
      keyword
    | identifier_keywords kCOLON {
        result = val[0]
      }

  keyword: tLKEYWORD | tUKEYWORD | tLKEYWORD_Q_E | tUKEYWORD_Q_E

  proc_type:
      params_opt block kARROW simple_type {
        location = (val[0] || val[1] || val[2]).location + val[3].location

        params = val[0]&.value || [[], [], nil, [], {}, {}, nil]

        type = Types::Function.new(
          required_positionals: params[0],
          optional_positionals: params[1],
          rest_positionals: params[2],
          trailing_positionals: params[3],
          required_keywords: params[4],
          optional_keywords: params[5],
          rest_keywords: params[6],
          return_type: val[3]
        )

        block = val[1].value

        result = LocatedValue.new(value: [type, block], location: location)
      }
    | simple_function_type {
        result = LocatedValue.new(value: [val[0].value, nil], location: val[0].location)
      }

  simple_function_type:
      kLPAREN params kRPAREN kARROW simple_type {
        location = val[0].location + val[4].location
        type = Types::Function.new(
          required_positionals: val[1][0],
          optional_positionals: val[1][1],
          rest_positionals: val[1][2],
          trailing_positionals: val[1][3],
          required_keywords: val[1][4],
          optional_keywords: val[1][5],
          rest_keywords: val[1][6],
          return_type: val[4],
        )

        result = LocatedValue.new(value: type, location: location)
      }
    | kARROW simple_type {
        location = val[0].location + val[1].location
        type = Types::Function.new(
          required_positionals: [],
          optional_positionals: [],
          rest_positionals: nil,
          trailing_positionals: [],
          required_keywords: {},
          optional_keywords: {},
          rest_keywords: nil,
          return_type: val[1]
        )

        result = LocatedValue.new(value: type, location: location)
      }

  params:
      required_positional kCOMMA params {
        result = val[2]
        result[0].unshift(val[0])
      }
    | required_positional {
        result = empty_params_result
        result[0].unshift(val[0])
      }
    | optional_positional_params

  optional_positional_params:
      optional_positional kCOMMA optional_positional_params {
        result = val[2]
        result[1].unshift(val[0])
      }
    | optional_positional {
        result = empty_params_result
        result[1].unshift(val[0])
      }
    | rest_positional_param

  rest_positional_param:
      rest_positional kCOMMA trailing_positional_params {
        result = val[2]
        result[2] = val[0]
      }
    | rest_positional {
        result = empty_params_result
        result[2] = val[0]
      }
    | trailing_positional_params

  trailing_positional_params:
      required_positional kCOMMA trailing_positional_params {
        result = val[2]
        result[3].unshift(val[0])
      }
    | required_positional {
        result = empty_params_result
        result[3].unshift(val[0])
      }
    | keyword_params

  keyword_params:
      {
        result = empty_params_result
      }
    | required_keyword kCOMMA keyword_params {
        result = val[2]
        result[4].merge!(val[0])
      }
    | required_keyword {
        result = empty_params_result
        result[4].merge!(val[0])
      }
    | optional_keyword kCOMMA keyword_params {
        result = val[2]
        result[5].merge!(val[0])
      }
    | optional_keyword {
        result = empty_params_result
        result[5].merge!(val[0])
      }
    | rest_keyword {
        result = empty_params_result
        result[6] = val[0]
      }

  required_positional:
      type var_name_opt {
        loc = val[0].location
        if var_name = val[1]
          loc = loc + var_name.location
        end
        loc = loc.with_children(optional: { name: var_name&.location })

        result = Types::Function::Param.new(
          type: val[0],
          name: var_name&.value&.to_sym,
          location: loc
        )
      }

  optional_positional:
      kQUESTION type var_name_opt {
        loc = val[0].location + val[1].location
        if var_name = val[2]
          loc = loc + var_name.location
        end
        loc = loc.with_children(optional: { name: var_name&.location })

        result = Types::Function::Param.new(
          type: val[1],
          name: val[2]&.value&.to_sym,
          location: loc
        )
      }

  rest_positional:
      kSTAR type var_name_opt {
        loc = val[0].location + val[1].location
        if var_name = val[2]
          loc = loc + var_name.location
        end
        loc = loc.with_children(optional: { name: var_name&.location })

        result = Types::Function::Param.new(
          type: val[1],
          name: val[2]&.value&.to_sym,
          location: loc
        )
      }

  required_keyword:
      keyword_name type var_name_opt {
        loc = val[0].location + val[1].location
        if var_name = val[2]
          loc = loc + var_name.location
        end

        loc = loc.with_children(optional: { name: var_name&.location })

        param = Types::Function::Param.new(
          type: val[1],
          name: val[2]&.value&.to_sym,
          location: loc
        )
        result = { val[0].value => param }
      }

  optional_keyword:
      kQUESTION keyword_name type var_name_opt {
        loc = val[0].location + val[2].location
        if var_name = val[3]
          loc = loc + var_name.location
        end

        loc = loc.with_children(optional: { name: var_name&.location })

        param = Types::Function::Param.new(
          type: val[2],
          name: val[3]&.value&.to_sym,
          location: loc
        )
        result = { val[1].value => param }
      }

  rest_keyword:
      kSTAR2 type var_name_opt {
        loc = val[0].location + val[1].location
        if var_name = val[2]
          loc = loc + var_name.location
        end

        loc = loc.with_children(optional: { name: var_name&.location })

        result = Types::Function::Param.new(
          type: val[1],
          name: val[2]&.value&.to_sym,
          location: loc
        )
      }

  var_name_opt:
    | tLIDENT | tINTERFACEIDENT | tQUOTEDIDENT | tUNDERSCOREIDENT | tPARAMNAME

  qualified_name:
      namespace simple_name {
        namespace = val[0]&.value || Namespace.empty
        name = val[1].value.to_sym
        type_name = TypeName.new(namespace: namespace, name: name)
        location = (loc0 = val[0]&.location) ? loc0 + val[1].location : val[1].location
        result = LocatedValue.new(value: type_name, location: location)
      }

  simple_name:
      tUIDENT | tLIDENT | tINTERFACEIDENT

  interface_name:
      namespace tINTERFACEIDENT {
        namespace = val[0]&.value || Namespace.empty
        name = val[1].value.to_sym
        type_name = TypeName.new(namespace: namespace, name: name)
        location = (loc0 = val[0]&.location) ? loc0 + val[1].location : val[1].location
        result = LocatedValue.new(value: type_name, location: location)
      }

  class_name:
      namespace tUIDENT {
        namespace = val[0]&.value || Namespace.empty
        name = val[1].value.to_sym
        type_name = TypeName.new(namespace: namespace, name: name)
        location = (loc0 = val[0]&.location) ? loc0 + val[1].location : val[1].location
        result = LocatedValue.new(value: type_name, location: location)
      }

  type_alias_name:
      namespace tLIDENT {
        namespace = val[0]&.value || Namespace.empty
        name = val[1].value.to_sym
        type_name = TypeName.new(namespace: namespace, name: name)
        location = (loc0 = val[0]&.location) ? loc0 + val[1].location : val[1].location
        result = LocatedValue.new(value: type_name, location: location)
      }


  namespace:
      {
        result = nil
      }
    | kCOLON2 {
        result = LocatedValue.new(value: Namespace.root, location: val[0].location)
      }
    | kCOLON2 tNAMESPACE {
        namespace = Namespace.parse(val[1].value).absolute!
        result = LocatedValue.new(value: namespace, location: val[0].location + val[1].location)
      }
    | tNAMESPACE {
        namespace = Namespace.parse(val[0].value)
        result = LocatedValue.new(value: namespace, location: val[0].location)
      }

  comma_opt:
      kCOMMA | # empty

end

---- inner

Types = RBS::Types
Namespace = RBS::Namespace
TypeName = RBS::TypeName
Declarations = RBS::AST::Declarations
Members = RBS::AST::Members
MethodType = RBS::MethodType
Annotation = RBS::AST::Annotation

class LocatedValue
  attr_reader :location
  attr_reader :value

  def initialize(location:, value:)
    @location = location
    @value = value
  end
end

attr_reader :input
attr_reader :buffer
attr_reader :eof_re

def initialize(type, buffer:, eof_re:)
  super()
  @type = type
  @buffer = buffer
  @input = CharScanner.new(buffer.content)
  @eof_re = eof_re
  @eof = false
  @bound_variables_stack = []
  @comments = {}
  @ascii_only = buffer.content.ascii_only?
end

def start_merged_variables_scope
  set = @bound_variables_stack.last&.dup || Set.new
  @bound_variables_stack.push set
end

def start_new_variables_scope
  @bound_variables_stack.push Set.new
end

def reset_variable_scope
  @bound_variables_stack.pop
end

def insert_bound_variable(var)
  @bound_variables_stack.last << var
end

def is_bound_variable?(var)
  (@bound_variables_stack.last || Set.new).member?(var)
end

def self.parse_signature(input, eof_re: nil)
  case input
  when RBS::Buffer
    buffer = input
  else
    buffer = RBS::Buffer.new(name: nil, content: input.to_s)
  end

  self.new(:SIGNATURE, buffer: buffer, eof_re: eof_re).do_parse
end

def self.parse_type(input, variables: [], eof_re: nil)
  case input
  when RBS::Buffer
    buffer = input
  else
    buffer = RBS::Buffer.new(name: nil, content: input.to_s)
  end

  self.new(:TYPE, buffer: buffer, eof_re: eof_re).yield_self do |parser|
    parser.start_new_variables_scope

    variables.each do |var|
      parser.insert_bound_variable var
    end

    parser.do_parse
  ensure
    parser.reset_variable_scope
  end
end

def self.parse_method_type(input, variables: [], eof_re: nil)
  case input
  when RBS::Buffer
    buffer = input
  else
    buffer = RBS::Buffer.new(name: nil, content: input.to_s)
  end

  self.new(:METHODTYPE, buffer: buffer, eof_re: eof_re).yield_self do |parser|
    parser.start_new_variables_scope

    variables.each do |var|
      parser.insert_bound_variable var
    end

    parser.do_parse
  ensure
    parser.reset_variable_scope
  end
end

def leading_comment(location)
  @comments[location.start_line-1]
end

def push_comment(string, location)
  if (comment = leading_comment(location)) && comment.location.start_column == location.start_column
    comment.concat(string: "#{string}\n", location: location)
    @comments[comment.location.end_line] = comment
  else
    new_comment = AST::Comment.new(string: "#{string}\n", location: location)
    @comments[new_comment.location.end_line] = new_comment
  end
end

def new_token(type, value = input.matched)
  charpos = charpos(input)
  matched = input.matched

  if matched
    start_index = charpos - matched.size
    end_index = charpos

    location = RBS::Location.new(buffer: buffer,
                                 start_pos: start_index,
                                 end_pos: end_index)

    [type, LocatedValue.new(location: location, value: value)]
  else
    # scanner hasn't matched yet
    [false, nil]
  end
end

def charpos(scanner)
  scanner.charpos
end

def empty_params_result
  [
    [],
    [],
    nil,
    [],
    {},
    {},
    nil
  ]
end

KEYWORDS = {
  "class" => :kCLASS,
  "type" => :kTYPE,
  "def" => :kDEF,
  "self" => :kSELF,
  "void" => :kVOID,
  "any" => :kANY,
  "untyped" => :kUNTYPED,
  "top" => :kTOP,
  "bot" => :kBOT,
  "instance" => :kINSTANCE,
  "bool" => :kBOOL,
  "nil" => :kNIL,
  "true" => :kTRUE,
  "false" => :kFALSE,
  "singleton" => :kSINGLETON,
  "interface" => :kINTERFACE,
  "end" => :kEND,
  "include" => :kINCLUDE,
  "extend" => :kEXTEND,
  "prepend" => :kPREPEND,
  "module" => :kMODULE,
  "attr_reader" => :kATTRREADER,
  "attr_writer" => :kATTRWRITER,
  "attr_accessor" => :kATTRACCESSOR,
  "public" => :kPUBLIC,
  "private" => :kPRIVATE,
  "alias" => :kALIAS,
  "extension" => :kEXTENSION,
  "incompatible" => :kINCOMPATIBLE,
  "unchecked" => :kUNCHECKED,
  "overload" => :kOVERLOAD,
  "out" => :kOUT,
  "in" => :kIN,
}
KEYWORDS_RE = /#{Regexp.union(*KEYWORDS.keys)}\b/

PUNCTS = {
  "===" => :tOPERATOR,
  "==" => :tOPERATOR,
  "=~" => :tOPERATOR,
  "!~" => :tOPERATOR,
  "!=" => :tOPERATOR,
  ">=" => :tOPERATOR,
  "<<" => :tOPERATOR,
  "<=>" => :tOPERATOR,
  "<=" => :tOPERATOR,
  ">>" => :tOPERATOR,
  ">" => :tOPERATOR,
  "~" => :tOPERATOR,
  "+@" => :tOPERATOR,
  "+" => :tOPERATOR,
  "[]=" => :tOPERATOR,
  "[]" => :tOPERATOR,
  "::" => :kCOLON2,
  ":" => :kCOLON,
  "(" => :kLPAREN,
  ")" => :kRPAREN,
  "[" => :kLBRACKET,
  "]" => :kRBRACKET,
  "{" => :kLBRACE,
  "}" => :kRBRACE,
  "," => :kCOMMA,
  "|" => :kBAR,
  "&" => :kAMP,
  "^" => :kHAT,
  "->" => :kARROW,
  "=>" => :kFATARROW,
  "=" => :kEQ,
  "?" => :kQUESTION,
  "!" => :kEXCLAMATION,
  "**" => :kSTAR2,
  "*" => :kSTAR,
  "..." => :kDOT3,
  "." => :kDOT,
  "<" => :kLT,
  "-@" => :tOPERATOR,
  "-" => :tOPERATOR,
  "/" => :tOPERATOR,
  "`" => :tOPERATOR,
  "%" => :tOPERATOR,
}
PUNCTS_RE = Regexp.union(*PUNCTS.keys)

ANNOTATION_RE = Regexp.union(/%a\{.*?\}/,
                             /%a\[.*?\]/,
                             /%a\(.*?\)/,
                             /%a\<.*?\>/,
                             /%a\|.*?\|/)

escape_sequences = %w[a b e f n r s t v "].map { |l| "\\\\#{l}" }
DBL_QUOTE_STR_ESCAPE_SEQUENCES_RE = /(#{escape_sequences.join("|")})/

def next_token
  if @type
    type = @type
    @type = nil
    return [:"type_#{type}", nil]
  end

  return new_token(false, '') if @eof

  while true
    return new_token(false, '') if input.eos?

    case
    when input.scan(/\s+/)
      # skip
    when input.scan(/#(( *)|( ?(?<string>.*)))\n/)
      charpos = charpos(input)
      start_index = charpos - input.matched.size
      end_index = charpos-1

      location = RBS::Location.new(buffer: buffer,
                                               start_pos: start_index,
                                               end_pos: end_index)

      push_comment input[:string] || "", location
    else
      break
    end
  end

  case
  when eof_re && input.scan(eof_re)
    @eof = true
    [:tEOF, input.matched]
  when input.scan(/`[a-zA-Z_]\w*`/)
    s = input.matched.yield_self {|s| s[1, s.length-2] }
    new_token(:tQUOTEDIDENT, s)
  when input.scan(/`(\\`|[^` :])+`/)
    s = input.matched.yield_self {|s| s[1, s.length-2] }.gsub(/\\`/, '`')
    new_token(:tQUOTEDMETHOD, s)
  when input.scan(ANNOTATION_RE)
    s = input.matched.yield_self {|s| s[3, s.length-4] }.strip
    new_token(:tANNOTATION, s)
  when input.scan(/self\?/)
    new_token(:kSELFQ, "self?")
  when input.scan(/(([a-zA-Z]\w*)|(_\w+))=/)
    new_token(:tWRITE_ATTR)
  when input.scan(KEYWORDS_RE)
    new_token(KEYWORDS[input.matched], input.matched.to_sym)
  when input.scan(/:((@{,2}|\$)?\w+(\?|\!)?|[|&\/%~`^]|<=>|={2,3}|=~|[<>]{2}|[<>]=?|[-+]@?|\*{1,2}|\[\]=?|![=~]?)\b?/)
    s = input.matched.yield_self {|s| s[1, s.length] }.to_sym
    new_token(:tSYMBOL, s)
  when input.scan(/[+-]?\d[\d_]*/)
    new_token(:tINTEGER, input.matched.to_i)
  when input.scan(PUNCTS_RE)
    new_token(PUNCTS[input.matched])
  when input.scan(/(::)?([A-Z]\w*::)+/)
    new_token(:tNAMESPACE)
  when input.scan(/[a-z_]\w*:/)
    new_token(:tLKEYWORD, input.matched.chop.to_sym)
  when input.scan(/[a-z_]\w*[?!]:/)
    new_token(:tLKEYWORD_Q_E, input.matched.chop.to_sym)
  when input.scan(/[A-Z]\w*:/)
    new_token(:tUKEYWORD, input.matched.chop.to_sym)
  when input.scan(/[A-Z]\w*[?!]:/)
    new_token(:tUKEYWORD_Q_E, input.matched.chop.to_sym)
  when input.scan(/\$([A-Za-z_]\w*|[~*$?!@\/\\;,.=:<>"&`'+]|\d+|-[0-9_A-Za-z])/)
    new_token(:tGLOBALIDENT)
  when input.scan(/@[a-zA-Z_]\w*/)
    new_token(:tIVAR, input.matched.to_sym)
  when input.scan(/@@[a-zA-Z_]\w*/)
    new_token(:tCLASSVAR, input.matched.to_sym)
  when input.scan(/_[A-Z]\w*\b/)
    new_token(:tINTERFACEIDENT)
  when input.scan(/[A-Z]\w*\b/)
    new_token(:tUIDENT)
  when input.scan(/[a-z]\w*\b/)
    new_token(:tLIDENT)
  when input.scan(/_[a-z]\w*\b/)
    new_token(:tUNDERSCOREIDENT)
  when input.scan(/_\w*\b/)
    new_token(:tPARAMNAME)
  when input.scan(/"(\\"|[^"])*"/)
    s = input.matched.yield_self {|s| s[1, s.length - 2] }
                     .gsub(DBL_QUOTE_STR_ESCAPE_SEQUENCES_RE) do |match|
                       case match
                       when '\\a' then "\a"
                       when '\\b' then "\b"
                       when '\\e' then "\e"
                       when '\\f' then "\f"
                       when '\\n' then "\n"
                       when '\\r' then "\r"
                       when '\\s' then "\s"
                       when '\\t' then "\t"
                       when '\\v' then "\v"
                       when '\\"' then '"'
                       end
                     end
    new_token(:tSTRING, s)
  when input.scan(/'(\\'|[^'])*'/)
    s = input.matched.yield_self {|s| s[1, s.length - 2] }.gsub(/\\'/, "'")
    new_token(:tSTRING, s)
  else
    text = input.peek(10)
    start_index = charpos(input)
    end_index = start_index + text.length
    location = RBS::Location.new(buffer: buffer, start_pos: start_index, end_pos: end_index)
    raise LexerError.new(input: text, location: location)
  end
end

def on_error(token_id, error_value, value_stack)
  raise SyntaxError.new(token_str: token_to_str(token_id), error_value: error_value, value_stack: value_stack)
end

def split_kw_loc(loc)
  buf = loc.buffer
  start_pos = loc.start_pos
  end_pos = loc.end_pos
  [
    Location.new(buffer: buf, start_pos: start_pos, end_pos: end_pos - 1),
    Location.new(buffer: buf, start_pos: end_pos - 1, end_pos: end_pos)
  ]
end

class SyntaxError < ParsingError
  attr_reader :token_str, :error_value, :value_stack

  def initialize(token_str:, error_value:, value_stack: nil)
    @token_str = token_str
    @error_value = error_value
    @value_stack = value_stack

    super "parse error on value: #{error_value.inspect} (#{token_str})"
  end
end

class SemanticsError < ParsingError
  attr_reader :subject, :location, :original_message

  def initialize(message, subject:, location:)
    @subject = subject
    @location = location
    @original_message = message

    super "parse error on #{location}: #{message}"
  end
end

class LexerError < ParsingError
  attr_reader :location, :input

  def initialize(input:, location:)
    @input = input
    @location = location

    super "Unexpected string: #{input}..."
  end
end

---- footer
