1 // Copyright 2020 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package com.google.api.generator.engine.ast; 16 17 import com.google.auto.value.AutoValue; 18 import com.google.common.base.Preconditions; 19 20 @AutoValue 21 public abstract class UnaryOperationExpr implements OperationExpr { 22 expr()23 public abstract Expr expr(); 24 25 @Override operatorKind()26 public abstract OperatorKind operatorKind(); 27 28 @Override type()29 public abstract TypeNode type(); 30 31 @Override accept(AstNodeVisitor visitor)32 public void accept(AstNodeVisitor visitor) { 33 visitor.visit(this); 34 } 35 isPostfixIncrement()36 public boolean isPostfixIncrement() { 37 return operatorKind().equals(OperatorKind.UNARY_POST_INCREMENT); 38 } 39 postfixIncrementWithExpr(Expr expr)40 public static UnaryOperationExpr postfixIncrementWithExpr(Expr expr) { 41 return builder() 42 .setExpr(expr) 43 .setOperatorKind(OperatorKind.UNARY_POST_INCREMENT) 44 .setType(expr.type()) 45 .build(); 46 } 47 logicalNotWithExpr(Expr expr)48 public static UnaryOperationExpr logicalNotWithExpr(Expr expr) { 49 return builder() 50 .setOperatorKind(OperatorKind.UNARY_LOGICAL_NOT) 51 .setExpr(expr) 52 .setType(TypeNode.BOOLEAN) 53 .build(); 54 } 55 builder()56 private static Builder builder() { 57 return new AutoValue_UnaryOperationExpr.Builder(); 58 } 59 60 @AutoValue.Builder 61 abstract static class Builder { 62 63 // Private setter. setExpr(Expr expr)64 abstract Builder setExpr(Expr expr); 65 66 // Private setter. setOperatorKind(OperatorKind operator)67 abstract Builder setOperatorKind(OperatorKind operator); 68 69 // Private setter. setType(TypeNode type)70 abstract Builder setType(TypeNode type); 71 autoBuild()72 abstract UnaryOperationExpr autoBuild(); 73 build()74 private UnaryOperationExpr build() { 75 UnaryOperationExpr unaryOperationExpr = autoBuild(); 76 TypeNode exprType = unaryOperationExpr.expr().type(); 77 OperatorKind operator = unaryOperationExpr.operatorKind(); 78 79 // Add final keyword checking for post/prefix ++, -- when needed. 80 if (operator.equals(OperatorKind.UNARY_POST_INCREMENT) 81 && unaryOperationExpr.expr() instanceof VariableExpr) { 82 VariableExpr varExpr = (VariableExpr) unaryOperationExpr.expr(); 83 Preconditions.checkState( 84 !varExpr.isFinal(), 85 String.format("Cannot increment the final variable '%s'.", varExpr.variable().name())); 86 87 Preconditions.checkState( 88 !varExpr.isDecl(), 89 String.format( 90 "Cannot increment the declaration of variable %s", varExpr.variable().name())); 91 } 92 93 final String errorMsg = 94 String.format( 95 "Unary operator %s can not be applied to %s. ", operator, exprType.toString()); 96 97 Preconditions.checkState( 98 !exprType.equals(TypeNode.VOID) && !exprType.equals(TypeNode.NULL), errorMsg); 99 100 if (operator.equals(OperatorKind.UNARY_LOGICAL_NOT)) { 101 Preconditions.checkState(isValidLogicalNotType(exprType), errorMsg); 102 } 103 104 if (operator.equals(OperatorKind.UNARY_POST_INCREMENT)) { 105 Preconditions.checkState(isValidIncrementType(exprType), errorMsg); 106 } 107 108 return unaryOperationExpr; 109 } 110 } 111 isValidLogicalNotType(TypeNode exprType)112 private static boolean isValidLogicalNotType(TypeNode exprType) { 113 // Logical not (!) can only be applied on boolean/Boolean type 114 return exprType.equals(TypeNode.BOOLEAN); 115 } 116 isValidIncrementType(TypeNode exprType)117 private static boolean isValidIncrementType(TypeNode exprType) { 118 // Increment (++) can be applied on numeric types(int, double, float, long, short, char) 119 // and its boxed type (exclude Boolean) 120 return TypeNode.isNumericType(exprType); 121 } 122 } 123