syntax_tree 3.6.3 → 4.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.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "3.6.3"
4
+ VERSION = "4.0.0"
5
5
  end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ # The environment class is used to keep track of local variables and arguments
5
+ # inside a particular scope
6
+ class Environment
7
+ # [Array[Local]] The local variables and arguments defined in this
8
+ # environment
9
+ attr_reader :locals
10
+
11
+ # This class tracks the occurrences of a local variable or argument
12
+ class Local
13
+ # [Symbol] The type of the local (e.g. :argument, :variable)
14
+ attr_reader :type
15
+
16
+ # [Array[Location]] The locations of all definitions and assignments of
17
+ # this local
18
+ attr_reader :definitions
19
+
20
+ # [Array[Location]] The locations of all usages of this local
21
+ attr_reader :usages
22
+
23
+ # initialize: (Symbol type) -> void
24
+ def initialize(type)
25
+ @type = type
26
+ @definitions = []
27
+ @usages = []
28
+ end
29
+
30
+ # add_definition: (Location location) -> void
31
+ def add_definition(location)
32
+ @definitions << location
33
+ end
34
+
35
+ # add_usage: (Location location) -> void
36
+ def add_usage(location)
37
+ @usages << location
38
+ end
39
+ end
40
+
41
+ # initialize: (Environment | nil parent) -> void
42
+ def initialize(parent = nil)
43
+ @locals = {}
44
+ @parent = parent
45
+ end
46
+
47
+ # Adding a local definition will either insert a new entry in the locals
48
+ # hash or append a new definition location to an existing local. Notice that
49
+ # it's not possible to change the type of a local after it has been
50
+ # registered
51
+ # add_local_definition: (Ident | Label identifier, Symbol type) -> void
52
+ def add_local_definition(identifier, type)
53
+ name = identifier.value.delete_suffix(":")
54
+
55
+ @locals[name] ||= Local.new(type)
56
+ @locals[name].add_definition(identifier.location)
57
+ end
58
+
59
+ # Adding a local usage will either insert a new entry in the locals
60
+ # hash or append a new usage location to an existing local. Notice that
61
+ # it's not possible to change the type of a local after it has been
62
+ # registered
63
+ # add_local_usage: (Ident | Label identifier, Symbol type) -> void
64
+ def add_local_usage(identifier, type)
65
+ name = identifier.value.delete_suffix(":")
66
+
67
+ @locals[name] ||= Local.new(type)
68
+ @locals[name].add_usage(identifier.location)
69
+ end
70
+
71
+ # Try to find the local given its name in this environment or any of its
72
+ # parents
73
+ # find_local: (String name) -> Local | nil
74
+ def find_local(name)
75
+ local = @locals[name]
76
+ return local unless local.nil?
77
+
78
+ @parent&.find_local(name)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ # WithEnvironment is a module intended to be included in classes inheriting
5
+ # from Visitor. The module overrides a few visit methods to automatically keep
6
+ # track of local variables and arguments defined in the current environment.
7
+ # Example usage:
8
+ # class MyVisitor < Visitor
9
+ # include WithEnvironment
10
+ #
11
+ # def visit_ident(node)
12
+ # # Check if we're visiting an identifier for an argument, a local
13
+ # variable or something else
14
+ # local = current_environment.find_local(node)
15
+ #
16
+ # if local.type == :argument
17
+ # # handle identifiers for arguments
18
+ # elsif local.type == :variable
19
+ # # handle identifiers for variables
20
+ # else
21
+ # # handle other identifiers, such as method names
22
+ # end
23
+ # end
24
+ module WithEnvironment
25
+ def current_environment
26
+ @current_environment ||= Environment.new
27
+ end
28
+
29
+ def with_new_environment
30
+ previous_environment = @current_environment
31
+ @current_environment = Environment.new(previous_environment)
32
+ yield
33
+ ensure
34
+ @current_environment = previous_environment
35
+ end
36
+
37
+ # Visits for nodes that create new environments, such as classes, modules
38
+ # and method definitions
39
+ def visit_class(node)
40
+ with_new_environment { super }
41
+ end
42
+
43
+ def visit_module(node)
44
+ with_new_environment { super }
45
+ end
46
+
47
+ def visit_method_add_block(node)
48
+ with_new_environment { super }
49
+ end
50
+
51
+ def visit_def(node)
52
+ with_new_environment { super }
53
+ end
54
+
55
+ def visit_defs(node)
56
+ with_new_environment { super }
57
+ end
58
+
59
+ def visit_def_endless(node)
60
+ with_new_environment { super }
61
+ end
62
+
63
+ # Visit for keeping track of local arguments, such as method and block
64
+ # arguments
65
+ def visit_params(node)
66
+ node.requireds.each do |param|
67
+ @current_environment.add_local_definition(param, :argument)
68
+ end
69
+
70
+ node.posts.each do |param|
71
+ @current_environment.add_local_definition(param, :argument)
72
+ end
73
+
74
+ node.keywords.each do |param|
75
+ @current_environment.add_local_definition(param.first, :argument)
76
+ end
77
+
78
+ node.optionals.each do |param|
79
+ @current_environment.add_local_definition(param.first, :argument)
80
+ end
81
+
82
+ super
83
+ end
84
+
85
+ def visit_rest_param(node)
86
+ name = node.name
87
+ @current_environment.add_local_definition(name, :argument) if name
88
+
89
+ super
90
+ end
91
+
92
+ def visit_kwrest_param(node)
93
+ name = node.name
94
+ @current_environment.add_local_definition(name, :argument) if name
95
+
96
+ super
97
+ end
98
+
99
+ def visit_blockarg(node)
100
+ name = node.name
101
+ @current_environment.add_local_definition(name, :argument) if name
102
+
103
+ super
104
+ end
105
+
106
+ # Visit for keeping track of local variable definitions
107
+ def visit_var_field(node)
108
+ value = node.value
109
+
110
+ if value.is_a?(SyntaxTree::Ident)
111
+ @current_environment.add_local_definition(value, :variable)
112
+ end
113
+
114
+ super
115
+ end
116
+
117
+ alias visit_pinned_var_ref visit_var_field
118
+
119
+ # Visits for keeping track of variable and argument usages
120
+ def visit_aref_field(node)
121
+ name = node.collection.value
122
+ @current_environment.add_local_usage(name, :variable) if name
123
+
124
+ super
125
+ end
126
+
127
+ def visit_var_ref(node)
128
+ value = node.value
129
+
130
+ if value.is_a?(SyntaxTree::Ident)
131
+ definition = @current_environment.find_local(value.value)
132
+
133
+ if definition
134
+ @current_environment.add_local_usage(value, definition.type)
135
+ end
136
+ end
137
+
138
+ super
139
+ end
140
+ end
141
+ end
data/lib/syntax_tree.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "delegate"
4
3
  require "etc"
