Class: ASTTransform::LineAlignedEmitter

Inherits:
Object
  • Object
show all
Defined in:
lib/ast_transform/line_aligned_emitter.rb

Overview

Emits a transformed AST as text in which every loc-carrying statement occupies its original source line, so that backtraces, breakpoints and debugger display are correct by construction — CRuby derives line numbers from physical text position, so placement is our line table.

Placement policy over statement sequences:

  1. Statement has loc: target its source line — the Layout pads to reach it, or packs (; ) when the cursor has already passed it. A user statement packing means the transform moved it — the alignment auditor's concern, not a runtime failure.
  2. No loc: pack onto the current line — synthetic code has no source-line truth to preserve.

The emitter owns the Ruby knowledge: walking statement structure, deciding which line each node targets, and that keywords can never be ;-packed. The pad-or-pack mechanics live in Layout; statement-to-text rendering (and its isolation workarounds) in StatementRenderer — both created per emission, so the emitter itself is stateless and an instance is a reusable collaborator.

Thunk nodes are lowered (ThunkLowering) before layout; the emitter's postcondition is that no custom node type (ast_* markers or types registered on ASTTransform::Node) crosses the unparse boundary — they are IR between stages that understand them.

Defined Under Namespace

Classes: UnloweredNodeTypeError

Constant Summary collapse

RECURSIVE_CONTAINER_TYPES =

Containers the emitter recurses into so nested statements align; every other node renders as an Unparser blob at its head line.

[:class, :module, :sclass, :def, :defs, :block, :numblock, :itblock, :kwbegin].freeze
BODY_INDEXES =
{
  class: 2, module: 1, sclass: 1, def: 2, defs: 3, block: 2, numblock: 2, itblock: 2
}.freeze
ASSIGNMENT_TYPES =

Assignments whose value is a block (e.g. the lowered thunk proc) recurse into the block so its body statements align.

[:lvasgn, :ivasgn, :gvasgn, :casgn].freeze
BLOCK_VALUE_TYPES =
[:block, :numblock, :itblock].freeze

Instance Method Summary collapse

Constructor Details

#initialize(thunk_lowering: ThunkLowering.new) ⇒ LineAlignedEmitter

Returns a new instance of LineAlignedEmitter.

Parameters:

  • thunk_lowering (ThunkLowering) (defaults to: ThunkLowering.new)

    the lowering run ahead of emission.



45
46
47
# File 'lib/ast_transform/line_aligned_emitter.rb', line 45

def initialize(thunk_lowering: ThunkLowering.new)
  @thunk_lowering = thunk_lowering
end

Instance Method Details

#emit(ast, source_path) ⇒ String

Returns transformed source, line-aligned.

Parameters:

  • ast (Parser::AST::Node)

    transformed AST

  • source_path (String)

    original file path (for error messages)

Returns:

  • (String)

    transformed source, line-aligned

Raises:



54
55
56
57
58
59
60
61
62
# File 'lib/ast_transform/line_aligned_emitter.rb', line 54

def emit(ast, source_path)
  lowered = @thunk_lowering.lower(ast)
  assert_no_custom_types(lowered, source_path)

  layout = Layout.new
  renderer = StatementRenderer.for_tree(lowered)
  emit_statements(statements_of(lowered), layout, renderer)
  layout.to_source
end