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 AssignmentOperationExpr implements OperationExpr {
variableExpr()22   public abstract VariableExpr variableExpr();
23 
valueExpr()24   public abstract Expr valueExpr();
25 
26   @Override
operatorKind()27   public abstract OperatorKind operatorKind();
28 
29   @Override
type()30   public TypeNode type() {
31     return variableExpr().type();
32   }
33 
34   @Override
accept(AstNodeVisitor visitor)35   public void accept(AstNodeVisitor visitor) {
36     visitor.visit(this);
37   }
38 
xorAssignmentWithExprs( VariableExpr variableExpr, Expr valueExpr)39   public static AssignmentOperationExpr xorAssignmentWithExprs(
40       VariableExpr variableExpr, Expr valueExpr) {
41     return builder()
42         .setVariableExpr(variableExpr)
43         .setValueExpr(valueExpr)
44         .setOperatorKind(OperatorKind.ASSIGNMENT_XOR)
45         .build();
46   }
47 
multiplyAssignmentWithExprs( VariableExpr variableExpr, Expr valueExpr)48   public static AssignmentOperationExpr multiplyAssignmentWithExprs(
49       VariableExpr variableExpr, Expr valueExpr) {
50     return builder()
51         .setVariableExpr(variableExpr)
52         .setValueExpr(valueExpr)
53         .setOperatorKind(OperatorKind.ASSIGNMENT_MULTIPLY)
54         .build();
55   }
56 
builder()57   private static Builder builder() {
58     return new AutoValue_AssignmentOperationExpr.Builder();
59   }
60 
61   @AutoValue.Builder
62   abstract static class Builder {
63     // Private setter.
setVariableExpr(VariableExpr variableExpr)64     abstract Builder setVariableExpr(VariableExpr variableExpr);
65 
66     // Private setter.
setValueExpr(Expr valueExpr)67     abstract Builder setValueExpr(Expr valueExpr);
68 
69     // Private setter.
setOperatorKind(OperatorKind operator)70     abstract Builder setOperatorKind(OperatorKind operator);
71 
autoBuild()72     abstract AssignmentOperationExpr autoBuild();
73 
build()74     private AssignmentOperationExpr build() {
75       AssignmentOperationExpr assignmentOperationExpr = autoBuild();
76       TypeNode lhsType = assignmentOperationExpr.variableExpr().variable().type();
77       TypeNode rhsType = assignmentOperationExpr.valueExpr().type();
78       OperatorKind operator = assignmentOperationExpr.operatorKind();
79 
80       // Check if the variable exprs have been declared, if yes, throw error.
81       Preconditions.checkState(
82           !assignmentOperationExpr.variableExpr().isDecl(),
83           String.format(
84               "Variable `%s` should not be declaration in the variable expression.",
85               assignmentOperationExpr.variableExpr().variable().name()));
86 
87       // errorMsg is type checking error message for operators.
88       final String errorMsg =
89           String.format(
90               "Assignment operator %s can not be applied to %s, %s.",
91               operator, lhsType.toString(), rhsType.toString());
92 
93       // Check type for multiply and assignment operator (*=).
94       if (operator.equals(OperatorKind.ASSIGNMENT_MULTIPLY)) {
95         Preconditions.checkState(isValidMultiplyAssignmentType(lhsType, rhsType), errorMsg);
96       }
97 
98       // Check type for XOR and assignment operator (^=).
99       if (operator.equals(OperatorKind.ASSIGNMENT_XOR)) {
100         Preconditions.checkState(isValidXorAssignmentType(lhsType, rhsType), errorMsg);
101       }
102       return assignmentOperationExpr;
103     }
104 
105     // isValidMultiplyAssignmentType validates the types for LHS variable expr and RHS expr.
106     // *= can be only applied on Primitive numeric type.
isValidMultiplyAssignmentType(TypeNode variableType, TypeNode valueType)107     private boolean isValidMultiplyAssignmentType(TypeNode variableType, TypeNode valueType) {
108       // LHS is numeric type, RHS should be any numeric type or any numeric boxed type.
109       if (TypeNode.isNumericType(variableType) && !TypeNode.isBoxedType(variableType)) {
110         return TypeNode.isNumericType(valueType);
111       }
112       // LHS is integer boxed type, RHS should be any numeric type except long, float, double.
113       if (variableType.equals(TypeNode.INT)) {
114         return TypeNode.isNumericType(valueType)
115             && !(valueType.equals(TypeNode.LONG) || TypeNode.isFloatingPointType(valueType));
116       }
117       // LHS is long boxed type, RHS should be any numeric type except float, double.
118       if (variableType.equals(TypeNode.LONG)) {
119         return TypeNode.isNumericType(valueType) && !TypeNode.isFloatingPointType(valueType);
120       }
121       // LHS is integer boxed type, RHS should be any numeric type except double.
122       if (variableType.equals(TypeNode.FLOAT)) {
123         return TypeNode.isNumericType(valueType) && !valueType.equals(TypeNode.DOUBLE);
124       }
125       // LHS is integer boxed type, RHS should be any numeric type or any numeric boxed type.
126       if (variableType.equals(TypeNode.DOUBLE)) {
127         return TypeNode.isNumericType(valueType);
128       }
129       // *= operator does not support boxed Short, Character, Byte, null, reference, void type.
130       return false;
131     }
132 
133     // isValidXorAssignmentType validates the types for LHS variable expr and RHS expr.
134     // ^= can be applied on boolean and non-floating-point numeric type.
isValidXorAssignmentType(TypeNode variableType, TypeNode valueType)135     private boolean isValidXorAssignmentType(TypeNode variableType, TypeNode valueType) {
136       // LHS is boolean or its boxed type, RHS should be boolean or its boxed type.
137       if (variableType.equals(TypeNode.BOOLEAN)) {
138         return valueType.equals(variableType);
139       }
140       // LHS is integer boxed type, RHS should be non-floating-point numeric types or their boxed
141       // types.
142       if (variableType.equals(TypeNode.INT)) {
143         return TypeNode.isNumericType(valueType) && !TypeNode.isFloatingPointType(valueType);
144       }
145       // LHS is non-floating-point numeric types, RHS should be non-float-point numeric types or
146       // their boxed types.
147       return TypeNode.isNumericType(variableType)
148           && TypeNode.isNumericType(valueType)
149           && !TypeNode.isFloatingPointType(variableType)
150           && !TypeNode.isFloatingPointType(valueType)
151           && !TypeNode.isBoxedType(variableType);
152     }
153   }
154 }
155