Module: Maccro
- Defined in:
- lib/maccro.rb,
lib/maccro/dsl.rb,
lib/maccro/impl.rb,
lib/maccro/rule.rb,
lib/maccro/builtin.rb,
lib/maccro/matched.rb,
lib/maccro/version.rb,
lib/maccro/dsl/node.rb,
lib/maccro/code_util.rb,
lib/maccro/dsl/value.rb,
lib/maccro/code_range.rb,
lib/maccro/dsl/assign.rb,
lib/maccro/dsl/literal.rb,
lib/maccro/dsl/expression.rb
Defined Under Namespace
Modules: Builtin, CodeUtil, DSL, Impl Classes: CodeRange, Match, Matched, Rule
Constant Summary collapse
- VERSION =
"0.2.0"
- @@dic =
{}
- @@trace_global =
nil
Class Method Summary collapse
-
.apply(mojule, method, rules: @@dic, verbose: false, from_trace: false, get_code: false) ⇒ Object
Maccro.apply(X, X.instance_method(:yay), verbose: true).
- .clear! ⇒ Object
-
.enable(target: nil, path: nil, rules: nil) ⇒ Object
TODO: check visibility: private method is still private method even after module_eval?.
- .enable_trace(target: nil, path: nil, globally: false, rule_names: nil) ⇒ Object
- .execute(block = nil, rules: @@dic, verbose: false, get_code: false, &block_param) ⇒ Object
- .register(name, before, after, under: nil, safe_reference: false) ⇒ Object
-
.rewrite(block = nil, rules: @@dic, verbose: false, get_code: false, &block_param) ⇒ Object
Maccro.rewrite(->(){ … }).call(args).
Class Method Details
.apply(mojule, method, rules: @@dic, verbose: false, from_trace: false, get_code: false) ⇒ Object
Maccro.apply(X, X.instance_method(:yay), verbose: true)
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/maccro.rb', line 64 def self.apply(mojule, method, rules: @@dic, verbose: false, from_trace: false, get_code: false) if !method.source_location raise "Native method can't be redefined" end ast = CodeUtil.proc_to_ast(method) if !ast if from_trace # unknown and unexpected loaded ruby code (which many not have visible source) return else raise "Failed to load AST nodes - source file may be invisible: #{method}" end end # This node should be SCOPE node (just under DEFN or DEFS) # But its code range is equal to code range of DEFN/DEFS is_singleton_method = (mojule != method.owner) source, path = CodeUtil.get_source_path(method) # The reason to get the entire source code is to capture/rewrite # the exact code snippet using CodeRange (positions in the entire file) rewrite_method_code_range = nil ast, source = Impl.update_by_rules(ast, source, rules) do |src, lineno, column| CodeUtil.get_method_node(CodeUtil.parse_to_ast(src), method.name, lineno, column, singleton_method: is_singleton_method) end # required to restore code positions of the method definition first_lineno = ast.first_lineno first_column = ast.first_column rewrite_method_code_range = CodeRange.from_node(ast) if source && path && rewrite_method_code_range eval_source = (" " * first_column) + rewrite_method_code_range.get(source) # restore the original indentation return eval_source if get_code puts eval_source if verbose CodeUtil.suppress_warning do mojule.module_eval(eval_source, path, ast.first_lineno) end end end |
.clear! ⇒ Object
25 26 27 |
# File 'lib/maccro.rb', line 25 def self.clear! @@dic = {} end |
.enable(target: nil, path: nil, rules: nil) ⇒ Object
TODO: check visibility: private method is still private method even after module_eval?
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/maccro.rb', line 109 def self.enable(target: nil, path: nil, rules: nil) if target || path enable_trace(target: target, path: path, rule_names: rules) else if rules raise "Cannot enable globally with specific rules" end enable_trace(globally: true) end end |
.enable_trace(target: nil, path: nil, globally: false, rule_names: nil) ⇒ Object
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/maccro.rb', line 120 def self.enable_trace(target: nil, path: nil, globally: false, rule_names: nil) if globally && @@trace_global return nil end if rule_names rules = rule_names.map{|n| [n, @@dic[n]] }.to_h else rules = @@dic end trace = TracePoint.new(:end) do |tp| current_location = tp.path next unless globally || target == tp.self || path == current_location this = tp.self methods = ( this.instance_methods(false).map{|m| this.instance_method(m) } + this.private_instance_methods(false).map{|m| this.instance_method(m) } + # NameError: undefined singleton method `provides?' for `Bundler::RubygemsIntegration::Legacy' this.singleton_methods.map{|m| this.singleton_method(m) rescue nil }.compact ) methods.each do |method| source_location = method.source_location next if !source_location # native method next if source_location.first == '-e' next if source_location.first != current_location # methods defined in other file Maccro.apply(this, method, rules: rules, from_trace: true) end end if globally @@trace_global = trace end trace.enable nil end |
.execute(block = nil, rules: @@dic, verbose: false, get_code: false, &block_param) ⇒ Object
29 30 31 32 33 34 35 36 |
# File 'lib/maccro.rb', line 29 def self.execute(block=nil, rules: @@dic, verbose: false, get_code: false, &block_param) block = block_param if !block && block_param if block.arity > 0 raise "Block with parameters can't be executed via Maccro" end rewrite(block, rules: rules, verbose: verbose, get_code: get_code).call end |
.register(name, before, after, under: nil, safe_reference: false) ⇒ Object
14 15 16 17 18 19 20 21 22 23 |
# File 'lib/maccro.rb', line 14 def self.register(name, before, after, under: nil, safe_reference: false) # Maccro.register(:double_less_than, 'e1 < e2 < e3', 'e1 < e2 && e2 < e3') # Maccro.register(:double_greater_than, 'e1 > e2 > e3', 'e1 > e2 && e2 > e3') # Maccro.register(:double_greater_than, 'e1 < e2 < e3', 'e1 < e2 && e2 < e3', safe_reference: true) # Maccro.register(:activerecord_where_equal, 'v1 = v2', 'v1 => v2', under: 'e.where($TARGET)') if safe_reference raise NotImplementedError, "TODO: implement it" end @@dic[name] = Rule.new(name, before, after, under: under, safe_reference: safe_reference) end |
.rewrite(block = nil, rules: @@dic, verbose: false, get_code: false, &block_param) ⇒ Object
Maccro.rewrite(->(){ … }).call(args)
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/maccro.rb', line 39 def self.rewrite(block=nil, rules: @@dic, verbose: false, get_code: false, &block_param) block = block_param if !block && block_param if !block.source_location raise "Native block can't be rewritten" end ast = CodeUtil.proc_to_ast(block) if !ast raise "Failed to load AST nodes - source file may be invisible" end source, _ = CodeUtil.get_source_path(block) ast, source = Impl.update_by_rules(ast, source, rules) do |src, lineno, column| CodeUtil.get_proc_node(CodeUtil.parse_to_ast(src), lineno, column) end eval_source = if ast.type == :SCOPE CodeUtil.convert_scope_to_lambda(CodeRange.from_node(ast).get(source)) else CodeRange.from_node(ast).get(source) end return eval_source if get_code puts eval_source if verbose block.binding.eval(eval_source) end |