lib/atp/processors/condition.rb in atp-0.7.0 vs lib/atp/processors/condition.rb in atp-0.8.0

- old
+ new

@@ -41,137 +41,134 @@ # (flow-flag "y" true # (test # (name "test4"))))))) # class Condition < Processor - CONDITION_NODES = [:flow_flag, :test_result, :test_executed, :group, :job] + def on_flow(node) + extract_volatiles(node) + node.updated(nil, optimize(process_all(node.children))) + end - def process(node) - # Bit of a hack - To get all of the nested conditions optimized away it is necessary - # to execute this recursively a few times. This guard ensures that the recursion is - # only performed on the top-level and not on every process operation. - if @top_level_called - super + def on_flow_flag(node) + flag, state, *nodes = *node + if conditions_to_remove.any? { |c| node.type == c.type && c.to_a == [flag, state] } + if volatile?(flag) + result = n(:inline, optimize(process_all(nodes))) + else + # This ensures any duplicate conditions matching the current one get removed + conditions_to_remove << node.updated(nil, [flag, state]) + result = n(:inline, optimize(process_all(nodes))) + conditions_to_remove.pop + end else - @top_level_called = true - ast1 = nil - ast2 = node - while ast1 != ast2 - ast1 = super(ast2) - ast2 = super(ast1) + if volatile?(flag) + result = node.updated(nil, [flag, state] + optimize(process_all(nodes))) + else + conditions_to_remove << node.updated(nil, [flag, state]) + result = node.updated(nil, [flag, state] + optimize(process_all(nodes))) + conditions_to_remove.pop end - @top_level_called = false - ast1 end + result end + alias_method :on_test_result, :on_flow_flag + alias_method :on_job, :on_flow_flag + alias_method :on_run_flag, :on_flow_flag + alias_method :on_test_executed, :on_flow_flag - def on_boolean_condition(node) - children = node.children.dup - name = children.shift - state = children.shift - remove_condition << node - children = extract_common_embedded_conditions(n(:temp, children)) - remove_condition.pop - if condition_to_be_removed?(node) - process_all(children) - else - node.updated(nil, [name, state] + process_all(children)) - end - end - alias_method :on_flow_flag, :on_boolean_condition - alias_method :on_test_result, :on_boolean_condition - alias_method :on_test_executed, :on_boolean_condition - alias_method :on_job, :on_boolean_condition - def on_group(node) - children = node.children.dup - name = children.shift - remove_condition << node - children = extract_common_embedded_conditions(n(:temp, children)) - remove_condition.pop - if condition_to_be_removed?(node) - process_all(children) + name, *nodes = *node + if conditions_to_remove.any? { |c| node.type == c.type && c.to_a == [name] } + conditions_to_remove << node.updated(nil, [name]) + result = n(:inline, optimize(process_all(nodes))) + conditions_to_remove.pop else - node.updated(nil, [name] + process_all(children)) + conditions_to_remove << node.updated(nil, [name]) + result = node.updated(nil, [name] + optimize(process_all(nodes))) + conditions_to_remove.pop end + result end - # Returns true if the given node contains the given condition within - # its immediate children - def has_condition?(condition, node) - ([node] + node.children.to_a).any? do |n| - if n.is_a?(ATP::AST::Node) - equal_conditions?(condition, n) + def optimize(nodes) + results = [] + node1 = nil + nodes.each do |node2| + if node1 + if can_be_combined?(node1, node2) + node1 = process(combine(node1, node2)) + else + results << node1 + node1 = node2 + end + else + node1 = node2 end end + results << node1 if node1 + results end - def condition_to_be_removed?(node) - remove_condition.any? { |c| equal_conditions?(c, node) } - end - - def equal_conditions?(node1, node2) - if node1.type == node2.type - if node1.type == :group - node1.to_a.take(1) == node2.to_a.take(1) - else - node1.to_a.take(2) == node2.to_a.take(2) - end + def can_be_combined?(node1, node2) + if condition_node?(node1) && condition_node?(node2) + !(conditions(node1) & conditions(node2)).empty? + else + false end end - def condition?(node) - node.is_a?(ATP::AST::Node) && CONDITION_NODES.include?(node.type) + def condition_node?(node) + node.respond_to?(:type) && + [:flow_flag, :run_flag, :test_result, :group, :job, :test_executed].include?(node.type) end - def on_flow(node) - name, *nodes = *node - nodes = extract_common_embedded_conditions(nodes) - node.updated(nil, [name] + nodes) - end + def combine(node1, node2) + common = conditions(node1) & conditions(node2) + common.each { |condition| conditions_to_remove << condition } + node1 = process(node1) + node1 = [node1] unless node1.is_a?(Array) + node2 = process(node2) + node2 = [node2] unless node2.is_a?(Array) + common.size.times { conditions_to_remove.pop } - def extract_common_embedded_conditions(nodes) - nodes = [nodes] unless nodes.is_a?(Array) - result = [] - cond_a = nil - test_a = nil - ConditionExtractor.new.run(nodes).each do |cond_b, test_b| - if cond_a - common = cond_a & cond_b - if common.empty? - result << combine(cond_a, extract_common_embedded_conditions(test_a)) - cond_a = cond_b - test_a = test_b - else - a = combine(cond_a - common, test_a) - b = combine(cond_b - common, test_b) - cond_a = common - test_a = [a, b].flatten - end + node = nil + common.reverse_each do |condition| + if node + node = condition.updated(nil, condition.children + [node]) else - cond_a = cond_b - test_a = test_b + node = condition.updated(nil, condition.children + node1 + node2) end end - if nodes == [test_a] - nodes - else - result << combine(cond_a, extract_common_embedded_conditions(test_a)) - result.flatten - end + node end - def combine(conditions, node) - if conditions && !conditions.empty? - conditions.reverse_each do |n| - node = n.updated(nil, n.children + (node.is_a?(Array) ? node : [node])) + def conditions(node) + result = [] + if [:flow_flag, :run_flag].include?(node.type) + flag, state, *children = *node + unless volatile?(flag) + result << node.updated(nil, [flag, state]) end + result += conditions(children.first) if children.first + elsif [:test_result, :job, :test_executed].include?(node.type) + flag, state, *children = *node + result << node.updated(nil, [flag, state]) + result += conditions(children.first) if children.first + elsif node.type == :group + name, *children = *node + # Sometimes a group can have an ID + if children.first.try(:type) == :id + result << node.updated(nil, [name, children.shift]) + else + result << node.updated(nil, [name]) + end + result += conditions(children.first) if children.first end - node + result end - def remove_condition - @remove_condition ||= [] + def conditions_to_remove + @conditions_to_remove ||= [] end end end end