ast_transform 3.0.0 → 3.1.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/README.md +52 -0
- data/lib/ast_transform/abstract_analysis.rb +38 -0
- data/lib/ast_transform/abstract_processor.rb +39 -0
- data/lib/ast_transform/abstract_transformation.rb +17 -32
- data/lib/ast_transform/kwargs_builder.rb +9 -10
- data/lib/ast_transform/source_parser.rb +59 -0
- data/lib/ast_transform/transformation.rb +5 -0
- data/lib/ast_transform/transformer.rb +4 -22
- data/lib/ast_transform/version.rb +1 -1
- data/lib/ast_transform.rb +1 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a74867dd7f6d788c78d286901e1e4c6a362e0a341300fb9bb22ccb92503a0223
|
|
4
|
+
data.tar.gz: c2968b8000a875e169f89d85deaa38868ad49dcc8d193db6a78c88ea4eafb986
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9f4e007503a09479a7df3f595097bf384eb0196fb192cd012855c63417582e34a32cfd318ae765f1a8122de408bbfd337c8e3adfe889d2fd1bc36b3a20c96599
|
|
7
|
+
data.tar.gz: 900412939feb57ee329d2c3adca9b9712c1da8243038268104db916f9270ea09427d0d96cbf5aded11f86d919d4b45e6629be20372eb4dfc1d0531f05757b1e5
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [3.1.0] - 2026-07-29
|
|
8
|
+
### Added
|
|
9
|
+
- Analysis passes: `ASTTransform::AbstractAnalysis`, the read-only counterpart of `AbstractTransformation`. Both leaves share the extracted traversal core `ASTTransform::AbstractProcessor` (sealed `process`, `process_node` hook, thunk descent, `TransformationHelper`) and differ only in what `run` returns — the rebuilt tree for transformations, the analysis instance (result readers) for analyses.
|
|
10
|
+
- Public parse seam: `ASTTransform::SourceParser` (`#parse(source, file_path:)` / `#parse_file(path)`) extracts the framework's Prism-backed parsing from `Transformer` so analysis-only consumers that never emit can instantiate it directly. `Transformer#build_ast` / `#build_ast_from_file` delegate to it (unchanged public behavior).
|
|
11
|
+
- Pass-taxonomy documentation on `AbstractTransformation`, `Transformation`, and the README: structural (`on_*` handlers, pattern matched anywhere), positional (node builders with the bare `run(node) → node` duck type, caller owns traversal), and sibling-annotation (marker statement + next sibling, matched in `process_node` where the child list is visible — `transform!` itself).
|
|
12
|
+
|
|
7
13
|
## [3.0.0] - 2026-07-24
|
|
8
14
|
### Added
|
|
9
15
|
- Line-aligned emission: transformed code is emitted with every loc-carrying statement on its original source line, making backtraces, breakpoints, and debugger display correct by construction (`LineAlignedEmitter`).
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -130,6 +130,16 @@ class MyTransformation < ASTTransformation::AbstractTransformation
|
|
|
130
130
|
end
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
#### Choosing a pass shape
|
|
134
|
+
|
|
135
|
+
Passes come in three shapes, distinguished by how the rewrite site is found:
|
|
136
|
+
|
|
137
|
+
* **Structural** — the pattern alone identifies the site. Subclass `AbstractTransformation` and write `on_*` handlers; the pass rewrites the pattern wherever it occurs in the tree.
|
|
138
|
+
* **Positional (node builders)** — the *caller* owns traversal and has already located the site. Implement a plain `run(node) → node` object (include `TransformationHelper` for the `s(...)` vocabulary); the builder maps a single node to its replacement subtree and never walks. Anything with that duck type composes with `Transformer` and other passes.
|
|
139
|
+
* **Sibling-annotation** — the pattern is a marker statement plus its NEXT sibling. Match in `process_node`, where a node's child list is visible: an `on_*` handler sees one node, never its siblings, and cannot delete itself from its parent. `ASTTransform::Transformation` (the `transform!` detector) is the canonical example.
|
|
140
|
+
|
|
141
|
+
For read-only passes that harvest information instead of rewriting, see [Analysis passes](#analysis-passes).
|
|
142
|
+
|
|
133
143
|
#### Transformation discoverability
|
|
134
144
|
|
|
135
145
|
ASTTransform automatically loads your transformations at compile time. As such, we expect your files to be located at a known path.
|
|
@@ -162,6 +172,48 @@ In the above, `node#updated` allows updating the node, either its type or its ch
|
|
|
162
172
|
|
|
163
173
|
The [ast gem](https://github.com/whitequark/ast) uses a pattern in which a Transformation may implement a method matching a node type, i.e. `on_class`, `on_send`, `on_lvar`, etc... This is very useful when transformations should process all nodes of this type.
|
|
164
174
|
|
|
175
|
+
### Analysis passes
|
|
176
|
+
|
|
177
|
+
Not every pass rewrites. An analysis walks the tree and harvests information — `Parser::AST::Processor` has no read-only mode (every walk functionally rebuilds the tree), so an analysis is simply a walk whose rebuilt tree is discarded. Derive from `ASTTransform::AbstractAnalysis`: it shares the traversal engine with `AbstractTransformation` (including thunk descent and the `s(...)` matching vocabulary) and differs only in what `run` returns — the analysis instance, so callers chain result readers off the run.
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
require 'ast_transform/abstract_analysis'
|
|
181
|
+
|
|
182
|
+
class TypeAliasRanges < ASTTransform::AbstractAnalysis
|
|
183
|
+
attr_reader :ranges
|
|
184
|
+
|
|
185
|
+
def initialize
|
|
186
|
+
@ranges = []
|
|
187
|
+
super
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def on_block(node)
|
|
191
|
+
send_node = node.children.first
|
|
192
|
+
@ranges << (node.loc.expression.first_line..node.loc.expression.last_line) if type_alias?(send_node)
|
|
193
|
+
super
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def type_alias?(send_node)
|
|
199
|
+
send_node == s(:send, s(:const, nil, :T), :type_alias)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Harvest state in `on_*` handlers and always call `super` so traversal continues. Node equality ignores source locations, so `s(...)` patterns match structurally.
|
|
205
|
+
|
|
206
|
+
Analysis-only consumers don't need a `Transformer`; instantiate the parsing seam directly (`Transformer` uses the same class internally) and keep the instance for as many parses as you need:
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
parser = ASTTransform::SourceParser.new
|
|
210
|
+
|
|
211
|
+
parser.parse(source) # => Parser::AST::Node
|
|
212
|
+
parser.parse_file(file_path) # => Parser::AST::Node
|
|
213
|
+
|
|
214
|
+
TypeAliasRanges.new.run(parser.parse(source)).ranges
|
|
215
|
+
```
|
|
216
|
+
|
|
165
217
|
### Line-aligned emission and the authoring contract
|
|
166
218
|
|
|
167
219
|
ASTTransform owns text and lines; transform authors own semantics and execution order. The contract:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ast_transform/abstract_processor"
|
|
4
|
+
|
|
5
|
+
module ASTTransform
|
|
6
|
+
# Base class for read-only analysis passes: subclass, harvest state in +on_*+ handlers (always call +super+ so
|
|
7
|
+
# traversal continues), and expose results through readers. The walk still functionally rebuilds the tree —
|
|
8
|
+
# Parser::AST::Processor has no read-only mode — but +run+ discards the rebuilt tree, so handlers never need to
|
|
9
|
+
# care what they return.
|
|
10
|
+
#
|
|
11
|
+
# class SendCounter < ASTTransform::AbstractAnalysis
|
|
12
|
+
# attr_reader :count
|
|
13
|
+
#
|
|
14
|
+
# def initialize
|
|
15
|
+
# @count = 0
|
|
16
|
+
# super
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# def on_send(node)
|
|
20
|
+
# @count += 1
|
|
21
|
+
# super
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# SendCounter.new.run(SourceParser.new.parse(source)).count
|
|
26
|
+
class AbstractAnalysis < AbstractProcessor
|
|
27
|
+
# Runs this analysis on +node+, discarding the rebuilt tree.
|
|
28
|
+
# Note: If you want to add one-time setup or result finalization, override this, then call super.
|
|
29
|
+
#
|
|
30
|
+
# @param node [Parser::AST::Node] The node to be analyzed.
|
|
31
|
+
#
|
|
32
|
+
# @return [ASTTransform::AbstractAnalysis] self, so callers can chain result readers off the run.
|
|
33
|
+
def run(node)
|
|
34
|
+
process(node)
|
|
35
|
+
self
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ast_transform/transformation_helper"
|
|
4
|
+
|
|
5
|
+
module ASTTransform
|
|
6
|
+
# Shared traversal core for tree passes. Parser::AST::Processor is a rewriting walker — every visit functionally
|
|
7
|
+
# rebuilds the tree — so transformation and analysis share one engine and differ only in what they keep:
|
|
8
|
+
# AbstractTransformation's +run+ returns the rebuilt tree, AbstractAnalysis's +run+ discards it and returns the
|
|
9
|
+
# harvested results. Subclass one of those leaves rather than this class.
|
|
10
|
+
class AbstractProcessor < Parser::AST::Processor
|
|
11
|
+
include TransformationHelper
|
|
12
|
+
|
|
13
|
+
# Used internally by Parser::AST::Processor to process each node. DO NOT OVERRIDE.
|
|
14
|
+
def process(node)
|
|
15
|
+
return node unless node.is_a?(Parser::AST::Node)
|
|
16
|
+
|
|
17
|
+
process_node(node)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Thunks are framework-owned IR: descend into the body so passes that don't know about thunks still process the
|
|
21
|
+
# wrapped statements. Without this, Processor's handler_missing default would pass the node through opaquely,
|
|
22
|
+
# hiding the body from every later pass. The token (first child) is not a node and passes through untouched.
|
|
23
|
+
def on_ast_thunk(node)
|
|
24
|
+
node.updated(nil, [node.children[0], *process_all(node.children.drop(1))])
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Processes the given +node+.
|
|
30
|
+
# Note: If you want to do processing on each node, override this.
|
|
31
|
+
#
|
|
32
|
+
# @param node [Parser::AST::Node] The node being visited.
|
|
33
|
+
#
|
|
34
|
+
# @return [Parser::AST::Node] The rebuilt node.
|
|
35
|
+
def process_node(node)
|
|
36
|
+
method(:process).super_method.call(node)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "ast_transform/
|
|
3
|
+
require "ast_transform/abstract_processor"
|
|
4
4
|
|
|
5
5
|
module ASTTransform
|
|
6
|
-
class
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
# Base class for structural transformations: subclass and write +on_*+ handlers to rewrite a pattern wherever it
|
|
7
|
+
# occurs in the tree. This is one of three authoring shapes — pick by how the rewrite site is found:
|
|
8
|
+
#
|
|
9
|
+
# - Structural (this class): the pattern alone identifies the site, so +on_*+ handlers rewrite it anywhere in
|
|
10
|
+
# the tree.
|
|
11
|
+
# - Positional (node builders): a plain object with +run(node) → node+, including only TransformationHelper. The
|
|
12
|
+
# caller owns traversal and applies the builder at a site it already located; the builder never walks. Anything
|
|
13
|
+
# with that duck type composes with Transformer and other passes — deriving from this class is just one way to
|
|
14
|
+
# implement it.
|
|
15
|
+
# - Sibling-annotation: the pattern is a marker statement plus its NEXT sibling, matched in +process_node+ where
|
|
16
|
+
# the child list is visible — an +on_*+ handler sees one node, never its siblings, and cannot delete itself
|
|
17
|
+
# from its parent. See ASTTransform::Transformation (the +transform!+ detector) for the canonical example.
|
|
18
|
+
#
|
|
19
|
+
# For read-only passes that harvest information instead of rewriting, see ASTTransform::AbstractAnalysis.
|
|
20
|
+
class AbstractTransformation < AbstractProcessor
|
|
21
|
+
# Runs this transformation on +node+ and returns the rebuilt tree.
|
|
10
22
|
# Note: If you want to add one-time checks to the transformation, override this, then call super.
|
|
11
23
|
#
|
|
12
24
|
# @param node [Parser::AST::Node] The node to be transformed.
|
|
@@ -15,32 +27,5 @@ module ASTTransform
|
|
|
15
27
|
def run(node)
|
|
16
28
|
process(node)
|
|
17
29
|
end
|
|
18
|
-
|
|
19
|
-
# Used internally by Parser::AST::Processor to process each node. DO NOT OVERRIDE.
|
|
20
|
-
def process(node)
|
|
21
|
-
return node unless node.is_a?(Parser::AST::Node)
|
|
22
|
-
|
|
23
|
-
process_node(node)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Thunks are framework-owned IR: descend into the body so passes that don't know about thunks still process the
|
|
27
|
-
# wrapped statements. Without this, Processor's handler_missing default would pass the node through opaquely,
|
|
28
|
-
# hiding the body from every later transformation. The token (first child) is not a node and passes through
|
|
29
|
-
# untouched.
|
|
30
|
-
def on_ast_thunk(node)
|
|
31
|
-
node.updated(nil, [node.children[0], *process_all(node.children.drop(1))])
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
private
|
|
35
|
-
|
|
36
|
-
# Processes the given +node+.
|
|
37
|
-
# Note: If you want to do processing on each node, override this.
|
|
38
|
-
#
|
|
39
|
-
# @param node [Parser::AST::Node] The node to be transformed.
|
|
40
|
-
#
|
|
41
|
-
# @return [Parser::AST::Node] The transformed node.
|
|
42
|
-
def process_node(node)
|
|
43
|
-
method(:process).super_method.call(node)
|
|
44
|
-
end
|
|
45
30
|
end
|
|
46
31
|
end
|
|
@@ -3,22 +3,21 @@
|
|
|
3
3
|
require "prism/translation/parser"
|
|
4
4
|
|
|
5
5
|
module ASTTransform
|
|
6
|
-
#
|
|
6
|
+
# The framework's parser builder: the stock builder with the parser gem's `emit_kwargs` opt-in, which
|
|
7
|
+
# distinguishes keyword arguments from hash literals in the AST.
|
|
7
8
|
#
|
|
8
|
-
#
|
|
9
|
-
# node type to decide whether to emit braces: :hash gets `{}`, :kwargs does not. Since Ruby 3.0+
|
|
10
|
-
# semantically different (strict keyword/positional separation),
|
|
9
|
+
# With the flag off (the gem's compatibility default) both `foo(bar: 1)` and `foo({ bar: 1 })` emit :hash nodes.
|
|
10
|
+
# Unparser uses the node type to decide whether to emit braces: :hash gets `{}`, :kwargs does not. Since Ruby 3.0+
|
|
11
|
+
# treats these as semantically different (strict keyword/positional separation), the AST must preserve the
|
|
12
|
+
# distinction. `emit_kwargs` rewrites at the call sites where kwargs semantics live (method calls, index,
|
|
13
|
+
# super/yield); the flag is a class-level ivar, so setting it here opts in this builder only — the global
|
|
14
|
+
# Parser::Builders::Default stays untouched.
|
|
11
15
|
#
|
|
12
16
|
# NOTE: parsed nodes deliberately stay plain Parser::AST::Node. Custom node classes exist only for registered
|
|
13
17
|
# custom types (see ASTTransform::Node), which are IR and never reach Unparser: AST::Node#eql? compares class, and
|
|
14
18
|
# Unparser verifies dynamic-string emission by re-parsing and comparing eql? against the freshly parsed
|
|
15
19
|
# (plain-class) node — custom-class nodes of standard types would fail that verification.
|
|
16
20
|
class KwargsBuilder < Prism::Translation::Parser::Builder
|
|
17
|
-
|
|
18
|
-
node = super
|
|
19
|
-
return node unless begin_t.nil? && end_t.nil?
|
|
20
|
-
|
|
21
|
-
node.updated(:kwargs)
|
|
22
|
-
end
|
|
21
|
+
self.emit_kwargs = true
|
|
23
22
|
end
|
|
24
23
|
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require "prism/translation/parser"
|
|
5
|
+
require "ast_transform/kwargs_builder"
|
|
6
|
+
|
|
7
|
+
module ASTTransform
|
|
8
|
+
# Owns source → AST parsing: Prism's C parser through its whitequark translation layer, so every consumer gets the
|
|
9
|
+
# node vocabulary Parser::AST::Processor understands. Transformer parses through this seam; analysis-only
|
|
10
|
+
# consumers that never emit instantiate it directly and keep the instance for as many parses as they need.
|
|
11
|
+
class SourceParser
|
|
12
|
+
# Constructs a new SourceParser instance.
|
|
13
|
+
#
|
|
14
|
+
# KwargsBuilder is a required implementation detail, not an injection seam: the framework's emission depends
|
|
15
|
+
# on the kwargs/hash distinction it preserves, so every parse goes through it.
|
|
16
|
+
def initialize
|
|
17
|
+
@builder = KwargsBuilder.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Parses the given +source+.
|
|
21
|
+
#
|
|
22
|
+
# @param source [String] The input source code.
|
|
23
|
+
# @param file_path [String] The file path recorded on source locations. This is important for source mapping
|
|
24
|
+
# in backtraces.
|
|
25
|
+
#
|
|
26
|
+
# @return [Parser::AST::Node] The AST.
|
|
27
|
+
def parse(source, file_path: "tmp")
|
|
28
|
+
# A fresh parser per parse: parser instances accumulate per-run state (lexer position, diagnostics), and
|
|
29
|
+
# constructing one is trivial next to the parse itself.
|
|
30
|
+
parser = Prism::Translation::Parser.new(@builder)
|
|
31
|
+
parser.parse(create_buffer(source, file_path, parser.default_encoding))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Parses the source in the given +file_path+.
|
|
35
|
+
#
|
|
36
|
+
# @param file_path [String] The input file path.
|
|
37
|
+
#
|
|
38
|
+
# @return [Parser::AST::Node] The AST.
|
|
39
|
+
def parse_file(file_path)
|
|
40
|
+
parse(File.read(file_path), file_path: file_path)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# Builds a source buffer over +source+ in the given +encoding+.
|
|
46
|
+
#
|
|
47
|
+
# @param source [String] The input source code.
|
|
48
|
+
# @param file_path [String] The file path recorded on the buffer.
|
|
49
|
+
# @param encoding [Encoding] The encoding the buffer's source is coerced to.
|
|
50
|
+
#
|
|
51
|
+
# @return [Parser::Source::Buffer] The buffer.
|
|
52
|
+
def create_buffer(source, file_path, encoding)
|
|
53
|
+
buffer = Parser::Source::Buffer.new(file_path)
|
|
54
|
+
buffer.source = source.dup.force_encoding(encoding)
|
|
55
|
+
|
|
56
|
+
buffer
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -6,6 +6,11 @@ require "ast_transform/transformer"
|
|
|
6
6
|
require "unparser"
|
|
7
7
|
|
|
8
8
|
module ASTTransform
|
|
9
|
+
# The +transform!+ detector — the canonical sibling-annotation pass (see AbstractTransformation for the taxonomy).
|
|
10
|
+
# A +transform!(...)+ statement is a pragma on the NEXT sibling: the transformations it names are applied to the
|
|
11
|
+
# following class definition or constant assignment, and the marker itself is deleted from the child list. Both
|
|
12
|
+
# effects need the parent's child list in view, which is why matching happens in +process_node+ — an +on_send+
|
|
13
|
+
# handler would see the marker node alone, with no access to its next sibling and no way to remove itself.
|
|
9
14
|
class Transformation < ASTTransform::AbstractTransformation
|
|
10
15
|
TRANSFORM_AST = s(:send, nil, :transform!)
|
|
11
16
|
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'prism'
|
|
4
|
-
require 'prism/translation/parser'
|
|
5
|
-
require 'unparser'
|
|
6
|
-
require 'ast_transform/kwargs_builder'
|
|
7
3
|
require 'ast_transform/line_aligned_emitter'
|
|
4
|
+
require 'ast_transform/source_parser'
|
|
8
5
|
|
|
9
6
|
module ASTTransform
|
|
10
7
|
class Transformer
|
|
@@ -15,6 +12,7 @@ module ASTTransform
|
|
|
15
12
|
def initialize(*transformations, emitter: LineAlignedEmitter.new)
|
|
16
13
|
@transformations = transformations
|
|
17
14
|
@emitter = emitter
|
|
15
|
+
@source_parser = SourceParser.new
|
|
18
16
|
end
|
|
19
17
|
|
|
20
18
|
# Builds the AST for the given +source+.
|
|
@@ -24,8 +22,7 @@ module ASTTransform
|
|
|
24
22
|
#
|
|
25
23
|
# @return [Parser::AST::Node] The AST.
|
|
26
24
|
def build_ast(source, file_path: "tmp")
|
|
27
|
-
|
|
28
|
-
parser.parse(buffer)
|
|
25
|
+
@source_parser.parse(source, file_path: file_path)
|
|
29
26
|
end
|
|
30
27
|
|
|
31
28
|
# Builds the AST for the given +file_path+.
|
|
@@ -34,8 +31,7 @@ module ASTTransform
|
|
|
34
31
|
#
|
|
35
32
|
# @return [Parser::AST::Node] The AST.
|
|
36
33
|
def build_ast_from_file(file_path)
|
|
37
|
-
|
|
38
|
-
build_ast(source, file_path: file_path)
|
|
34
|
+
@source_parser.parse_file(file_path)
|
|
39
35
|
end
|
|
40
36
|
|
|
41
37
|
# Transforms the given +source+.
|
|
@@ -89,19 +85,5 @@ module ASTTransform
|
|
|
89
85
|
transformation.run(ast)
|
|
90
86
|
end
|
|
91
87
|
end
|
|
92
|
-
|
|
93
|
-
private
|
|
94
|
-
|
|
95
|
-
def create_buffer(source, file_path)
|
|
96
|
-
buffer = Parser::Source::Buffer.new(file_path)
|
|
97
|
-
buffer.source = source.dup.force_encoding(parser.default_encoding)
|
|
98
|
-
|
|
99
|
-
buffer
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def parser
|
|
103
|
-
@parser&.reset
|
|
104
|
-
@parser ||= Prism::Translation::Parser.new(ASTTransform::KwargsBuilder.new)
|
|
105
|
-
end
|
|
106
88
|
end
|
|
107
89
|
end
|
data/lib/ast_transform.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "ast_transform/version"
|
|
|
4
4
|
require "ast_transform/instruction_sequence"
|
|
5
5
|
require "ast_transform/instruction_sequence/mixin"
|
|
6
6
|
require "ast_transform/instruction_sequence/bootsnap_mixin"
|
|
7
|
+
require "ast_transform/source_parser"
|
|
7
8
|
|
|
8
9
|
module ASTTransform
|
|
9
10
|
DEFAULT_OUTPUT_PATH = Pathname.new("").join("tmp", "ast_transform").to_s
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ast_transform
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.1.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-07-
|
|
11
|
+
date: 2026-07-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: parser
|
|
@@ -80,6 +80,8 @@ files:
|
|
|
80
80
|
- dependencies.rb
|
|
81
81
|
- dev.yml
|
|
82
82
|
- lib/ast_transform.rb
|
|
83
|
+
- lib/ast_transform/abstract_analysis.rb
|
|
84
|
+
- lib/ast_transform/abstract_processor.rb
|
|
83
85
|
- lib/ast_transform/abstract_transformation.rb
|
|
84
86
|
- lib/ast_transform/instruction_sequence.rb
|
|
85
87
|
- lib/ast_transform/instruction_sequence/bootsnap_mixin.rb
|
|
@@ -89,6 +91,7 @@ files:
|
|
|
89
91
|
- lib/ast_transform/layout.rb
|
|
90
92
|
- lib/ast_transform/line_aligned_emitter.rb
|
|
91
93
|
- lib/ast_transform/node.rb
|
|
94
|
+
- lib/ast_transform/source_parser.rb
|
|
92
95
|
- lib/ast_transform/statement_renderer.rb
|
|
93
96
|
- lib/ast_transform/testing/assertions.rb
|
|
94
97
|
- lib/ast_transform/thunk.rb
|