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 +7 -0
- data/.rspec +3 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +38 -0
- data/README.md +35 -0
- data/Rakefile +8 -0
- data/lib/parser_node_ext/version.rb +5 -0
- data/lib/parser_node_ext.rb +239 -0
- data/parser_node_ext.gemspec +36 -0
- data/sig/parser_node_ext.rbs +18 -0
- metadata +69 -0
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
data/Gemfile
ADDED
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,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: []
|