module Steep class AnnotationParser VAR_NAME = /[a-z][A-Za-z0-9_]*/ METHOD_NAME = Regexp.union( /[^.:\s]+/ ) CONST_NAME = Regexp.union( /(::)?([A-Z][A-Za-z0-9_]*::)*[A-Z][A-Za-z0-9_]*/ ) DYNAMIC_NAME = /(self\??\.)?#{METHOD_NAME}/ IVAR_NAME = /@[^:\s]+/ attr_reader :factory def initialize(factory:) @factory = factory end class SyntaxError < StandardError attr_reader :source attr_reader :location def initialize(source:, location:, exn: nil) @source = source @location = location message = case exn when RBS::ParsingError Diagnostic::Signature::SyntaxError.parser_syntax_error_message(exn) when Exception exn.message end super message end end TYPE = /(?.*)/ COLON = /\s*:\s*/ PARAM = /[A-Z][A-Za-z0-9_]*/ TYPE_PARAMS = /(\[(?#{PARAM}(,\s*#{PARAM})*)\])?/ def parse_type(string) factory.type(RBS::Parser.parse_type(string)) end def keyword_subject_type(keyword, name) /@type\s+#{keyword}\s+(?#{name})#{COLON}#{TYPE}/ end def keyword_and_type(keyword) /@type\s+#{keyword}#{COLON}#{TYPE}/ end def parse(src, location:) case src when keyword_subject_type("var", VAR_NAME) Regexp.last_match.yield_self do |match| match or raise name = match[:name] or raise type = match[:type] or raise AST::Annotation::VarType.new(name: name.to_sym, type: parse_type(type), location: location) end when keyword_subject_type("method", METHOD_NAME) Regexp.last_match.yield_self do |match| match or raise name = match[:name] or raise type = match[:type] or raise method_type = factory.method_type(RBS::Parser.parse_method_type(type), method_decls: Set[]) AST::Annotation::MethodType.new(name: name.to_sym, type: method_type, location: location) end when keyword_subject_type("const", CONST_NAME) Regexp.last_match.yield_self do |match| match or raise name = match[:name] or raise type = parse_type(match[:type] || raise) AST::Annotation::ConstType.new(name: TypeName(name), type: type, location: location) end when keyword_subject_type("ivar", IVAR_NAME) Regexp.last_match.yield_self do |match| match or raise name = match[:name] or raise type = parse_type(match[:type] || raise) AST::Annotation::IvarType.new(name: name.to_sym, type: type, location: location) end when keyword_and_type("return") Regexp.last_match.yield_self do |match| match or raise type = parse_type(match[:type] || raise) AST::Annotation::ReturnType.new(type: type, location: location) end when keyword_and_type("block") Regexp.last_match.yield_self do |match| match or raise type = parse_type(match[:type] || raise) AST::Annotation::BlockType.new(type: type, location: location) end when keyword_and_type("self") Regexp.last_match.yield_self do |match| match or raise type = parse_type(match[:type] || raise) AST::Annotation::SelfType.new(type: type, location: location) end when keyword_and_type("instance") Regexp.last_match.yield_self do |match| match or raise type = parse_type(match[:type] || raise) AST::Annotation::InstanceType.new(type: type, location: location) end when keyword_and_type("module") Regexp.last_match.yield_self do |match| match or raise type = parse_type(match[:type] || raise) AST::Annotation::ModuleType.new(type: type, location: location) end when keyword_and_type("break") Regexp.last_match.yield_self do |match| match or raise type = parse_type(match[:type] || raise) AST::Annotation::BreakType.new(type: type, location: location) end when /@dynamic\s+(?(#{DYNAMIC_NAME}\s*,\s*)*#{DYNAMIC_NAME})/ Regexp.last_match.yield_self do |match| match or raise names = (match[:names] || raise).split(/\s*,\s*/) AST::Annotation::Dynamic.new( names: names.map {|name| case when name.delete_prefix!("self.") AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :module) when name.delete_prefix!("self?.") AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :module_instance) else AST::Annotation::Dynamic::Name.new(name: name.to_sym, kind: :instance) end }, location: location ) end when /@implements\s+(?#{CONST_NAME})#{TYPE_PARAMS}$/ Regexp.last_match.yield_self do |match| match or raise type_name = TypeName(match[:name] || raise) params = match[:params]&.yield_self {|params| params.split(/,/).map {|param| param.strip.to_sym } } || [] name = AST::Annotation::Implements::Module.new(name: type_name, args: params) AST::Annotation::Implements.new(name: name, location: location) end end rescue RBS::ParsingError => exn raise SyntaxError.new(source: src, location: location, exn: exn) end end end