module Maccro::CodeUtil
Public Class Methods
code_position_to_index(source, lineno, column)
click to toggle source
# File lib/maccro/code_util.rb, line 3 def self.code_position_to_index(source, lineno, column) source_lines = source.lines # including newline at the end of line if source_lines.size < lineno raise "too few lines for specified position: lineno:#{lineno}, column:#{column}" end counter = 1 index = 0 while counter < lineno index += source_lines.shift.size counter += 1 end if source_lines.empty? raise "too few lines for specified position: lineno:#{lineno}, column:#{column}" end # column is 0 origin if source_lines.first.size < 1 raise "empty line at the end of source" end if source_lines.first.size < column raise "too few chars in the line for specified position: lineno:#{lineno}, column:#{column}" end return index + column end
code_range_to_code(source, code_range)
click to toggle source
# File lib/maccro/code_util.rb, line 33 def self.code_range_to_code(source, code_range) source[code_range_to_range(source, code_range)] end
code_range_to_range(source, code_range)
click to toggle source
# File lib/maccro/code_util.rb, line 27 def self.code_range_to_range(source, code_range) begin_index = code_position_to_index(source, code_range.first_lineno, code_range.first_column) end_index = code_position_to_index(source, code_range.last_lineno, code_range.last_column) Range.new(begin_index, end_index, true) # exclude end char end
convert_scope_to_lambda(scope_source)
click to toggle source
# File lib/maccro/code_util.rb, line 69 def self.convert_scope_to_lambda(scope_source) raise "Scope source must start with '{'" unless scope_source.start_with?('{') raise "Scope source must end with '}'" unless scope_source.end_with?('}') if m = scope_source.match(/^\{\s*\|(.*)\|/o) matched_source = m[0] args_source = m[1] return "->(#{args_source})" + scope_source.sub(matched_source, '{') end "->" + scope_source end
dig_method_node(node, def_type, method_name_index, method_name, lineno, column)
click to toggle source
# File lib/maccro/code_util.rb, line 150 def self.dig_method_node(node, def_type, method_name_index, method_name, lineno, column) return nil unless node.is_a?(RubyVM::AbstractSyntaxTree::Node) if node.type == def_type && node.children[method_name_index] == method_name && node.first_lineno == lineno && node.first_column == column return node elsif node.respond_to?(:children) node.children.each do |n| r = dig_method_node(n, def_type, method_name_index, method_name, lineno, column) return r if r end end nil end
dig_proc_node(node, lineno, column)
click to toggle source
# File lib/maccro/code_util.rb, line 101 def self.dig_proc_node(node, lineno, column) return nil unless node.is_a?(RubyVM::AbstractSyntaxTree::Node) is_target_scope = ->(n){ n.type == :SCOPE && n.first_lineno == lineno && n.first_column == column } case node.type when :LAMBDA # ->(){ } if is_target_scope.call(node.children[0]) return node end when :ITER # method call with block (iterator?) # lambda{}, proc{} if node.children[0].type == :FCALL \ && (node.children[0].children[0] == :lambda || node.children[0].children[0] == :proc) \ && is_target_scope.call(node.children[1]) return node # Kernel.lambda, Kernel.proc{}, Proc.new{} elsif node.children[0].type == :CALL \ && node.children[0].children[0].type == :CONST \ && (node.children[0].children[0].children[0] == :Kernel && (node.children[0].children[1] == :lambda || node.children[0].children[1] == :proc) \ || node.children[0].children[0].children[0] == :Proc && node.children[0].children[1] == :new ) \ && is_target_scope.call(node.children[1]) return node end when :SCOPE # for block parameters if is_target_scope.call(node) return node end end if node.respond_to?(:children) node.children.each do |n| r = dig_proc_node(n, lineno, column) return r if r end end nil end
extend_tree_with_wrapper(tree)
click to toggle source
# File lib/maccro/code_util.rb, line 61 def self.extend_tree_with_wrapper(tree) return unless tree.is_a?(RubyVM::AbstractSyntaxTree::Node) tree.extend Maccro::DSL::ASTNodeWrapper unless tree.is_a?(Maccro::DSL::ASTNodeWrapper) tree.children.each do |c| extend_tree_with_wrapper(c) end end
get_method_node(node, method_name, lineno, column, singleton_method: false)
click to toggle source
# File lib/maccro/code_util.rb, line 140 def self.get_method_node(node, method_name, lineno, column, singleton_method: false) if singleton_method # TODO: consider receiver filter # 0: (SELF@57:6-57:10) dig_method_node(node, :DEFS, 1, method_name, lineno, column) else dig_method_node(node, :DEFN, 0, method_name, lineno, column) end end
get_proc_node(node, lineno, column)
click to toggle source
# File lib/maccro/code_util.rb, line 96 def self.get_proc_node(node, lineno, column) return nil unless node.type == :SCOPE dig_proc_node(node, lineno, column) end
get_source_path(block)
click to toggle source
# File lib/maccro/code_util.rb, line 82 def self.get_source_path(block) iseq = CodeUtil.proc_to_iseq(block) if !iseq raise "Native methods can't be redefined" end path = iseq.absolute_path if !path # STDIN or -e raise "Methods from stdin or -e can't be redefined" end source = File.read(path) return source, path end
parse_to_ast(code)
click to toggle source
# File lib/maccro/code_util.rb, line 45 def self.parse_to_ast(code) suppress_warning do RubyVM::AbstractSyntaxTree.parse(code) end end
proc_to_ast(block)
click to toggle source
# File lib/maccro/code_util.rb, line 51 def self.proc_to_ast(block) suppress_warning do RubyVM::AbstractSyntaxTree.of(block) end end
proc_to_iseq(block)
click to toggle source
# File lib/maccro/code_util.rb, line 57 def self.proc_to_iseq(block) RubyVM::InstructionSequence.of(block) end
suppress_warning() { || ... }
click to toggle source
# File lib/maccro/code_util.rb, line 37 def self.suppress_warning v = $VERBOSE $VERBOSE = nil yield ensure $VERBOSE = v end