module Ruby2JS class Converter # (and # (...) # (...)) # (or # (...) # (...)) # Note: not handled below # (...)) handle :and, :or do |left, right| type = @ast.type if es2020 and type == :and node = rewrite(left, right) if node.type == :csend return parse right.updated(node.type, node.children) else left, right = node.children end end op_index = operator_index type lgroup = LOGICAL.include?( left.type ) && op_index < operator_index( left.type ) lgroup = true if left and left.type == :begin rgroup = LOGICAL.include?( right.type ) && op_index < operator_index( right.type ) rgroup = true if right.type == :begin put '(' if lgroup; parse left; put ')' if lgroup put (type==:and ? ' && ' : ((@or == :nullish and es2020) ? ' ?? ' : ' || ')) put '(' if rgroup; parse right; put ')' if rgroup end # (not # (...)) handle :not do |expr| if expr.type == :send and INVERT_OP.include? expr.children[1] parse(s(:send, expr.children[0], INVERT_OP[expr.children[1]], expr.children[2])) elsif expr.type == :defined? parse s(:undefined?, *expr.children) elsif expr.type == :or parse s(:and, s(:not, expr.children[0]), s(:not, expr.children[1])) elsif expr.type == :and parse s(:or, s(:not, expr.children[0]), s(:not, expr.children[1])) else group = LOGICAL.include?( expr.type ) && operator_index( :not ) < operator_index( expr.type ) group = true if expr and %i[begin in?].include? expr.type put '!'; put '(' if group; parse expr; put ')' if group end end # rewrite a && a.b to a&.b def rewrite(left, right) if left && left.type == :and left = rewrite(*left.children) end if right.type != :send or OPERATORS.flatten.include? right.children[1] s(:and, left, right) elsif conditionally_equals(left, right.children.first) # a && a.b => a&.b right.updated(:csend, [left, *right.children[1..-1]]) elsif conditionally_equals(left.children.last, right.children.first) # a && b && b.c => a && b&.c left.updated(:and, [left.children.first, left.children.last.updated(:csend, [left.children.last, *right.children[1..-1]])]) else s(:and, left, right) end end # determine if two trees are identical, modulo conditionalilties # in other words a.b == a&.b def conditionally_equals(left, right) if left == right true elsif !left.respond_to?(:type) or !left or !right or left.type != :csend or right.type != :send false else conditionally_equals(left.children.first, right.children.first) && conditionally_equals(left.children.last, right.children.last) end end end end