001/* 002 * Copyright (c) 2012-2021 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package gololang.ir; 012 013import java.util.Arrays; 014import java.util.List; 015import org.eclipse.golo.runtime.InvalidDestructuringException; 016 017/** 018 * Represents a binary operation. 019 * 020 * @see OperatorType 021 */ 022public final class BinaryOperation extends ExpressionStatement<BinaryOperation> { 023 024 private final OperatorType type; 025 private ExpressionStatement<?> leftExpression; 026 private ExpressionStatement<?> rightExpression; 027 028 private BinaryOperation(OperatorType type) { 029 super(); 030 this.type = type; 031 } 032 033 /** 034 * Create a binary operation. 035 * 036 * @param type a {@link OperatorType} or a {@code String} representing the operator. 037 */ 038 public static BinaryOperation of(Object type) { 039 return new BinaryOperation(OperatorType.of(type)); 040 } 041 042 /** 043 * Full generic binary operation creation in one call. 044 * 045 * <p>Less readable than the fluent API, but useful when doing meta-generation 046 * 047 * @param type the type of the operation ({@link BinaryOperation#of(Object)}) 048 * @param left the left expression. 049 * @param right the right expression. 050 * @return a configured binary operation. 051 */ 052 public static BinaryOperation create(Object type, Object left, Object right) { 053 return of(type).left(left).right(right); 054 } 055 056 protected BinaryOperation self() { return this; } 057 058 public OperatorType getType() { 059 return type; 060 } 061 062 public ExpressionStatement<?> left() { 063 return leftExpression; 064 } 065 066 /** 067 * Define the left expression of this operation. 068 * 069 * <p>This is a builder method. 070 * 071 * @param expr the {@link gololang.ir.ExpressionStatement} to use as the left 072 * operand 073 * @return this operation 074 */ 075 public BinaryOperation left(Object expr) { 076 this.leftExpression = makeParentOf(ExpressionStatement.of(expr)); 077 return this; 078 } 079 080 /** 081 * Define the right expression of this operation. 082 * 083 * <p>This is a builder method. 084 * 085 * @param expr the {@link gololang.ir.ExpressionStatement} to use as the 086 * right operand 087 * @return this operation 088 */ 089 public BinaryOperation right(Object expr) { 090 this.rightExpression = makeParentOf(ExpressionStatement.of(expr)); 091 if (this.type == OperatorType.ELVIS_METHOD_CALL && this.rightExpression instanceof MethodInvocation) { 092 ((MethodInvocation) this.rightExpression).nullSafe(true); 093 } 094 return this; 095 } 096 097 public ExpressionStatement<?> right() { 098 return rightExpression; 099 } 100 101 @Override 102 public String toString() { 103 return String.format("%s %s %s", leftExpression, type, rightExpression); 104 } 105 106 public boolean isMethodCall() { 107 return this.getType() == OperatorType.METHOD_CALL 108 || this.getType() == OperatorType.ELVIS_METHOD_CALL 109 || this.getType() == OperatorType.ANON_CALL; 110 } 111 112 /** 113 * New style destructuring helper. 114 * 115 * <p>The destructuring must be to exactly two values. No remainer syntax is allowed. 116 * <p>The destructured values are the left and right expressions. 117 * 118 * @param number number of variable that will be affected. 119 * @param substruct whether the destructuring is complete or should contains a sub structure. 120 * @param toSkip a boolean array indicating the elements to skip. 121 * @return an array containing the values to assign. 122 */ 123 public Object[] __$$_destruct(int number, boolean substruct, Object[] toSkip) { 124 if (number == 2 && !substruct) { 125 return new Object[]{leftExpression, rightExpression}; 126 } 127 throw new InvalidDestructuringException("A BinaryOperation must destructure to exactly two values"); 128 } 129 130 /** 131 * {@inheritDoc} 132 */ 133 @Override 134 public void accept(GoloIrVisitor visitor) { 135 visitor.visitBinaryOperation(this); 136 } 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override 142 public List<GoloElement<?>> children() { 143 return Arrays.asList(leftExpression, rightExpression); 144 } 145 146 /** 147 * {@inheritDoc} 148 */ 149 @Override 150 protected void replaceElement(GoloElement<?> original, GoloElement<?> newElement) { 151 if (!(newElement instanceof ExpressionStatement)) { 152 throw cantConvert("ExpressionStatement", newElement); 153 } 154 if (leftExpression.equals(original)) { 155 left(newElement); 156 } else if (rightExpression.equals(original)) { 157 right(newElement); 158 } else { 159 throw cantReplace(original, newElement); 160 } 161 } 162}