<commit_before>module Pione
  module PNML
    class Compiler
      # Compile from the net to PIONE document.
      #
      # @param net [PNML::Net]
      #   source net
      # @return [String]
      #   compiled PIONE document
      def self.compile(net, option={})
        new(net, option).compile
      end

      def initialize(net, option={})
        @net = net
        @net_name = option[:flow_rule_name] || "Main"

        @option = option
        @net_rewriter = NetRewriter.new do |rules|
          rules << IsolatedElementElimination
          rules << InvalidArcElimination
          rules << OutputReduction
          rules << InputReduction
          rules << IOExpansion
          rules << InputMergeComplement
          rules << InputParallelizationComplement
          rules << OutputDecompositionComplement
          rules << OutputSynchronizationComplement
          rules << TicketInstantiation
        end
        @actions = []
      end

      # Compile a PNML file into PIONE document as a string.
      def compile
        # annotations
        annotations = AnnotationExtractor.new(@net, @option).extract

        # apply net rewriting rules
        @net_rewriter.rewrite(@net)

        # build rules
        rules, flow_elements = ConstituentRuleBuilder.new(@net, @net_name).build()
        definition_main = FlowRuleBuilder.new(@net, @net_name).build(flow_elements)

        # merge literate actions
        rules.each do |rule|
          if @option[:literate_actions]
            if action = @option[:literate_actions][rule.name]
              rule.action_content = action[:content]
            end
          end
        end

        # textize
        sections = []
        if annotations and annotations.size > 0
          sections << annotations << ""
        end
        sections << definition_main.textize
        rules.each {|rule| sections << rule.textize}
        return sections.join("\n")
      end
    end

    # ConstituentRuleBuilder builds constituent rule definitions.
    class ConstituentRuleBuilder
      # @param net [PNML::Net]
      #   net
      # @param net_name [String]
      #   name of the net
      def initialize(net, net_name)
        @net = net
        @net_name = net_name
      end

      # Build a consituent rule definitions by transitions in the net.
      #
      # @return [Array(Array<RuleDefinition>,Array)]
      def build
        definition = build_rule_definition_table()

        # save all inner rules
        rules = definition.values.compact
        flow_elements = definition.values.compact

        # conditional branch
        @net.transitions.each do |transition|
          places = @net.find_all_places_by_target_id(transition.id)
          input_places = places.select {|place| Perspective.file?(place)}
          inputs = input_places.map {|input| InputData.new(input)}

          if Perspective.keyword_if?(transition)
            flow_elements << create_if_branch(transition, definition, inputs, flow_elements)
          elsif Perspective.keyword_case?(transition)
            flow_elements << create_case_branch(transition, definition, inputs, flow_elements)
          end
        end

        return [rules, flow_elements]
      end

      private

      # Build rule definition table.
      #
      # @return [Hash{String=>RuleDefinition}]
      #   relatioin table between transition ID and rule definition
      def build_rule_definition_table
        return @net.transitions.each_with_object({}) do |transition, table|
          if Perspective.rule?(transition)
            type = :action
            rule_name = Perspective.normalize_rule_name(transition.name)
            is_external = Perspective.external_rule?(transition)
            rule = RuleDefinition.new(rule_name, type, is_external, @net_name, table.size)

            # setup rule conditions
            rule.inputs = find_inputs(transition)
            rule.outputs = find_outputs(transition)
            rule.params = find_params(transition)
            rule.constraints = find_constraints(transition)
            rule.source_tickets = find_source_tickets(transition)
            rule.target_tickets = find_target_tickets(transition)

            table[transition.id] = rule
          end
        end
      end

      # Find inputs from net.
      #
      # @param transition [Transition]
      #   base transition
      # @return [Array<InputData>]
      #   rule inputs
      def find_inputs(transition)
        @net.find_all_places_by_target_id(transition.id).each_with_object([]) do |place, inputs|
          begin
            # consideration for constraint nodes
            prev_transition = @net.find_transition_by_target_id(place.id)
            if Perspective.keyword_constraint?(prev_transition)
              @net.find_all_places_by_target_id(prev_transition.id).each do |_place|
                if Perspective.file?(_place)
                  inputs << InputData.new(_place)
                end
              end
            else
              if Perspective.file?(place)
                inputs << InputData.new(place)
              end
            end
          rescue AmbiguousNetQueryResult
            # ignore
          end
        end
      end

      # Find outputs from net.
      #
      # @param transition [Transition]
      #   base transition
      # @return [Array]
      #   rule outputs
      def find_outputs(transition)
        @net.find_all_places_by_source_id(transition.id).each_with_object([]) do |place, outputs|
          if Perspective.file?(place)
            outputs << OutputData.new(place)
          end
        end
      end

      # Find parameters from net.
      #
      # @param transition [Transition]
      #   base transition
      # @return [Array<Param>]
      #   rule parameters
      def find_params(transition)
        @net.find_all_places_by_target_id(transition.id).each_with_object([]) do |place, params|
          if Perspective.param?(place)
            params << Param.new(place.name)
          end
        end
      end

      # Find constraints from net.
      #
      # @param transition [Transition]
      #   base transition<commit_msg>Fixed parameter bug of POP compiler.<commit_after>module Pione
  module PNML
    class Compiler
      # Compile from the net to PIONE document.
      #
      # @param net [PNML::Net]
      #   source net
      # @return [String]
      #   compiled PIONE document
      def self.compile(net, option={})
        new(net, option).compile
      end

      def initialize(net, option={})
        @net = net
        @net_name = option[:flow_rule_name] || "Main"

        @option = option
        @net_rewriter = NetRewriter.new do |rules|
          rules << IsolatedElementElimination
          rules << InvalidArcElimination
          rules << OutputReduction
          rules << InputReduction
          rules << IOExpansion
          rules << InputMergeComplement
          rules << InputParallelizationComplement
          rules << OutputDecompositionComplement
          rules << OutputSynchronizationComplement
          rules << TicketInstantiation
        end
        @actions = []
      end

      # Compile a PNML file into PIONE document as a string.
      def compile
        # annotations
        annotations = AnnotationExtractor.new(@net, @option).extract

        # apply net rewriting rules
        @net_rewriter.rewrite(@net)

        # build rules
        rules, flow_elements = ConstituentRuleBuilder.new(@net, @net_name).build()
        definition_main = FlowRuleBuilder.new(@net, @net_name).build(flow_elements)

        # merge literate actions
        rules.each do |rule|
          if @option[:literate_actions]
            if action = @option[:literate_actions][rule.name]
              rule.action_content = action[:content]
            end
          end
        end

        # textize
        sections = []
        if annotations and annotations.size > 0
          sections << annotations << ""
        end
        sections << definition_main.textize
        rules.each {|rule| sections << rule.textize}
        return sections.join("\n")
      end
    end

    # ConstituentRuleBuilder builds constituent rule definitions.
    class ConstituentRuleBuilder
      # @param net [PNML::Net]
      #   net
      # @param net_name [String]
      #   name of the net
      def initialize(net, net_name)
        @net = net
        @net_name = net_name
      end

      # Build a consituent rule definitions by transitions in the net.
      #
      # @return [Array(Array<RuleDefinition>,Array)]
      def build
        definition = build_rule_definition_table()

        # save all inner rules
        rules = definition.values.compact
        flow_elements = definition.values.compact

        # conditional branch
        @net.transitions.each do |transition|
          places = @net.find_all_places_by_target_id(transition.id)
          input_places = places.select {|place| Perspective.file?(place)}
          inputs = input_places.map {|input| InputData.new(input)}

          if Perspective.keyword_if?(transition)
            flow_elements << create_if_branch(transition, definition, inputs, flow_elements)
          elsif Perspective.keyword_case?(transition)
            flow_elements << create_case_branch(transition, definition, inputs, flow_elements)
          end
        end

        return [rules, flow_elements]
      end

      private

      # Build rule definition table.
      #
      # @return [Hash{String=>RuleDefinition}]
      #   relatioin table between transition ID and rule definition
      def build_rule_definition_table
        return @net.transitions.each_with_object({}) do |transition, table|
          if Perspective.rule?(transition)
            type = :action
            rule_name = Perspective.normalize_rule_name(transition.name)
            is_external = Perspective.external_rule?(transition)
            rule = RuleDefinition.new(rule_name, type, is_external, @net_name, table.size)

            # setup rule conditions
            rule.inputs = find_inputs(transition)
            rule.outputs = find_outputs(transition)
            rule.params = find_params(transition)
            rule.constraints = find_constraints(transition)
            rule.source_tickets = find_source_tickets(transition)
            rule.target_tickets = find_target_tickets(transition)

            table[transition.id] = rule
          end
        end
      end

      # Find inputs from net.
      #
      # @param transition [Transition]
      #   base transition
      # @return [Array<InputData>]
      #   rule inputs
      def find_inputs(transition)
        @net.find_all_places_by_target_id(transition.id).each_with_object([]) do |place, inputs|
          begin
            # consideration for constraint nodes
            prev_transition = @net.find_transition_by_target_id(place.id)
            if Perspective.keyword_constraint?(prev_transition)
              @net.find_all_places_by_target_id(prev_transition.id).each do |_place|
                if Perspective.file?(_place)
                  inputs << InputData.new(_place)
                end
              end
            else
              if Perspective.file?(place)
                inputs << InputData.new(place)
              end
            end
          rescue AmbiguousNetQueryResult
            # ignore
          end
        end
      end

      # Find outputs from net.
      #
      # @param transition [Transition]
      #   base transition
      # @return [Array]
      #   rule outputs
      def find_outputs(transition)
        @net.find_all_places_by_source_id(transition.id).each_with_object([]) do |place, outputs|
          if Perspective.file?(place)
            outputs << OutputData.new(place)
          end
        end
      end

      # Find parameters from net.
      #
      # @param transition [Transition]
      #   base transition
      # @return [Array<Param>]
      #   rule parameters
      def find_params(transition)
        @net.find_all_places_by_target_id(transition.id).each_with_object([]) do |place, params|
          if Perspective.param?(place)
            params << Param.new(place)
          end
        end
      end

      # Find constraints from net.
      #
      # @param transition [Transition]
      #   base transition
{'fraction_non_alphanumeric': 0.08031802693493428, 'fraction_numerical': 0.00016225864027259452, 'mean_word_length': 2.034711964549483, 'pattern_counts': {'":': 0, '<': 89, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4481423', 'n_tokens_mistral': 3303, 'n_tokens_neox': 3217, 'n_words': 772}