lib/opal/nodes/node_with_args/shortcuts.rb
# frozen_string_literal: true module Opal module Nodes class NodeWithArgs < ScopeNode # Shortcuts for the simplest kinds of methods Shortcut = Struct.new(:name, :for, :when, :transform) do def match?(node) node.instance_exec(&self.when) end def compile(node) node.helper name node.instance_exec(&transform) end end @shortcuts = [] @shortcuts_for = {} def self.define_shortcut(name, **kwargs, &block) kwargs[:for] ||= :def @shortcuts << Shortcut.new(name, kwargs[:for], kwargs[:when], block) end def self.shortcuts_for(node_type) @shortcuts_for[node_type] ||= @shortcuts.select do |shortcut| [node_type, :*].include? shortcut.for end end def compile_body_or_shortcut # The shortcuts don't check arity. If we want to check arity, # we can't use them. return compile_body if compiler.arity_check? node_type = is_a?(DefNode) ? :def : :iter NodeWithArgs.shortcuts_for(node_type).each do |shortcut| if shortcut.match?(self) if ENV['OPAL_DEBUG_SHORTCUTS'] node_desc = node_type == :def ? "def #{mid}" : "iter" warn "* shortcut #{shortcut.name} used for #{node_desc}" end return shortcut.compile(self) end end compile_body end # Shortcut definitions # -------------------- # def a; self; end define_shortcut :return_self, when: -> { stmts.type == :self } do push '$return_self' end def simple_value?(node = stmts) %i[true false nil int float str sym].include?(node.type) end # def a; 123; end define_shortcut :return_val, for: :*, when: -> { simple_value? } do push '$return_val(', expr(stmts), ')' end # def a; @x; end define_shortcut :return_ivar, when: -> { stmts.type == :ivar } do name = stmts.children.first.to_s[1..-1].to_sym push '$return_ivar(', expr(stmts.updated(:sym, [name])), ')' end # def a; @x = 5; end define_shortcut :assign_ivar, when: -> { stmts.type == :ivasgn && inline_args.children.length == 1 && inline_args.children.last.type == :arg && stmts.children.last.type == :lvar && stmts.children.last.children.last == inline_args.children.last.children.last } do name = stmts.children.first.to_s[1..-1].to_sym name = expr(stmts.updated(:sym, [name])) push '$assign_ivar(', name, ')' end # def a(x); @x = x; end define_shortcut :assign_ivar_val, when: -> { stmts.type == :ivasgn && simple_value?(stmts.children.last) } do name = stmts.children.first.to_s[1..-1].to_sym name = expr(stmts.updated(:sym, [name])) push '$assign_ivar_val(', name, ', ', expr(stmts.children.last), ')' end end end end