ast_utils 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []