Commit eefc22eb authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Extract BatchProcessor from BatchParser.

Let the BatchParser only deal with the actual parsing part.
A separate BatchProcessor then deals with combining the parsed
source files into classes and doing all kinds of additional
processing on them.

The top-level JsDuck::App is now also simpler as it just saves
the @parsed_files and @relations into separate instance variables,
and can easily use them later without needing to ask list of
parsed files from BatchProcessor instance.

Both BatchParser and BatchProcessor are now singleton classes keeping no
state inside them - so they're also simpler to reason about.
parent 3bde89f9
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
require 'jsduck/batch_parser'
require 'jsduck/batch_processor'
require 'jsduck/assets'
require 'jsduck/tag_registry'
require 'jsduck/export_writer'
@@ -29,8 +30,8 @@ module JsDuck
    private

    def parse
      @batch_parser = BatchParser.new(@opts)
      @relations = @batch_parser.run
      @parsed_files = BatchParser.parse(@opts)
      @relations = BatchProcessor.process(@parsed_files, @opts)
    end

    def init_assets
@@ -46,7 +47,7 @@ module JsDuck
    end

    def generate_web_page
      WebWriter.new(@relations, @assets, @batch_parser.parsed_files, @opts).write
      WebWriter.new(@relations, @assets, @parsed_files, @opts).write
    end

  end
+5 −74
Original line number Diff line number Diff line
require 'jsduck/util/parallel'
require 'jsduck/util/io'
require 'jsduck/source/file'
require 'jsduck/aggregator'
require 'jsduck/class'
require 'jsduck/relations'
require 'jsduck/logger'
require 'jsduck/process/ignored_classes'
require 'jsduck/process/global_members'
require 'jsduck/process/enums'
require 'jsduck/process/accessors'
require 'jsduck/process/ext4_events'
require 'jsduck/process/overrides'
require 'jsduck/process/inherit_doc'
require 'jsduck/process/versions'
require 'jsduck/process/return_values'
require 'jsduck/process/lint'
require 'jsduck/process/circular_deps'

module JsDuck

  # Performs the parsing of all input files.  Input files are read
  # from options object (originating from command line).
  # Parses of all input files.  Input files are read from options
  # object (originating from command line).
  class BatchParser
    def initialize(opts)
      @opts = opts
    end

    # Array of Source::File objects.
    # Available after calling the #run method.
    attr_reader :parsed_files

    # Parses the files and returns instance of Relations class.
    def run
      @parsed_files = parallel_parse(@opts.input_files)
      result = aggregate(@parsed_files)
      @relations = to_class_objects(result)
      apply_extra_processing
      return @relations
    end

    private

    # Parses the files in parallel using as many processes as available CPU-s
    def parallel_parse(filenames)
      Util::Parallel.map(filenames) do |fname|
    def self.parse(opts)
      Util::Parallel.map(opts.input_files) do |fname|
        Logger.log("Parsing", fname)
        begin
          Source::File.new(Util::IO.read(fname), fname, @opts)
          Source::File.new(Util::IO.read(fname), fname, opts)
        rescue
          Logger.fatal_backtrace("Error while parsing #{fname}", $!)
          exit(1)
@@ -54,42 +21,6 @@ module JsDuck
      end
    end

    # Aggregates parsing results sequencially
    def aggregate(parsed_files)
      agr = Aggregator.new
      parsed_files.each do |file|
        Logger.log("Aggregating", file.filename)
        agr.aggregate(file)
      end
      classes_hash = agr.result

      Process::IgnoredClasses.new(classes_hash).process_all!
      Process::GlobalMembers.new(classes_hash, @opts).process_all!
      Process::Accessors.new(classes_hash).process_all!
      Process::Ext4Events.new(classes_hash, @opts).process_all!
      Process::Enums.new(classes_hash).process_all!
      # Ignore override classes after applying them to actual classes
      @opts.external_classes += Process::Overrides.new(classes_hash).process_all!

      classes_hash.values
    end

    # Turns all aggregated data into Class objects and places the
    # classes inside Relations container.
    def to_class_objects(docs)
      classes = docs.map {|d| Class.new(d) }
      Relations.new(classes, @opts.external_classes)
    end

    # Do all kinds of post-processing on relations.
    def apply_extra_processing
      Process::CircularDeps.new(@relations).process_all!
      Process::InheritDoc.new(@relations).process_all!
      Process::Versions.new(@relations, @opts).process_all!
      Process::ReturnValues.new(@relations).process_all!
      Process::Lint.new(@relations).process_all!
    end

  end

end
+78 −0
Original line number Diff line number Diff line
require 'jsduck/aggregator'
require 'jsduck/class'
require 'jsduck/relations'
require 'jsduck/logger'
require 'jsduck/util/singleton'
require 'jsduck/process/ignored_classes'
require 'jsduck/process/global_members'
require 'jsduck/process/enums'
require 'jsduck/process/accessors'
require 'jsduck/process/ext4_events'
require 'jsduck/process/overrides'
require 'jsduck/process/inherit_doc'
require 'jsduck/process/versions'
require 'jsduck/process/return_values'
require 'jsduck/process/lint'
require 'jsduck/process/circular_deps'

module JsDuck

  # Processes the parsing results into Relations object.
  class BatchProcessor
    include Util::Singleton

    # Processes array of Source::File objects from BatchParser and
    # returns instance of Relations class.
    def process(parsed_files, opts)
      r = aggregate(parsed_files)
      r = pre_process(r, opts)
      r = to_class_objects(r, opts)
      return post_process(r, opts)
    end

    private

    # Aggregates parsing results sequencially
    def aggregate(parsed_files)
      agr = Aggregator.new
      parsed_files.each do |file|
        Logger.log("Aggregating", file.filename)
        agr.aggregate(file)
      end
      agr.result
    end

    # Do all kinds of processing on the classes hash before turning it
    # into Relations object.
    def pre_process(classes_hash, opts)
      Process::IgnoredClasses.new(classes_hash).process_all!
      Process::GlobalMembers.new(classes_hash, opts).process_all!
      Process::Accessors.new(classes_hash).process_all!
      Process::Ext4Events.new(classes_hash, opts).process_all!
      Process::Enums.new(classes_hash).process_all!
      # Ignore override classes after applying them to actual classes
      opts.external_classes += Process::Overrides.new(classes_hash).process_all!

      classes_hash.values
    end

    # Turns all aggregated data into Class objects and places the
    # classes inside Relations container.
    def to_class_objects(docs, opts)
      classes = docs.map {|d| Class.new(d) }
      Relations.new(classes, opts.external_classes)
    end

    # Do all kinds of post-processing on Relations object.
    def post_process(relations, opts)
      Process::CircularDeps.new(relations).process_all!
      Process::InheritDoc.new(relations).process_all!
      Process::Versions.new(relations, opts).process_all!
      Process::ReturnValues.new(relations).process_all!
      Process::Lint.new(relations).process_all!
      relations
    end

  end

end