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 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
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # CHANGELOG
2
+
3
+ ## 1.0.0 (2024-04-27)
4
+
5
+ * Initial release
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
+ [![Build Status](https://github.com/synvert-hq/node-visitor-ruby/actions/workflows/main.yml/badge.svg)](https://github.com/synvert-hq/node-visitor-ruby/actions/workflows/main.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/node_visitor.svg)](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,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -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
@@ -0,0 +1,3 @@
1
+ class NodeVisitor
2
+ VERSION = "1.0.0"
3
+ end
@@ -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: []