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 | 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(/#(( *)|( ?(?.*)))\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*/) 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