lib/opal/nodes/logic.rb
# frozen_string_literal: true require 'opal/nodes/base' module Opal module Nodes class NextNode < Base handle :next def compile if in_while? push 'continue;' else push expr_or_nil(value) wrap 'return ', ';' end end def value case children.size when 0 s(:nil) when 1 children.first else s(:array, *children) end end end class BreakNode < Base handle :break children :value def compile if in_while? compile_while elsif scope.iter? compile_iter else error 'void value expression: cannot use break outside of iter/while' end end def compile_while if while_loop[:closure] push 'return ', expr_or_nil(value) else push 'break;' end end def compile_iter error 'break must be used as a statement' unless stmt? line 'Opal.brk(', break_val, ', $brk)' end def break_val if value.nil? expr(s(:nil)) else expr(value) end end end class RedoNode < Base handle :redo def compile if in_while? compile_while elsif scope.iter? compile_iter else push 'REDO()' end end def compile_while while_loop[:use_redo] = true push "#{while_loop[:redo_var]} = true; continue;" end def compile_iter push "return #{scope.identity}.apply(null, $slice.call(arguments))" end end class SplatNode < Base handle :splat children :value def empty_splat? value == s(:array) end def compile if empty_splat? push '[]' else push 'Opal.to_a(', recv(value), ')' end end end class BinaryOp < Base def compile if rhs.type == :break compile_if else compile_ternary end end def compile_ternary raise NotImplementedError end def compile_if raise NotImplementedError end end class OrNode < BinaryOp handle :or children :lhs, :rhs def compile_ternary helper :truthy with_temp do |tmp| push "($truthy(#{tmp} = ", expr(lhs), ") ? #{tmp} : ", expr(rhs), ')' end end def compile_if helper :truthy with_temp do |tmp| push "if ($truthy(#{tmp} = ", expr(lhs), ')) {' indent { line tmp } line '} else {' indent { line expr(rhs) } line '}' end end end class AndNode < BinaryOp handle :and children :lhs, :rhs def compile_ternary truthy_opt = nil with_temp do |tmp| if truthy_opt = js_truthy_optimize(lhs) push "((#{tmp} = ", truthy_opt push ') ? ' push expr(rhs) push ' : ', expr(lhs), ')' else helper :truthy push "($truthy(#{tmp} = ", expr(lhs), ') ? ', expr(rhs), " : #{tmp})" end end end def compile_if helper :truthy condition = js_truthy_optimize(lhs) || expr(lhs) line 'if ($truthy(', condition, ')) {' indent { line expr(rhs) } line '} else {' indent { line expr(lhs) } line '}' end end class ReturnNode < Base handle :return children :value def return_val if value.nil? expr(s(:nil)) elsif children.size > 1 expr(s(:array, *children)) else expr(value) end end def return_in_iter? if scope.iter? && parent_def = scope.find_parent_def parent_def end end def return_expr_in_def? return scope if expr? && scope.def? end def scope_to_catch_return return_in_iter? || return_expr_in_def? end def compile if def_scope = scope_to_catch_return def_scope.catch_return = true push 'Opal.ret(', return_val, ')' elsif stmt? push 'return ', return_val else raise SyntaxError, 'void value expression: cannot return as an expression' end end end class JSReturnNode < Base handle :js_return children :value def compile push 'return ' push expr(value) end end class JSTempNode < Base handle :js_tmp children :value def compile push value.to_s end end class BlockPassNode < Base handle :block_pass children :value def compile push expr(s(:send, value, :to_proc, s(:arglist))) end end end end