5
4
  require "json"
6
5
  require "pp"
@@ -10,7 +9,6 @@ require "stringio"
10
9
 
11
10
  require_relative "syntax_tree/formatter"
12
11
  require_relative "syntax_tree/node"
13
- require_relative "syntax_tree/parser"
14
12
  require_relative "syntax_tree/version"
15
13
 
16
14
  require_relative "syntax_tree/basic_visitor"
@@ -19,6 +17,20 @@ require_relative "syntax_tree/visitor/field_visitor"
19
17
  require_relative "syntax_tree/visitor/json_visitor"
20
18
  require_relative "syntax_tree/visitor/match_visitor"
21
19
  require_relative "syntax_tree/visitor/pretty_print_visitor"
20
+ require_relative "syntax_tree/visitor/environment"
21
+ require_relative "syntax_tree/visitor/with_environment"
22
+
23
+ require_relative "syntax_tree/parser"
24
+
25
+ # We rely on Symbol#name being available, which is only available in Ruby 3.0+.
26
+ # In case we're running on an older Ruby version, we polyfill it here.
27
+ unless :+.respond_to?(:name)
28
+ class Symbol # rubocop:disable Style/Documentation
29
+ def name
30
+ to_s.freeze
31
+ end
32
+ end
33
+ end
22
34
 
23
35
  # Syntax Tree is a suite of tools built on top of the internal CRuby parser. It
24
36
  # provides the ability to generate a syntax tree from source, as well as the
data/syntax_tree.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = %w[lib]
27
27
 
28
- spec.add_dependency "prettier_print"
28
+ spec.add_dependency "prettier_print", ">= 1.0.0"
29
29
 
30
30
  spec.add_development_dependency "bundler"
31
31
  spec.add_development_dependency "minitest"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntax_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.3
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Newton
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-11 00:00:00.000000000 Z
11
+ date: 2022-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prettier_print
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 1.0.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: 1.0.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -137,10 +137,12 @@ files:
137
137
  - lib/syntax_tree/rake_tasks.rb
138
138
  - lib/syntax_tree/version.rb
139
139
  - lib/syntax_tree/visitor.rb
140
+ - lib/syntax_tree/visitor/environment.rb
140
141
  - lib/syntax_tree/visitor/field_visitor.rb
141
142
  - lib/syntax_tree/visitor/json_visitor.rb
142
143
  - lib/syntax_tree/visitor/match_visitor.rb
143
144
  - lib/syntax_tree/visitor/pretty_print_visitor.rb
145
+ - lib/syntax_tree/visitor/with_environment.rb
144
146
  - syntax_tree.gemspec
145
147
  homepage: https://github.com/kddnewton/syntax_tree
146
148
  licenses: