parser_node_ext 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
+ SHA256:
3
+ metadata.gz: c0d6b0cc2e9e72c692cbc566acb7ef8cd180f428b5f473ce82457e26bdffc661
4
+ data.tar.gz: 37de56dc2293bb387ca423291bf7a6fa84be97ebf21c1652aba095b76cc9817d
5
+ SHA512:
6
+ metadata.gz: 998948687de1e0555ae1f8d2e69452c382a4320d69f5106d75ba6855a5ef0b3718bf3a28acf36a3f1a1f0e4fc86f19ed7f5d8c9e482719ddf32d26fdf3887b82
7
+ data.tar.gz: f339dfc3553d68ddffa96ffd4903832346cc1d29cb679eb1ec21dbe94a64551af9535de1f0d1548b90ef8e49df017cf8efa3a2f0503268cb4a9dcbdc49ed109a
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in parser_node_ext.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+
10
+ gem "rspec", "~> 3.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ parser_node_ext (0.1.0)
5
+ parser
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ diff-lcs (1.5.0)
12
+ parser (3.1.2.0)
13
+ ast (~> 2.4.1)
14
+ rake (13.0.6)
15
+ rspec (3.11.0)
16
+ rspec-core (~> 3.11.0)
17
+ rspec-expectations (~> 3.11.0)
18
+ rspec-mocks (~> 3.11.0)
19
+ rspec-core (3.11.0)
20
+ rspec-support (~> 3.11.0)
21
+ rspec-expectations (3.11.0)
22
+ diff-lcs (>= 1.2.0, < 2.0)
23
+ rspec-support (~> 3.11.0)
24
+ rspec-mocks (3.11.1)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.11.0)
27
+ rspec-support (3.11.0)
28
+
29
+ PLATFORMS
30
+ x86_64-darwin-21
31
+
32
+ DEPENDENCIES
33
+ parser_node_ext!
34
+ rake (~> 13.0)
35
+ rspec (~> 3.0)
36
+
37
+ BUNDLED WITH
38
+ 2.3.7
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # ParserNodeExt
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/parser_node_ext`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'parser_node_ext'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install parser_node_ext
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/parser_node_ext.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ParserNodeExt
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "parser_node_ext/version"
4
+
5
+ require 'parser'
6
+
7
+ module ParserNodeExt
8
+ class Error < StandardError; end
9
+
10
+ class MethodNotSupported < StandardError; end
11
+ # Your code goes here...
12
+
13
+ TYPE_CHILDREN = {
14
+ and: %i[left_value right_value],
15
+ arg: %i[name],
16
+ begin: %i[body],
17
+ block: %i[caller arguments body],
18
+ blockarg: %i[name],
19
+ const: %i[parent_const name],
20
+ class: %i[name parent_class body],
21
+ csend: %i[receiver message arguments],
22
+ cvasgn: %i[left_value right_value],
23
+ cvar: %i[name],
24
+ def: %i[name arguments body],
25
+ definded?: %i[arguments],
26
+ defs: %i[self name arguments body],
27
+ hash: %i[pairs],
28
+ ivasgn: %i[left_value right_value],
29
+ ivar: %i[name],
30
+ lvar: %i[name],
31
+ lvasgn: %i[left_value right_value],
32
+ masgn: %i[left_value right_value],
33
+ module: %i[name body],
34
+ or: %i[left_value right_value],
35
+ or_asgn: %i[left_value right_value],
36
+ pair: %i[key value],
37
+ restarg: %i[name],
38
+ send: %i[receiver message arguments],
39
+ super: %i[arguments],
40
+ zsuper: %i[]
41
+ }
42
+
43
+ def self.included(base)
44
+ base.class_eval do
45
+ # Initialize a Node.
46
+ #
47
+ # It extends {Parser::AST::Node} and set parent for its child nodes.
48
+ def initialize(type, children = [], properties = {})
49
+ @mutable_attributes = {}
50
+ super
51
+ # children could be nil for s(:array)
52
+ Array(children).each do |child_node|
53
+ if child_node.is_a?(Parser::AST::Node)
54
+ child_node.parent = self
55
+ end
56
+ end
57
+ end
58
+
59
+ # Get the parent node.
60
+ # @return [Parser::AST::Node] parent node.
61
+ def parent
62
+ @mutable_attributes[:parent]
63
+ end
64
+
65
+ # Set the parent node.
66
+ # @param node [Parser::AST::Node] parent node.
67
+ def parent=(node)
68
+ @mutable_attributes[:parent] = node
69
+ end
70
+
71
+ # Get the sibling nodes.
72
+ # @return [Array<Parser::AST::Node>] sibling nodes.
73
+ def siblings
74
+ index = parent.children.index(self)
75
+ parent.children[index + 1..]
76
+ end
77
+
78
+ # Dyamically defined method
79
+ # caller, key, left_value, message, name, pairs, parent_class, parent_const, receivr, rgith_value and value.
80
+ # based on const TYPE_CHILDREN.
81
+ %i[
82
+ caller
83
+ key
84
+ left_value
85
+ message
86
+ name
87
+ pairs
88
+ parent_class
89
+ parent_const
90
+ receiver
91
+ right_value
92
+ value
93
+ ].each do |method_name|
94
+ define_method(method_name) do
95
+ index = TYPE_CHILDREN[type]&.index(method_name)
96
+ return children[index] if index
97
+
98
+ raise Synvert::Core::MethodNotSupported, "#{method_name} is not handled for #{debug_info}"
99
+ end
100
+ end
101
+
102
+ # Return the left value of node.
103
+ # It supports :and, :cvagn, :lvasgn, :masgn, :or and :or_asgn nodes.
104
+ # @example
105
+ # node # s(:or_asgn, s(:lvasgn, :a), s(:int, 1))
106
+ # node.left_value # :a
107
+ # @return [Parser::AST::Node] left value of node.
108
+ # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
109
+ def left_value
110
+ return children[0].children[0] if type == :or_asgn
111
+
112
+ index = TYPE_CHILDREN[type]&.index(:left_value)
113
+ return children[index] if index
114
+
115
+ raise Synvert::Core::MethodNotSupported, "#{left_value} is not handled for #{debug_info}"
116
+ end
117
+
118
+ # Get arguments of node.
119
+ # It supports :block, :csend, :def, :defined?, :defs and :send nodes.
120
+ # @example
121
+ # node # s(:send, s(:const, nil, :FactoryGirl), :create, s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post"))))
122
+ # node.arguments # [s(:sym, :post), s(:hash, s(:pair, s(:sym, :title), s(:str, "post")))]
123
+ # @return [Array<Parser::AST::Node>] arguments of node.
124
+ # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
125
+ def arguments
126
+ case type
127
+ when :def, :block
128
+ children[1].children
129
+ when :defs
130
+ children[2].children
131
+ when :send, :csend
132
+ children[2..-1]
133
+ when :defined?
134
+ children
135
+ else
136
+ raise Synvert::Core::MethodNotSupported, "arguments is not handled for #{debug_info}"
137
+ end
138
+ end
139
+
140
+ # Get body of node.
141
+ # It supports :begin, :block, :class, :def, :defs and :module node.
142
+ # @example
143
+ # node # s(:block, s(:send, s(:const, nil, :RSpec), :configure), s(:args, s(:arg, :config)), s(:send, nil, :include, s(:const, s(:const, nil, :EmailSpec), :Helpers)))
144
+ # node.body # [s(:send, nil, :include, s(:const, s(:const, nil, :EmailSpec), :Helpers))]
145
+ # @return [Array<Parser::AST::Node>] body of node.
146
+ # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
147
+ def body
148
+ case type
149
+ when :begin
150
+ children
151
+ when :def, :block, :class, :module
152
+ return [] if children[2].nil?
153
+
154
+ :begin == children[2].type ? children[2].body : children[2..-1]
155
+ when :defs
156
+ return [] if children[3].nil?
157
+
158
+ :begin == children[3].type ? children[3].body : children[3..-1]
159
+ else
160
+ raise Synvert::Core::MethodNotSupported, "body is not handled for #{debug_info}"
161
+ end
162
+ end
163
+
164
+ # Get condition of node.
165
+ # It supports :if node.
166
+ # @example
167
+ # node # s(:if, s(:defined?, s(:const, nil, :Bundler)), nil, nil)
168
+ # node.condition # s(:defined?, s(:const, nil, :Bundler))
169
+ # @return [Parser::AST::Node] condition of node.
170
+ # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
171
+ def condition
172
+ if :if == type
173
+ children[0]
174
+ else
175
+ raise Synvert::Core::MethodNotSupported, "condition is not handled for #{debug_info}"
176
+ end
177
+ end
178
+
179
+ # Get keys of :hash node.
180
+ # @example
181
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
182
+ # node.keys # [s(:sym, :foo), s(:str, "foo")]
183
+ # @return [Array<Parser::AST::Node>] keys of node.
184
+ # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
185
+ def keys
186
+ if :hash == type
187
+ children.map { |child| child.children[0] }
188
+ else
189
+ raise Synvert::Core::MethodNotSupported, "keys is not handled for #{debug_info}"
190
+ end
191
+ end
192
+
193
+ # Get values of :hash node.
194
+ # @example
195
+ # node # s(:hash, s(:pair, s(:sym, :foo), s(:sym, :bar)), s(:pair, s(:str, "foo"), s(:str, "bar")))
196
+ # node.values # [s(:sym, :bar), s(:str, "bar")]
197
+ # @return [Array<Parser::AST::Node>] values of node.
198
+ # @raise [Synvert::Core::MethodNotSupported] if calls on other node.
199
+ def values
200
+ if :hash == type
201
+ children.map { |child| child.children[1] }
202
+ else
203
+ raise Synvert::Core::MethodNotSupported, "keys is not handled for #{debug_info}"
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ # Extend Parser::AST::Node.
211
+ # {https://github.com/whitequark/parser/blob/master/lib/parser/ast/node.rb}
212
+ #
213
+ # Rules
214
+ #
215
+ # Synvert compares ast nodes with key / value pairs, each ast node has
216
+ # multiple attributes, e.g. +receiver+, +message+ and +arguments+, it
217
+ # matches only when all of key / value pairs match.
218
+ #
219
+ # +type: 'send', message: :include, arguments: ['FactoryGirl::Syntax::Methods']+
220
+ #
221
+ # Synvert does comparison based on the value type
222
+ #
223
+ # 1. if value is a symbol, then compares ast node value as symbol, e.g. +message: :include+
224
+ # 2. if value is a string, then compares ast node original source code, e.g. +name: 'Synvert::Application'+
225
+ # 3. if value is a regexp, then compares ast node original source code, e.g. +message: /find_all_by_/+
226
+ # 4. if value is an array, then compares each ast node, e.g. +arguments: ['FactoryGirl::Syntax::Methods']+
227
+ # 5. if value is nil, then check if ast node is nil, e.g. +arguments: [nil]+
228
+ # 6. if value is true or false, then check if ast node is :true or :false, e.g. +arguments: [false]+
229
+ # 7. if value is ast, then compare ast node directly, e.g. +to_ast: Parser::CurrentRuby.parse("self.class.serialized_attributes")+
230
+ #
231
+ # It can also compare nested key / value pairs, like
232
+ #
233
+ # +type: 'send', receiver: { type: 'send', receiver: { type: 'send', message: 'config' }, message: 'active_record' }, message: 'identity_map='+
234
+ #
235
+ # Source Code to Ast Node
236
+ # {https://synvert-playground.xinminlabs.com/ruby}
237
+ class Parser::AST::Node
238
+ include ParserNodeExt
239
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/parser_node_ext/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "parser_node_ext"
7
+ spec.version = ParserNodeExt::VERSION
8
+ spec.authors = ["Richard Huang"]
9
+ spec.email = ["flyerhzm@gmail.com"]
10
+
11
+ spec.summary = "extend parser node"
12
+ spec.description = "extend parser node, add parent and sibling, use meaning properties to get child node"
13
+ spec.homepage = "https://github.com/xinminlabs/parser_node_ext"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/xinminlabs/parser_node_ext"
18
+ spec.metadata["changelog_uri"] = "https://github.com/xinminlabs/parser_node_ext/blob/main/CHANGELOG.md"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ # Uncomment to register a new dependency of your gem
32
+ spec.add_dependency "parser"
33
+
34
+ # For more information and examples about making a new gem, check out our
35
+ # guide at: https://bundler.io/guides/creating_gem.html
36
+ end
@@ -0,0 +1,18 @@
1
+ module ParserNodeExt
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+
5
+ def arguments: Array[Parser::AST::Node]
6
+ def body: Array[Parser::AST::Node]
7
+ def caller: Parser::AST::Node
8
+ def key: Parser::AST::Node
9
+ def left_value: Parser::AST::Node | Symbol
10
+ def message: Symbol
11
+ def name: Parser::AST::Node | Symbol
12
+ def pairs: Array[Parser::AST::Node]
13
+ def parent_class: Parser::AST::Node
14
+ def receiver: Parser::AST::Node
15
+ def right_value: Parser::AST::Node
16
+ def self: Parser::AST::Node
17
+ def value: Parser::AST::Node
18
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parser_node_ext
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Richard Huang
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-06-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: extend parser node, add parent and sibling, use meaning properties to
28
+ get child node
29
+ email:
30
+ - flyerhzm@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".rspec"
36
+ - Gemfile
37
+ - Gemfile.lock
38
+ - README.md
39
+ - Rakefile
40
+ - lib/parser_node_ext.rb
41
+ - lib/parser_node_ext/version.rb
42
+ - parser_node_ext.gemspec
43
+ - sig/parser_node_ext.rbs
44
+ homepage: https://github.com/xinminlabs/parser_node_ext
45
+ licenses: []
46
+ metadata:
47
+ homepage_uri: https://github.com/xinminlabs/parser_node_ext
48
+ source_code_uri: https://github.com/xinminlabs/parser_node_ext
49
+ changelog_uri: https://github.com/xinminlabs/parser_node_ext/blob/main/CHANGELOG.md
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 2.6.0
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.3.7
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: extend parser node
69
+ test_files: []