Module: ASTTransform::TransformationHelper::Methods
- Defined in:
- lib/ast_transform/transformation_helper.rb
Instance Method Summary collapse
-
#run_after(statements, run:, after:) ⇒ Array<Parser::AST::Node>
The paved road for execution reordering in flat statement sequences.
-
#s(type, *children, **properties) ⇒ ASTTransform::Node
Builds a loc-less node.
-
#s_at(anchor, type, *children) ⇒ ASTTransform::Node
Builds a fresh node anchored to another node's source location.
-
#thunk(*statements) ⇒ ASTTransform::Thunk
The low-level reordering primitive.
Instance Method Details
#run_after(statements, run:, after:) ⇒ Array<Parser::AST::Node>
The paved road for execution reordering in flat statement sequences. Named for the constraint, not the
mechanism — with text pinned to source lines the only physical lever is delaying execution, so "run X after
Y" is the constraint an author states. Returns a NEW sequence in which the run statements are removed and a
thunk wrapping them is inserted immediately after after.
All membership checks are by identity (equal?), never ==: node equality ignores location, so two textually identical statements on different lines compare == and value matching could splice the wrong one.
95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/ast_transform/transformation_helper.rb', line 95 def run_after(statements, run:, after:) run_range = contiguous_identity_range(statements, run) raise ArgumentError, "run: must be a contiguous run of elements of statements (by identity)" unless run_range after_index = statements.index { |statement| statement.equal?(after) } raise ArgumentError, "after: must be an element of statements (by identity)" unless after_index raise ArgumentError, "after: cannot be inside run:" if run_range.cover?(after_index) reordered = statements.dup reordered[run_range] = [] insertion_index = reordered.index { |statement| statement.equal?(after) } reordered.insert(insertion_index + 1, thunk(*run)) end |
#s(type, *children, **properties) ⇒ ASTTransform::Node
Builds a loc-less node. The emitter packs loc-less nodes onto the current output line — the correct default for synthetic code, which has no source-line truth to preserve.
37 38 39 |
# File 'lib/ast_transform/transformation_helper.rb', line 37 def s(type, *children, **properties) Node.build(type, children, properties) end |
#s_at(anchor, type, *children) ⇒ ASTTransform::Node
Builds a fresh node anchored to another node's source location. Use when composing a replacement tree whose
root isn't derived from the node it replaces (otherwise prefer anchor.updated(...)). The attached map is a
clean expression-only Source::Map over anchor.loc.expression — no stale typed sub-ranges (selector etc.).
Anchor inheritance is shallow; children keep or lack their own locs.
51 52 53 54 55 56 |
# File 'lib/ast_transform/transformation_helper.rb', line 51 def s_at(anchor, type, *children) expression = anchor.loc&.expression raise MissingLocationError, "anchor #{anchor.type} node has no source location" unless expression s(type, *children, location: ::Parser::Source::Map.new(expression)) end |
#thunk(*statements) ⇒ ASTTransform::Thunk
The low-level reordering primitive. Thunking is the one reordering lever: text never moves and execution can only move later, so "hoist A above B" is expressed as "run B after A". Returns a single Thunk node: splice it where the statements must RUN — statement position or composed inside an expression, e.g. as an assert_raises block body. The wrapped statements keep their own locs, and the lowering derives the hidden proc's textual placement from them, so the body still emits on its source lines even though execution waits. Reuse the same node to execute one body from several points.
Semantics are near-transparent (see ThunkLowering): return still returns from the enclosing method
(non-lambda proc), and locals the wrapped statements assign stay method-scope (pre-declared before the proc).
Jump keywords whose owner lies outside the wrapped statements keep Ruby's native behavior — +break+/+retry+
fail loudly at the jump's own source line, +next+/+redo+ silently end or restart the thunk body. Weigh that
when choosing what your surface thunks.
Prefer run_after when the execution point sits in the same statement sequence as the statements.
75 76 77 |
# File 'lib/ast_transform/transformation_helper.rb', line 75 def thunk(*statements) s(:ast_thunk, Thunk::Id.new, *statements) end |