Make it possible to use an enum in an expression (i.e. selecting one of the enum values: Foo.A)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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<Node> {
|
||||
Map<String, VariableDeclaration> vars = new HashMap<>();
|
||||
@@ -319,43 +318,68 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
}
|
||||
|
||||
@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<String>();
|
||||
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<Node> {
|
||||
structDef.line = line;
|
||||
structDef.col = col;
|
||||
|
||||
return super.visitStructDef(ctx);
|
||||
return structDef;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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<Node> {
|
||||
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<Node> {
|
||||
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<Node> {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -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> R welcome(Visitor<R> v) {
|
||||
return v.visit(this);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -484,7 +484,7 @@ public class EvalVisitor implements Visitor<Value> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value visit(StructFieldAccessExpression e) {
|
||||
public Value visit(MemberAccessExpression e) {
|
||||
Value var = this.env.get(e.varName);
|
||||
Map<String, Value> struct = var.asStruct();
|
||||
|
||||
@@ -496,6 +496,11 @@ public class EvalVisitor implements Visitor<Value> {
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value visit(EnumAccessExpression e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value visit(ConstructorCall e) {
|
||||
StructDefinition structDef = this.structs.get(e.structName);
|
||||
|
||||
@@ -792,7 +792,7 @@ public class GenASM implements Visitor<Void> {
|
||||
}
|
||||
|
||||
@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<Void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(EnumAccessExpression e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(ConstructorCall e) {
|
||||
// push arguments onto the stack
|
||||
|
||||
@@ -250,7 +250,12 @@ class GetVars implements Visitor<Void> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(StructFieldAccessExpression e) {
|
||||
public Void visit(MemberAccessExpression e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(EnumAccessExpression e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -420,7 +420,7 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
||||
}
|
||||
|
||||
@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<Void> {
|
||||
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 + "(");
|
||||
|
||||
@@ -40,7 +40,8 @@ public interface Visitor<R> {
|
||||
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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user