Skip to content

Commit

Permalink
feat(ast): array expressions (#1080)
Browse files Browse the repository at this point in the history
* feat(ast): anonymous arrays for annotations

* feat(ast): add convenience methods in anon array

* feat(annotation): allow adding anon arrays

* fix: fixed comments, test util class

* feat(ast): arrays to allow VarExp and ValueExpr

* fix(ast): add description only for assignments

* fix(ast): comment correction, test array assignmnt

* feat(test): test anon array assignment

* fix(license): add licenses

* fix(ast): various fixes

* Perform validation in build()
* Ensure type() to be explicitly set
* Expose add Expr, but validate that they are either ValueExpr or
  VariableExpr

* fix(ast): ArrayExpr ImportWriterVisitor fix & test

* fix: remove unecessary comment
  • Loading branch information
diegomarquezp committed Nov 7, 2022
1 parent f5d5524 commit bd9532c
Show file tree
Hide file tree
Showing 10 changed files with 411 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ public Builder setDescription(VariableExpr variableExpr) {
return setDescriptionExprs(Arrays.asList(variableExpr));
}

/**
* To set single ArrayExpr as description.
*
* @param arrayExpr
* @return Builder
*/
public Builder setDescription(ArrayExpr arrayExpr) {
Preconditions.checkState(descriptionExprs() == null, REPEAT_SINGLE_EXCEPTION_MESSAGE);
return setDescriptionExprs(Arrays.asList(arrayExpr));
}

/**
* To add an AssignmentExpr as parameter. Can be used repeatedly to add multiple parameters.
*
Expand Down
92 changes: 92 additions & 0 deletions src/main/java/com/google/api/generator/engine/ast/ArrayExpr.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2022 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;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.List;

@AutoValue
public abstract class ArrayExpr implements Expr {

public abstract List<Expr> exprs();

public abstract TypeNode type();

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

public static ArrayExpr.Builder builder() {
return new AutoValue_ArrayExpr.Builder().setExprs(ImmutableList.of());
}

public static ArrayExpr withStrings(String... stringValues) {
ArrayExpr.Builder builder = ArrayExpr.builder();
Arrays.asList(stringValues).stream().forEach(s -> builder.addExpr(s));
return builder.build();
}

public static ArrayExpr withExprs(Expr... exprs) {
return ArrayExpr.builder().setExprs(Arrays.asList(exprs)).build();
}

@AutoValue.Builder
public abstract static class Builder {

private static final String SAME_TYPE_EXPRS_MESSAGE =
"All expressions must be of the type" + " specified in this ArrayExpr";
private static final String EXPR_ALLOWED_CLASSES_MESSAGE =
"Only VariableExpr and ValueExpr can be used as elements of ArrayExpr";

abstract List<Expr> exprs();

abstract TypeNode type();

/**
* To add a string expression same-type validation is performed
*
* @param expr
* @return Builder
*/
public ArrayExpr.Builder addExpr(String expr) {
return addExpr(ValueExpr.withValue(StringObjectValue.withValue(expr)));
}

public ArrayExpr.Builder addExpr(Expr expr) {
return setExprs((new ImmutableList.Builder<Expr>().addAll(exprs()).add(expr).build()));
}

public abstract ArrayExpr.Builder setExprs(List<Expr> exprs);

public abstract ArrayExpr.Builder setType(TypeNode type);

abstract ArrayExpr autoBuild();

public ArrayExpr build() {
Preconditions.checkState(
exprs().stream().allMatch(exp -> exp instanceof ValueExpr || exp instanceof VariableExpr),
EXPR_ALLOWED_CLASSES_MESSAGE);
TypeNode elementType = TypeNode.createElementTypeFromArrayType(type());
Preconditions.checkState(
exprs().stream().allMatch(exp -> exp.type().equals(elementType)),
SAME_TYPE_EXPRS_MESSAGE);
return autoBuild();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public interface AstNodeVisitor {

public void visit(AnnotationNode annotation);

public void visit(ArrayExpr expr);

public void visit(ConcreteReference reference);

public void visit(VaporReference reference);
Expand Down
17 changes: 17 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 @@ -101,6 +101,23 @@ public enum TypeKind {

public abstract boolean isArray();

public static TypeNode createArrayTypeOf(TypeNode type) {
return builder()
.setTypeKind(type.typeKind())
.setReference(type.reference())
.setIsArray(true)
.build();
}

public static TypeNode createElementTypeFromArrayType(TypeNode type) {
Preconditions.checkArgument(type.isArray(), "Input type must be an array");
return builder()
.setTypeKind(type.typeKind())
.setReference(type.reference())
.setIsArray(false)
.build();
}

@Nullable
public abstract Reference reference();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.google.api.generator.engine.ast.AnnotationNode;
import com.google.api.generator.engine.ast.AnonymousClassExpr;
import com.google.api.generator.engine.ast.ArithmeticOperationExpr;
import com.google.api.generator.engine.ast.ArrayExpr;
import com.google.api.generator.engine.ast.AssignmentExpr;
import com.google.api.generator.engine.ast.AssignmentOperationExpr;
import com.google.api.generator.engine.ast.AstNodeVisitor;
Expand Down Expand Up @@ -167,6 +168,11 @@ public void visit(AnnotationNode annotation) {
annotation.type().accept(this);
}

@Override
public void visit(ArrayExpr expr) {
expr.type().accept(this);
}

/** =============================== EXPRESSIONS =============================== */
@Override
public void visit(ValueExpr valueExpr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.google.api.generator.engine.ast.AnnotationNode;
import com.google.api.generator.engine.ast.AnonymousClassExpr;
import com.google.api.generator.engine.ast.ArithmeticOperationExpr;
import com.google.api.generator.engine.ast.ArrayExpr;
import com.google.api.generator.engine.ast.AssignmentExpr;
import com.google.api.generator.engine.ast.AssignmentOperationExpr;
import com.google.api.generator.engine.ast.AstNodeVisitor;
Expand Down Expand Up @@ -165,6 +166,19 @@ public void visit(ScopeNode scope) {
buffer.append(scope.toString());
}

@Override
public void visit(ArrayExpr expr) {
buffer.append(LEFT_BRACE);
for (int i = 0; i < expr.exprs().size(); i++) {
expr.exprs().get(i).accept(this);
if (i < expr.exprs().size() - 1) {
buffer.append(COMMA);
buffer.append(SPACE);
}
}
buffer.append(RIGHT_BRACE);
}

@Override
public void visit(AnnotationNode annotation) {
buffer.append(AT);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2022 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 com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;

import com.google.api.generator.util.TestUtils;
import org.junit.Test;

public class ArrayExprTest {

@Test
public void validAnonymousArray_sametype() {
ArrayExpr.Builder exprBuilder =
ArrayExpr.builder()
.setType(TypeNode.createArrayTypeOf(TypeNode.STRING))
.addExpr(TestUtils.generateStringValueExpr("test1"))
.addExpr(TestUtils.generateStringValueExpr("test2"))
.addExpr(TestUtils.generateStringValueExpr("test3"))
.addExpr(
ValueExpr.withValue(
PrimitiveValue.builder().setValue("1").setType(TypeNode.INT).build()));

Exception thrown = assertThrows(IllegalStateException.class, () -> exprBuilder.build());
assertThat(thrown)
.hasMessageThat()
.contains("All expressions must be of the type specified in this ArrayExpr");
}

@Test
public void validAnonymousArray_unsetTypeThrows() {
ArrayExpr.Builder exprBuilder = ArrayExpr.builder();
IllegalStateException thrown =
assertThrows(IllegalStateException.class, () -> exprBuilder.build());
assertThat(thrown).hasMessageThat().contains("Property \"type\" has not been set");
}

@Test
public void validAnonymousArray_onlyVariableAndValueExprs() {
ArrayExpr.Builder exprBuilder =
ArrayExpr.builder().setType(TypeNode.createArrayTypeOf(TypeNode.INT));
Variable variable = Variable.builder().setName("x").setType(TypeNode.INT).build();
VariableExpr variableExpr =
VariableExpr.builder().setVariable(variable).setIsDecl(true).build();
Value value = PrimitiveValue.builder().setType(TypeNode.INT).setValue("3").build();
Expr valueExpr = ValueExpr.builder().setValue(value).build();
AssignmentExpr assignExpr =
AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build();
exprBuilder.addExpr(assignExpr);
IllegalStateException thrown =
assertThrows(IllegalStateException.class, () -> exprBuilder.build());
assertThat(thrown)
.hasMessageThat()
.contains("Only VariableExpr and ValueExpr can be used as elements of ArrayExpr");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.google.api.generator.engine.ast.AnnotationNode;
import com.google.api.generator.engine.ast.AnonymousClassExpr;
import com.google.api.generator.engine.ast.ArithmeticOperationExpr;
import com.google.api.generator.engine.ast.ArrayExpr;
import com.google.api.generator.engine.ast.AssignmentExpr;
import com.google.api.generator.engine.ast.BlockComment;
import com.google.api.generator.engine.ast.CommentStatement;
Expand Down Expand Up @@ -919,6 +920,20 @@ public void writeLambdaExprImports() {
writerVisitor.write());
}

@Test
public void importArrayExprTypes() {
ArrayExpr arrayExpr =
ArrayExpr.builder()
.setType(
TypeNode.createArrayTypeOf(
TypeNode.withReference(ConcreteReference.withClazz(UnaryOperationExpr.class))))
.build();
arrayExpr.accept(writerVisitor);
assertEquals(
"import com.google.api.generator.engine.ast.UnaryOperationExpr;\n\n",
writerVisitor.write());
}

/** =============================== HELPERS =============================== */
private static Variable createVariable(String variableName, TypeNode type) {
return Variable.builder().setName(variableName).setType(type).build();
Expand Down

0 comments on commit bd9532c

Please sign in to comment.