Compare commits

...

5 Commits

Author SHA1 Message Date
e835bd0f06 GenASM: Make GenASM quietly rewrite a user's function if it's called main.
We generate our own main function that executes the user's specified expression at the end of his file. This auto generated function has to be called "main" in order for it to be executed on startup. If the user chooses to call one of his own functions "main" our auto generated function would collide with the user's function. That's why we quietly rewrite the user's function to "main_by_user". This way we prevent a collision.

Note for the future: We should change our Syntax to allow for no expression as the last definition of a file. This way the user can choose if a particular source file needs to contain a main function or not (just like c does it). This is also one of the requirements for modules to work.
2023-03-20 21:05:24 +01:00
ea1c04ae0a Build: Add a main manifest attribute to the generated jar.
This makes it possible to directly execute the packaged jar.
2023-03-20 19:55:37 +01:00
198bd74a47 Enums: Make the EnumAccessExpression save a reference to the EnumValue it is referencing.
This can be used during assembler generation to easily find the correct EnumValue for a given EnumAccessExpression.
2023-03-20 19:54:48 +01:00
0594542167 Enums: Make EnumDefinition use EnumValues instead of Strings as children.
This allows us to store the index of the enum value along the name. The index can be used to compare two enum values in assembler.

Later on this might be used to enable users of KLang to set arbitrary values as the index of an enum value.
2023-03-20 19:30:07 +01:00
77fe360ffa Evaluate: Implement evaluation for enums. 2023-03-20 19:10:40 +01:00
17 changed files with 141 additions and 38 deletions

View File

@@ -72,6 +72,11 @@
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>de.hsrm.compiler.Klang.Klang</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>

View File

@@ -355,12 +355,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
}
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 enumValue = Arrays.stream(enumDef.enums)
.filter(e -> e.value.equals(enumValueName))
.findFirst()
.orElseThrow(() -> {
var error = "Unknown enum value " + enumValueName + " of enum " + enumDef.name + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
});
var enumAccessExpression = new EnumAccessExpression(baseName, enumValueName);
var enumAccessExpression = new EnumAccessExpression(baseName, enumValueName, enumValue);
enumAccessExpression.type = enumDef.type;
enumAccessExpression.line = line;
enumAccessExpression.col = col;

View File

