ast_transform 2.1.4 → 3.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.
metadata CHANGED
@@ -1,113 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ast_transform
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jean-Philippe Duchesne
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-25 00:00:00.000000000 Z
11
+ date: 2026-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '2.1'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '2.1'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '13.0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '13.0'
41
- - !ruby/object:Gem::Dependency
42
- name: minitest
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '5.14'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '5.14'
55
- - !ruby/object:Gem::Dependency
56
- name: minitest-reporters
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.4'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.4'
69
- - !ruby/object:Gem::Dependency
70
- name: pry
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0.14'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0.14'
83
- - !ruby/object:Gem::Dependency
84
- name: simplecov
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '0.22'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '0.22'
97
13
  - !ruby/object:Gem::Dependency
98
14
  name: parser
99
15
  requirement: !ruby/object:Gem::Requirement
100
16
  requirements:
101
17
  - - ">="
102
18
  - !ruby/object:Gem::Version
103
- version: '3.0'
19
+ version: '3.3'
104
20
  type: :runtime
105
21
  prerelease: false
106
22
  version_requirements: !ruby/object:Gem::Requirement
107
23
  requirements:
108
24
  - - ">="
109
25
  - !ruby/object:Gem::Version
110
- version: '3.0'
26
+ version: '3.3'
111
27
  - !ruby/object:Gem::Dependency
112
28
  name: prism
113
29
  requirement: !ruby/object:Gem::Requirement
@@ -128,14 +44,14 @@ dependencies:
128
44
  requirements:
129
45
  - - ">="
130
46
  - !ruby/object:Gem::Version
131
- version: '0.6'
47
+ version: '0.8'
132
48
  type: :runtime
133
49
  prerelease: false
134
50
  version_requirements: !ruby/object:Gem::Requirement
135
51
  requirements:
136
52
  - - ">="
137
53
  - !ruby/object:Gem::Version
138
- version: '0.6'
54
+ version: '0.8'
139
55
  description: An AST transformation framework.
140
56
  email:
141
57
  - jpduchesne89@gmail.com
@@ -146,9 +62,12 @@ files:
146
62
  - ".claude/CLAUDE.md"
147
63
  - ".cursor/rules/base.mdc"
148
64
  - ".cursor/rules/best-practices.mdc"
65
+ - ".github/CODEOWNERS"
149
66
  - ".github/workflows/ci.yml"
150
67
  - ".github/workflows/release.yml"
151
68
  - ".gitignore"
69
+ - ".rubocop.yml"
70
+ - ".ruby-version"
152
71
  - CHANGELOG.md
153
72
  - Gemfile
154
73
  - Gemfile.lock
@@ -158,6 +77,8 @@ files:
158
77
  - ast_transform.gemspec
159
78
  - bin/console
160
79
  - bin/setup
80
+ - dependencies.rb
81
+ - dev.yml
161
82
  - lib/ast_transform.rb
162
83
  - lib/ast_transform/abstract_transformation.rb
163
84
  - lib/ast_transform/instruction_sequence.rb
@@ -165,7 +86,13 @@ files:
165
86
  - lib/ast_transform/instruction_sequence/mixin.rb
166
87
  - lib/ast_transform/instruction_sequence/mixin_utils.rb
167
88
  - lib/ast_transform/kwargs_builder.rb
168
- - lib/ast_transform/source_map.rb
89
+ - lib/ast_transform/layout.rb
90
+ - lib/ast_transform/line_aligned_emitter.rb
91
+ - lib/ast_transform/node.rb
92
+ - lib/ast_transform/statement_renderer.rb
93
+ - lib/ast_transform/testing/assertions.rb
94
+ - lib/ast_transform/thunk.rb
95
+ - lib/ast_transform/thunk_lowering.rb
169
96
  - lib/ast_transform/transformation.rb
170
97
  - lib/ast_transform/transformation_helper.rb
171
98
  - lib/ast_transform/transformer.rb
@@ -182,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
182
109
  requirements:
183
110
  - - ">="
184
111
  - !ruby/object:Gem::Version
185
- version: '3.2'
112
+ version: '3.3'
186
113
  required_rubygems_version: !ruby/object:Gem::Requirement
187
114
  requirements:
188
115
  - - ">="
