node_visitor 1.0.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 +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +53 -0
- data/README.md +68 -0
- data/Rakefile +6 -0
- data/lib/node_visitor/adapter/parser.rb +18 -0
- data/lib/node_visitor/adapter/prism.rb +36 -0
- data/lib/node_visitor/adapter/syntax_tree.rb +19 -0
- data/lib/node_visitor/adapter.rb +24 -0
- data/lib/node_visitor/version.rb +3 -0
- data/lib/node_visitor.rb +55 -0
- data/node_visitor.gemspec +32 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a596ef648c5a209a47fa0355a8956a14df436f9b82d178560323809387a235aa
|
4
|
+
data.tar.gz: 98d3fe4e938c5d841c9d1e34c037cdfa1f1c209cf521c005f732d07cefc8da65
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: beb38d152e58c83c48c7a1d714b4ba635e6ce226e804d04e00b38568afc6b10f6f4a82e9074ac1e998d30ebd0ff2cdcb271c09748cdb2e3993a132d3e9f4ab06
|
7
|
+
data.tar.gz: 5ad89b6ee986b7ac6a7d4ff7834905633a5302018a2e23de16769dab74bd3798ac4ace624f6c5641db3f2ef26de833914b710ca7bc744f7ddb65255c01460732
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in node_visitor.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
gem "rspec", "~> 3.0"
|
10
|
+
gem 'parser_node_ext'
|
11
|
+
gem 'prism_ext'
|
12
|
+
gem 'syntax_tree_ext'
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
node_visitor (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
diff-lcs (1.5.1)
|
11
|
+
parser (3.3.0.5)
|
12
|
+
ast (~> 2.4.1)
|
13
|
+
racc
|
14
|
+
parser_node_ext (1.3.2)
|
15
|
+
parser
|
16
|
+
prettier_print (1.2.1)
|
17
|
+
prism (0.27.0)
|
18
|
+
prism_ext (0.3.2)
|
19
|
+
prism
|
20
|
+
racc (1.7.3)
|
21
|
+
rake (13.2.1)
|
22
|
+
rspec (3.13.0)
|
23
|
+
rspec-core (~> 3.13.0)
|
24
|
+
rspec-expectations (~> 3.13.0)
|
25
|
+
rspec-mocks (~> 3.13.0)
|
26
|
+
rspec-core (3.13.0)
|
27
|
+
rspec-support (~> 3.13.0)
|
28
|
+
rspec-expectations (3.13.0)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.13.0)
|
31
|
+
rspec-mocks (3.13.0)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.13.0)
|
34
|
+
rspec-support (3.13.1)
|
35
|
+
syntax_tree (6.2.0)
|
36
|
+
prettier_print (>= 1.2.0)
|
37
|
+
syntax_tree_ext (0.8.2)
|
38
|
+
syntax_tree
|
39
|
+
|
40
|
+
PLATFORMS
|
41
|
+
ruby
|
42
|
+
x86_64-darwin-23
|
43
|
+
|
44
|
+
DEPENDENCIES
|
45
|
+
node_visitor!
|
46
|
+
parser_node_ext
|
47
|
+
prism_ext
|
48
|
+
rake (~> 13.0)
|
49
|
+
rspec (~> 3.0)
|
50
|
+
syntax_tree_ext
|
51
|
+
|
52
|
+
BUNDLED WITH
|
53
|
+
2.3.7
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# NodeVisitor
|
2
|
+
|
3
|
+
[](https://github.com/synvert-hq/node-visitor-ruby/actions/workflows/main.yml)
|
4
|
+
[](https://rubygems.org/gems/node_visitor)
|
5
|
+
|
6
|
+
NodeVisitor allows you to define callbacks based on node type, then visit the AST nodes.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'node_visitor'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install node_visitor
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
1. initialize the visitor
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
visitor = NodeVisitor.new(adapter: 'prism')
|
30
|
+
```
|
31
|
+
|
32
|
+
2. add callbacks
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
# this will be triggered when it starts to visit class_node
|
36
|
+
visitor.add_callback(:class_node, at: 'start') do |node|
|
37
|
+
node.name # get the name of class node
|
38
|
+
end
|
39
|
+
|
40
|
+
# this will be triggered after it finishes to visit class_node
|
41
|
+
visitor.add_callback(:class_node, at: 'end') do |node|
|
42
|
+
node.name # get the name of class node
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
There is a special node type, `:all`, which is triggered before or after visiting all nodes.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# this will be triggered after it finishes to visit all nodes
|
50
|
+
visitor.add_callback(:class_node, at: 'end') do
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
3. visit the AST node
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
visitor.visit(node)
|
58
|
+
```
|
59
|
+
|
60
|
+
## Development
|
61
|
+
|
62
|
+
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.
|
63
|
+
|
64
|
+
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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
65
|
+
|
66
|
+
## Contributing
|
67
|
+
|
68
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/synvert-hq/node_visitor.
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'parser'
|
4
|
+
require 'parser_node_ext'
|
5
|
+
|
6
|
+
class NodeVisitor::ParserAdapter
|
7
|
+
def is_node?(node)
|
8
|
+
node.is_a?(Parser::AST::Node)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_node_type(node)
|
12
|
+
node.type
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_children(node)
|
16
|
+
node.children
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'prism'
|
4
|
+
require 'prism_ext'
|
5
|
+
|
6
|
+
class NodeVisitor::PrismAdapter
|
7
|
+
def is_node?(node)
|
8
|
+
node.is_a?(Prism::Node)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_node_type(node)
|
12
|
+
node.type
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_children(node)
|
16
|
+
keys = []
|
17
|
+
children = []
|
18
|
+
node.deconstruct_keys([]).each do |key, value|
|
19
|
+
next if [:flags, :location].include?(key)
|
20
|
+
|
21
|
+
if key.to_s.end_with?('_loc')
|
22
|
+
new_key = key.to_s[0..-5]
|
23
|
+
unless keys.include?(new_key)
|
24
|
+
keys << new_key
|
25
|
+
children << node.send(new_key)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
unless keys.include?(key.to_s)
|
29
|
+
keys << key.to_s
|
30
|
+
children << value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
children
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'syntax_tree'
|
4
|
+
require 'syntax_tree_ext'
|
5
|
+
|
6
|
+
class NodeVisitor::SyntaxTreeAdapter
|
7
|
+
def is_node?(node)
|
8
|
+
node.is_a?(SyntaxTree::Node)
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_node_type(node)
|
12
|
+
node.class.name.split('::').last.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
def get_children(node)
|
16
|
+
node.deconstruct_keys([]).filter { |key, _value| ![:location, :comments].include?(key) }
|
17
|
+
.values
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Abstract Adapter class
|
4
|
+
class NodeVisitor::Adapter
|
5
|
+
# Check if it is a node
|
6
|
+
# @return [Boolean]
|
7
|
+
def is_node?(node)
|
8
|
+
raise NotImplementedError, 'get_node_type is not implemented'
|
9
|
+
end
|
10
|
+
|
11
|
+
# Get the type of node
|
12
|
+
# @param node [Node] ast node
|
13
|
+
# @return [Symbol] node type
|
14
|
+
def get_node_type(node)
|
15
|
+
raise NotImplementedError, 'get_node_type is not implemented'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get the children of node
|
19
|
+
# @param node [Node] ast node
|
20
|
+
# @return [Array<Node>] node children
|
21
|
+
def get_children(node)
|
22
|
+
raise NotImplementedError, 'get_children is not implemented'
|
23
|
+
end
|
24
|
+
end
|
data/lib/node_visitor.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative "node_visitor/version"
|
2
|
+
|
3
|
+
class NodeVisitor
|
4
|
+
class InvalidAdapterError < StandardError; end
|
5
|
+
|
6
|
+
autoload :Adapter, "node_visitor/adapter"
|
7
|
+
autoload :ParserAdapter, "node_visitor/adapter/parser"
|
8
|
+
autoload :PrismAdapter, "node_visitor/adapter/prism"
|
9
|
+
autoload :SyntaxTreeAdapter, "node_visitor/adapter/syntax_tree"
|
10
|
+
|
11
|
+
def initialize(adapter:)
|
12
|
+
@adapter = get_adapter_instance(adapter)
|
13
|
+
@callbacks = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_callback(node_type, at:, &block)
|
17
|
+
@callbacks[node_type] ||= []
|
18
|
+
@callbacks[node_type] << { block: block, at: at }
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit(node, block_context = self)
|
22
|
+
callbacks = @callbacks[:all]
|
23
|
+
callbacks.each { |callback| block_context.instance_exec(node, &callback[:block]) if callback[:at] == 'start' } if callbacks
|
24
|
+
visit_node(node)
|
25
|
+
callbacks.each { |callback| block_context.instance_exec(node, &callback[:block]) if callback[:at] == 'end' } if callbacks
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def get_adapter_instance(adapter)
|
31
|
+
case adapter.to_sym
|
32
|
+
when :parser
|
33
|
+
ParserAdapter.new
|
34
|
+
when :syntax_tree
|
35
|
+
SyntaxTreeAdapter.new
|
36
|
+
when :prism
|
37
|
+
PrismAdapter.new
|
38
|
+
else
|
39
|
+
raise InvalidAdapterError, "adapter #{adapter} is not supported"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def visit_node(node)
|
44
|
+
if node.is_a?(Array)
|
45
|
+
node.each { |child_node| visit_node(child_node) }
|
46
|
+
return
|
47
|
+
end
|
48
|
+
return unless @adapter.is_node?(node)
|
49
|
+
|
50
|
+
callbacks = @callbacks[@adapter.get_node_type(node)]
|
51
|
+
callbacks.each { |callback| instance_exec(node, &callback[:block]) if callback[:at] == 'start' } if callbacks
|
52
|
+
@adapter.get_children(node).each { |child_node| visit_node(child_node) }
|
53
|
+
callbacks.each { |callback| instance_exec(node, &callback[:block]) if callback[:at] == 'end' } if callbacks
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require_relative "lib/node_visitor/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = "node_visitor"
|
6
|
+
spec.version = NodeVisitor::VERSION
|
7
|
+
spec.authors = ["Richard Huang"]
|
8
|
+
spec.email = ["flyerhzm@gmail.com"]
|
9
|
+
|
10
|
+
spec.summary = "visit ast nodes"
|
11
|
+
spec.description = "visit ast nodes"
|
12
|
+
spec.homepage = "https://github.com/synvert-hq/node-visitor-ruby"
|
13
|
+
spec.required_ruby_version = ">= 2.6.0"
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/synvert-hq/node-visitor-ruby"
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/synvert-hq/node-visitor-ruby/blob/master/CHANGELOG.md"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
23
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
spec.bindir = "exe"
|
27
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
# For more information and examples about making a new gem, check out our
|
31
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: node_visitor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Richard Huang
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-04-27 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: visit ast nodes
|
14
|
+
email:
|
15
|
+
- flyerhzm@gmail.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- ".rspec"
|
21
|
+
- CHANGELOG.md
|
22
|
+
- Gemfile
|
23
|
+
- Gemfile.lock
|
24
|
+
- README.md
|
25
|
+
- Rakefile
|
26
|
+
- lib/node_visitor.rb
|
27
|
+
- lib/node_visitor/adapter.rb
|
28
|
+
- lib/node_visitor/adapter/parser.rb
|
29
|
+
- lib/node_visitor/adapter/prism.rb
|
30
|
+
- lib/node_visitor/adapter/syntax_tree.rb
|
31
|
+
- lib/node_visitor/version.rb
|
32
|
+
- node_visitor.gemspec
|
33
|
+
homepage: https://github.com/synvert-hq/node-visitor-ruby
|
34
|
+
licenses: []
|
35
|
+
metadata:
|
36
|
+
homepage_uri: https://github.com/synvert-hq/node-visitor-ruby
|
37
|
+
source_code_uri: https://github.com/synvert-hq/node-visitor-ruby
|
38
|
+
changelog_uri: https://github.com/synvert-hq/node-visitor-ruby/blob/master/CHANGELOG.md
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.6.0
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubygems_version: 3.5.9
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: visit ast nodes
|
58
|
+
test_files: []
|