@@ -107,20 +107,26 @@ public class GetDefinitions extends KlangBaseVisitor<Node> {
}
// IDENT() includes the enumName as the first entry, which we skip
var enumFields = new LinkedHashSet<String>();
var enumValues = new LinkedHashMap<String, EnumValue>();
for (int i = 1; i < ctx.IDENT().size(); i++) {
var currentEnumField = ctx.IDENT(i);
var currentEnumFieldName = currentEnumField.getText();
if (enumFields.contains(currentEnumFieldName)) {
var line = currentEnumField.getSymbol().getLine();
var col = currentEnumField.getSymbol().getCharPositionInLine();
var line = currentEnumField.getSymbol().getLine();
var col = currentEnumField.getSymbol().getCharPositionInLine();
if (enumValues.containsKey(currentEnumFieldName)) {
var error = " Duplicate enum value " + currentEnumFieldName + " in enum " + enumName + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
enumFields.add(currentEnumFieldName);
var enumValue = new EnumValue(currentEnumFieldName, i - 1);
enumValue.line = line;
enumValue.col = col;
enumValues.put(currentEnumFieldName, enumValue);
}
var enumDef = new EnumDefinition(enumName, enumFields.toArray(new String[0]));
var enumDef = new EnumDefinition(enumName, enumValues.values().toArray(new EnumValue[0]));
enumDef.line = ctx.start.getLine();
enumDef.col = ctx.start.getCharPositionInLine();
enumDef.type = new NamedType(enumName);

View File

@@ -5,9 +5,9 @@ import de.hsrm.compiler.Klang.visitors.Visitor;
public class EnumDefinition extends Node {
public String name;
public String[] enums;
public EnumValue[] enums;
public EnumDefinition(String name, String[] enums) {
public EnumDefinition(String name, EnumValue[] enums) {
this.name = name;
this.enums = enums;
}

View File

@@ -0,0 +1,18 @@
package de.hsrm.compiler.Klang.nodes;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class EnumValue extends Node {
public String value;
public int index;
public EnumValue(String value, int index) {
this.value = value;
this.index = index;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -1,14 +1,22 @@
package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.EnumValue;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class EnumAccessExpression extends Expression {
public String enumName;
public String enumValueName;
public EnumValue enumValue;
public EnumAccessExpression(String enumName, String enumValueName) {
public EnumAccessExpression(
String enumName,
String enumValueName,
EnumValue enumValue
) {
this.enumName = enumName;
this.enumValueName = enumValueName;
this.enumValue = enumValue;
}
@Override

View File

@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class BooleanType extends PrimitiveType {
private static BooleanType instance = null;
@@ -33,4 +35,8 @@ public class BooleanType extends PrimitiveType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asBoolean() == b.asBoolean();
}
}

View File

@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class FloatType extends NumericType {
private static FloatType instance = null;
@@ -37,4 +39,8 @@ public class FloatType extends NumericType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asFloat() == b.asFloat();
}
}

View File

@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class IntegerType extends NumericType {
private static IntegerType instance = null;
@@ -37,4 +39,9 @@ public class IntegerType extends NumericType {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asInteger() == b.asInteger();
}
}

View File

@@ -1,5 +1,12 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import java.util.Map;
public class NamedType extends Type {
public String name;
@@ -21,6 +28,11 @@ public class NamedType extends Type {
throw new RuntimeException("Type mismatch: cannot combine " + getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asObject().equals(b.asObject());
}
@Override
public boolean isPrimitiveType() {
return false;

View File

@@ -1,5 +1,7 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class NullType extends Type {
private static NullType instance = null;
@@ -28,6 +30,11 @@ public class NullType extends Type {
return that;
}
@Override
public boolean valuesEqual(Value a, Value b) {
return a.asObject() == b.asObject();
}
@Override
public boolean isPrimitiveType() {
return false;

View File

@@ -1,6 +1,11 @@
package de.hsrm.compiler.Klang.types;
import java.util.Set;
import de.hsrm.compiler.Klang.Value;
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import java.util.Map;
public abstract class Type {
@@ -34,6 +39,7 @@ public abstract class Type {
public abstract String getName();
public abstract Type combine(Type that);
public abstract boolean valuesEqual(Value a, Value b);
public abstract boolean isPrimitiveType();
public abstract boolean isNumericType();
}

View File

@@ -46,25 +46,11 @@ public class EvalVisitor implements Visitor<Value> {
@Override
public Value visit(EqualityExpression e) {
Value lhs = e.lhs.welcome(this);
Value rhs = e.rhs.welcome(this);
Type resultType = Type.getBooleanType();
Type combineType = lhs.type.combine(rhs.type);
var lhs = e.lhs.welcome(this);
var rhs = e.rhs.welcome(this);
var combinedType = lhs.type.combine(rhs.type);
switch(combineType.getName()) {
case "bool": {
return new Value(lhs.asBoolean() == rhs.asBoolean(), resultType);
}
case "int": {
return new Value(lhs.asInteger() == rhs.asInteger(), resultType);
}
case "float": {
return new Value(lhs.asFloat() == rhs.asFloat(), resultType);
}
default: {
return new Value(lhs.asObject() == rhs.asObject(), resultType);
}
}
return new Value(combinedType.valuesEqual(lhs, rhs), Type.getBooleanType());
}
@Override
@@ -471,6 +457,11 @@ public class EvalVisitor implements Visitor<Value> {
return null;
}
@Override
public Value visit(EnumValue e) {
return null;
}
@Override
public Value visit(StructDefinition e) {
// We get these from a previous visitor
@@ -498,7 +489,7 @@ public class EvalVisitor implements Visitor<Value> {
@Override
public Value visit(EnumAccessExpression e) {
return null;
return new Value(e.enumValueName, e.type);
}
@Override

View File

@@ -567,6 +567,13 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(FunctionDefinition e) {
// If the user chooses "main" as one of his function names then
// rename it and hope that they didn't use the renamed function name
// as well :D
if (e.name.equals("main")) {
e.name = "main_by_user";
}
int lblStart = ++lCount;
this.currentFunctionStartLabel = lblStart;
this.currentFunctionParams = e.parameters;
@@ -744,7 +751,8 @@ public class GenASM implements Visitor<Void> {
asm.add("q", stackStartOffset, "%rsp");
}
asm.call(e.name);
// We rename a function name if it is "main"
asm.call(e.name.equals("main") ? "main_by_user": e.name);
return null;
}
@@ -769,7 +777,7 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(Parameter e) {
// The work for a paremeter node is implement
// The work for a parameter node is implement
// in the function definition visitor
return null;
}
@@ -779,6 +787,11 @@ public class GenASM implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumValue e) {
return null;
}
@Override
public Void visit(StructDefinition e) {
// We get these from a previous visitor
@@ -819,6 +832,9 @@ public class GenASM implements Visitor<Void> {
@Override
public Void visit(EnumAccessExpression e) {
// Since the access to an enum simply results in an integer (i.e. the index
// of the enum value), we can just let IntegerExpression handle the expected behaviour.
new IntegerExpression(e.enumValue.index).welcome(this);
return null;
}

View File

@@ -239,6 +239,11 @@ class GetVars implements Visitor<Void> {
return null;
}
@Override
public Void visit(EnumValue e) {
return null;
}
@Override
public Void visit(StructDefinition e) {
return null;

View File

@@ -387,18 +387,24 @@ public class PrettyPrintVisitor implements Visitor<Void> {
public Void visit(EnumDefinition e) {
ex.write("enum " + e.name + " { ");
var first = true;
for(var enumName: e.enums) {
for(var enumValue: e.enums) {
if (!first) {
ex.write(", ");
} else {
first = false;
}
ex.write(enumName);
enumValue.welcome(this);
}
ex.write(" }");
return null;
}
@Override
public Void visit(EnumValue e) {
ex.write(e.value);
return null;
}
@Override
public Void visit(StructDefinition e) {
ex.write("struct " + e.name + " {");

View File

@@ -38,6 +38,7 @@ public interface Visitor<R> {
R visit(Program e);
R visit(Parameter e);
R visit(EnumDefinition e);
R visit(EnumValue e);
R visit(StructDefinition e);
R visit(StructField e);
R visit(MemberAccessExpression e);