Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public interface AstNodeVisitor {

public void visit(ArithmeticOperationExpr arithmeticOperationExpr);

public void visit(UnaryOperationExpr unaryOperationExpr);

/** =============================== COMMENT =============================== */
public void visit(LineComment lineComment);

Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/google/api/generator/engine/ast/TypeNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
Expand Down Expand Up @@ -66,6 +67,14 @@ public enum TypeKind {
withReference(ConcreteReference.withClazz(Short.class));

private static final Map<TypeNode, TypeNode> BOXED_TYPE_MAP = createBoxedTypeMap();
private static final EnumSet<TypeKind> NUMERIC_TYPE_SET =
EnumSet.of(
Comment thread
summer-ji-eng marked this conversation as resolved.
Outdated
TypeKind.INT,
TypeKind.DOUBLE,
TypeKind.LONG,
TypeKind.SHORT,
TypeKind.FLOAT,
TypeKind.CHAR);

public static final TypeNode VOID = builder().setTypeKind(TypeKind.VOID).build();

Expand Down Expand Up @@ -125,10 +134,18 @@ public static boolean isReferenceType(TypeNode type) {
&& !type.equals(TypeNode.NULL);
}

public static boolean isBoxedType(TypeNode type) {
Comment thread
summer-ji-eng marked this conversation as resolved.
Outdated
return BOXED_TYPE_MAP.containsValue(type);
}

public boolean isPrimitiveType() {
return isPrimitiveType(typeKind());
}

public boolean isNumericType() {
return isNumericType(typeKind());
}

public boolean isSupertypeOrEquals(TypeNode other) {
boolean oneTypeIsNull = this.equals(TypeNode.NULL) ^ other.equals(TypeNode.NULL);
return !isPrimitiveType()
Expand Down Expand Up @@ -200,6 +217,10 @@ private static boolean isPrimitiveType(TypeKind typeKind) {
return !typeKind.equals(TypeKind.OBJECT);
}

private static boolean isNumericType(TypeKind typeKind) {
return NUMERIC_TYPE_SET.contains(typeKind);
}

private static Map<TypeNode, TypeNode> createBoxedTypeMap() {
Map<TypeNode, TypeNode> map = new HashMap<>();
map.put(TypeNode.INT, TypeNode.INT_OBJECT);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.generator.engine.ast;

import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;

@AutoValue
public abstract class UnaryOperationExpr implements OperationExpr {

public abstract Expr expr();

public abstract OperatorKind operatorKind();

public abstract TypeNode type();

public void accept(AstNodeVisitor visitor) {
visitor.visit(this);
}

public static UnaryOperationExpr postfixIncrementWithExpr(Expr expr) {
return builder()
.setExpr(expr)
.setOperatorKind(OperatorKind.UNARY_POST_INCREMENT)
.setType(expr.type())
.build();
}

public static UnaryOperationExpr logicalNotWithExpr(Expr expr) {
return builder()
.setOperatorKind(OperatorKind.UNARY_LOGICAL_NOT)
.setExpr(expr)
.setType(TypeNode.BOOLEAN)
.build();
}

public static Builder builder() {
Comment thread
summer-ji-eng marked this conversation as resolved.
Outdated
return new AutoValue_UnaryOperationExpr.Builder();
}

@AutoValue.Builder
abstract static class Builder {

// private setter.
Comment thread
summer-ji-eng marked this conversation as resolved.
Outdated
abstract Builder setExpr(Expr expr);

// private setter.
abstract Builder setOperatorKind(OperatorKind operator);

// private setter.
abstract Builder setType(TypeNode type);

abstract UnaryOperationExpr autoBuild();

private UnaryOperationExpr build() {
UnaryOperationExpr unaryOperationExpr = autoBuild();
TypeNode exprType = unaryOperationExpr.expr().type();
OperatorKind operator = unaryOperationExpr.operatorKind();
final String errorMsg =
String.format(
"Unary operator %s can not be applied to %s. ", operator, exprType.toString());

// Unary Operators can not be applied on Void type
Comment thread
summer-ji-eng marked this conversation as resolved.
Outdated
Preconditions.checkState(
!exprType.equals(TypeNode.VOID) && !exprType.equals(TypeNode.NULL), errorMsg);

if (operator.equals(OperatorKind.UNARY_LOGICAL_NOT)) {
Preconditions.checkState(isValidLogicalNotType(exprType), errorMsg);
}

if (operator.equals(OperatorKind.UNARY_POST_INCREMENT)) {
Preconditions.checkState(isValidIncrementType(exprType), errorMsg);
}

return unaryOperationExpr;
}
}

private static boolean isValidLogicalNotType(TypeNode exprType) {
// Logical not (!) can only be applied on boolean/Boolean type
return exprType.equals(TypeNode.BOOLEAN) || exprType.equals(TypeNode.BOOLEAN_OBJECT);
Comment thread
summer-ji-eng marked this conversation as resolved.
Outdated
}

private static boolean isValidIncrementType(TypeNode exprType) {
// Increment (++) can be applied on Numeric Type(int, double, float, long, short, char)
Comment thread
summer-ji-eng marked this conversation as resolved.
Outdated
// and its boxed type (exclude Boolean)
return exprType.isNumericType()
|| (TypeNode.isBoxedType(exprType) && !exprType.equals(TypeNode.BOOLEAN_OBJECT));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.google.api.generator.engine.ast.ThrowExpr;
import com.google.api.generator.engine.ast.TryCatchStatement;
import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.engine.ast.UnaryOperationExpr;
import com.google.api.generator.engine.ast.ValueExpr;
import com.google.api.generator.engine.ast.VariableExpr;
import com.google.api.generator.engine.ast.WhileStatement;
Expand Down Expand Up @@ -218,6 +219,11 @@ public void visit(ArithmeticOperationExpr arithmeticOperationExpr) {
arithmeticOperationExpr.rhsExpr().accept(this);
}

@Override
public void visit(UnaryOperationExpr unaryOperationExpr) {
unaryOperationExpr.expr().accept(this);
}

/** =============================== STATEMENTS =============================== */
@Override
public void visit(ExprStatement exprStatement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.google.api.generator.engine.ast.TryCatchStatement;
import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.engine.ast.TypeNode.TypeKind;
import com.google.api.generator.engine.ast.UnaryOperationExpr;
import com.google.api.generator.engine.ast.ValueExpr;
import com.google.api.generator.engine.ast.Variable;
import com.google.api.generator.engine.ast.VariableExpr;
Expand Down Expand Up @@ -394,6 +395,17 @@ public void visit(ArithmeticOperationExpr arithmeticOperationExpr) {
arithmeticOperationExpr.rhsExpr().accept(this);
}

@Override
public void visit(UnaryOperationExpr unaryOperationExpr) {
if (unaryOperationExpr.operatorKind().isPrefixOperator()) {
operator(unaryOperationExpr.operatorKind());
unaryOperationExpr.expr().accept(this);
} else {
unaryOperationExpr.expr().accept(this);
operator(unaryOperationExpr.operatorKind());
}
}

/** =============================== STATEMENTS =============================== */
@Override
public void visit(ExprStatement exprStatement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ TESTS = [
"SuperObjectValueTest",
"WhileStatementTest",
"ArithmeticOperationExprTest",
"UnaryOperationExprTest",
]

filegroup(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.api.generator.engine.ast;

import static org.junit.Assert.assertThrows;

import org.junit.Test;

public class UnaryOperationExprTest {
/** =============================== Logic Not Operation Expr =============================== */
@Test
public void logicalNotOperationExpr_validBasic() {
VariableExpr variableExpr =
VariableExpr.withVariable(
Variable.builder().setName("x").setType(TypeNode.BOOLEAN).build());
UnaryOperationExpr.logicalNotWithExpr(variableExpr);
// No exception thrown, we're good.
}

@Test
public void logicalNot_validBoxedType() {
VariableExpr variableExpr =
VariableExpr.withVariable(
Variable.builder().setName("x").setType(TypeNode.BOOLEAN_OBJECT).build());
UnaryOperationExpr.logicalNotWithExpr(variableExpr);
// No exception thrown, we're good.
}

@Test
public void logicalNot_invalidNumericType() {
VariableExpr variableExpr =
VariableExpr.withVariable(Variable.builder().setName("x").setType(TypeNode.INT).build());
assertThrows(
IllegalStateException.class, () -> UnaryOperationExpr.logicalNotWithExpr(variableExpr));
}

@Test
public void logicalNot_invalidReferenceType() {
VariableExpr variableExpr =
VariableExpr.withVariable(Variable.builder().setName("x").setType(TypeNode.STRING).build());
assertThrows(
IllegalStateException.class, () -> UnaryOperationExpr.logicalNotWithExpr(variableExpr));
}

/**
* =============================== Post Increment Operation Expr ===============================
*/
@Test
public void postIncrement_validBasic() {
VariableExpr variableExpr =
VariableExpr.withVariable(Variable.builder().setName("x").setType(TypeNode.INT).build());
UnaryOperationExpr.postfixIncrementWithExpr(variableExpr);
// No exception thrown, we're good.
}

@Test
public void postIncrement_validBoxedType() {
VariableExpr variableExpr =
VariableExpr.withVariable(
Variable.builder().setName("x").setType(TypeNode.FLOAT_OBJECT).build());
UnaryOperationExpr.postfixIncrementWithExpr(variableExpr);
// No exception thrown, we're good.
}

@Test
public void postIncrement_invalidBoxedBooleanType() {
VariableExpr variableExpr =
VariableExpr.withVariable(
Variable.builder().setName("x").setType(TypeNode.BOOLEAN_OBJECT).build());
assertThrows(
IllegalStateException.class,
() -> UnaryOperationExpr.postfixIncrementWithExpr(variableExpr));
}

@Test
public void postIncrement_invalidReferenceType() {
VariableExpr variableExpr =
VariableExpr.withVariable(Variable.builder().setName("x").setType(TypeNode.STRING).build());
assertThrows(
IllegalStateException.class,
() -> UnaryOperationExpr.postfixIncrementWithExpr(variableExpr));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.google.api.generator.engine.ast.ThisObjectValue;
import com.google.api.generator.engine.ast.ThrowExpr;
import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.engine.ast.UnaryOperationExpr;
import com.google.api.generator.engine.ast.ValueExpr;
import com.google.api.generator.engine.ast.VaporReference;
import com.google.api.generator.engine.ast.Variable;
Expand Down Expand Up @@ -867,6 +868,32 @@ public void writeSynchronizedStatementImports_basicVariableExpr() {
"import java.util.Map;\n\n"));
}

@Test
public void writeUnaryOperationExprImports_LogicalNot() {
MethodInvocationExpr expr =
MethodInvocationExpr.builder()
.setStaticReferenceType(TypeNode.withReference(ConcreteReference.withClazz(Expr.class)))
.setMethodName("isEmpty")
.setReturnType(TypeNode.BOOLEAN)
.build();
UnaryOperationExpr unaryOperationExpr = UnaryOperationExpr.logicalNotWithExpr(expr);
unaryOperationExpr.accept(writerVisitor);
assertEquals(writerVisitor.write(), "import com.google.api.generator.engine.ast.Expr;\n\n");
}

@Test
public void writeUnaryOperationExprImports_PostIncrement() {
MethodInvocationExpr expr =
MethodInvocationExpr.builder()
.setStaticReferenceType(TypeNode.withReference(ConcreteReference.withClazz(Expr.class)))
.setMethodName("getNumber")
.setReturnType(TypeNode.INT)
.build();
UnaryOperationExpr unaryOperationExpr = UnaryOperationExpr.postfixIncrementWithExpr(expr);
unaryOperationExpr.accept(writerVisitor);
assertEquals(writerVisitor.write(), "import com.google.api.generator.engine.ast.Expr;\n\n");
}

private static TypeNode createType(Class clazz) {
return TypeNode.withReference(ConcreteReference.withClazz(clazz));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import com.google.api.generator.engine.ast.ThrowExpr;
import com.google.api.generator.engine.ast.TryCatchStatement;
import com.google.api.generator.engine.ast.TypeNode;
import com.google.api.generator.engine.ast.UnaryOperationExpr;
import com.google.api.generator.engine.ast.Value;
import com.google.api.generator.engine.ast.ValueExpr;
import com.google.api.generator.engine.ast.VaporReference;
Expand Down Expand Up @@ -1975,6 +1976,29 @@ public void writeSuperObjectValue_accessFieldAndInvokeMethod() {
assertThat(writerVisitor.write()).isEqualTo("super.name = super.getName()");
}

@Test
public void writeUnaryOperationExpr_postfixIncrement() {
VariableExpr variableExpr =
VariableExpr.withVariable(Variable.builder().setType(TypeNode.INT).setName("i").build());
UnaryOperationExpr postIncrementOperationExpr =
UnaryOperationExpr.postfixIncrementWithExpr(variableExpr);
postIncrementOperationExpr.accept(writerVisitor);
assertThat(writerVisitor.write()).isEqualTo("i++");
}

@Test
public void writeUnaryOperationExpr_logicalNot() {
MethodInvocationExpr methodInvocationExpr =
MethodInvocationExpr.builder()
.setMethodName("isEmpty")
.setReturnType(TypeNode.BOOLEAN)
.build();
UnaryOperationExpr logicalNotOperationExpr =
UnaryOperationExpr.logicalNotWithExpr(methodInvocationExpr);
logicalNotOperationExpr.accept(writerVisitor);
assertThat(writerVisitor.write()).isEqualTo("!isEmpty()");
}

private static String createLines(int numLines) {
return new String(new char[numLines]).replace("\0", "%s");
}
Expand Down