Added Basic RefTypes to InstVar and SematicCheck
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
This commit is contained in:
parent
56d316a6d0
commit
e552bd5ada
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||||
|
</state>
|
||||||
|
</component>
|
13
pom.xml
13
pom.xml
@ -27,13 +27,22 @@
|
|||||||
<artifactId>antlr4-runtime</artifactId>
|
<artifactId>antlr4-runtime</artifactId>
|
||||||
<version>4.13.1</version>
|
<version>4.13.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.ow2.asm</groupId>
|
<groupId>org.ow2.asm</groupId>
|
||||||
<artifactId>asm</artifactId>
|
<artifactId>asm</artifactId>
|
||||||
<version>9.7</version>
|
<version>9.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<version>2.16.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>3.26.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -20,6 +20,8 @@ public class ClassNode implements ASTNode, Visitable {
|
|||||||
public List<MemberNode> members = new ArrayList<>();
|
public List<MemberNode> members = new ArrayList<>();
|
||||||
public boolean hasConstructor = false;
|
public boolean hasConstructor = false;
|
||||||
|
|
||||||
|
public ClassNode() {}
|
||||||
|
|
||||||
public ClassNode(AccessTypeNode accessType, String identifier){
|
public ClassNode(AccessTypeNode accessType, String identifier){
|
||||||
this.accessType = accessType;
|
this.accessType = accessType;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
@ -1,8 +1,25 @@
|
|||||||
package ast.expression;
|
package ast.expression;
|
||||||
|
|
||||||
import ast.ASTNode;
|
import ast.ASTNode;
|
||||||
|
import ast.statement.AssignmentStatementNode;
|
||||||
|
import ast.statement.IfStatementNode;
|
||||||
|
import ast.statement.VariableDeclarationStatementNode;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
import visitor.Visitable;
|
import visitor.Visitable;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = BinaryExpressionNode.class, name = "Binary"),
|
||||||
|
@JsonSubTypes.Type(value = This.class, name = "This"),
|
||||||
|
@JsonSubTypes.Type(value = InstVar.class, name = "InstVar"),
|
||||||
|
@JsonSubTypes.Type(value = IdentifierExpressionNode.class, name = "Identifier"),
|
||||||
|
@JsonSubTypes.Type(value = LiteralNode.class, name = "Literal"),
|
||||||
|
@JsonSubTypes.Type(value = UnaryExpressionNode.class, name = "Unary")}
|
||||||
|
)
|
||||||
|
|
||||||
public interface ExpressionNode extends ASTNode, Visitable {
|
public interface ExpressionNode extends ASTNode, Visitable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ public class InstVar implements ExpressionNode, Visitable {
|
|||||||
public ExpressionNode expression;
|
public ExpressionNode expression;
|
||||||
public TypeNode type;
|
public TypeNode type;
|
||||||
|
|
||||||
|
public InstVar(){}
|
||||||
|
|
||||||
public InstVar(ExpressionNode expression, String identifier) {
|
public InstVar(ExpressionNode expression, String identifier) {
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
@ -1,23 +1,25 @@
|
|||||||
package ast;
|
package ast.expression;
|
||||||
|
|
||||||
import ast.expression.ExpressionNode;
|
import ast.type.TypeNode;
|
||||||
import semantic.SemanticVisitor;
|
import semantic.SemanticVisitor;
|
||||||
import typechecker.TypeCheckResult;
|
import typechecker.TypeCheckResult;
|
||||||
|
|
||||||
public class LiteralNode implements ExpressionNode {
|
public class LiteralNode implements ExpressionNode {
|
||||||
|
|
||||||
int value;
|
int value;
|
||||||
private String type;
|
private TypeNode type;
|
||||||
|
|
||||||
|
public LiteralNode(){}
|
||||||
|
|
||||||
public LiteralNode(int value) {
|
public LiteralNode(int value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getType() {
|
public TypeNode getType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(String type) {
|
public void setType(TypeNode type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
26
src/main/java/ast/expression/This.java
Normal file
26
src/main/java/ast/expression/This.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package ast.expression;
|
||||||
|
|
||||||
|
import ast.ASTNode;
|
||||||
|
import ast.type.ReferenceTypeNode;
|
||||||
|
import ast.type.TypeNode;
|
||||||
|
import semantic.SemanticVisitor;
|
||||||
|
import typechecker.TypeCheckResult;
|
||||||
|
|
||||||
|
public class This implements ASTNode, ExpressionNode {
|
||||||
|
private TypeNode type;
|
||||||
|
|
||||||
|
public This(){}
|
||||||
|
|
||||||
|
public This(String className) {
|
||||||
|
type = new ReferenceTypeNode(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeCheckResult accept(SemanticVisitor visitor) {
|
||||||
|
return visitor.analyze(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeNode getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,8 @@ public class FieldNode implements MemberNode, Visitable {
|
|||||||
public TypeNode type;
|
public TypeNode type;
|
||||||
public String identifier;
|
public String identifier;
|
||||||
|
|
||||||
|
public FieldNode(){}
|
||||||
|
|
||||||
public FieldNode(AccessTypeNode accessTypeNode, TypeNode type, String name){
|
public FieldNode(AccessTypeNode accessTypeNode, TypeNode type, String name){
|
||||||
this.accessTypeNode = accessTypeNode;
|
this.accessTypeNode = accessTypeNode;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -1,6 +1,18 @@
|
|||||||
package ast.member;
|
package ast.member;
|
||||||
|
|
||||||
import ast.ASTNode;
|
import ast.ASTNode;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = MethodNode.class, name = "Method"),
|
||||||
|
|
||||||
|
@JsonSubTypes.Type(value = FieldNode.class, name = "Field") }
|
||||||
|
)
|
||||||
|
|
||||||
public interface MemberNode extends ASTNode {
|
public interface MemberNode extends ASTNode {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,8 @@ public class MethodNode implements MemberNode, Visitable {
|
|||||||
|
|
||||||
public List<StatementNode> statements = new ArrayList<>();
|
public List<StatementNode> statements = new ArrayList<>();
|
||||||
|
|
||||||
|
public MethodNode(){}
|
||||||
|
|
||||||
public MethodNode(AccessTypeNode visibility, TypeNode type, String identifier, ParameterListNode parameters,
|
public MethodNode(AccessTypeNode visibility, TypeNode type, String identifier, ParameterListNode parameters,
|
||||||
List<StatementNode> statements){
|
List<StatementNode> statements){
|
||||||
this.visibility = visibility;
|
this.visibility = visibility;
|
||||||
|
@ -8,6 +8,8 @@ import java.util.List;
|
|||||||
public class ParameterListNode implements ASTNode {
|
public class ParameterListNode implements ASTNode {
|
||||||
public List<ParameterNode> parameters = new ArrayList<>();
|
public List<ParameterNode> parameters = new ArrayList<>();
|
||||||
|
|
||||||
|
public ParameterListNode() {}
|
||||||
|
|
||||||
public ParameterListNode(List<ParameterNode> parameters){
|
public ParameterListNode(List<ParameterNode> parameters){
|
||||||
this.parameters = parameters;
|
this.parameters = parameters;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@ public class ParameterNode implements ASTNode {
|
|||||||
public TypeNode type;
|
public TypeNode type;
|
||||||
public String identifier;
|
public String identifier;
|
||||||
|
|
||||||
|
public ParameterNode(){}
|
||||||
|
|
||||||
public ParameterNode(TypeNode type, String identifier) {
|
public ParameterNode(TypeNode type, String identifier) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
@ -10,7 +10,9 @@ import visitor.Visitable;
|
|||||||
public class AssignmentStatementNode extends StatementNode implements Visitable {
|
public class AssignmentStatementNode extends StatementNode implements Visitable {
|
||||||
public ExpressionNode expressionLeft;
|
public ExpressionNode expressionLeft;
|
||||||
public ExpressionNode expressionRight;
|
public ExpressionNode expressionRight;
|
||||||
public TypeNode type;
|
// public TypeNode type;
|
||||||
|
|
||||||
|
public AssignmentStatementNode(){}
|
||||||
|
|
||||||
public AssignmentStatementNode(ExpressionNode expressionLeft, ExpressionNode expressionRight) {
|
public AssignmentStatementNode(ExpressionNode expressionLeft, ExpressionNode expressionRight) {
|
||||||
this.expressionLeft = expressionLeft;
|
this.expressionLeft = expressionLeft;
|
||||||
|
@ -1,8 +1,22 @@
|
|||||||
package ast.statement;
|
package ast.statement;
|
||||||
|
|
||||||
import ast.ASTNode;
|
import ast.ASTNode;
|
||||||
|
import ast.type.BaseTypeNode;
|
||||||
|
import ast.type.ReferenceTypeNode;
|
||||||
import visitor.Visitable;
|
import visitor.Visitable;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = IfStatementNode.class, name = "IF"),
|
||||||
|
@JsonSubTypes.Type(value = AssignmentStatementNode.class, name = "Assignment"),
|
||||||
|
@JsonSubTypes.Type(value = VariableDeclarationStatementNode.class, name = "VariableDeclaration") }
|
||||||
|
)
|
||||||
|
|
||||||
public abstract class StatementNode implements ASTNode, Visitable {
|
public abstract class StatementNode implements ASTNode, Visitable {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,9 @@ public class VariableDeclarationStatementNode extends StatementNode {
|
|||||||
public TypeNode type;
|
public TypeNode type;
|
||||||
public String identifier;
|
public String identifier;
|
||||||
public ExpressionNode expression;
|
public ExpressionNode expression;
|
||||||
|
|
||||||
|
public VariableDeclarationStatementNode(){}
|
||||||
|
|
||||||
public VariableDeclarationStatementNode(TypeNode type, String identifier, ExpressionNode expression) {
|
public VariableDeclarationStatementNode(TypeNode type, String identifier, ExpressionNode expression) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.identifier = identifier;
|
this.identifier = identifier;
|
||||||
|
@ -5,6 +5,8 @@ import ast.ASTNode;
|
|||||||
public class AccessTypeNode implements ASTNode {
|
public class AccessTypeNode implements ASTNode {
|
||||||
public EnumAccessTypeNode enumAccessTypeNode;
|
public EnumAccessTypeNode enumAccessTypeNode;
|
||||||
|
|
||||||
|
public AccessTypeNode(){}
|
||||||
|
|
||||||
public AccessTypeNode(EnumAccessTypeNode enumAccessTypeNode) {
|
public AccessTypeNode(EnumAccessTypeNode enumAccessTypeNode) {
|
||||||
this.enumAccessTypeNode = enumAccessTypeNode;
|
this.enumAccessTypeNode = enumAccessTypeNode;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ public class BaseTypeNode implements ASTNode, TypeNode {
|
|||||||
|
|
||||||
public EnumTypeNode enumType;
|
public EnumTypeNode enumType;
|
||||||
|
|
||||||
|
public BaseTypeNode(){}
|
||||||
|
|
||||||
public BaseTypeNode(EnumTypeNode enumType) {
|
public BaseTypeNode(EnumTypeNode enumType) {
|
||||||
this.enumType = enumType;
|
this.enumType = enumType;
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,34 @@ package ast.type;
|
|||||||
import ast.ASTNode;
|
import ast.ASTNode;
|
||||||
|
|
||||||
public class ReferenceTypeNode implements ASTNode, TypeNode {
|
public class ReferenceTypeNode implements ASTNode, TypeNode {
|
||||||
|
|
||||||
|
private String identifier;
|
||||||
|
|
||||||
|
public ReferenceTypeNode() {}
|
||||||
|
|
||||||
|
public ReferenceTypeNode(String identifier) {
|
||||||
|
this.identifier = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
ReferenceTypeNode other = (ReferenceTypeNode) obj;
|
||||||
|
if (identifier == null) {
|
||||||
|
if (other.identifier != null)
|
||||||
|
return false;
|
||||||
|
} else if (!identifier.equals(other.identifier))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
package ast.type;
|
|
||||||
|
|
||||||
import ast.ASTNode;
|
|
||||||
import ast.expression.ExpressionNode;
|
|
||||||
import semantic.SemanticVisitor;
|
|
||||||
import typechecker.TypeCheckResult;
|
|
||||||
|
|
||||||
public class This implements ASTNode, ExpressionNode {
|
|
||||||
@Override
|
|
||||||
public TypeCheckResult accept(SemanticVisitor visitor) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,5 +2,17 @@ package ast.type;
|
|||||||
|
|
||||||
import ast.ASTNode;
|
import ast.ASTNode;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
|
||||||
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = BaseTypeNode.class, name = "Base"),
|
||||||
|
|
||||||
|
@JsonSubTypes.Type(value = ReferenceTypeNode.class, name = "Reference") }
|
||||||
|
)
|
||||||
|
|
||||||
public interface TypeNode extends ASTNode {
|
public interface TypeNode extends ASTNode {
|
||||||
}
|
}
|
@ -1,11 +1,7 @@
|
|||||||
package parser;
|
package parser;
|
||||||
|
|
||||||
import ast.*;
|
import ast.*;
|
||||||
import ast.expression.BinaryExpressionNode;
|
import ast.expression.*;
|
||||||
import ast.expression.ExpressionNode;
|
|
||||||
import ast.expression.ExpresssionOperator;
|
|
||||||
import ast.expression.IdentifierExpressionNode;
|
|
||||||
import ast.expression.UnaryExpressionNode;
|
|
||||||
import ast.member.FieldNode;
|
import ast.member.FieldNode;
|
||||||
import ast.member.MemberNode;
|
import ast.member.MemberNode;
|
||||||
import ast.member.MethodNode;
|
import ast.member.MethodNode;
|
||||||
@ -210,7 +206,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
|
|||||||
int intValue = Integer.parseInt(literalContext.getText());
|
int intValue = Integer.parseInt(literalContext.getText());
|
||||||
LiteralNode literalNode = new LiteralNode(intValue);
|
LiteralNode literalNode = new LiteralNode(intValue);
|
||||||
|
|
||||||
literalNode.setType("int");
|
literalNode.setType(new BaseTypeNode(EnumTypeNode.INT));
|
||||||
return literalNode;
|
return literalNode;
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException ignored) {}
|
||||||
|
|
||||||
|
@ -10,6 +10,9 @@ import ast.member.MethodNode;
|
|||||||
import ast.parameter.ParameterListNode;
|
import ast.parameter.ParameterListNode;
|
||||||
import ast.parameter.ParameterNode;
|
import ast.parameter.ParameterNode;
|
||||||
import ast.statement.*;
|
import ast.statement.*;
|
||||||
|
import ast.type.ReferenceTypeNode;
|
||||||
|
import ast.expression.This;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -17,8 +20,8 @@ import java.util.Objects;
|
|||||||
|
|
||||||
import ast.type.BaseTypeNode;
|
import ast.type.BaseTypeNode;
|
||||||
import ast.type.EnumTypeNode;
|
import ast.type.EnumTypeNode;
|
||||||
import ast.type.This;
|
|
||||||
import ast.type.TypeNode;
|
import ast.type.TypeNode;
|
||||||
|
import semantic.context.Context;
|
||||||
import semantic.exeptions.AlreadyDeclearedException;
|
import semantic.exeptions.AlreadyDeclearedException;
|
||||||
import semantic.exeptions.NotDeclearedException;
|
import semantic.exeptions.NotDeclearedException;
|
||||||
import semantic.exeptions.TypeMismatchException;
|
import semantic.exeptions.TypeMismatchException;
|
||||||
@ -30,6 +33,7 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
|||||||
|
|
||||||
public static ArrayList<Exception> errors = new ArrayList<>();
|
public static ArrayList<Exception> errors = new ArrayList<>();
|
||||||
|
|
||||||
|
private Context context;
|
||||||
private static Scope currentScope;
|
private static Scope currentScope;
|
||||||
private static ClassNode currentClass;
|
private static ClassNode currentClass;
|
||||||
|
|
||||||
@ -60,6 +64,7 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
|||||||
var valid = true;
|
var valid = true;
|
||||||
|
|
||||||
currentScope = new Scope();
|
currentScope = new Scope();
|
||||||
|
context = new Context(node);
|
||||||
|
|
||||||
List<ClassNode> classes = node.classes;
|
List<ClassNode> classes = node.classes;
|
||||||
for (ClassNode classNode : classes) {
|
for (ClassNode classNode : classes) {
|
||||||
@ -74,7 +79,7 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
|||||||
var valid = true;
|
var valid = true;
|
||||||
|
|
||||||
currentClass = classNode;
|
currentClass = classNode;
|
||||||
|
currentFields.clear();
|
||||||
List<MemberNode> members = classNode.members;
|
List<MemberNode> members = classNode.members;
|
||||||
for (MemberNode memberNode : members) {
|
for (MemberNode memberNode : members) {
|
||||||
if (memberNode instanceof FieldNode fieldNode) {
|
if (memberNode instanceof FieldNode fieldNode) {
|
||||||
@ -238,23 +243,38 @@ public class SemanticAnalyzer implements SemanticVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TypeCheckResult analyze(LiteralNode toCheck) {
|
public TypeCheckResult analyze(LiteralNode toCheck) {
|
||||||
return new TypeCheckResult(true, new BaseTypeNode(EnumTypeNode.INT));
|
return new TypeCheckResult(true, toCheck.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TypeCheckResult analyze(InstVar toCheck) {
|
public TypeCheckResult analyze(InstVar toCheck) {
|
||||||
boolean valid = true;
|
boolean valid = true;
|
||||||
|
|
||||||
if(toCheck.expression instanceof This){
|
var result = toCheck.expression.accept(this);
|
||||||
if(currentFields.get(toCheck.identifier) == null){
|
|
||||||
errors.add(new NotDeclearedException("Not declared " + toCheck.identifier + " in this scope"));
|
if(result.getType() instanceof BaseTypeNode){
|
||||||
valid = false;
|
throw new RuntimeException("BaseType has no Methods or Fields");
|
||||||
} else {
|
} else {
|
||||||
TypeNode typeNode = currentFields.get(toCheck.identifier);
|
//Get typ of Field
|
||||||
return new TypeCheckResult(valid, typeNode);
|
|
||||||
|
var type = (ReferenceTypeNode)result.getType();
|
||||||
|
var classContext = context.getClass(type.getIdentifier());
|
||||||
|
|
||||||
|
if(classContext == null){
|
||||||
|
errors.add(new NotDeclearedException("Not declared " + type.getIdentifier() + " in this scope"));
|
||||||
|
return new TypeCheckResult(false, null);
|
||||||
|
} else {
|
||||||
|
var field = classContext.getField(toCheck.identifier);
|
||||||
|
|
||||||
|
return new TypeCheckResult(valid, field.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new TypeCheckResult(valid, null);
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeCheckResult analyze(This toCheck) {
|
||||||
|
return new TypeCheckResult(true, toCheck.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ package semantic;
|
|||||||
|
|
||||||
|
|
||||||
import ast.ClassNode;
|
import ast.ClassNode;
|
||||||
import ast.LiteralNode;
|
import ast.expression.LiteralNode;
|
||||||
import ast.ProgramNode;
|
import ast.ProgramNode;
|
||||||
import ast.expression.BinaryExpressionNode;
|
import ast.expression.BinaryExpressionNode;
|
||||||
import ast.expression.IdentifierExpressionNode;
|
import ast.expression.IdentifierExpressionNode;
|
||||||
@ -11,6 +11,7 @@ import ast.expression.UnaryExpressionNode;
|
|||||||
import ast.member.FieldNode;
|
import ast.member.FieldNode;
|
||||||
import ast.member.MethodNode;
|
import ast.member.MethodNode;
|
||||||
import ast.statement.*;
|
import ast.statement.*;
|
||||||
|
import ast.expression.This;
|
||||||
import typechecker.TypeCheckResult;
|
import typechecker.TypeCheckResult;
|
||||||
|
|
||||||
public interface SemanticVisitor {
|
public interface SemanticVisitor {
|
||||||
@ -42,4 +43,6 @@ public interface SemanticVisitor {
|
|||||||
TypeCheckResult analyze(LiteralNode toCheck);
|
TypeCheckResult analyze(LiteralNode toCheck);
|
||||||
|
|
||||||
TypeCheckResult analyze(InstVar toCheck);
|
TypeCheckResult analyze(InstVar toCheck);
|
||||||
|
|
||||||
|
TypeCheckResult analyze(This toCheck);
|
||||||
}
|
}
|
27
src/main/java/semantic/context/ClassContext.java
Normal file
27
src/main/java/semantic/context/ClassContext.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package semantic.context;
|
||||||
|
|
||||||
|
import ast.ClassNode;
|
||||||
|
import ast.member.FieldNode;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class ClassContext {
|
||||||
|
|
||||||
|
private HashMap<String, FieldContext> fields;
|
||||||
|
|
||||||
|
public ClassContext(ClassNode classNode) {
|
||||||
|
|
||||||
|
fields = new HashMap<>();
|
||||||
|
|
||||||
|
classNode.members.forEach(member -> {
|
||||||
|
if(member instanceof FieldNode fieldNode) {
|
||||||
|
fields.put(fieldNode.identifier, new FieldContext(fieldNode));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public FieldContext getField(String name) {
|
||||||
|
return fields.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
25
src/main/java/semantic/context/Context.java
Normal file
25
src/main/java/semantic/context/Context.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package semantic.context;
|
||||||
|
|
||||||
|
import ast.ClassNode;
|
||||||
|
import ast.ProgramNode;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class Context {
|
||||||
|
|
||||||
|
private HashMap<String, ClassContext> classes;
|
||||||
|
|
||||||
|
public Context(ProgramNode programNode) {
|
||||||
|
classes = new HashMap<>();
|
||||||
|
|
||||||
|
programNode.classes.forEach(classNode -> {
|
||||||
|
ClassContext classContext = new ClassContext(classNode);
|
||||||
|
classes.put(classNode.identifier, classContext);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassContext getClass(String identifier) {
|
||||||
|
return classes.get(identifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
22
src/main/java/semantic/context/FieldContext.java
Normal file
22
src/main/java/semantic/context/FieldContext.java
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package semantic.context;
|
||||||
|
|
||||||
|
import ast.member.FieldNode;
|
||||||
|
import ast.type.AccessTypeNode;
|
||||||
|
import ast.type.EnumAccessTypeNode;
|
||||||
|
import ast.type.TypeNode;
|
||||||
|
|
||||||
|
public class FieldContext {
|
||||||
|
|
||||||
|
private AccessTypeNode accessModifier;
|
||||||
|
private TypeNode type;
|
||||||
|
|
||||||
|
public FieldContext(FieldNode field) {
|
||||||
|
accessModifier = field.accessTypeNode;
|
||||||
|
type = field.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeNode getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package semantic;
|
package semantic;
|
||||||
|
|
||||||
import ast.ClassNode;
|
import ast.ClassNode;
|
||||||
import ast.LiteralNode;
|
import ast.expression.LiteralNode;
|
||||||
import ast.ProgramNode;
|
import ast.ProgramNode;
|
||||||
import ast.expression.*;
|
import ast.expression.*;
|
||||||
import ast.member.FieldNode;
|
import ast.member.FieldNode;
|
||||||
@ -39,16 +39,14 @@ public class Mocker {
|
|||||||
|
|
||||||
List<StatementNode> statementNodeList = new ArrayList<StatementNode>();
|
List<StatementNode> statementNodeList = new ArrayList<StatementNode>();
|
||||||
|
|
||||||
ExpressionNode expressionNodeLeft = new InstVar(new This(), "objectVar");
|
ExpressionNode expressionNodeLeft = new InstVar(new This("testClass"), "objectVar");
|
||||||
|
|
||||||
ExpressionNode expressionNodeRight = new LiteralNode(1);
|
LiteralNode expressionNodeRight = new LiteralNode();
|
||||||
|
expressionNodeRight.setType(new BaseTypeNode(EnumTypeNode.INT));
|
||||||
|
|
||||||
StatementNode statementNode1 = new AssignmentStatementNode(expressionNodeLeft, expressionNodeRight);
|
StatementNode statementNode1 = new AssignmentStatementNode(expressionNodeLeft, expressionNodeRight);
|
||||||
statementNodeList.add(statementNode1);
|
statementNodeList.add(statementNode1);
|
||||||
|
|
||||||
StatementNode statementNode2 = new VariableDeclarationStatementNode(new BaseTypeNode(EnumTypeNode.CHAR), "objectVar", new LiteralNode(1));
|
|
||||||
statementNodeList.add(statementNode2);
|
|
||||||
|
|
||||||
MemberNode memberNode3 = new MethodNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2",parameterListNode, statementNodeList );
|
MemberNode memberNode3 = new MethodNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2",parameterListNode, statementNodeList );
|
||||||
classNode.members.add(memberNode3);
|
classNode.members.add(memberNode3);
|
||||||
|
|
||||||
@ -76,5 +74,4 @@ public class Mocker {
|
|||||||
|
|
||||||
return programNode;
|
return programNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,20 +2,19 @@ package semantic;
|
|||||||
|
|
||||||
|
|
||||||
import ast.*;
|
import ast.*;
|
||||||
import ast.member.FieldNode;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import ast.member.MemberNode;
|
import org.assertj.core.api.MatcherAssert;
|
||||||
import ast.type.AccessTypeNode;
|
|
||||||
import ast.type.BaseTypeNode;
|
|
||||||
import ast.type.EnumAccessTypeNode;
|
|
||||||
import ast.type.EnumTypeNode;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import semantic.exeptions.AlreadyDeclearedException;
|
import semantic.exeptions.AlreadyDeclearedException;
|
||||||
|
import semantic.exeptions.TypeMismatchException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.io.File;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class SemanticTest {
|
public class SemanticTest {
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ public class SemanticTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void alreadyDeclaredLocalFieldVar(){
|
public void alreadyDeclaredLocalFieldVar() {
|
||||||
|
|
||||||
//Arrange
|
//Arrange
|
||||||
|
|
||||||
@ -44,7 +43,7 @@ public class SemanticTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void typeMismatch(){
|
public void alreadyDecleared() {
|
||||||
|
|
||||||
//Arrange
|
//Arrange
|
||||||
|
|
||||||
@ -57,13 +56,13 @@ public class SemanticTest {
|
|||||||
//Assert
|
//Assert
|
||||||
|
|
||||||
assertEquals(1, SemanticAnalyzer.errors.size());
|
assertEquals(1, SemanticAnalyzer.errors.size());
|
||||||
assertEquals(true, SemanticAnalyzer.errors.get(0) instanceof AlreadyDeclearedException);
|
assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst());
|
||||||
assertEquals(null, typedAst);
|
assertNull(typedAst);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldWorkWithNoError(){
|
public void shouldWorkWithNoError() {
|
||||||
|
|
||||||
//Arrange
|
//Arrange
|
||||||
|
|
||||||
@ -80,4 +79,85 @@ public class SemanticTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void refTypeCorrect() {
|
||||||
|
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
ProgramNode programNode = null;
|
||||||
|
try{
|
||||||
|
programNode = objectMapper.readValue(new File("src/test/resources/semantic/correctRefType.json"), ProgramNode.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Act
|
||||||
|
|
||||||
|
ASTNode typedAst = SemanticAnalyzer.generateTast(programNode);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
|
||||||
|
assertEquals(0, SemanticAnalyzer.errors.size());
|
||||||
|
assertEquals(programNode, typedAst);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void jsonWriteTest() {
|
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
ProgramNode programNode = Mocker.mockCorrectProgrammNode();
|
||||||
|
try{
|
||||||
|
objectMapper.writeValue(new File("src/test/resources/semantic/test.json"), programNode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void jsonReadTest() {
|
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
ProgramNode programNode1 = null;
|
||||||
|
try{
|
||||||
|
programNode1 = objectMapper.readValue(new File("src/test/resources/semantic/test.json"), ProgramNode.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProgramNode programNode2 = Mocker.mockCorrectProgrammNode();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void typeMismatch() {
|
||||||
|
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
ProgramNode programNode = null;
|
||||||
|
try{
|
||||||
|
programNode = objectMapper.readValue(new File("src/test/resources/semantic/refTypeMismatch.json"), ProgramNode.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Act
|
||||||
|
|
||||||
|
ASTNode typedAst = SemanticAnalyzer.generateTast(programNode);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
|
||||||
|
assertEquals(1, SemanticAnalyzer.errors.size());
|
||||||
|
assertInstanceOf(TypeMismatchException.class, SemanticAnalyzer.errors.getFirst());
|
||||||
|
assertNull(typedAst);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
70
src/test/resources/semantic/correctRefType.json
Normal file
70
src/test/resources/semantic/correctRefType.json
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"identifier": "testClass1",
|
||||||
|
"accessType": {
|
||||||
|
"enumAccessTypeNode": "PUBLIC"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"@type": "Field",
|
||||||
|
"accessTypeNode": {
|
||||||
|
"enumAccessTypeNode": "PUBLIC"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "testVar1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Method",
|
||||||
|
"visibility": {
|
||||||
|
"enumAccessTypeNode": "PUBLIC"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "testMethod",
|
||||||
|
"parameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "param1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"@type": "Assignment",
|
||||||
|
"expressionLeft": {
|
||||||
|
"@type": "InstVar",
|
||||||
|
"identifier": "testVar1",
|
||||||
|
"expression": {
|
||||||
|
"@type": "This",
|
||||||
|
"type": {
|
||||||
|
"@type": "Reference",
|
||||||
|
"identifier": "testClass1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": null
|
||||||
|
},
|
||||||
|
"expressionRight": {
|
||||||
|
"@type": "Literal",
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hasConstructor": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
133
src/test/resources/semantic/refTypeMismatch.json
Normal file
133
src/test/resources/semantic/refTypeMismatch.json
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
{
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"identifier": "testClass1",
|
||||||
|
"accessType": {
|
||||||
|
"enumAccessTypeNode": "PUBLIC"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"@type": "Field",
|
||||||
|
"accessTypeNode": {
|
||||||
|
"enumAccessTypeNode": "PUBLIC"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "testVar1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Method",
|
||||||
|
"visibility": {
|
||||||
|
"enumAccessTypeNode": "PUBLIC"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "testMethod",
|
||||||
|
"parameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "param1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"@type": "Assignment",
|
||||||
|
"expressionLeft": {
|
||||||
|
"@type": "InstVar",
|
||||||
|
"identifier": "testVar1",
|
||||||
|
"expression": {
|
||||||
|
"@type": "This",
|
||||||
|
"type": {
|
||||||
|
"@type": "Reference",
|
||||||
|
"identifier": "testClass1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": null
|
||||||
|
},
|
||||||
|
"expressionRight": {
|
||||||
|
"@type": "Literal",
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "BOOLEAN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hasConstructor": false,
|
||||||
|
"methods": [
|
||||||
|
{
|
||||||
|
"@type": "Method",
|
||||||
|
"visibility": {
|
||||||
|
"enumAccessTypeNode": "PUBLIC"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "testMethod",
|
||||||
|
"parameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "INT"
|
||||||
|
},
|
||||||
|
"identifier": "param1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"statements": [
|
||||||
|
{
|
||||||
|
"@type": "Assignment",
|
||||||
|
"expressionLeft": {
|
||||||
|
"@type": "InstVar",
|
||||||
|
"identifier": "testVar",
|
||||||
|
"expression": {
|
||||||
|
"@type": "InstVar",
|
||||||
|
"identifier": "testVar",
|
||||||
|
"expression": {
|
||||||
|
"@type": "This",
|
||||||
|
"type": {
|
||||||
|
"@type": "Reference",
|
||||||
|
"identifier": "testClass2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": null
|
||||||
|
},
|
||||||
|
"type": null
|
||||||
|
},
|
||||||
|
"expressionRight": {
|
||||||
|
"@type": "Literal",
|
||||||
|
"type": null
|
||||||
|
},
|
||||||
|
"type": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "VariableDeclaration",
|
||||||
|
"type": {
|
||||||
|
"@type": "Base",
|
||||||
|
"enumType": "CHAR"
|
||||||
|
},
|
||||||
|
"identifier": "objectVar",
|
||||||
|
"expression": {
|
||||||
|
"@type": "Literal",
|
||||||
|
"type": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
src/test/resources/semantic/test.json
Normal file
1
src/test/resources/semantic/test.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"classes":[{"identifier":"testClass","accessType":{"enumAccessTypeNode":"PUBLIC"},"members":[{"@type":"Field","accessTypeNode":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"testVar1"},{"@type":"Field","accessTypeNode":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"objectVar"},{"@type":"Method","visibility":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"testVar2","parameters":{"parameters":[{"type":{"@type":"Base","enumType":"INT"},"identifier":"param1"}]},"statements":[{"@type":"Assignment","expressionLeft":{"@type":"InstVar","identifier":"objectVar","expression":{"@type":"This","type":{"@type":"Reference","identifier":"testClass"}},"type":null},"expressionRight":{"@type":"Literal","type":{"@type":"Base","enumType":"INT"}}}]}],"hasConstructor":false,"methods":[{"@type":"Method","visibility":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"testVar2","parameters":{"parameters":[{"type":{"@type":"Base","enumType":"INT"},"identifier":"param1"}]},"statements":[{"@type":"Assignment","expressionLeft":{"@type":"InstVar","identifier":"objectVar","expression":{"@type":"This","type":{"@type":"Reference","identifier":"testClass"}},"type":null},"expressionRight":{"@type":"Literal","type":{"@type":"Base","enumType":"INT"}}}]}]}]}
|
Loading…
Reference in New Issue
Block a user