lib/yard/parser/source_parser.rb in yard-0.2.2 vs lib/yard/parser/source_parser.rb in yard-0.2.3
- old
+ new
@@ -1,15 +1,24 @@
require 'stringio'
+require 'continuation' unless RUBY18
module YARD
module Parser
+ class UndocumentableError < Exception; end
class LoadOrderError < Exception; end
# Responsible for parsing a source file into the namespace
class SourceParser
class << self
+ attr_accessor :parser_type
+
+ def parser_type=(value)
+ @parser_type = validated_parser_type(value)
+ end
+
def parse(paths = "lib/**/*.rb", level = log.level)
+ log.debug("Parsing #{paths} with `#{parser_type}` parser")
if paths.is_a?(Array)
files = paths.map {|p| Dir[p] }.flatten
else
files = Dir[File.join(Dir.pwd, paths)]
end
@@ -17,13 +26,21 @@
log.enter_level(level) do
parse_in_order(*files.uniq)
end
end
- def parse_string(content)
- new.parse(StringIO.new(content))
+ def parse_string(content, ptype = parser_type)
+ new(ptype).parse(StringIO.new(content))
end
+
+ def tokenize(content, ptype = parser_type)
+ new(ptype).tokenize(content)
+ end
+
+ def validated_parser_type(type)
+ RUBY18 && type == :ruby ? :ruby18 : type
+ end
private
def parse_in_order(*files)
while file = files.shift
@@ -31,79 +48,93 @@
if file.is_a?(Array) && file.last.is_a?(Continuation)
log.debug("Re-processing #{file.first}")
file.last.call
elsif file.is_a?(String)
log.debug("Processing #{file}...")
- new(true).parse(file)
+ new(parser_type, true).parse(file)
end
rescue LoadOrderError => e
# Out of order file. Push the context to the end and we'll call it
files.push([file, e.message])
end
end
end
end
- attr_reader :file
- attr_accessor :namespace, :visibility, :scope, :owner, :load_order_errors
+ self.parser_type = :ruby
+
+ attr_reader :file, :parser_type
- def initialize(load_order_errors = false)
- @file = "<STDIN>"
- @namespace = YARD::Registry.root
- @visibility = :public
- @scope = :instance
- @owner = @namespace
+ def initialize(parser_type = SourceParser.parser_type, load_order_errors = false)
@load_order_errors = load_order_errors
+ @file = '(stdin)'
+ self.parser_type = parser_type
end
##
# Creates a new SourceParser that parses a file and returns
# analysis information about it.
#
- # @param [String, TokenList, StatementList, #read] content the source file to parse
+ # @param [String, #read, Object] content the source file to parse
def parse(content = __FILE__)
case content
when String
@file = content
- statements = StatementList.new(IO.read(content))
- when TokenList
- statements = StatementList.new(content)
- when StatementList
- statements = content
+ content = IO.read(content)
+ self.parser_type = parser_type_for_filename(file)
else
- if content.respond_to? :read
- statements = StatementList.new(content.read)
- else
- raise ArgumentError, "Invalid argument for SourceParser::parse: #{content.inspect}:#{content.class}"
- end
+ content = content.read if content.respond_to? :read
end
+
+ @parser = parse_statements(content)
+ post_process
+ @parser
+ end
+
+ def tokenize(content)
+ case parser_type
+ when :c
+ raise NotImplementedError, "no support for C/C++ files"
+ when :ruby18
+ Ruby::Legacy::TokenList.new(content)
+ when :ruby
+ Ruby::RubyParser.parse(content).tokens
+ else
+ raise ArgumentError, "invalid parser type or unrecognized file"
+ end
+ end
+
+ private
- top_level_parse(statements)
+ def post_process
+ post = Handlers::Processor.new(@file, @load_order_errors, @parser_type)
+ post.process(@parser.enumerator)
end
- private
- def top_level_parse(statements)
- statements.each do |stmt|
- find_handlers(stmt).each do |handler|
- begin
- handler.new(self, stmt).process
- rescue LoadOrderError => loaderr
- raise # Pass this up
- rescue Handlers::UndocumentableError => undocerr
- log.warn "in #{handler.to_s}: Undocumentable #{undocerr.message}"
- log.warn "\tin file '#{file}':#{stmt.tokens.first.line_no}:\n\n" + stmt.inspect + "\n"
- rescue => e
- log.error "Unhandled exception in #{handler.to_s}:"
- log.error "#{e.class.class_name}: #{e.message}"
- log.error " in `#{file}`:#{stmt.tokens.first.line_no}:\n\n#{stmt.inspect}\n"
- log.error "Stack trace:" + e.backtrace[0..5].map {|x| "\n\t#{x}" }.join + "\n"
- end
- end
- end
+ def parser_type=(value)
+ @parser_type = self.class.validated_parser_type(value)
+ end
+
+ def parser_type_for_filename(filename)
+ case (File.extname(filename)[1..-1] || "").downcase
+ when "c", "cpp", "cxx"
+ :c
+ else # when "rb", "rbx", "erb"
+ parser_type == :ruby18 ? :ruby18 : :ruby
end
-
- def find_handlers(stmt)
- Handlers::Base.subclasses.find_all {|sub| sub.handles? stmt.tokens }
+ end
+
+ def parse_statements(content)
+ case parser_type
+ when :c
+ raise NotImplementedError, "no support for C/C++ files"
+ when :ruby18
+ Ruby::Legacy::StatementList.new(content)
+ when :ruby
+ Ruby::RubyParser.parse(content, file)
+ else
+ raise ArgumentError, "invalid parser type or unrecognized file"
end
+ end
end
end
end