@@ -1,233 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'parser'
3
-
4
- module ASTTransform
5
- class SourceMap
6
- class << self
7
- # Registers the given SourceMap.
8
- #
9
- # @param source_map [SourceMap] The source map to be registered.
10
- #
11
- # @return [void]
12
- def register_source_map(source_map)
13
- source_maps[source_map.transformed_file_path] = source_map
14
- source_maps[source_map.source_file_path] = source_map
15
-
16
- nil
17
- end
18
-
19
- # Retrieves the SourceMap for the given +file_path+.
20
- #
21
- # @param file_path [String] The transformed file path.
22
- #
23
- # @return [SourceMap|nil] The associated source map.
24
- def for_file_path(file_path)
25
- source_maps[file_path]
26
- end
27
-
28
- private
29
-
30
- def source_maps
31
- @@source_maps ||= {}
32
- end
33
- end
34
-
35
- # Constructs a new SourceMap instance.
36
- #
37
- # Note: +source_ranges_ast+ and +transformed_ranges_ast+ must be equivalent ASTs.
38
- #
39
- # @param source_file_path [String] The path to the source file.
40
- # @param transformed_file_path [String] The path to the transformed file.
41
- # @param source_ranges_ast [Parser::AST::Node] A transformed AST that contains the source code ranges.
42
- # @param transformed_ranges_ast [Parser::AST::Node] A transformed AST that contains the ranges for the executed
43
- # code.
44
- def initialize(source_file_path, transformed_file_path, source_ranges_ast, transformed_ranges_ast)
45
- @source_file_path = source_file_path
46
- @transformed_file_path = transformed_file_path
47
- @source_ranges_ast = source_ranges_ast
48
- @transformed_ranges_ast = transformed_ranges_ast
49
-
50
- @lines = Hash.new { |hash, key| hash[key] = [] }
51
- extract_source_map_data(@transformed_ranges_ast, [])
52
- @source_map = build_source_map.freeze
53
- end
54
-
55
- attr_reader :source_file_path, :transformed_file_path, :source_map
56
-
57
- # Retrieves the mapped line number for the given +line_number+.
58
- #
59
- # @param line_number [Integer] The line number in the executed code to be mapped to the source.
60
- #
61
- # @return [Integer|nil] The mapped line number, otherwise nil if not found.
62
- def line(line_number)
63
- @source_map[line_number]
64
- end
65
-
66
- # Retrieves the line count for the executed code.
67
- #
68
- # @return [Integer] The line count.
69
- def line_count
70
- @transformed_ranges_ast&.loc&.expression&.last_line || 0
71
- end
72
-
73
- private
74
-
75
- # Extracts SourceMap data from the given node.
76
- #
77
- # @param node [Parser::AST::Node] The node containing ranges for the executed code.
78
- #
79
- # @return [void]
80
- def extract_source_map_data(node, indexes)
81
- return false unless node&.is_a?(Parser::AST::Node)
82
-
83
- range = node.loc&.expression
84
-
85
- if range && range.line == range.last_line
86
- @lines[range.line] << indexes.dup
87
- end
88
-
89
- node.children.each.with_index do |child, index|
90
- extract_source_map_data(child, indexes.dup << index)
91
- end
92
-
93
- nil
94
- end
95
-
96
- # Builds the source map.
97
- #
98
- # @return [Hash] A Hash containing line numbers from executed code to source code.
99
- def build_source_map
100
- (1..line_count).each.with_object({}) {|it, hash| hash[it] = source_line(it) }
101
- end
102
-
103
- # Retrieves the source line for the given +line_number+ in the executed code.
104
- #
105
- # @param line_number [Integer] The line number in the executed code.
106
- #
107
- # @return [Integer|nil] The line number in the source code, or nil if cannot be mapped.
108
- def source_line(line_number)
109
- if @lines.key?(line_number)
110
- @lines[line_number].each do |dig_array|
111
- source_node = approximate_dig_last_valid_node(@source_ranges_ast, dig_array)
112
- next unless source_node
113
-
114
- range = search_range(source_node, 1)
115
- return range.line if range
116
- end
117
- end
118
-
119
- nil
120
- end
121
-
122
- # Recursively look for node represented by +indexes+ in +node+. If not found, goes back +depth+ nodes and search for
123
- # the node pointed to by +indexes+.
124
- #
125
- # @param node [Parser::AST::Node] The node to search into. This must be a node in the +@source_ranges_ast+.
126
- # @param indexes [Array<Integer>] Child indexes pointing to the node we're looking for in +node+.
127
- # @param depth [Integer] Number of nodes to go up to search for the node pointed to by +indexes+.
128
- #
129
- # @return [Parser::AST::Node|nil] The node found, nil otherwise.
130
- def approximate_dig_last_valid_node(node, indexes, depth = 1)
131
- return node if indexes.empty?
132
-
133
- result = dig_node(node, indexes)
134
- return result if result.is_a?(Parser::AST::Node) || depth <= 0
135
-
136
- queried_node = dig_last_valid_node(@transformed_ranges_ast, indexes)
137
-
138
- last_known_index = dig_last_valid_node_index(node, indexes[0...-depth])
139
- query_indexes = indexes[0...last_known_index]
140
-
141
- last_known_node = dig_node(node, query_indexes)
142
-
143
- search_node(last_known_node, queried_node)
144
- end
145
-
146
- # Recursively search the children of +node+ for an equivalent +queried_node+.
147
- #
148
- # @param node [Parser::AST::Node] The current node to search in.
149
- # @param queried_node [Parser::AST::Node] The equivalent node to search for.
150
- #
151
- # @return [Parser::AST::Node|nil] The found node from the +node+ graph, nil otherwise.
152
- def search_node(node, queried_node)
153
- return unless node&.is_a?(Parser::AST::Node)
154
- return node if node == queried_node
155
-
156
- node.children.each do |child_node|
157
- result = search_node(child_node, queried_node)
158
- return result if result
159
- end
160
-
161
- nil
162
- end
163
-
164
- # Recursively search the given +node+ for a range.
165
- #
166
- # @param node [Parser::AST::Node] The current node to search in.
167
- # @param max_range [Integer|nil] The max range to consider valid. Nil means any range is valid. If 1, only ranges
168
- # which span one line will be considered, etc...
169
- #
170
- # @return [Parser::Source::Range|nil] The range, or nil if no range was found. This occurs when the tree contains
171
- # no ranges, i.e. they're all virtually built nodes.
172
- def search_range(node, max_range = nil)
173
- return unless node&.is_a?(Parser::AST::Node)
174
-
175
- range = node.loc&.expression
176
- if range && max_range && range.last_line - range.line < max_range || range && max_range.nil?
177
- return range
178
- else
179
- node.children.each do |child_node|
180
- result = search_range(child_node, max_range)
181
- return result if result
182
- end
183
- end
184
-
185
- nil
186
- end
187
-
188
- # Finds the index for the last valid node represented by +indexes+ in the children of +node+.
189
- #
190
- # @param node [Parser::AST::Node] The current node to search in.
191
- # @param indexes [Array<Integer>] The array of indexes pointing to the child node to be retrieved from +node+.
192
- #
193
- # @return [Integer|nil] The index of the node if found, nil otherwise.
194
- def dig_last_valid_node_index(node, indexes)
195
- return if indexes.empty?
196
-
197
- result = dig_node(node, indexes)
198
- current_index = indexes&.size
199
- return current_index if result.is_a?(Parser::AST::Node)
200
-
201
- dig_last_valid_node_index(node, indexes[0...-1])
202
- end
203
-
204
- # Recursively look for the node represented by +indexes+ in the children of +node+. If not found, returns the last
205
- # valid node.
206
- #
207
- # @param node [Parser::AST::Node] The node to look into.
208
- # @param indexes [Array<Integer>] The array of indexes pointing to the child node to be retrieved from +node+.
209
- #
210
- # @return [Parser::AST::Node|nil] The node if found, nil otherwise.
211
- def dig_last_valid_node(node, indexes)
212
- return node if indexes.empty?
213
-
214
- result = dig_node(node, indexes)
215
- return result if result.is_a?(Parser::AST::Node)
216
-
217
- dig_last_valid_node(node, indexes[0...-1])
218
- end
219
-
220
- # Recursively look for the node represented by +indexes+ in the children of +node+.
221
- #
222
- # @param node [Parser::AST::Node] The node to look into.
223
- # @param indexes [Array<Integer>] The array of indexes pointing to the child node to be retrieved from +node+.
224
- #
225
- # @return [Parser::AST::Node|nil] The node if found, nil otherwise.
226
- def dig_node(node, indexes)
227
- indexes.inject(node) do |node, index|
228
- return nil unless node.is_a?(Parser::AST::Node)
229
- node.children[index]
230
- end
231
- end
232
- end
233
- end