ast_utils 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e1090f235aaa912ae5a85844b1e10463faaf584d
4
+ data.tar.gz: 14426ab5c982587d9e8a2dcc910addf95af061df
5
+ SHA512:
6
+ metadata.gz: d699fb6c01dd21bdb32a4c95d4786167aa4a8581ef49c1facab6ee2e1fbd885f2e36dcdb6e71e8d8e20e686d75fa9bb7265c96c4896541a43e3e28a770c7fb7b
7
+ data.tar.gz: f8b6a0131c03bb38afcaa03cca9724afba47f6af3e0127aa8b8dac838d884eb6a9d0172b65fe98908cf916c8e0f384f3a6d676edc4df4a98d04b4228c785c0b9
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.13.7
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ast_utils.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # ASTUtils
2
+
3
+ ASTUtils provides some utility over parser gem AST, which aims to help analyzing Ruby programs.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'ast_utils'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install ast_utils
20
+
21
+ ## Usage
22
+
23
+ ### ASTUtils::Labeling
24
+
25
+ `ASTUTils::Labeling` implements local variables labeling.
26
+
27
+ In Ruby, local variable scope can be nested, and identification on local variables is not trivial.
28
+ This utility give labels to identify local variables.
29
+
30
+ ```rb
31
+ require "ast_utils"
32
+
33
+ node = Parser::CurrentRuby.parse(source)
34
+ labeled = ASTUtils::Labeling.translate(node: node)
35
+ ```
36
+
37
+ ### ASTUTils::Navigation
38
+
39
+ `AST` has `children` but no pointer to its parent.
40
+
41
+ ```rb
42
+ require "ast_utils"
43
+
44
+ node = Parser::CurrentRuby.parse(source)
45
+ navigation = ASTUtils::Navigation.from(node: node)
46
+
47
+ parent = navigation.parent(child_node)
48
+ ```
49
+
50
+ ### ASTUtils::Scope
51
+
52
+ `Scope` is about *scope* in Ruby.
53
+
54
+ It associates outer scope and its inner scope.
55
+
56
+ ```rb
57
+ require "ast_utils"
58
+
59
+ node = Parser::CurrentRuby.parse(source)
60
+ labeled = ASTUtils::Labeling.translate(node: node)
61
+ scope = ASTUtils::Scope.from(node: labeled)
62
+
63
+ scope.root
64
+ scope.children(root)
65
+ scope.subs(root)
66
+ ```
67
+
68
+ `children` includes `def` and iterator block, but `subs` only includes blocks.
69
+
70
+ It also defines `assignments` and `references` to refere assignments and references for local variables in the scope.
71
+
72
+ `assignments` returns pair of:
73
+
74
+ * Node which implements assignment
75
+ * Labeled local variable name which is assigned
76
+
77
+ ## Contributing
78
+
79
+ Bug reports and pull requests are welcome on GitHub at https://github.com/soutaro/ast_utils.
80
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/ast_utils.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ast_utils/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ast_utils"
8
+ spec.version = ASTUtils::VERSION
9
+ spec.authors = ["Soutaro Matsumoto"]
10
+ spec.email = ["matsumoto@soutaro.com"]
11
+
12
+ spec.summary = %q{Ruby AST Utility}
13
+ spec.description = %q{Ruby AST Utility}
14
+ spec.homepage = "https://github.com/soutaro/ast_utils"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.13"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "minitest", "~> 5.0"
26
+
27
+ spec.add_runtime_dependency "parser", "~> 2.4"
28
+ spec.add_runtime_dependency "thor", "~> 0.19.4"
29
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ast_utils"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/ast_utils ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.join(__dir__, "../lib")
4
+ require "ast_utils"
5
+ require "ast_utils/cli"
6
+
7
+ ASTUtils::CLI.start(ARGV)
@@ -0,0 +1,16 @@
1
+ require "thor"
2
+
3
+ module ASTUtils
4
+ class CLI < Thor
5
+ desc "label SCRIPTS...", "labeling Ruby scripts given as SCRIPTS..."
6
+ def label(*scripts)
7
+ scripts.map {|script| Pathname(script) }.each do |path|
8
+ puts "Parsing #{path}..."
9
+ node = Parser::CurrentRuby.parse(path.read, path.to_s)
10
+ puts "Translating node..."
11
+ labeled = Labeling.translate(node: node)
12
+ puts "#{labeled.inspect}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,178 @@
1
+ module ASTUtils
2
+ class Labeling
3
+ class LabeledName
4
+ attr_reader :name
5
+ attr_reader :label
6
+
7
+ def initialize(name:, label:)
8
+ @name = name
9
+ @label = label
10
+ end
11
+
12
+ def inspect
13
+ "#{name}@#{label}"
14
+ end
15
+
16
+ def hash
17
+ name.hash ^ label.hash
18
+ end
19
+
20
+ def ==(other)
21
+ other.is_a?(LabeledName) && other.name == name && other.label == label
22
+ end
23
+
24
+ def equal?(other)
25
+ self == other
26
+ end
27
+
28
+ def eql?(other)
29
+ self == other
30
+ end
31
+ end
32
+
33
+ include NodeHelper
34
+
35
+ attr_accessor :counter
36
+
37
+ def initialize
38
+ self.counter = 0
39
+ end
40
+
41
+ def next_label!
42
+ self.counter += 1
43
+
44
+ new_label = self.counter
45
+
46
+ if block_given?
47
+ yield new_label
48
+ else
49
+ new_label
50
+ end
51
+ end
52
+
53
+ def translate(node, env)
54
+ case node.type
55
+ when :lvasgn
56
+ children = PartialMap.apply(node.children) do |map|
57
+ map.on!(0) {|name| lookup_env(name: name, env: env) }
58
+ map.on?(1) {|child| translate(child, env) }
59
+ end
60
+
61
+ node.updated(nil, children, nil)
62
+
63
+ when :lvar
64
+ children = replace(node.children, 0) {|name| lookup_env(name: name, env: env) }
65
+ node.updated(nil, children, nil)
66
+
67
+ when :arg, :restarg, :kwarg, :kwrestarg, :blockarg
68
+ name = node.children[0]
69
+
70
+ labeled_name = LabeledName.new(name: name, label: next_label!)
71
+ env[name] = labeled_name
72
+
73
+ children = replace(node.children, 0) {|_| labeled_name }
74
+ node.updated(nil, children, nil)
75
+
76
+ when :procarg0
77
+ case node.children[0]
78
+ when AST::Node
79
+ children = map_child_node(node) {|child| translate(child, env) }
80
+ node.updated(nil, children, nil)
81
+ when Symbol
82
+ name = node.children[0]
83
+
84
+ labeled_name = LabeledName.new(name: name, label: next_label!)
85
+ env[name] = labeled_name
86
+
87
+ children = replace(node.children, 0) {|_| labeled_name }
88
+ node.updated(nil, children, nil)
89
+ else
90
+ raise "Unexpected node structure: #{node}"
91
+ end
92
+ when :optarg, :kwoptarg
93
+ children = PartialMap.apply(node.children) do |map|
94
+ map.on!(0) {|name| lookup_env(name: name, env: env) }
95
+ map.on?(1) {|child| translate(child, env) }
96
+ end
97
+
98
+ node.updated(nil, children, nil)
99
+
100
+ when :def
101
+ env_ = {}
102
+ children = map_child_node(node) {|child| translate(child, env_) }
103
+ node.updated(nil, children, nil)
104
+
105
+ when :block
106
+ children = node.children.dup
107
+ translate_child!(children, 0, env)
108
+
109
+ block_env = env.dup
110
+ translate_child!(children, 1, block_env)
111
+ translate_child!(children, 2, block_env)
112
+
113
+ node.updated(nil, children, nil)
114
+
115
+ when :class
116
+ children = PartialMap.apply(node.children) do |map|
117
+ map.on!(0) {|child| translate(child, env) }
118
+ map.on?(1) {|child| translate(child, env) }
119
+ map.on?(2) {|child| translate(child, {}) }
120
+ end
121
+
122
+ node.updated(nil, children, nil)
123
+
124
+ when :module
125
+ children = PartialMap.apply(node.children) do |map|
126
+ map.on!(0) {|child| translate(child, env) }
127
+ map.on?(1) {|child| translate(child, {}) }
128
+ end
129
+
130
+ node.updated(nil, children, nil)
131
+
132
+ when :match_with_lvasgn
133
+ names = self.class.extract_variables(node.children[0].children[0].children[0])
134
+ vars = names.map {|name| lookup_env(name: name, env: env) }
135
+
136
+ children = map_child_node(node) {|child| translate(child, env) }
137
+ node.updated(nil, children + [vars], nil)
138
+
139
+ else
140
+ children = map_child_node(node) {|child| translate(child, env) }
141
+ node.updated(nil, children, nil)
142
+ end
143
+ end
144
+
145
+ def lookup_env(name:, env:)
146
+ labeled_name = env[name]
147
+
148
+ unless labeled_name
149
+ labeled_name = LabeledName.new(name: name, label: next_label!)
150
+ env[name] = labeled_name
151
+ end
152
+
153
+ labeled_name
154
+ end
155
+
156
+ def translate_child!(children, index, env)
157
+ if children[index]
158
+ children[index] = translate(children[index], env)
159
+ end
160
+ end
161
+
162
+ def replace(array, index)
163
+ array = array.dup
164
+ array[index] = yield(array[index])
165
+ array
166
+ end
167
+
168
+ def self.extract_variables(string)
169
+ string.scan(/\(\?(\<([\w_]+)\>)|(\'([\w_]+)\')/).map do |_, b, _, d|
170
+ (b || d).to_sym
171
+ end
172
+ end
173
+
174
+ def self.translate(node:)
175
+ self.new.translate(node, {})
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,36 @@
1
+ module ASTUtils
2
+ class Navigation
3
+ include NodeHelper
4
+
5
+ attr_reader :nodes
6
+ attr_reader :parents
7
+ attr_reader :root
8
+
9
+ def initialize(node:)
10
+ @root = node
11
+ @nodes = NodeSet.new
12
+ @parents = {}
13
+ end
14
+
15
+ def construct
16
+ set_parent(root)
17
+ end
18
+
19
+ def set_parent(node)
20
+ nodes << node
21
+
22
+ each_child_node(node) do |child|
23
+ parents[child.__id__] = node
24
+ set_parent(child)
25
+ end
26
+ end
27
+
28
+ def parent(node)
29
+ parents[node.__id__]
30
+ end
31
+
32
+ def self.from(node:)
33
+ new(node: node).tap {|nav| nav.construct }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ module ASTUtils
2
+ module NodeHelper
3
+ def each_child_node(node)
4
+ if block_given?
5
+ node.children.each do |child|
6
+ if child.is_a?(AST::Node)
7
+ yield child
8
+ end
9
+ end
10
+ else
11
+ enum_for :each_child_node, node
12
+ end
13
+ end
14
+
15
+ # order preserved
16
+ def map_child_node(node)
17
+ if block_given?
18
+ node.children.each.with_object([]) do |child, array|
19
+ if child.is_a?(AST::Node)
20
+ array << yield(child)
21
+ else
22
+ array << child
23
+ end
24
+ end
25
+ else
26
+ enum_for :map_child_node, node
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,55 @@
1
+ module ASTUtils
2
+ class NodeSet
3
+ class Item
4
+ attr_reader :object
5
+
6
+ def initialize(object)
7
+ @object = object
8
+ end
9
+
10
+ def hash
11
+ object.__id__
12
+ end
13
+
14
+ def eql?(other)
15
+ other.is_a?(Item) && other.object.__id__ == object.__id__
16
+ end
17
+
18
+ def ==(other)
19
+ eql?(other)
20
+ end
21
+ end
22
+
23
+ attr_reader :set
24
+
25
+ def initialize(objects = [])
26
+ @set = Set.new(objects.map {|object| Item.new(object) })
27
+ end
28
+
29
+ def <<(node)
30
+ set << Item.new(node)
31
+ end
32
+
33
+ def delete(node)
34
+ set.delete Item.new(node)
35
+ end
36
+
37
+ def each(&block)
38
+ set.map(&:object).each(&block)
39
+ end
40
+
41
+ def empty?
42
+ size == 0
43
+ end
44
+
45
+ def size
46
+ set.size
47
+ end
48
+
49
+ include Enumerable
50
+
51
+ def +(other)
52
+ self.class.new(self.set + other.set)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,53 @@
1
+ module ASTUtils
2
+ class PartialMap
3
+ attr_reader :enumerable
4
+ attr_reader :updaters
5
+
6
+ def initialize(enumerable)
7
+ @enumerable = enumerable
8
+ @updaters = Array.new(enumerable.count)
9
+ end
10
+
11
+ def self.apply(enumerable)
12
+ map = new(enumerable)
13
+ yield map
14
+ map.apply
15
+ end
16
+
17
+ def on(index, &block)
18
+ updaters[index] = [:on, block]
19
+ end
20
+
21
+ def on!(index, &block)
22
+ updaters[index] = [:on!, block]
23
+ end
24
+
25
+ def on?(index, &block)
26
+ updaters[index] = [:on?, block]
27
+ end
28
+
29
+ def apply
30
+ enumerable.map.with_index do |value, index|
31
+ updater = updaters[index]
32
+
33
+ if updater
34
+ case updater&.first
35
+ when :on
36
+ updater.last[value]
37
+ when :on!
38
+ raise if value.nil?
39
+ updater.last[value]
40
+ when :on?
41
+ unless value.nil?
42
+ updater.last[value]
43
+ else
44
+ value
45
+ end
46
+ end
47
+ else
48
+ value
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,187 @@
1
+ module ASTUtils
2
+ class Scope
3
+ class Assignment
4
+ attr_reader :node, :variable
5
+
6
+ def initialize(node:, variable:)
7
+ @node = node
8
+ @variable = variable
9
+ end
10
+
11
+ def hash
12
+ node.__id__ ^ variable.hash
13
+ end
14
+
15
+ def eql?(other)
16
+ other.is_a?(Assignment) && other.node.__id__ == node.__id__ && other.variable == variable
17
+ end
18
+
19
+ def ==(other)
20
+ eql?(other)
21
+ end
22
+ end
23
+
24
+ include NodeHelper
25
+
26
+ attr_reader :root
27
+ attr_reader :all_scopes
28
+ attr_reader :child_scopes
29
+ attr_reader :parent_scopes
30
+ attr_reader :sub_scopes
31
+ attr_reader :super_scopes
32
+ attr_reader :assignment_nodes
33
+ attr_reader :reference_nodes
34
+
35
+ def initialize(root:)
36
+ @root = root
37
+ @all_scopes = NodeSet.new
38
+ @child_scopes = {}
39
+ @parent_scopes = {}
40
+ @sub_scopes = {}
41
+ @super_scopes = {}
42
+ @assignment_nodes = {}
43
+ @reference_nodes = {}
44
+ end
45
+
46
+ def children(scope)
47
+ child_scopes[scope.__id__]
48
+ end
49
+
50
+ def parent(scope)
51
+ parent_scopes[scope.__id__]
52
+ end
53
+
54
+ def subs(scope)
55
+ sub_scopes[scope.__id__]
56
+ end
57
+
58
+ def sup(scope)
59
+ super_scopes[scope.__id__]
60
+ end
61
+
62
+ def assignments(scope, include_subs: false)
63
+ if include_subs
64
+ subs(scope).inject(assignment_nodes[scope.__id__]) {|assignments, scope_|
65
+ assignments + assignments(scope_, include_subst: true)
66
+ }
67
+ else
68
+ assignment_nodes[scope.__id__]
69
+ end
70
+ end
71
+
72
+ def references(scope, include_subs: false)
73
+ if include_subs
74
+ subs(scope).inject(reference_nodes[scope.__id__]) {|references, scope_|
75
+ references + references(scope_, include_subs: true)
76
+ }
77
+ else
78
+ reference_nodes[scope.__id__]
79
+ end
80
+
81
+ end
82
+
83
+ def each(&block)
84
+ all_scopes.each(&block)
85
+ end
86
+
87
+ def construct
88
+ if Scope.scope_node?(root)
89
+ child_scope!(root, nil)
90
+ else
91
+ add_scope(root)
92
+ construct_node(root, root)
93
+ end
94
+ end
95
+
96
+ def add_scope(scope)
97
+ all_scopes << scope
98
+ child_scopes[scope.__id__] = NodeSet.new
99
+ sub_scopes[scope.__id__] = NodeSet.new
100
+ assignment_nodes[scope.__id__] = Set.new
101
+ reference_nodes[scope.__id__] = Set.new
102
+ end
103
+
104
+ def child_scope!(scope, parent_scope)
105
+ add_scope(scope)
106
+
107
+ if parent_scope
108
+ parent_scopes[scope.__id__] = parent_scope
109
+ child_scopes[parent_scope.__id__] << scope
110
+ end
111
+
112
+ each_child_node(scope) do |child|
113
+ construct_node(child, scope)
114
+ end
115
+ end
116
+
117
+ def nested_scope!(scope, super_scope)
118
+ add_scope(scope)
119
+
120
+ if super_scope
121
+ parent_scopes[scope.__id__] = super_scope
122
+ child_scopes[super_scope.__id__] << scope
123
+ super_scopes[scope.__id__] = super_scope
124
+ sub_scopes[super_scope.__id__] << scope
125
+ end
126
+
127
+ each_child_node(scope) do |child|
128
+ construct_node(child, scope)
129
+ end
130
+ end
131
+
132
+ def construct_node(node, current_scope)
133
+ case node.type
134
+ when :class, :module, :def
135
+ child_scope!(node, current_scope)
136
+ when :block
137
+ nested_scope!(node, current_scope)
138
+ when :lvar
139
+ reference_nodes[current_scope.__id__] << node
140
+ when :lvasgn, :arg, :optarg, :restarg, :kwarg, :kwoptarg, :kwrestarg, :blockarg
141
+ assignment_nodes[current_scope.__id__] << Assignment.new(node: node, variable: node.children[0])
142
+ construct_children(node, current_scope)
143
+ when :procarg0
144
+ case node.children[0]
145
+ when AST::Node
146
+ construct_children(node, current_scope)
147
+ else
148
+ assignment_nodes[current_scope.__id__] << Assignment.new(node: node, variable: node.children[0])
149
+ construct_children(node, current_scope)
150
+ end
151
+ when :match_with_lvasgn
152
+ node.children[2].each do |var|
153
+ assignment_nodes[current_scope.__id__] << Assignment.new(node: node, variable: var)
154
+ end
155
+
156
+ construct_children(node, current_scope)
157
+ else
158
+ construct_children(node, current_scope)
159
+ end
160
+ end
161
+
162
+ def construct_children(node, current_scope)
163
+ each_child_node(node) do |child|
164
+ construct_node(child, current_scope)
165
+ end
166
+ end
167
+
168
+ def valid_scope!(node)
169
+ unless root.equal?(node) || Scope.scope_node?(node)
170
+ raise "Invalid scope node given: #{node}"
171
+ end
172
+ end
173
+
174
+ def self.from(node:)
175
+ new(root: node).tap(&:construct)
176
+ end
177
+
178
+ def self.scope_node?(node)
179
+ case node.type
180
+ when :class, :module, :def, :block
181
+ true
182
+ else
183
+ false
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,3 @@
1
+ module ASTUtils
2
+ VERSION = "0.1.0"
3
+ end
data/lib/ast_utils.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "ast_utils/version"
2
+
3
+ require "parser/current"
4
+ require "pathname"
5
+ require "set"
6
+
7
+ require "ast_utils/node_set"
8
+ require "ast_utils/node_helper"
9
+ require "ast_utils/partial_map"
10
+ require "ast_utils/labeling"
11
+ require "ast_utils/navigation"
12
+ require "ast_utils/scope"
13
+
14
+ Parser::Builders::Default.emit_lambda = true
15
+ Parser::Builders::Default.emit_procarg0 = true
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ast_utils
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Soutaro Matsumoto
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-09-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: parser
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.4'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.19.4
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.19.4
83
+ description: Ruby AST Utility
84
+ email:
85
+ - matsumoto@soutaro.com
86
+ executables:
87
+ - ast_utils
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - README.md
95
+ - Rakefile
96
+ - ast_utils.gemspec
97
+ - bin/console
98
+ - bin/setup
99
+ - exe/ast_utils
100
+ - lib/ast_utils.rb
101
+ - lib/ast_utils/cli.rb
102
+ - lib/ast_utils/labeling.rb
103
+ - lib/ast_utils/navigation.rb
104
+ - lib/ast_utils/node_helper.rb
105
+ - lib/ast_utils/node_set.rb
106
+ - lib/ast_utils/partial_map.rb
107
+ - lib/ast_utils/scope.rb
108
+ - lib/ast_utils/version.rb
109
+ homepage: https://github.com/soutaro/ast_utils
110
+ licenses: []
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.6.8
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: Ruby AST Utility
132
+ test_files: []