From cdc458383f0b519b9e9320a0b99948e9e49d16fd Mon Sep 17 00:00:00 2001 From: Andrey Tarantsov Date: Sun, 5 Sep 2010 15:02:47 +0700 Subject: [PATCH] Item patchers refactoring: merge execution paths by parsing inserted lines --- lib/xdry.rb | 2 +- lib/xdry/generators/dealloc.rb | 52 +++++-------------- lib/xdry/parsing/driver.rb | 75 +++++++++++++++++++-------- lib/xdry/parsing/model.rb | 1 + lib/xdry/parsing/pos.rb | 1 + lib/xdry/parsing/scope_stack.rb | 8 ++- lib/xdry/parsing/scopes_support.rb | 18 ++++++- lib/xdry/patching/insertion_points.rb | 72 +++++++++++++++++++++++++ lib/xdry/patching/item_patchers.rb | 47 +++++++++++++++++ lib/xdry/patching/patcher.rb | 11 ++-- 10 files changed, 217 insertions(+), 70 deletions(-) create mode 100644 lib/xdry/patching/insertion_points.rb create mode 100644 lib/xdry/patching/item_patchers.rb diff --git a/lib/xdry.rb b/lib/xdry.rb index a4252ad..9b5357a 100644 --- a/lib/xdry.rb +++ b/lib/xdry.rb @@ -11,7 +11,7 @@ module XDry parsing/scopes_support parsing/scopes parsing/scope_stack parsing/driver - patching/emitter patching/patcher + patching/emitter patching/patcher patching/insertion_points patching/item_patchers boxing generators_support run }.each { |name| require File.join(File.dirname(__FILE__), 'xdry', name) } diff --git a/lib/xdry/generators/dealloc.rb b/lib/xdry/generators/dealloc.rb index 7816c7b..5dbe11e 100644 --- a/lib/xdry/generators/dealloc.rb +++ b/lib/xdry/generators/dealloc.rb @@ -2,42 +2,24 @@ module XDry module Generators - class MethodPatcher - - attr_reader :oclass - attr_reader :omethod - - def initialize oclass - @oclass = oclass - find! - end - - def found? - not omethod.nil? - end - - protected + class DeallocMethodPatcher < MethodPatcher def find + find_method_impl_by_selector('dealloc') end - def find_method_impl_by_selector selector - m = oclass.find_method(selector) - m && (m.has_impl? ? m : nil) + def empty_implementation + [ + "", + "- (void)dealloc {", + "\t[super dealloc];", + "}", + "", + ] end - private - - def find! - @omethod = find - end - - end - - class DeallocMethodPatcher < MethodPatcher - - def find - find_method_impl_by_selector('dealloc') + def insertion_point + ImplementationStartIP.new(oclass) end end @@ -47,7 +29,7 @@ class Dealloc < Generator def process_class oclass - dealloc_method_area = DeallocMethodPatcher.new(oclass) + dealloc_method_area = DeallocMethodPatcher.new(oclass, patcher) if dealloc_method_area.found? dealloc_method = dealloc_method_area.omethod impl = dealloc_method.impl @@ -66,14 +48,6 @@ def process_class oclass lines = lines.collect { |l| indent + l } @patcher.insert_before ending_node.pos, lines unless lines.empty? - else - lines = generate_release_calls_if(oclass) { true } - unless lines.empty? - lines = ["", "- (void)dealloc {"] + lines.collect { |l| INDENT_STEP+l } + - [INDENT_STEP + "[super dealloc];", "}", ""] - node = oclass.main_implementation.start_node - @patcher.insert_after node.pos, lines - end end end diff --git a/lib/xdry/parsing/driver.rb b/lib/xdry/parsing/driver.rb index 36886c7..76a317b 100644 --- a/lib/xdry/parsing/driver.rb +++ b/lib/xdry/parsing/driver.rb @@ -11,25 +11,6 @@ def initialize oglobal @verbose = false end - def new_lines_generator g, file_ref, io - line_no = 0 - io.each_line do |line| - line_no += 1 - orig_line = line.dup - line.strip! - - # strip end-of-line comments, but keep comment lines - eol_comments = '' - line_without_comments = line.sub(%r`//.*$`) { eol_comments = $&; '' } - unless line_without_comments.empty? - line = line_without_comments - end - - indent = if orig_line =~ /^(\s+)/ then $1 else '' end - g.yield [orig_line, line, Pos.new(file_ref, line_no), eol_comments, indent] - end - end - def parse_file file_name gen = Generator.new { |g| file_ref = FileRef.new(file_name) @@ -37,7 +18,7 @@ def parse_file file_name new_lines_generator g, file_ref, file end } - parse_data gen + parse_data_in_file_scope gen end def parse_string file_name, source @@ -45,28 +26,76 @@ def parse_string file_name, source file_ref = TestFileRef.new(file_name, source) new_lines_generator g, file_ref, StringIO.new(source) } - parse_data gen + parse_data_in_file_scope gen + end + + def parse_fragment file_ref, lines, start_lineno, start_scope + gen = Generator.new { |g| + new_lines_from_array_generator g, file_ref, start_lineno, lines + } + parse_data_in_scopes gen, start_scope.all_scopes end private - def parse_data gen - scope_stack = ScopeStack.new(@oglobal.new_file_scope) + def parse_data_in_file_scope gen + parse_data_in_scopes gen, [@oglobal.new_file_scope] + end + + def parse_data_in_scopes gen, scopes + scope_stack = ScopeStack.new(scopes) scope_stack.verbose = @verbose while gen.next? orig_line, line, pos, eol_comments, indent = gen.next puts " #{pos} #{orig_line}" if @verbose + pos.scope_before = scope_stack.current_scope scope_stack.parse_line line, eol_comments do |scope, child| # child is a Node or a Scope if child.is_a? Node child.pos = pos child.indent = indent end + puts "#{scope} << #{child}" if @verbose scope << child end + pos.scope_after = scope_stack.current_scope + end + end + + def new_lines_generator g, file_ref, io + line_no = 0 + io.each_line do |line| + line_no += 1 + orig_line, line, eol_comments, indent = split_line(line) + g.yield [orig_line, line, Pos.new(file_ref, line_no), eol_comments, indent] + end + end + + def new_lines_from_array_generator g, file_ref, start_lineno, lines + line_no = start_lineno - 1 + lines.each do |line| + line_no += 1 + orig_line, line, eol_comments, indent = split_line(line.dup) + g.yield [orig_line, line, Pos.new(file_ref, line_no), eol_comments, indent] end end + + def split_line line + orig_line = line.dup + line.strip! + + # strip end-of-line comments, but keep comment lines + eol_comments = '' + line_without_comments = line.sub(%r`//.*$`) { eol_comments = $&; '' } + unless line_without_comments.empty? + line = line_without_comments + end + + indent = if orig_line =~ /^(\s+)/ then $1 else '' end + return [orig_line, line, eol_comments, indent] + end + end end diff --git a/lib/xdry/parsing/model.rb b/lib/xdry/parsing/model.rb index 82fcde6..df2e693 100644 --- a/lib/xdry/parsing/model.rb +++ b/lib/xdry/parsing/model.rb @@ -32,6 +32,7 @@ def lookup_class name end class OClass + attr_reader :oglobal attr_reader :name, :field_defs, :attributes, :methods attr_reader :interfaces attr_reader :implementations diff --git a/lib/xdry/parsing/pos.rb b/lib/xdry/parsing/pos.rb index b4e336f..56057a9 100644 --- a/lib/xdry/parsing/pos.rb +++ b/lib/xdry/parsing/pos.rb @@ -27,6 +27,7 @@ def read class Pos attr_reader :file_ref, :line_no + attr_accessor :scope_before, :scope_after def initialize file_ref, line_no @file_ref = file_ref diff --git a/lib/xdry/parsing/scope_stack.rb b/lib/xdry/parsing/scope_stack.rb index 834f702..7fd95bf 100644 --- a/lib/xdry/parsing/scope_stack.rb +++ b/lib/xdry/parsing/scope_stack.rb @@ -5,9 +5,9 @@ class ScopeStack attr_accessor :verbose - def initialize root_scope + def initialize root_scopes @stack = [] - push root_scope + root_scopes.each { |scope| push(scope) } end def parse_line line, eol_comments @@ -30,6 +30,10 @@ def parse_line line, eol_comments end end + def current_scope + @current_scope + end + private def push subscope diff --git a/lib/xdry/parsing/scopes_support.rb b/lib/xdry/parsing/scopes_support.rb index 08327f0..cb28db6 100644 --- a/lib/xdry/parsing/scopes_support.rb +++ b/lib/xdry/parsing/scopes_support.rb @@ -29,7 +29,7 @@ def parser def subscope_for node if subscope_class = self.class.child_subscope_table[node.class] - subscope_class.new(node) + subscope_class.new(self, node) else nil end @@ -54,6 +54,14 @@ def to_s "#{self.class.name}:#{@model}" end + def parent_scopes + [] + end + + def all_scopes + parent_scopes + [self] + end + protected def create_parser @@ -118,12 +126,18 @@ def child_collections class ChildScope < Scope attr_reader :start_node + attr_reader :parent_scope - def initialize start_node + def initialize parent_scope, start_node super() + @parent_scope = parent_scope @start_node = start_node end + def parent_scopes + parent_scope.all_scopes + end + end end diff --git a/lib/xdry/patching/insertion_points.rb b/lib/xdry/patching/insertion_points.rb new file mode 100644 index 0000000..de2434f --- /dev/null +++ b/lib/xdry/patching/insertion_points.rb @@ -0,0 +1,72 @@ + +module XDry + + class InsertionPoint + + attr_reader :method, :node + + def initialize + find! + end + + def insert patcher, lines + raise StandardError, "#{self.class.name} has not been found but trying to insert" unless found? + patcher.send(@method, @node.pos, lines) + end + + def found? + not @method.nil? + end + + protected + + def before node + @method = :insert_before + @node = node + end + + def after node + @method = :insert_after + @node = node + end + + def try insertion_point + if insertion_point.found? + @method, @node = insertion_point.method, insertion_point.node + true + else + false + end + end + + def find! + end + + end + + class ImplementationStartIP < InsertionPoint + + def initialize oclass + @oclass = oclass + super() + end + + def find! + after @oclass.main_implementation.start_node + end + + end + + class MultiIP < InsertionPoint + + def initialize *insertion_points + @insertion_points = insertion_points + end + + def find! + @insertion_points.detect { |ip| try ip } + end + + end + +end diff --git a/lib/xdry/patching/item_patchers.rb b/lib/xdry/patching/item_patchers.rb new file mode 100644 index 0000000..c26bbf6 --- /dev/null +++ b/lib/xdry/patching/item_patchers.rb @@ -0,0 +1,47 @@ + +module XDry + + class MethodPatcher + + attr_reader :oclass + attr_reader :omethod + attr_reader :patcher + + def initialize oclass, patcher + @oclass = oclass + @patcher = patcher + find! + end + + def found? + not omethod.nil? + end + + protected + + def find + end + + def find_method_impl_by_selector selector + m = oclass.find_method(selector) + m && (m.has_impl? ? m : nil) + end + + private + + def find! + @omethod = find + if @omethod.nil? + patch! + @omethod = find + raise AssertionError, "#{seld.class.name} cannot find method even after adding a new one" if @omethod.nil? + end + end + + def patch! + insertion_point.insert patcher, empty_implementation + end + + end + +end diff --git a/lib/xdry/patching/patcher.rb b/lib/xdry/patching/patcher.rb index 3463a9e..1817e8b 100644 --- a/lib/xdry/patching/patcher.rb +++ b/lib/xdry/patching/patcher.rb @@ -11,15 +11,16 @@ def initialize end def insert_after pos, new_lines, indent = '' - do_insert_after pos.file_ref, pos.line_no - 1, new_lines, indent + do_insert_after pos.file_ref, pos.scope_after, pos.line_no - 1, new_lines, indent end def insert_before pos, new_lines, indent = '' - do_insert_after pos.file_ref, pos.line_no - 2, new_lines, indent + do_insert_after pos.file_ref, pos.scope_before, pos.line_no - 2, new_lines, indent end - def do_insert_after file_ref, line_index, new_lines, indent + def do_insert_after file_ref, start_scope, line_index, new_lines, indent new_lines = new_lines.collect { |line| line.blank? ? line : indent + line } + new_lines = new_lines.collect { |line| line.gsub("\t", INDENT_STEP) } lines = patched_lines_of(file_ref) if @verbose @@ -48,6 +49,10 @@ def do_insert_after file_ref, line_index, new_lines, indent new_lines = new_lines[0 .. -(trailing_lines_to_remove+1)] lines[line_index+1 .. line_index+1] = new_lines.collect { |line| "#{line}\n" } + [lines[line_index+1]] + + driver = ParsingDriver.new(nil) + driver.verbose = @verbose + driver.parse_fragment file_ref, new_lines, line_index+1+1, start_scope end def save!