lib/prism/translation/ripper.rb in prism-0.23.0 vs lib/prism/translation/ripper.rb in prism-0.24.0

- old
+ new

@@ -220,10 +220,48 @@ args_val = visit_elements(node.arguments.arguments) on_break(on_args_add_block(args_val, false)) end + # Visit an AliasMethodNode. + def visit_alias_method_node(node) + # For both the old and new name, if there is a colon in the symbol + # name (e.g. 'alias :foo :bar') then we do *not* emit the [:symbol] wrapper around + # the lexer token (e.g. :@ident) inside [:symbol_literal]. But if there + # is no colon (e.g. 'alias foo bar') then we *do* still emit the [:symbol] wrapper. + + if node.new_name.is_a?(SymbolNode) && !node.new_name.opening + new_name_val = visit_symbol_literal_node(node.new_name, no_symbol_wrapper: true) + else + new_name_val = visit(node.new_name) + end + if node.old_name.is_a?(SymbolNode) && !node.old_name.opening + old_name_val = visit_symbol_literal_node(node.old_name, no_symbol_wrapper: true) + else + old_name_val = visit(node.old_name) + end + + on_alias(new_name_val, old_name_val) + end + + # Visit an AliasGlobalVariableNode. + def visit_alias_global_variable_node(node) + on_var_alias(visit(node.new_name), visit(node.old_name)) + end + + # Visit a GlobalVariableReadNode. + def visit_global_variable_read_node(node) + bounds(node.location) + on_gvar(node.name.to_s) + end + + # Visit a BackReferenceReadNode. + def visit_back_reference_read_node(node) + bounds(node.location) + on_backref(node.name.to_s) + end + # Visit an AndNode. def visit_and_node(node) visit_binary_operator(node) end @@ -324,45 +362,26 @@ on_xstring_literal(on_xstring_add(on_xstring_new, tstring_val)) end # Visit an InterpolatedStringNode node. def visit_interpolated_string_node(node) - parts = node.parts.map do |part| - case part - when StringNode - bounds(part.content_loc) - on_tstring_content(part.content) - when EmbeddedStatementsNode - on_string_embexpr(visit(part)) - else - raise NotImplementedError, "Unexpected node type in InterpolatedStringNode" - end - end - - string_list = parts.inject(on_string_content) do |items, item| - on_string_add(items, item) - end - - on_string_literal(string_list) + on_string_literal(visit_enumerated_node(node)) end # Visit an EmbeddedStatementsNode node. def visit_embedded_statements_node(node) visit(node.statements) end # Visit a SymbolNode node. def visit_symbol_node(node) - if (opening = node.opening) && (['"', "'"].include?(opening[-1]) || opening.start_with?("%s")) - bounds(node.value_loc) - tstring_val = on_tstring_content(node.value.to_s) - return on_dyna_symbol(on_string_add(on_string_content, tstring_val)) - end + visit_symbol_literal_node(node) + end - bounds(node.value_loc) - ident_val = on_ident(node.value.to_s) - on_symbol_literal(on_symbol(ident_val)) + # Visit an InterpolatedSymbolNode node. + def visit_interpolated_symbol_node(node) + on_dyna_symbol(visit_enumerated_node(node)) end # Visit a StatementsNode node. def visit_statements_node(node) bounds(node.location) @@ -457,10 +476,29 @@ elements.inject(on_args_new) do |args, element| on_args_add(args, visit(element)) end end + # Visit an InterpolatedStringNode or an InterpolatedSymbolNode node. + def visit_enumerated_node(node) + parts = node.parts.map do |part| + case part + when StringNode + bounds(part.content_loc) + on_tstring_content(part.content) + when EmbeddedStatementsNode + on_string_embexpr(visit(part)) + else + raise NotImplementedError, "Unexpected node type in visit_enumerated_node" + end + end + + parts.inject(on_string_content) do |items, item| + on_string_add(items, item) + end + end + # Visit an operation-and-assign node, such as +=. def visit_binary_op_assign(node, operator: node.operator) bounds(node.name_loc) ident_val = on_ident(node.name.to_s) @@ -483,9 +521,90 @@ def visit_aref_field_node(node) first_arg_val = visit(node.arguments.arguments[0]) args_val = on_args_add_block(on_args_add(on_args_new, first_arg_val), false) assign_val = visit(node.arguments.arguments[1]) on_assign(on_aref_field(visit(node.receiver), args_val), assign_val) + end + + # In an alias statement Ripper will emit @kw instead of @ident if the object + # being aliased is a Ruby keyword. For instance, in the line "alias :foo :if", + # the :if is treated as a lexer keyword. So we need to know what symbols are + # also keywords. + RUBY_KEYWORDS = [ + "alias", + "and", + "begin", + "BEGIN", + "break", + "case", + "class", + "def", + "defined?", + "do", + "else", + "elsif", + "end", + "END", + "ensure", + "false", + "for", + "if", + "in", + "module", + "next", + "nil", + "not", + "or", + "redo", + "rescue", + "retry", + "return", + "self", + "super", + "then", + "true", + "undef", + "unless", + "until", + "when", + "while", + "yield", + "__ENCODING__", + "__FILE__", + "__LINE__", + ] + + # Ripper has several methods of emitting a symbol literal. Inside an alias + # sometimes it suppresses the [:symbol] wrapper around ident. If the symbol + # is also the name of a keyword (e.g. :if) it will emit a :@kw wrapper, not + # an :@ident wrapper, with similar treatment for constants and operators. + def visit_symbol_literal_node(node, no_symbol_wrapper: false) + if (opening = node.opening) && (['"', "'"].include?(opening[-1]) || opening.start_with?("%s")) + bounds(node.value_loc) + str_val = node.value.to_s + if str_val == "" + return on_dyna_symbol(on_string_content) + else + tstring_val = on_tstring_content(str_val) + return on_dyna_symbol(on_string_add(on_string_content, tstring_val)) + end + end + + bounds(node.value_loc) + node_name = node.value.to_s + if RUBY_KEYWORDS.include?(node_name) + token_val = on_kw(node_name) + elsif node_name.length == 0 + raise NotImplementedError + elsif /[[:upper:]]/.match(node_name[0]) + token_val = on_const(node_name) + elsif /[[:punct:]]/.match(node_name[0]) + token_val = on_op(node_name) + else + token_val = on_ident(node_name) + end + sym_val = no_symbol_wrapper ? token_val : on_symbol(token_val) + on_symbol_literal(sym_val) end # Visit a node that represents a number. We need to explicitly handle the # unary - operator. def visit_number(node)