class Prism::Translation::Parser::Compiler
A visitor that knows how to convert a prism syntax tree into the whitequark/parser gem’s syntax tree.
Constants
- Range
Locations in the parser gem AST are generated using this class. We store a reference to its constant to make it slightly faster to look up.
Attributes
The Parser::Builders::Default instance that is being used to build the AST.
The types of values that can be forwarded in the current scope.
Whether or not the current node is in a destructure.
Whether or not the current node is in a pattern.
The offset cache that is used to map between byte and character offsets in the file.
The Parser::Base instance that is being used to build the AST.
The Parser::Source::Buffer instance that is holding a reference to the source code.
Public Class Methods
Initialize a new compiler with the given parser, offset cache, and options.
# File lib/prism/translation/parser/compiler.rb, line 39 def initialize(parser, offset_cache, forwarding: [], in_destructure: false, in_pattern: false) @parser = parser @builder = parser.builder @source_buffer = parser.source_buffer @offset_cache = offset_cache @forwarding = forwarding @in_destructure = in_destructure @in_pattern = in_pattern end
Public Instance Methods
alias $foo $bar ^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 58 def visit_alias_global_variable_node(node) builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name)) end
alias foo bar ^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 52 def visit_alias_method_node(node) builder.alias(token(node.keyword_loc), visit(node.new_name), visit(node.old_name)) end
foo => bar | baz
^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 64 def visit_alternation_pattern_node(node) builder.match_alt(visit(node.left), token(node.operator_loc), visit(node.right)) end
a and b ^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 70 def visit_and_node(node) builder.logical_op(:and, visit(node.left), token(node.operator_loc), visit(node.right)) end
foo(bar)
^^^
# File lib/prism/translation/parser/compiler.rb, line 101 def visit_arguments_node(node) visit_all(node.arguments) end
-
^^
# File lib/prism/translation/parser/compiler.rb, line 76 def visit_array_node(node) builder.array(token(node.opening_loc), visit_all(node.elements), token(node.closing_loc)) end
foo => [bar]
^^^^^
# File lib/prism/translation/parser/compiler.rb, line 82 def visit_array_pattern_node(node) elements = [*node.requireds] elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode) elements.concat(node.posts) visited = visit_all(elements) if node.rest.is_a?(ImplicitRestNode) visited[-1] = builder.match_with_trailing_comma(visited[-1], token(node.rest.location)) end if node.constant builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.array_pattern(nil, visited, nil), token(node.closing_loc)) else builder.array_pattern(token(node.opening_loc), visited, token(node.closing_loc)) end end
{ a: 1 }
^^^^
# File lib/prism/translation/parser/compiler.rb, line 107 def visit_assoc_node(node) if in_pattern if node.value.is_a?(ImplicitNode) if node.key.is_a?(SymbolNode) builder.match_hash_var([node.key.unescaped, srange(node.key.location)]) else builder.match_hash_var_from_str(token(node.key.opening_loc), visit_all(node.key.parts), token(node.key.closing_loc)) end else builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value)) end elsif node.value.is_a?(ImplicitNode) if (value = node.value.value).is_a?(LocalVariableReadNode) builder.pair_keyword( [node.key.unescaped, srange(node.key)], builder.ident([value.name, srange(node.key.value_loc)]).updated(:lvar) ) else builder.pair_label([node.key.unescaped, srange(node.key.location)]) end elsif node.operator_loc builder.pair(visit(node.key), token(node.operator_loc), visit(node.value)) elsif node.key.is_a?(SymbolNode) && node.key.opening_loc.nil? builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value)) else parts = if node.key.is_a?(SymbolNode) [builder.string_internal([node.key.unescaped, srange(node.key.value_loc)])] else visit_all(node.key.parts) end builder.pair_quoted(token(node.key.opening_loc), parts, token(node.key.closing_loc), visit(node.value)) end end
def foo(**); bar(**); end
^^
{ **foo }
^^^^^
# File lib/prism/translation/parser/compiler.rb, line 148 def visit_assoc_splat_node(node) if node.value.nil? && forwarding.include?(:**) builder.forwarded_kwrestarg(token(node.operator_loc)) else builder.kwsplat(token(node.operator_loc), visit(node.value)) end end
$+ ^^
# File lib/prism/translation/parser/compiler.rb, line 158 def visit_back_reference_read_node(node) builder.back_ref(token(node.location)) end
begin end ^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 164 def visit_begin_node(node) rescue_bodies = [] if (rescue_clause = node.rescue_clause) begin find_start_offset = (rescue_clause.reference&.location || rescue_clause.exceptions.last&.location || rescue_clause.keyword_loc).end_offset find_end_offset = (rescue_clause.statements&.location&.start_offset || rescue_clause.consequent&.location&.start_offset || (find_start_offset + 1)) rescue_bodies << builder.rescue_body( token(rescue_clause.keyword_loc), rescue_clause.exceptions.any? ? builder.array(nil, visit_all(rescue_clause.exceptions), nil) : nil, token(rescue_clause.operator_loc), visit(rescue_clause.reference), srange_find(find_start_offset, find_end_offset, [";"]), visit(rescue_clause.statements) ) end until (rescue_clause = rescue_clause.consequent).nil? end begin_body = builder.begin_body( visit(node.statements), rescue_bodies, token(node.else_clause&.else_keyword_loc), visit(node.else_clause), token(node.ensure_clause&.ensure_keyword_loc), visit(node.ensure_clause&.statements) ) if node.begin_keyword_loc builder.begin_keyword(token(node.begin_keyword_loc), begin_body, token(node.end_keyword_loc)) else begin_body end end
foo(&bar)
^^^^
# File lib/prism/translation/parser/compiler.rb, line 202 def visit_block_argument_node(node) builder.block_pass(token(node.operator_loc), visit(node.expression)) end
foo { |; bar| }
^^^
# File lib/prism/translation/parser/compiler.rb, line 208 def visit_block_local_variable_node(node) builder.shadowarg(token(node.location)) end
A block on a keyword or method call.
# File lib/prism/translation/parser/compiler.rb, line 213 def visit_block_node(node) raise CompilationError, "Cannot directly compile block nodes" end
def foo(&bar); end
^^^^
# File lib/prism/translation/parser/compiler.rb, line 219 def visit_block_parameter_node(node) builder.blockarg(token(node.operator_loc), token(node.name_loc)) end
A block’s parameters.
# File lib/prism/translation/parser/compiler.rb, line 224 def visit_block_parameters_node(node) [*visit(node.parameters)].concat(visit_all(node.locals)) end
break ^^^^^
break foo ^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 233 def visit_break_node(node) builder.keyword_cmd(:break, token(node.keyword_loc), nil, visit(node.arguments) || [], nil) end
foo.bar &&= baz ^^^^^^^^^^^^^^^
foo ^^^
foo.bar ^^^^^^^
foo.bar() {} ^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 245 def visit_call_node(node) name = node.name arguments = node.arguments&.arguments || [] block = node.block if block.is_a?(BlockArgumentNode) arguments = [*arguments, block] block = nil end if node.call_operator_loc.nil? case name when :-@ case (receiver = node.receiver).type when :integer_node, :float_node, :rational_node, :imaginary_node return visit(numeric_negate(node.message_loc, receiver)) end when :! return visit_block(builder.not_op(token(node.message_loc), token(node.opening_loc), visit(node.receiver), token(node.closing_loc)), block) when :=~ if (receiver = node.receiver).is_a?(RegularExpressionNode) return builder.match_op(visit(receiver), token(node.message_loc), visit(node.arguments.arguments.first)) end when :[] return visit_block(builder.index(visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc)), block) when :[]= if node.message != "[]=" && node.arguments && block.nil? && !node.safe_navigation? arguments = node.arguments.arguments[...-1] arguments << node.block if node.block return visit_block( builder.assign( builder.index_asgn( visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc), ), srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]), visit(node.arguments.arguments.last) ), block ) end end end message_loc = node.message_loc call_operator_loc = node.call_operator_loc call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc visit_block( if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil? builder.assign( builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)), srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]), visit(node.arguments.arguments.last) ) else builder.call_method( visit(node.receiver), call_operator, message_loc ? [node.name, srange(message_loc)] : nil, token(node.opening_loc), visit_all(arguments), token(node.closing_loc) ) end, block ) end
foo.bar += baz ^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 319 def visit_call_operator_write_node(node) call_operator_loc = node.call_operator_loc builder.op_assign( builder.call_method( visit(node.receiver), call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)], node.message_loc ? [node.read_name, srange(node.message_loc)] : nil, nil, [], nil ), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
foo.bar ||= baz ^^^^^^^^^^^^^^^
foo.bar, = 1 ^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 346 def visit_call_target_node(node) call_operator_loc = node.call_operator_loc builder.attr_asgn( visit(node.receiver), call_operator_loc.nil? ? nil : [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)], token(node.message_loc) ) end
foo => bar => baz
^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 358 def visit_capture_pattern_node(node) builder.match_as(visit(node.value), token(node.operator_loc), visit(node.target)) end
case foo; in bar; end ^^^^^^^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 377 def visit_case_match_node(node) builder.case_match( token(node.case_keyword_loc), visit(node.predicate), visit_all(node.conditions), token(node.consequent&.else_keyword_loc), visit(node.consequent), token(node.end_keyword_loc) ) end
case foo; when bar; end ^^^^^^^^^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 364 def visit_case_node(node) builder.case( token(node.case_keyword_loc), visit(node.predicate), visit_all(node.conditions), token(node.consequent&.else_keyword_loc), visit(node.consequent), token(node.end_keyword_loc) ) end
class Foo; end ^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 390 def visit_class_node(node) builder.def_class( token(node.class_keyword_loc), visit(node.constant_path), token(node.inheritance_operator_loc), visit(node.superclass), node.body&.accept(copy_compiler(forwarding: [])), token(node.end_keyword_loc) ) end
@@foo &&= bar ^^^^^^^^^^^^^
@@foo += bar ^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 419 def visit_class_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.cvar(token(node.name_loc))), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
@@foo ||= bar ^^^^^^^^^^^^^
@@foo ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 403 def visit_class_variable_read_node(node) builder.cvar(token(node.location)) end
@@foo, = bar ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 437 def visit_class_variable_target_node(node) builder.assignable(builder.cvar(token(node.location))) end
@@foo = 1 ^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 409 def visit_class_variable_write_node(node) builder.assign( builder.assignable(builder.cvar(token(node.name_loc))), token(node.operator_loc), visit(node.value) ) end
Foo &&= bar ^^^^^^^^^^^^
Foo += bar ^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 458 def visit_constant_operator_write_node(node) builder.op_assign( builder.assignable(builder.const([node.name, srange(node.name_loc)])), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
Foo ||= bar ^^^^^^^^^^^^
Foo::Bar &&= baz ^^^^^^^^^^^^^^^^
Foo::Bar ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 482 def visit_constant_path_node(node) if node.parent.nil? builder.const_global( token(node.delimiter_loc), [node.child.name, srange(node.child.location)] ) else builder.const_fetch( visit(node.parent), token(node.delimiter_loc), [node.child.name, srange(node.child.location)] ) end end
Foo::Bar += baz ^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 512 def visit_constant_path_operator_write_node(node) builder.op_assign( builder.assignable(visit(node.target)), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
Foo::Bar ||= baz ^^^^^^^^^^^^^^^^
Foo::Bar, = baz ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 530 def visit_constant_path_target_node(node) builder.assignable(visit_constant_path_node(node)) end
Foo::Bar = 1 ^^^^^^^^^^^^
Foo::Foo, Bar::Bar = 1 ^^^^^^^^ ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 502 def visit_constant_path_write_node(node) builder.assign( builder.assignable(visit(node.target)), token(node.operator_loc), visit(node.value) ) end
Foo ^^^
# File lib/prism/translation/parser/compiler.rb, line 443 def visit_constant_read_node(node) builder.const([node.name, srange(node.location)]) end
Foo, = bar ^^^
# File lib/prism/translation/parser/compiler.rb, line 476 def visit_constant_target_node(node) builder.assignable(builder.const([node.name, srange(node.location)])) end
Foo = 1 ^^^^^^^
Foo, Bar = 1 ^^^ ^^^
# File lib/prism/translation/parser/compiler.rb, line 452 def visit_constant_write_node(node) builder.assign(builder.assignable(builder.const([node.name, srange(node.name_loc)])), token(node.operator_loc), visit(node.value)) end
def foo; end ^^^^^^^^^^^^
def self.foo; end ^^^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 539 def visit_def_node(node) if node.equal_loc if node.receiver builder.def_endless_singleton( token(node.def_keyword_loc), visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver), token(node.operator_loc), token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), token(node.equal_loc), node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))) ) else builder.def_endless_method( token(node.def_keyword_loc), token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), token(node.equal_loc), node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))) ) end elsif node.receiver builder.def_singleton( token(node.def_keyword_loc), visit(node.receiver.is_a?(ParenthesesNode) ? node.receiver.body : node.receiver), token(node.operator_loc), token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))), token(node.end_keyword_loc) ) else builder.def_method( token(node.def_keyword_loc), token(node.name_loc), builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false), node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))), token(node.end_keyword_loc) ) end end
defined? a ^^^^^^^^^^
defined?(a) ^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 586 def visit_defined_node(node) builder.keyword_cmd( :defined?, token(node.keyword_loc), token(node.lparen_loc), [visit(node.value)], token(node.rparen_loc) ) end
if foo then bar else baz end
^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 598 def visit_else_node(node) visit(node.statements) end
“foo #{bar}”
^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 604 def visit_embedded_statements_node(node) builder.begin( token(node.opening_loc), visit(node.statements), token(node.closing_loc) ) end
“foo #@bar”
^^^^^
# File lib/prism/translation/parser/compiler.rb, line 614 def visit_embedded_variable_node(node) visit(node.variable) end
begin; foo; ensure; bar; end
^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 620 def visit_ensure_node(node) raise CompilationError, "Cannot directly compile ensure nodes" end
false ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 626 def visit_false_node(node) builder.false(token(node.location)) end
foo => [*, bar, *]
^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 632 def visit_find_pattern_node(node) elements = [node.left, *node.requireds, node.right] if node.constant builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.find_pattern(nil, visit_all(elements), nil), token(node.closing_loc)) else builder.find_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc)) end end
1.0 ^^^
# File lib/prism/translation/parser/compiler.rb, line 644 def visit_float_node(node) visit_numeric(node, builder.float([node.value, srange(node.location)])) end
for foo in bar do end ^^^^^^^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 650 def visit_for_node(node) builder.for( token(node.for_keyword_loc), visit(node.index), token(node.in_keyword_loc), visit(node.collection), if node.do_keyword_loc token(node.do_keyword_loc) else srange_find(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset, [";"]) end, visit(node.statements), token(node.end_keyword_loc) ) end
def foo(…); bar(…); end
^^^
# File lib/prism/translation/parser/compiler.rb, line 668 def visit_forwarding_arguments_node(node) builder.forwarded_args(token(node.location)) end
def foo(…); end
^^^
# File lib/prism/translation/parser/compiler.rb, line 674 def visit_forwarding_parameter_node(node) builder.forward_arg(token(node.location)) end
super ^^^^^
super {} ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 683 def visit_forwarding_super_node(node) visit_block( builder.keyword_cmd( :zsuper, ["super", srange_offsets(node.location.start_offset, node.location.start_offset + 5)] ), node.block ) end
$foo &&= bar ^^^^^^^^^^^^
$foo += bar ^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 711 def visit_global_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.gvar(token(node.name_loc))), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
$foo ||= bar ^^^^^^^^^^^^
$foo ^^^^
# File lib/prism/translation/parser/compiler.rb, line 695 def visit_global_variable_read_node(node) builder.gvar(token(node.location)) end
$foo, = bar ^^^^
# File lib/prism/translation/parser/compiler.rb, line 729 def visit_global_variable_target_node(node) builder.assignable(builder.gvar([node.slice, srange(node.location)])) end
$foo = 1 ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 701 def visit_global_variable_write_node(node) builder.assign( builder.assignable(builder.gvar(token(node.name_loc))), token(node.operator_loc), visit(node.value) ) end
{} ^^
# File lib/prism/translation/parser/compiler.rb, line 735 def visit_hash_node(node) builder.associate( token(node.opening_loc), visit_all(node.elements), token(node.closing_loc) ) end
foo => {}
^^
# File lib/prism/translation/parser/compiler.rb, line 745 def visit_hash_pattern_node(node) elements = [*node.elements, *node.rest] if node.constant builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.hash_pattern(nil, visit_all(elements), nil), token(node.closing_loc)) else builder.hash_pattern(token(node.opening_loc), visit_all(elements), token(node.closing_loc)) end end
if foo then bar end ^^^^^^^^^^^^^^^^^^^
bar if foo ^^^^^^^^^^
foo ? bar : baz ^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 763 def visit_if_node(node) if !node.if_keyword_loc builder.ternary( visit(node.predicate), token(node.then_keyword_loc), visit(node.statements), token(node.consequent.else_keyword_loc), visit(node.consequent) ) elsif node.if_keyword_loc.start_offset == node.location.start_offset builder.condition( token(node.if_keyword_loc), visit(node.predicate), if node.then_keyword_loc token(node.then_keyword_loc) else srange_find(node.predicate.location.end_offset, (node.statements&.location || node.consequent&.location || node.end_keyword_loc).start_offset, [";"]) end, visit(node.statements), case node.consequent when IfNode token(node.consequent.if_keyword_loc) when ElseNode token(node.consequent.else_keyword_loc) end, visit(node.consequent), if node.if_keyword != "elsif" token(node.end_keyword_loc) end ) else builder.condition_mod( visit(node.statements), visit(node.consequent), token(node.if_keyword_loc), visit(node.predicate) ) end end
1i ^^
# File lib/prism/translation/parser/compiler.rb, line 805 def visit_imaginary_node(node) visit_numeric(node, builder.complex([imaginary_value(node), srange(node.location)])) end
{ foo: }
^^^^
# File lib/prism/translation/parser/compiler.rb, line 811 def visit_implicit_node(node) raise CompilationError, "Cannot directly compile implicit nodes" end
foo { |bar,| }
^
# File lib/prism/translation/parser/compiler.rb, line 817 def visit_implicit_rest_node(node) raise CompilationError, "Cannot compile implicit rest nodes" end
case foo; in bar; end ^^^^^^^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 823 def visit_in_node(node) pattern = nil guard = nil case node.pattern when IfNode pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) } guard = builder.if_guard(token(node.pattern.if_keyword_loc), visit(node.pattern.predicate)) when UnlessNode pattern = within_pattern { |compiler| node.pattern.statements.accept(compiler) } guard = builder.unless_guard(token(node.pattern.keyword_loc), visit(node.pattern.predicate)) else pattern = within_pattern { |compiler| node.pattern.accept(compiler) } end builder.in_pattern( token(node.in_loc), pattern, guard, srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset || node.location.end_offset, [";", "then"]), visit(node.statements) ) end
foo &&= baz ^^^^^^^^^^^^^^^^
foo += baz ^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 849 def visit_index_operator_write_node(node) arguments = node.arguments&.arguments || [] arguments << node.block if node.block builder.op_assign( builder.index( visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc) ), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
foo ||= baz ^^^^^^^^^^^^^^^^
foo, = 1 ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 875 def visit_index_target_node(node) builder.index_asgn( visit(node.receiver), token(node.opening_loc), visit_all(node.arguments.arguments), token(node.closing_loc), ) end
@foo &&= bar ^^^^^^^^^^^^
@foo += bar ^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 902 def visit_instance_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.ivar(token(node.name_loc))), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
@foo ||= bar ^^^^^^^^^^^^
@foo ^^^^
# File lib/prism/translation/parser/compiler.rb, line 886 def visit_instance_variable_read_node(node) builder.ivar(token(node.location)) end
@foo, = bar ^^^^
# File lib/prism/translation/parser/compiler.rb, line 920 def visit_instance_variable_target_node(node) builder.assignable(builder.ivar(token(node.location))) end
@foo = 1 ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 892 def visit_instance_variable_write_node(node) builder.assign( builder.assignable(builder.ivar(token(node.name_loc))), token(node.operator_loc), visit(node.value) ) end
1 ^
# File lib/prism/translation/parser/compiler.rb, line 926 def visit_integer_node(node) visit_numeric(node, builder.integer([node.value, srange(node.location)])) end
if /foo #{bar}/ then end
^^^^^^^^^^^^
/foo #{bar}/ ^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 932 def visit_interpolated_regular_expression_node(node) builder.regexp_compose( token(node.opening_loc), visit_all(node.parts), [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)], builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)]) ) end
“foo #{bar}” ^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 947 def visit_interpolated_string_node(node) if node.heredoc? children, closing = visit_heredoc(node) opening = token(node.opening_loc) start_offset = node.opening_loc.end_offset + 1 end_offset = node.parts.first.location.start_offset # In the below case, the offsets should be the same: # # <<~HEREDOC # a #{b} # HEREDOC # # But in this case, the end_offset would be greater than the start_offset: # # <<~HEREDOC # #{b} # HEREDOC # # So we need to make sure the result node's heredoc range is correct, without updating the children result = if start_offset < end_offset # We need to add a padding string to ensure that the heredoc has correct range for its body padding_string_node = builder.string_internal(["", srange_offsets(start_offset, end_offset)]) node_with_correct_location = builder.string_compose(opening, [padding_string_node, *children], closing) # But the padding string should not be included in the final AST, so we need to update the result's children node_with_correct_location.updated(:dstr, children) else builder.string_compose(opening, children, closing) end return result end parts = if node.parts.one? { |part| part.type == :string_node } node.parts.flat_map do |node| if node.type == :string_node && node.unescaped.lines.count >= 2 start_offset = node.content_loc.start_offset node.unescaped.lines.map do |line| end_offset = start_offset + line.length offsets = srange_offsets(start_offset, end_offset) start_offset = end_offset builder.string_internal([line, offsets]) end else visit(node) end end else visit_all(node.parts) end builder.string_compose( token(node.opening_loc), parts, token(node.closing_loc) ) end
:“foo #{bar}” ^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1010 def visit_interpolated_symbol_node(node) builder.symbol_compose( token(node.opening_loc), visit_all(node.parts), token(node.closing_loc) ) end
‘foo #{bar}` ^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1020 def visit_interpolated_x_string_node(node) if node.heredoc? children, closing = visit_heredoc(node) builder.xstring_compose(token(node.opening_loc), children, closing) else builder.xstring_compose( token(node.opening_loc), visit_all(node.parts), token(node.closing_loc) ) end end
foo(bar: baz)
^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1035 def visit_keyword_hash_node(node) builder.associate(nil, visit_all(node.elements), nil) end
def foo(**bar); end
^^^^^
def foo(**); end
^^
# File lib/prism/translation/parser/compiler.rb, line 1044 def visit_keyword_rest_parameter_node(node) builder.kwrestarg( token(node.operator_loc), node.name ? [node.name, srange(node.name_loc)] : nil ) end
-> {} ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1053 def visit_lambda_node(node) parameters = node.parameters builder.block( builder.call_lambda(token(node.operator_loc)), [node.opening, srange(node.opening_loc)], if parameters.nil? builder.args(nil, [], nil, false) elsif node.parameters.is_a?(NumberedParametersNode) visit(node.parameters) else builder.args( token(node.parameters.opening_loc), visit(node.parameters), token(node.parameters.closing_loc), false ) end, node.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))), [node.closing, srange(node.closing_loc)] ) end
foo &&= bar ^^^^^^^^^^^
foo += bar ^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1094 def visit_local_variable_operator_write_node(node) builder.op_assign( builder.assignable(builder.ident(token(node.name_loc))), [node.operator_loc.slice.chomp("="), srange(node.operator_loc)], visit(node.value) ) end
foo ||= bar ^^^^^^^^^^^
foo ^^^
# File lib/prism/translation/parser/compiler.rb, line 1078 def visit_local_variable_read_node(node) builder.ident([node.name, srange(node.location)]).updated(:lvar) end
foo, = bar ^^^
# File lib/prism/translation/parser/compiler.rb, line 1112 def visit_local_variable_target_node(node) if in_pattern builder.assignable(builder.match_var([node.name, srange(node.location)])) else builder.assignable(builder.ident(token(node.location))) end end
foo = 1 ^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1084 def visit_local_variable_write_node(node) builder.assign( builder.assignable(builder.ident(token(node.name_loc))), token(node.operator_loc), visit(node.value) ) end
foo in bar ^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1122 def visit_match_predicate_node(node) builder.match_pattern_p( visit(node.value), token(node.operator_loc), within_pattern { |compiler| node.pattern.accept(compiler) } ) end
foo => bar ^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1132 def visit_match_required_node(node) builder.match_pattern( visit(node.value), token(node.operator_loc), within_pattern { |compiler| node.pattern.accept(compiler) } ) end
/(?<foo>foo)/ =~ bar ^^^^^^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1142 def visit_match_write_node(node) builder.match_op( visit(node.call.receiver), token(node.call.message_loc), visit(node.call.arguments.arguments.first) ) end
A node that is missing from the syntax tree. This is only used in the case of a syntax error. The parser gem doesn’t have such a concept, so we invent our own here.
# File lib/prism/translation/parser/compiler.rb, line 1153 def visit_missing_node(node) ::AST::Node.new(:missing, [], location: ::Parser::Source::Map.new(srange(node.location))) end
module Foo; end ^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1159 def visit_module_node(node) builder.def_module( token(node.module_keyword_loc), visit(node.constant_path), node.body&.accept(copy_compiler(forwarding: [])), token(node.end_keyword_loc) ) end
foo, bar = baz ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1170 def visit_multi_target_node(node) elements = [*node.lefts] elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode) elements.concat(node.rights) builder.multi_lhs( token(node.lparen_loc), visit_all(elements), token(node.rparen_loc) ) end
foo, bar = baz ^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1184 def visit_multi_write_node(node) elements = [*node.lefts] elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode) elements.concat(node.rights) builder.multi_assign( builder.multi_lhs( token(node.lparen_loc), visit_all(elements), token(node.rparen_loc) ), token(node.operator_loc), visit(node.value) ) end
next ^^^^
next foo ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1205 def visit_next_node(node) builder.keyword_cmd( :next, token(node.keyword_loc), nil, visit(node.arguments) || [], nil ) end
nil ^^^
# File lib/prism/translation/parser/compiler.rb, line 1217 def visit_nil_node(node) builder.nil(token(node.location)) end
def foo(**nil); end
^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1223 def visit_no_keywords_parameter_node(node) if in_pattern builder.match_nil_pattern(token(node.operator_loc), token(node.keyword_loc)) else builder.kwnilarg(token(node.operator_loc), token(node.keyword_loc)) end end
-> { _1 + _2 } ^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1233 def visit_numbered_parameters_node(node) builder.numargs(node.maximum) end
$1 ^^
# File lib/prism/translation/parser/compiler.rb, line 1239 def visit_numbered_reference_read_node(node) builder.nth_ref([node.number, srange(node.location)]) end
def foo(bar: baz); end
^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1245 def visit_optional_keyword_parameter_node(node) builder.kwoptarg([node.name, srange(node.name_loc)], visit(node.value)) end
def foo(bar = 1); end
^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1251 def visit_optional_parameter_node(node) builder.optarg(token(node.name_loc), token(node.operator_loc), visit(node.value)) end
a or b ^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1257 def visit_or_node(node) builder.logical_op(:or, visit(node.left), token(node.operator_loc), visit(node.right)) end
def foo(bar, *baz); end
^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1263 def visit_parameters_node(node) params = [] if node.requireds.any? node.requireds.each do |required| if required.is_a?(RequiredParameterNode) params << visit(required) else compiler = copy_compiler(in_destructure: true) params << required.accept(compiler) end end end params.concat(visit_all(node.optionals)) if node.optionals.any? params << visit(node.rest) if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode) if node.posts.any? node.posts.each do |post| if post.is_a?(RequiredParameterNode) params << visit(post) else compiler = copy_compiler(in_destructure: true) params << post.accept(compiler) end end end params.concat(visit_all(node.keywords)) if node.keywords.any? params << visit(node.keyword_rest) if !node.keyword_rest.nil? params << visit(node.block) if !node.block.nil? params end
() ^^
(1) ^^^
# File lib/prism/translation/parser/compiler.rb, line 1302 def visit_parentheses_node(node) builder.begin( token(node.opening_loc), visit(node.body), token(node.closing_loc) ) end
foo => ^(bar)
^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1312 def visit_pinned_expression_node(node) expression = builder.begin(token(node.lparen_loc), visit(node.expression), token(node.rparen_loc)) builder.pin(token(node.operator_loc), expression) end
foo = 1 and bar => ^foo
^^^^
# File lib/prism/translation/parser/compiler.rb, line 1319 def visit_pinned_variable_node(node) builder.pin(token(node.operator_loc), visit(node.variable)) end
END {}
# File lib/prism/translation/parser/compiler.rb, line 1324 def visit_post_execution_node(node) builder.postexe( token(node.keyword_loc), token(node.opening_loc), visit(node.statements), token(node.closing_loc) ) end
BEGIN {}
# File lib/prism/translation/parser/compiler.rb, line 1334 def visit_pre_execution_node(node) builder.preexe( token(node.keyword_loc), token(node.opening_loc), visit(node.statements), token(node.closing_loc) ) end
The top-level program node.
# File lib/prism/translation/parser/compiler.rb, line 1344 def visit_program_node(node) visit(node.statements) end
0..5 ^^^^
# File lib/prism/translation/parser/compiler.rb, line 1350 def visit_range_node(node) if node.exclude_end? builder.range_exclusive( visit(node.left), token(node.operator_loc), visit(node.right) ) else builder.range_inclusive( visit(node.left), token(node.operator_loc), visit(node.right) ) end end
1r ^^
# File lib/prism/translation/parser/compiler.rb, line 1372 def visit_rational_node(node) visit_numeric(node, builder.rational([rational_value(node), srange(node.location)])) end
redo ^^^^
# File lib/prism/translation/parser/compiler.rb, line 1378 def visit_redo_node(node) builder.keyword_cmd(:redo, token(node.location)) end
/foo/ ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1384 def visit_regular_expression_node(node) builder.regexp_compose( token(node.opening_loc), [builder.string_internal(token(node.content_loc))], [node.closing[0], srange_offsets(node.closing_loc.start_offset, node.closing_loc.start_offset + 1)], builder.regexp_options([node.closing[1..], srange_offsets(node.closing_loc.start_offset + 1, node.closing_loc.end_offset)]) ) end
def foo(bar:); end
^^^^
# File lib/prism/translation/parser/compiler.rb, line 1399 def visit_required_keyword_parameter_node(node) builder.kwarg([node.name, srange(node.name_loc)]) end
def foo(bar); end
^^^
# File lib/prism/translation/parser/compiler.rb, line 1405 def visit_required_parameter_node(node) builder.arg(token(node.location)) end
foo rescue bar ^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1411 def visit_rescue_modifier_node(node) builder.begin_body( visit(node.expression), [ builder.rescue_body( token(node.keyword_loc), nil, nil, nil, nil, visit(node.rescue_expression) ) ] ) end
begin; rescue; end
^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1429 def visit_rescue_node(node) raise CompilationError, "Cannot directly compile rescue nodes" end
def foo(*bar); end
^^^^
def foo(*); end
^
# File lib/prism/translation/parser/compiler.rb, line 1438 def visit_rest_parameter_node(node) builder.restarg(token(node.operator_loc), token(node.name_loc)) end
retry ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1444 def visit_retry_node(node) builder.keyword_cmd(:retry, token(node.location)) end
return ^^^^^^
return 1 ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1453 def visit_return_node(node) builder.keyword_cmd( :return, token(node.keyword_loc), nil, visit(node.arguments) || [], nil ) end
self ^^^^
# File lib/prism/translation/parser/compiler.rb, line 1465 def visit_self_node(node) builder.self(token(node.location)) end
class << self; end ^^^^^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1476 def visit_singleton_class_node(node) builder.def_sclass( token(node.class_keyword_loc), token(node.operator_loc), visit(node.expression), node.body&.accept(copy_compiler(forwarding: [])), token(node.end_keyword_loc) ) end
__ENCODING__ ^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1488 def visit_source_encoding_node(node) builder.accessible(builder.__ENCODING__(token(node.location))) end
__FILE__ ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1494 def visit_source_file_node(node) builder.accessible(builder.__FILE__(token(node.location))) end
__LINE__ ^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1500 def visit_source_line_node(node) builder.accessible(builder.__LINE__(token(node.location))) end
foo(*bar)
^^^^
def foo((bar, *baz)); end
^^^^
def foo(*); bar(*); end
^
# File lib/prism/translation/parser/compiler.rb, line 1512 def visit_splat_node(node) if node.expression.nil? && forwarding.include?(:*) builder.forwarded_restarg(token(node.operator_loc)) elsif in_destructure builder.restarg(token(node.operator_loc), token(node.expression&.location)) elsif in_pattern builder.match_rest(token(node.operator_loc), token(node.expression&.location)) else builder.splat(token(node.operator_loc), visit(node.expression)) end end
A list of statements.
# File lib/prism/translation/parser/compiler.rb, line 1525 def visit_statements_node(node) builder.compstmt(visit_all(node.body)) end
“foo” ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1531 def visit_string_node(node) if node.heredoc? children, closing = visit_heredoc(node.to_interpolated) builder.string_compose(token(node.opening_loc), children, closing) elsif node.opening == "?" builder.character([node.unescaped, srange(node.location)]) else parts = if node.content.lines.count <= 1 || node.unescaped.lines.count <= 1 [builder.string_internal([node.unescaped, srange(node.content_loc)])] else start_offset = node.content_loc.start_offset [node.content.lines, node.unescaped.lines].transpose.map do |content_line, unescaped_line| end_offset = start_offset + content_line.length offsets = srange_offsets(start_offset, end_offset) start_offset = end_offset builder.string_internal([unescaped_line, offsets]) end end builder.string_compose( token(node.opening_loc), parts, token(node.closing_loc) ) end end
super(foo) ^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1562 def visit_super_node(node) arguments = node.arguments&.arguments || [] block = node.block if block.is_a?(BlockArgumentNode) arguments = [*arguments, block] block = nil end visit_block( builder.keyword_cmd( :super, token(node.keyword_loc), token(node.lparen_loc), visit_all(arguments), token(node.rparen_loc) ), block ) end
:foo ^^^^
# File lib/prism/translation/parser/compiler.rb, line 1585 def visit_symbol_node(node) if node.closing_loc.nil? if node.opening_loc.nil? builder.symbol_internal([node.unescaped, srange(node.location)]) else builder.symbol([node.unescaped, srange(node.location)]) end else parts = if node.value.lines.one? [builder.string_internal([node.unescaped, srange(node.value_loc)])] else start_offset = node.value_loc.start_offset node.value.lines.map do |line| end_offset = start_offset + line.length offsets = srange_offsets(start_offset, end_offset) start_offset = end_offset builder.string_internal([line, offsets]) end end builder.symbol_compose( token(node.opening_loc), parts, token(node.closing_loc) ) end end
true ^^^^
# File lib/prism/translation/parser/compiler.rb, line 1617 def visit_true_node(node) builder.true(token(node.location)) end
undef foo ^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1623 def visit_undef_node(node) builder.undef_method(token(node.keyword_loc), visit_all(node.names)) end
unless foo; bar end ^^^^^^^^^^^^^^^^^^^
bar unless foo ^^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1632 def visit_unless_node(node) if node.keyword_loc.start_offset == node.location.start_offset builder.condition( token(node.keyword_loc), visit(node.predicate), if node.then_keyword_loc token(node.then_keyword_loc) else srange_find(node.predicate.location.end_offset, (node.statements&.location || node.consequent&.location || node.end_keyword_loc).start_offset, [";"]) end, visit(node.consequent), token(node.consequent&.else_keyword_loc), visit(node.statements), token(node.end_keyword_loc) ) else builder.condition_mod( visit(node.consequent), visit(node.statements), token(node.keyword_loc), visit(node.predicate) ) end end
until foo; bar end ^^^^^^^^^^^^^^^^^
bar until foo ^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1662 def visit_until_node(node) if node.location.start_offset == node.keyword_loc.start_offset builder.loop( :until, token(node.keyword_loc), visit(node.predicate), srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]), visit(node.statements), token(node.closing_loc) ) else builder.loop_mod( :until, visit(node.statements), token(node.keyword_loc), visit(node.predicate) ) end end
case foo; when bar; end
^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1684 def visit_when_node(node) builder.when( token(node.keyword_loc), visit_all(node.conditions), if node.then_keyword_loc token(node.then_keyword_loc) else srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset || (node.conditions.last.location.end_offset + 1), [";"]) end, visit(node.statements) ) end
while foo; bar end ^^^^^^^^^^^^^^^^^^
bar while foo ^^^^^^^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1702 def visit_while_node(node) if node.location.start_offset == node.keyword_loc.start_offset builder.loop( :while, token(node.keyword_loc), visit(node.predicate), srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, [";", "do"]), visit(node.statements), token(node.closing_loc) ) else builder.loop_mod( :while, visit(node.statements), token(node.keyword_loc), visit(node.predicate) ) end end
‘foo` ^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1724 def visit_x_string_node(node) if node.heredoc? children, closing = visit_heredoc(node.to_interpolated) builder.xstring_compose(token(node.opening_loc), children, closing) else parts = if node.unescaped.lines.one? [builder.string_internal([node.unescaped, srange(node.content_loc)])] else start_offset = node.content_loc.start_offset node.unescaped.lines.map do |line| end_offset = start_offset + line.length offsets = srange_offsets(start_offset, end_offset) start_offset = end_offset builder.string_internal([line, offsets]) end end builder.xstring_compose( token(node.opening_loc), parts, token(node.closing_loc) ) end end
yield ^^^^^
yield 1 ^^^^^^^
# File lib/prism/translation/parser/compiler.rb, line 1756 def visit_yield_node(node) builder.keyword_cmd( :yield, token(node.keyword_loc), token(node.lparen_loc), visit(node.arguments) || [], token(node.rparen_loc) ) end
Private Instance Methods
Initialize a new compiler with the given option overrides, used to visit a subtree with the given options.
# File lib/prism/translation/parser/compiler.rb, line 1770 def copy_compiler(forwarding: self.forwarding, in_destructure: self.in_destructure, in_pattern: self.in_pattern) Compiler.new(parser, offset_cache, forwarding: forwarding, in_destructure: in_destructure, in_pattern: in_pattern) end
When *, **, &, or … are used as an argument in a method call, we check if they were allowed by the current context. To determine that we build this lookup table.
# File lib/prism/translation/parser/compiler.rb, line 1777 def find_forwarding(node) return [] if node.nil? forwarding = [] forwarding << :* if node.rest.is_a?(RestParameterNode) && node.rest.name.nil? forwarding << :** if node.keyword_rest.is_a?(KeywordRestParameterNode) && node.keyword_rest.name.nil? forwarding << :& if !node.block.nil? && node.block.name.nil? forwarding |= [:&, :"..."] if node.keyword_rest.is_a?(ForwardingParameterNode) forwarding end
Because we have mutated the AST to allow for newlines in the middle of a rational, we need to manually handle the value here.
# File lib/prism/translation/parser/compiler.rb, line 1791 def imaginary_value(node) Complex(0, node.numeric.is_a?(RationalNode) ? rational_value(node.numeric) : node.numeric.value) end
Negate the value of a numeric node. This is a special case where you have a negative sign on one line and then a number on the next line. In normal Ruby, this will always be a method call. The parser gem, however, marks this as a numeric literal. We have to massage the tree here to get it into the correct form.
# File lib/prism/translation/parser/compiler.rb, line 1800 def numeric_negate(message_loc, receiver) case receiver.type when :integer_node, :float_node receiver.copy(value: -receiver.value, location: message_loc.join(receiver.location)) when :rational_node, :imaginary_node receiver.copy(numeric: numeric_negate(message_loc, receiver.numeric), location: message_loc.join(receiver.location)) end end
Blocks can have a special set of parameters that automatically expand when given arrays if they have a single required parameter and no other parameters.
# File lib/prism/translation/parser/compiler.rb, line 1812 def procarg0?(parameters) parameters && parameters.requireds.length == 1 && parameters.optionals.empty? && parameters.rest.nil? && parameters.posts.empty? && parameters.keywords.empty? && parameters.keyword_rest.nil? && parameters.block.nil? end
Because we have mutated the AST to allow for newlines in the middle of a rational, we need to manually handle the value here.
# File lib/prism/translation/parser/compiler.rb, line 1825 def rational_value(node) if node.numeric.is_a?(IntegerNode) Rational(node.numeric.value) else Rational(node.slice.gsub(/\s/, "").chomp("r")) end end
Constructs a new source range from the given start and end offsets.
# File lib/prism/translation/parser/compiler.rb, line 1839 def srange(location) Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset]) if location end
Constructs a new source range by finding the given tokens between the given start offset and end offset. If the needle is not found, it returns nil.
# File lib/prism/translation/parser/compiler.rb, line 1851 def srange_find(start_offset, end_offset, tokens) tokens.find do |token| next unless (index = source_buffer.source.byteslice(start_offset...end_offset).index(token)) offset = start_offset + index return [token, Range.new(source_buffer, offset_cache[offset], offset_cache[offset + token.length])] end end
Constructs a new source range from the given start and end offsets.
# File lib/prism/translation/parser/compiler.rb, line 1844 def srange_offsets(start_offset, end_offset) Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset]) end
Transform a location into a token that the parser gem expects.
# File lib/prism/translation/parser/compiler.rb, line 1860 def token(location) [location.slice, Range.new(source_buffer, offset_cache[location.start_offset], offset_cache[location.end_offset])] if location end
Visit a block node on a call.
# File lib/prism/translation/parser/compiler.rb, line 1865 def visit_block(call, block) if block parameters = block.parameters builder.block( call, token(block.opening_loc), if parameters.nil? builder.args(nil, [], nil, false) elsif parameters.is_a?(NumberedParametersNode) visit(parameters) else builder.args( token(parameters.opening_loc), if procarg0?(parameters.parameters) parameter = parameters.parameters.requireds.first [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals)) else visit(parameters) end, token(parameters.closing_loc), false ) end, block.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))), token(block.closing_loc) ) else call end end
Visit a heredoc that can be either a string or an xstring.
# File lib/prism/translation/parser/compiler.rb, line 1898 def visit_heredoc(node) children = Array.new node.parts.each do |part| pushing = if part.is_a?(StringNode) && part.unescaped.include?("\n") unescaped = part.unescaped.lines(chomp: true) escaped = part.content.lines(chomp: true) escaped_lengths = if node.opening.end_with?("'") escaped.map { |line| line.bytesize + 1 } else escaped.chunk_while { |before, after| before.match?(/(?<!\\)\\$/) }.map { |line| line.join.bytesize + line.length } end start_offset = part.location.start_offset end_offset = nil unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length| end_offset = start_offset + (escaped_length || 0) inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)]) start_offset = end_offset inner_part end else [visit(part)] end pushing.each do |child| if child.type == :str && child.children.last == "" # nothing elsif child.type == :str && children.last && children.last.type == :str && !children.last.children.first.end_with?("\n") children.last.children.first << child.children.first else children << child end end end closing = node.closing closing_t = [closing.chomp, srange_offsets(node.closing_loc.start_offset, node.closing_loc.end_offset - (closing[/\s+$/]&.length || 0))] [children, closing_t] end
Visit a numeric node and account for the optional sign.
# File lib/prism/translation/parser/compiler.rb, line 1944 def visit_numeric(node, value) if (slice = node.slice).match?(/^[+-]/) builder.unary_num( [slice[0].to_sym, srange_offsets(node.location.start_offset, node.location.start_offset + 1)], value ) else value end end
Within the given block, track that we’re within a pattern.
# File lib/prism/translation/parser/compiler.rb, line 1956 def within_pattern begin parser.pattern_variables.push yield copy_compiler(in_pattern: true) ensure parser.pattern_variables.pop end end