From 6fd3f5a2e69d74c57304b6cf1cd158b3cfc8116c Mon Sep 17 00:00:00 2001 From: nitrix Date: Wed, 15 Mar 2023 19:14:04 +0100 Subject: [PATCH] Make it possible to use an enum in an expression (i.e. selecting one of the enum values: Foo.A) --- .../antlr4/de/hsrm/compiler/Klang/Klang.g4 | 2 +- .../hsrm/compiler/Klang/ContextAnalysis.java | 62 +++++++++++++------ .../hsrm/compiler/Klang/GetDefinitions.java | 9 ++- .../expressions/EnumAccessExpression.java | 18 ++++++ ...ssion.java => MemberAccessExpression.java} | 4 +- .../types/{EnumType.java => NamedType.java} | 11 ++-- .../hsrm/compiler/Klang/types/StructType.java | 54 ---------------- .../de/hsrm/compiler/Klang/types/Type.java | 4 +- .../compiler/Klang/visitors/EvalVisitor.java | 7 ++- .../hsrm/compiler/Klang/visitors/GenASM.java | 7 ++- .../hsrm/compiler/Klang/visitors/GetVars.java | 7 ++- .../Klang/visitors/PrettyPrintVisitor.java | 11 +++- .../hsrm/compiler/Klang/visitors/Visitor.java | 3 +- src/test/java/FunctionDefinitionTest.java | 2 +- 14 files changed, 107 insertions(+), 94 deletions(-) create mode 100644 src/main/java/de/hsrm/compiler/Klang/nodes/expressions/EnumAccessExpression.java rename src/main/java/de/hsrm/compiler/Klang/nodes/expressions/{StructFieldAccessExpression.java => MemberAccessExpression.java} (69%) rename src/main/java/de/hsrm/compiler/Klang/types/{EnumType.java => NamedType.java} (78%) delete mode 100644 src/main/java/de/hsrm/compiler/Klang/types/StructType.java diff --git a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 index a6fb2e0..efe054d 100644 --- a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 +++ b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 @@ -82,7 +82,7 @@ destroy_statement expression : atom #atomExpression - | IDENT (PERIOD IDENT)+ #structFieldAccessExpression + | IDENT (PERIOD IDENT)+ #memberAccessExpression | OPAR expression CPAR #parenthesisExpression | lhs=expression MUL rhs=expression #multiplicationExpression | lhs=expression DIV rhs=expression #divisionExpression diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 825a5bc..f2478c3 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -9,8 +9,7 @@ import de.hsrm.compiler.Klang.nodes.loops.WhileLoop; import de.hsrm.compiler.Klang.nodes.statements.*; import de.hsrm.compiler.Klang.types.Type; -import java.util.HashMap; -import java.util.Map; +import java.util.*; public class ContextAnalysis extends KlangBaseVisitor { Map vars = new HashMap<>(); @@ -319,43 +318,68 @@ public class ContextAnalysis extends KlangBaseVisitor { } @Override - public Node visitStructFieldAccessExpression(KlangParser.StructFieldAccessExpressionContext ctx) { - String varName = ctx.IDENT(0).getText(); - int line = ctx.start.getLine(); - int col = ctx.start.getCharPositionInLine(); - String[] path = new String[ctx.IDENT().size() - 1]; + public Node visitMemberAccessExpression(KlangParser.MemberAccessExpressionContext ctx) { + var baseName = ctx.IDENT(0).getText(); + var line = ctx.start.getLine(); + var col = ctx.start.getCharPositionInLine(); + // Create a list of member names. This excludes + // the first entry as it is the base name. + var path = new ArrayList(); for (int i = 1; i < ctx.IDENT().size(); i++) { - path[i - 1] = ctx.IDENT(i).getText(); + path.add(ctx.IDENT(i).getText()); + } + + // Determine if the base name points to an enum or a variable + var enumDef = enumDefs.get(baseName); + if (enumDef != null) { + if (path.size() != 1) { + var error = "Illegal access to enum " + enumDef.name + "."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + + var enumValueName = path.get(0); + if (Arrays.stream(enumDef.enums).noneMatch(e -> e.equals(enumValueName))) { + var error = "Unknown enum value " + enumValueName + " of enum " + enumDef.name + "."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + + var enumAccessExpression = new EnumAccessExpression(baseName, enumValueName); + enumAccessExpression.type = enumDef.type; + enumAccessExpression.line = line; + enumAccessExpression.col = col; + + return enumAccessExpression; } // Get the referenced variable, make sure it is defined - var variableDef = this.vars.get(varName); + var variableDef = vars.get(baseName); if (variableDef == null) { - String error = "Variable with name " + varName + " not defined."; + var error = "Variable with name " + baseName + " not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } // Make sure it references a struct if (variableDef.type.isPrimitiveType()) { - String error = "Variable must reference a struct but references " + variableDef.type.getName() + "."; + var error = "Variable must reference a struct but references " + variableDef.type.getName() + "."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } // Get the type of the result of this expression - String structName = variableDef.type.getName(); + var structName = variableDef.type.getName(); Type resultType; try { - resultType = Helper.drillType(this.structDefs, structName, path, 0); + resultType = Helper.drillType(structDefs, structName, path.toArray(new String[0]), 0); } catch (Exception e) { throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage()); } - Node result = new StructFieldAccessExpression(varName, structName, path); - result.type = resultType; - result.line = line; - result.col = col; - return result; + var memberAccessExpression = new MemberAccessExpression(baseName, structName, path.toArray(new String[0])); + memberAccessExpression.type = resultType; + memberAccessExpression.line = line; + memberAccessExpression.col = col; + + return memberAccessExpression; } @Override @@ -749,7 +773,7 @@ public class ContextAnalysis extends KlangBaseVisitor { structDef.line = line; structDef.col = col; - return super.visitStructDef(ctx); + return structDef; } @Override diff --git a/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java b/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java index 7ce8cee..13136e9 100644 --- a/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java +++ b/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java @@ -2,8 +2,7 @@ package de.hsrm.compiler.Klang; import de.hsrm.compiler.Klang.helper.Helper; import de.hsrm.compiler.Klang.nodes.*; -import de.hsrm.compiler.Klang.types.EnumType; -import de.hsrm.compiler.Klang.types.StructType; +import de.hsrm.compiler.Klang.types.NamedType; import de.hsrm.compiler.Klang.types.Type; import java.util.HashMap; @@ -127,7 +126,7 @@ public class GetDefinitions extends KlangBaseVisitor { var enumDef = new EnumDefinition(enumName, enumFields.toArray(new String[0])); enumDef.line = ctx.start.getLine(); enumDef.col = ctx.start.getCharPositionInLine(); - enumDef.type = new EnumType(enumName); + enumDef.type = new NamedType(enumName); enumDefs.put(enumName, enumDef); return null; @@ -169,7 +168,7 @@ public class GetDefinitions extends KlangBaseVisitor { var structDef = new StructDefinition(structName, structFields.values().toArray(new StructField[0])); structDef.line = line; structDef.col = col; - structDef.type = new StructType(structName); + structDef.type = new NamedType(structName); structDefs.put(structName, structDef); return null; @@ -209,7 +208,7 @@ public class GetDefinitions extends KlangBaseVisitor { } var functionDef = new FunctionDefinition(funcName, parameters.values().toArray(new Parameter[0]), null); - functionDef.type = Type.getByName(ctx.returnType.getText()); + functionDef.type = Type.getByName(ctx.returnType.type().getText()); functionDef.line = line; functionDef.col = col; functionDefs.put(funcName, functionDef); diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/EnumAccessExpression.java b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/EnumAccessExpression.java new file mode 100644 index 0000000..476f9a5 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/EnumAccessExpression.java @@ -0,0 +1,18 @@ +package de.hsrm.compiler.Klang.nodes.expressions; + +import de.hsrm.compiler.Klang.visitors.Visitor; + +public class EnumAccessExpression extends Expression { + public String enumName; + public String enumValueName; + + public EnumAccessExpression(String enumName, String enumValueName) { + this.enumName = enumName; + this.enumValueName = enumValueName; + } + + @Override + public R welcome(Visitor v) { + return v.visit(this); + } +} diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/MemberAccessExpression.java similarity index 69% rename from src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java rename to src/main/java/de/hsrm/compiler/Klang/nodes/expressions/MemberAccessExpression.java index 73b9ac8..3a71418 100644 --- a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/MemberAccessExpression.java @@ -2,12 +2,12 @@ package de.hsrm.compiler.Klang.nodes.expressions; import de.hsrm.compiler.Klang.visitors.Visitor; -public class StructFieldAccessExpression extends Expression { +public class MemberAccessExpression extends Expression { public String varName; public String structName; public String[] path; - public StructFieldAccessExpression(String varName, String structName, String[] path) { + public MemberAccessExpression(String varName, String structName, String[] path) { this.varName = varName; this.structName = structName; this.path = path; diff --git a/src/main/java/de/hsrm/compiler/Klang/types/EnumType.java b/src/main/java/de/hsrm/compiler/Klang/types/NamedType.java similarity index 78% rename from src/main/java/de/hsrm/compiler/Klang/types/EnumType.java rename to src/main/java/de/hsrm/compiler/Klang/types/NamedType.java index 292a6fb..d3e27b1 100644 --- a/src/main/java/de/hsrm/compiler/Klang/types/EnumType.java +++ b/src/main/java/de/hsrm/compiler/Klang/types/NamedType.java @@ -1,10 +1,9 @@ package de.hsrm.compiler.Klang.types; -public class EnumType extends Type { - +public class NamedType extends Type { public String name; - public EnumType(String name) { + public NamedType(String name) { this.name = name; } @@ -19,7 +18,7 @@ public class EnumType extends Type { return this; } - throw new RuntimeException("Type mismatch: cannot combine enum " + getName() + " and " + that.getName()); + throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + that.getName()); } @Override @@ -38,8 +37,8 @@ public class EnumType extends Type { return true; } - if (that instanceof EnumType) { - var thatType = (EnumType) that; + if (that instanceof NamedType) { + var thatType = (NamedType) that; return getName().equals(thatType.getName()); } diff --git a/src/main/java/de/hsrm/compiler/Klang/types/StructType.java b/src/main/java/de/hsrm/compiler/Klang/types/StructType.java deleted file mode 100644 index 23cac1c..0000000 --- a/src/main/java/de/hsrm/compiler/Klang/types/StructType.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.hsrm.compiler.Klang.types; - -public class StructType extends Type { - - public String name; - - public StructType(String name) { - this.name = name; - } - - @Override - public String getName() { - return this.name; - } - - @Override - public Type combine(Type that) { - if (that.equals(this)) { - return this; - } - - // If you combine a null type with a struct type, you - // always get the struct type back. - if (that == NullType.getType()) { - return this; - } - - throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); - } - - @Override - public boolean isPrimitiveType() { - return false; - } - - @Override - public boolean equals(Object that) { - if (this == that) { - return true; - } - - if (that instanceof StructType) { - StructType thatType = (StructType) that; - return this.getName().equals(thatType.getName()); - } - - return false; - } - - @Override - public boolean isNumericType() { - return false; - } -} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/types/Type.java b/src/main/java/de/hsrm/compiler/Klang/types/Type.java index 15b2810..3a40d18 100644 --- a/src/main/java/de/hsrm/compiler/Klang/types/Type.java +++ b/src/main/java/de/hsrm/compiler/Klang/types/Type.java @@ -1,5 +1,7 @@ package de.hsrm.compiler.Klang.types; +import java.util.Set; + public abstract class Type { // Returns an instance of IntegerType @@ -26,7 +28,7 @@ public abstract class Type { case "int": return getIntegerType(); case "float": return getFloatType(); case "null": return getNullType(); - default: return new StructType(name); + default: return new NamedType(name); } } diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java index b1738d7..81ec6c7 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java @@ -484,7 +484,7 @@ public class EvalVisitor implements Visitor { } @Override - public Value visit(StructFieldAccessExpression e) { + public Value visit(MemberAccessExpression e) { Value var = this.env.get(e.varName); Map struct = var.asStruct(); @@ -496,6 +496,11 @@ public class EvalVisitor implements Visitor { return currentValue; } + @Override + public Value visit(EnumAccessExpression e) { + return null; + } + @Override public Value visit(ConstructorCall e) { StructDefinition structDef = this.structs.get(e.structName); diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java index f9afa96..d824e56 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java @@ -792,7 +792,7 @@ public class GenASM implements Visitor { } @Override - public Void visit(StructFieldAccessExpression e) { + public Void visit(MemberAccessExpression e) { var structDef = this.structs.get(e.structName); int offset = this.env.get(e.varName); @@ -817,6 +817,11 @@ public class GenASM implements Visitor { return null; } + @Override + public Void visit(EnumAccessExpression e) { + return null; + } + @Override public Void visit(ConstructorCall e) { // push arguments onto the stack diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java index 6e66d0e..15f94ca 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java @@ -250,7 +250,12 @@ class GetVars implements Visitor { } @Override - public Void visit(StructFieldAccessExpression e) { + public Void visit(MemberAccessExpression e) { + return null; + } + + @Override + public Void visit(EnumAccessExpression e) { return null; } diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java index 5a35800..fdd89be 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java @@ -420,7 +420,7 @@ public class PrettyPrintVisitor implements Visitor { } @Override - public Void visit(StructFieldAccessExpression e) { + public Void visit(MemberAccessExpression e) { ex.write(e.varName); for (int i = 0; i < e.path.length; i++) { ex.write("."); @@ -429,6 +429,15 @@ public class PrettyPrintVisitor implements Visitor { return null; } + @Override + public Void visit(EnumAccessExpression e) { + ex.write(e.enumName); + ex.write("."); + ex.write(e.enumValueName); + + return null; + } + @Override public Void visit(ConstructorCall e) { ex.write("create " + e.structName + "("); diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java index f8d7e77..4337ee5 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java @@ -40,7 +40,8 @@ public interface Visitor { R visit(EnumDefinition e); R visit(StructDefinition e); R visit(StructField e); - R visit(StructFieldAccessExpression e); + R visit(MemberAccessExpression e); + R visit(EnumAccessExpression e); R visit(ConstructorCall e); R visit(NullExpression e); R visit(DestructorCall e); diff --git a/src/test/java/FunctionDefinitionTest.java b/src/test/java/FunctionDefinitionTest.java index 0d79c6a..79299a1 100644 --- a/src/test/java/FunctionDefinitionTest.java +++ b/src/test/java/FunctionDefinitionTest.java @@ -10,7 +10,7 @@ public class FunctionDefinitionTest { @Test void shouldNotThrowIfReturnTypeIsReferringToAnEnum() { // given - var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo();"); + var tree = Helper.prepareParser(" enum bar {A,B,C} function foo(a: int): bar { return bar.A; } foo(1);"); var ctxAnal = new ContextAnalysis( Helper.getFuncs(tree), Helper.getStructs(tree),