Merge branch '24-structs' into 'master'

Resolve "Structs"

Closes #24

See merge request mkais001/klang!17
This commit is contained in:
Dennis Kaiser
2020-03-09 14:43:17 +01:00
30 changed files with 1199 additions and 19 deletions

View File

@@ -16,6 +16,7 @@ import de.hsrm.compiler.Klang.types.Type;
public class ContextAnalysis extends KlangBaseVisitor<Node> {
Map<String, VariableDeclaration> vars = new HashMap<>();
Map<String, FunctionInformation> funcs;
Map<String, StructDefinition> structs;
Type currentDeclaredReturnType;
private void checkNumeric(Node lhs, Node rhs, int line, int col) {
@@ -25,18 +26,21 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
}
}
public ContextAnalysis(Map<String, FunctionInformation> funcs) {
public ContextAnalysis(Map<String, FunctionInformation> funcs, Map<String, StructDefinition> structs) {
this.funcs = funcs;
this.structs = structs;
}
@Override
public Node visitProgram(KlangParser.ProgramContext ctx) {
FunctionDefinition[] funcs = new FunctionDefinition[ctx.functionDef().size()];
for (int i = 0; i < ctx.functionDef().size(); i++) {
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
}
Expression expression = (Expression) this.visit(ctx.expression());
Program result = new Program(funcs, expression);
Program result = new Program(funcs, this.structs, expression);
result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
@@ -172,6 +176,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
int col = ctx.start.getCharPositionInLine();
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
if (!declaredType.isPrimitiveType() && this.structs.get(declaredType.getName()) == null) {
String error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (this.vars.get(name) != null) {
String error = "Redeclaration of variable with name \"" + name + "\".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
@@ -243,6 +252,93 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
return result;
}
@Override
public Node visitField_assignment(KlangParser.Field_assignmentContext 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];
for (int i = 1; i < ctx.IDENT().size(); i++) {
path[i - 1] = ctx.IDENT(i).getText();
}
// Get the referenced variable, make sure it is defined
var variableDef = this.vars.get(varName);
if (variableDef == null) {
String error = "Variable with name " + varName + " 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() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Get the type of the result of this expression
String structName = variableDef.type.getName();
Type fieldType;
try {
fieldType = Helper.drillType(this.structs, structName, path, 0);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
// Get the expression and make sure the type combines properly
Expression expression = (Expression) this.visit(ctx.expression());
try {
fieldType.combine(expression.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
Node result = new FieldAssignment(varName, structName, path, expression);
result.col = col;
result.line = line;
return result;
}
@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];
for (int i = 1; i < ctx.IDENT().size(); i++) {
path[i - 1] = ctx.IDENT(i).getText();
}
// Get the referenced variable, make sure it is defined
var variableDef = this.vars.get(varName);
if (variableDef == null) {
String error = "Variable with name " + varName + " 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() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Get the type of the result of this expression
String structName = variableDef.type.getName();
Type resultType;
try {
resultType = Helper.drillType(this.structs, structName, path, 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;
}
@Override
public Node visitOrExpression(KlangParser.OrExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
@@ -608,6 +704,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
return n;
}
@Override
public Node visitNullAtom(KlangParser.NullAtomContext ctx) {
Node n = new NullExpression();
n.type = Type.getNullType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n;
}
@Override
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
String name = ctx.funcName.getText();
@@ -616,6 +721,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Type returnType = Type.getByName(ctx.returnType.type().getText());
this.currentDeclaredReturnType = returnType;
if (!returnType.isPrimitiveType() && this.structs.get(returnType.getName()) == null) {
String error = "Type " + returnType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Create a new set for the variables of the current function
// this will be filled in the variable declaration visitor aswell
this.vars = new HashMap<>();
@@ -653,11 +763,19 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override
public Node visitParameter(KlangParser.ParameterContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type type = Type.getByName(ctx.type_annotation().type().getText());
if (!type.isPrimitiveType() && this.structs.get(type.getName()) == null) {
String error = "Type " + type.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
Parameter result = new Parameter(name);
result.type = type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
result.line = line;
result.col = col;
return result;
}
@@ -697,4 +815,62 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
result.col = col;
return result;
}
@Override
public Node visitConstructorCallExpression(KlangParser.ConstructorCallExpressionContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
// Get the corresponding struct definition
var struct = this.structs.get(name);
if (struct == null) {
String error = "Struct with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Make sure the number of arguments match the number of struct fields
int fieldCount = struct.fields.length;
int argCount = ctx.arguments().expression().size();
if (argCount != fieldCount) {
String error = "Struct \"" + name + "\" defined " + fieldCount + " fields, but got " + argCount + " constructor parameters.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Evaluate each expression
Expression[] args = new Expression[argCount];
for (int i = 0; i < argCount; i++) {
Expression expr = (Expression) this.visit(ctx.arguments().expression(i));
try {
expr.type.combine(struct.fields[i].type); // Make sure the types are matching
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(expr.line, expr.col) + "argument " + i + " " + e.getMessage());
}
args[i] = expr;
}
ConstructorCall result = new ConstructorCall(name, args);
result.type = struct.type;
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitDestroy_statement(KlangParser.Destroy_statementContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
VariableDeclaration var = this.vars.get(name);
if (var == null) {
String error = "Variable with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
Node result = new DestructorCall(name);
result.line = line;
result.col = col;
return result;
}
}

View File

@@ -0,0 +1,37 @@
package de.hsrm.compiler.Klang;
import java.util.Set;
import de.hsrm.compiler.Klang.helper.Helper;
public class GetStructNames extends KlangBaseVisitor<Void> {
private Set<String> structNames;
public GetStructNames(Set<String> structNames) {
this.structNames = structNames;
}
@Override
public Void visitProgram(KlangParser.ProgramContext ctx) {
for (int i = 0; i < ctx.structDef().size(); i++) {
this.visit(ctx.structDef(i));
}
return null;
}
@Override
public Void visitStructDef(KlangParser.StructDefContext ctx) {
String name = ctx.structName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (this.structNames.contains(name)) {
String error = "Struct " + name + " defined multiple times.";
throw new Error(Helper.getErrorPrefix(line, col) + error);
}
this.structNames.add(name);
return null;
}
}

View File

@@ -0,0 +1,70 @@
package de.hsrm.compiler.Klang;
import java.util.Map;
import java.util.Set;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.Node;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.types.StructType;
import de.hsrm.compiler.Klang.types.Type;
public class GetStructs extends KlangBaseVisitor<Node> {
private Set<String> structNames;
private Map<String, StructDefinition> structs;
public GetStructs(Set<String> structNames, Map<String, StructDefinition> structs) {
this.structs = structs;
this.structNames = structNames;
}
@Override
public Node visitProgram(KlangParser.ProgramContext ctx) {
for (int i = 0; i < ctx.structDef().size(); i++) {
this.visit(ctx.structDef(i));
}
return null;
}
@Override
public Node visitStructDef(KlangParser.StructDefContext ctx) {
String name = ctx.structName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
StructField[] fields = new StructField[ctx.structField().size()];
for (int i = 0; i < ctx.structField().size(); i++) {
StructField field = (StructField) this.visit(ctx.structField(i));
fields[i] = field;
}
StructDefinition result = new StructDefinition(name, fields);
result.line = line;
result.col = col;
result.type = new StructType(name);
this.structs.put(name, result);
return null;
}
@Override
public Node visitStructField(KlangParser.StructFieldContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type type = Type.getByName(ctx.type_annotation().type().getText());
if (!type.isPrimitiveType() && !this.structNames.contains(type.getName())) {
String error = "Type " + type.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
Node result = new StructField(name);
result.type = type;
result.line = line;
result.col = col;
return result;
}
}

View File

@@ -8,8 +8,10 @@ import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import de.hsrm.compiler.Klang.nodes.Node;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.visitors.*;
import de.hsrm.compiler.Klang.helper.*;
@@ -85,13 +87,22 @@ public class Klang {
// Context Analysis and DAST generation
Node root;
HashMap<String, StructDefinition> structs;
try {
// Extract information about all functions
var functionDefinitions = new HashMap<String, FunctionInformation>();
new GetFunctions(functionDefinitions).visit(tree);
// Extract names of all structs
var structNames = new HashSet<String>();
new GetStructNames(structNames).visit(tree);
// Extract information about all structs
structs = new HashMap<String, StructDefinition>();
new GetStructs(structNames, structs).visit(tree);
// Create the DAST
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions);
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions, structs);
root = ctxAnal.visit(tree);
} catch (Exception e) {
System.err.println(e.getMessage());
@@ -110,7 +121,7 @@ public class Klang {
if (evaluate) {
// Evaluate the sourcecode and print the result
System.out.println("\nEvaluating the source code:");
EvalVisitor evalVisitor = new EvalVisitor();
EvalVisitor evalVisitor = new EvalVisitor(structs);
Value result = root.welcome(evalVisitor);
generateOutput(out, "Result was: " + result.asObject().toString());
return;
@@ -120,7 +131,7 @@ public class Klang {
// System.out.println("\nPrinting the assembler code");
StringWriter wAsm = new StringWriter();
GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm);
GenASM genasm = new GenASM(exAsm, mainName);
GenASM genasm = new GenASM(exAsm, mainName, structs);
root.welcome(genasm);
generateOutput(out, wAsm.toString());
}

View File

@@ -1,6 +1,7 @@
package de.hsrm.compiler.Klang;
import de.hsrm.compiler.Klang.types.Type;
import java.util.Map;
public class Value {
public Type type;
@@ -30,4 +31,9 @@ public class Value {
public boolean asBoolean() {
return (boolean) this.value;
}
@SuppressWarnings("unchecked")
public Map<String, Value> asStruct() {
return (Map<String, Value>) this.value;
}
}

View File

@@ -1,7 +1,66 @@
package de.hsrm.compiler.Klang.helper;
import java.util.Map;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.types.Type;
public class Helper {
public static String getErrorPrefix(int line, int col) {
return "Error in line " + line + ":" + col + " ";
public static String getErrorPrefix(int line, int col) {
return "Error in line " + line + ":" + col + " ";
}
public static Type drillType(Map<String, StructDefinition> structs, String name, String[] path, int pathIndex) {
// Search for the referenced field
var structDef = structs.get(name);
for (var field : structDef.fields) {
if (field.name.equals(path[pathIndex])) {
if (!field.type.isPrimitiveType()) {
// this references a struct!
// if we exhausted the path, this field type is our type
if (pathIndex == path.length - 1) {
return field.type;
}
// we did not exhaust the path, go on
return drillType(structs, field.type.getName(), path, pathIndex + 1);
} else {
// this references a primitive, we hit bedrock!
// make sure we exhausted the complete path
if (pathIndex < path.length - 1) {
throw new RuntimeException(field.name + " must be a struct but is of type " + field.type.getName() + ":");
}
// hooray, we exhausted the path, this field type is our type
return field.type;
}
}
}
throw new RuntimeException("Struct " + structDef.name + " does not contain field " + path[pathIndex]);
}
public static int getFieldIndex(StructDefinition structDef, String fieldName) {
for (int i = 0; i < structDef.fields.length; i++) {
if (structDef.fields[i].name.equals(fieldName)) {
return i;
}
}
return -1;
}
public static int getFieldOffset(StructDefinition structDef, int fieldIndex) {
return fieldIndex * 8;
}
public static int getFieldOffset(StructDefinition structDef, String fieldName) {
return getFieldIndex(structDef, fieldName) * 8;
}
public static int getFieldSizeBytes(StructDefinition structDef) {
return structDef.fields.length * 8;
}
}

View File

@@ -1,15 +1,19 @@
package de.hsrm.compiler.Klang.nodes;
import java.util.Map;
import de.hsrm.compiler.Klang.nodes.expressions.Expression;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class Program extends Node {
public FunctionDefinition[] funcs;
public Map<String, StructDefinition> structs;
public Expression expression;
public Program(FunctionDefinition[] funcs, Expression expression) {
public Program(FunctionDefinition[] funcs, Map<String, StructDefinition> structs, Expression expression) {
this.funcs = funcs;
this.structs = structs;
this.expression = expression;
}
@@ -17,4 +21,4 @@ public class Program extends Node {
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}
}

View File

@@ -0,0 +1,19 @@
package de.hsrm.compiler.Klang.nodes;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class StructDefinition extends Node {
public String name;
public StructField[] fields;
public StructDefinition(String name, StructField[] fields) {
this.name = name;
this.fields = fields;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

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

View File

@@ -0,0 +1,19 @@
package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class ConstructorCall extends Expression {
public String structName;
public Expression[] args;
public ConstructorCall(String structName, Expression[] args) {
this.structName = structName;
this.args = args;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -0,0 +1,12 @@
package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class NullExpression extends Expression {
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -0,0 +1,20 @@
package de.hsrm.compiler.Klang.nodes.expressions;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class StructFieldAccessExpression extends Expression {
public String varName;
public String structName;
public String[] path;
public StructFieldAccessExpression(String varName, String structName, String[] path) {
this.varName = varName;
this.structName = structName;
this.path = path;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -0,0 +1,17 @@
package de.hsrm.compiler.Klang.nodes.statements;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class DestructorCall extends Statement {
public String name;
public DestructorCall(String name) {
this.name = name;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -0,0 +1,25 @@
package de.hsrm.compiler.Klang.nodes.statements;
import de.hsrm.compiler.Klang.nodes.expressions.Expression;
import de.hsrm.compiler.Klang.visitors.Visitor;
public class FieldAssignment extends Statement {
public String varName;
public String structName;
public String[] path;
public Expression expression;
public FieldAssignment(String varName, String structName, String[] path, Expression expression) {
this.varName = varName;
this.structName = structName;
this.path = path;
this.expression = expression;
}
@Override
public <R> R welcome(Visitor<R> v) {
return v.visit(this);
}
}

View File

@@ -0,0 +1,41 @@
package de.hsrm.compiler.Klang.types;
public class NullType extends Type {
private static NullType instance = null;
public static NullType getType() {
if (instance != null) {
return instance;
}
instance = new NullType();
return instance;
}
@Override
public String getName() {
return "naught";
}
@Override
public Type combine(Type that) {
// You can not combine null with a primitive type
if (that.isPrimitiveType()) {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
}
// Everything else combines with null to the type it was before
return that;
}
@Override
public boolean isPrimitiveType() {
return false;
}
@Override
public boolean isNumericType() {
return false;
}
}

View File

@@ -0,0 +1,54 @@
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;
}
}

View File

@@ -16,12 +16,17 @@ public abstract class Type {
return FloatType.getType();
}
public static NullType getNullType() {
return NullType.getType();
}
public static Type getByName(String name) {
switch (name) {
case "bool": return getBooleanType();
case "int": return getIntegerType();
case "float": return getFloatType();
default: throw new RuntimeException("Unknown type " + name);
case "null": return getNullType();
default: return new StructType(name);
}
}

View File

@@ -8,6 +8,8 @@ import de.hsrm.compiler.Klang.nodes.Block;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.Parameter;
import de.hsrm.compiler.Klang.nodes.Program;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
@@ -18,7 +20,13 @@ import de.hsrm.compiler.Klang.types.Type;
public class EvalVisitor implements Visitor<Value> {
Map<String, FunctionDefinition> funcs = new HashMap<>();
Map<String, StructDefinition> structs;
Map<String, Value> env = new HashMap<>();
Map<String, Map<String, Value>> heap = new HashMap<>();
public EvalVisitor(Map<String, StructDefinition> structs) {
this.structs = structs;
}
@Override
public Value visit(IntegerExpression e) {
@@ -463,4 +471,70 @@ public class EvalVisitor implements Visitor<Value> {
return null;
}
@Override
public Value visit(StructDefinition e) {
// We get these from a previous visitor
return null;
}
@Override
public Value visit(StructField e) {
// Nothing to do here...
return null;
}
@Override
public Value visit(StructFieldAccessExpression e) {
Value var = this.env.get(e.varName);
Map<String, Value> struct = var.asStruct();
Value currentValue = struct.get(e.path[0]);
for (int i = 1; i < e.path.length; i++) {
currentValue = currentValue.asStruct().get(e.path[i]);
}
return currentValue;
}
@Override
public Value visit(ConstructorCall e) {
StructDefinition structDef = this.structs.get(e.structName);
Map<String, Value> struct = new HashMap<>();
for (int i = 0; i < e.args.length; i++) {
var arg = e.args[i].welcome(this);
struct.put(structDef.fields[i].name, arg);
}
return new Value(struct);
}
@Override
public Value visit(NullExpression e) {
return null;
}
@Override
public Value visit(DestructorCall e) {
this.env.remove(e.name);
return null;
}
@Override
public Value visit(FieldAssignment e) {
Value val = this.env.get(e.varName);
String fieldNameToUpdate = e.path[e.path.length - 1];
// Find the struct that holds the field to be updated
Map<String, Value> struct = val.asStruct();
for (int i = 0; i < e.path.length - 1; i++) {
struct = struct.get(e.path[i]).asStruct();
}
// if we are here, struct contains a reference to the struct that holds the field to be updated
struct.put(fieldNameToUpdate, e.expression.welcome(this));
return null;
}
}

View File

@@ -7,6 +7,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
@@ -107,6 +108,7 @@ public class GenASM implements Visitor<Void> {
private FloatWriter fw = new FloatWriter();
private String mainName;
Map<String, Integer> env = new HashMap<>();
Map<String, StructDefinition> structs;
Set<String> vars;
String[] registers = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" };
String[] floatRegisters = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" };
@@ -147,14 +149,15 @@ public class GenASM implements Visitor<Void> {
return false;
}
}
public GenASM(ExWriter ex, String mainName) {
public GenASM(ExWriter ex, String mainName, Map<String, StructDefinition> structs) {
this.ex = ex;
this.mainName = mainName;
this.structs = structs;
}
public GenASM(ExWriter ex) {
this.ex = ex;
this.mainName = "main";
public GenASM(ExWriter ex, Map<String, StructDefinition> structs) {
this(ex, "main", structs);
}
@Override
@@ -790,4 +793,115 @@ public class GenASM implements Visitor<Void> {
return null;
}
@Override
public Void visit(StructDefinition e) {
// We get these from a previous visitor
return null;
}
@Override
public Void visit(StructField e) {
// Nothing to do here...
return null;
}
@Override
public Void visit(StructFieldAccessExpression e) {
var structDef = this.structs.get(e.structName);
int offset = this.env.get(e.varName);
// move struct address into rax
this.ex.write(" movq " + offset + "(%rbp), %rax\n");
// "follow" the first path element by moving the referenced value into rax
this.ex.write(" movq " + Helper.getFieldOffset(structDef, e.path[0]) + "(%rax), %rax\n");
for (int i = 1; i < e.path.length; i++) {
// "follow" the current path element
structDef = this.structs.get(structDef.fields[Helper.getFieldIndex(structDef, e.path[i - 1])].type.getName());
this.ex.write(" movq " + Helper.getFieldOffset(structDef, e.path[i]) + "(%rax), %rax\n");
}
// desired value now in rax
// push rax to xmm0 if the result type is a float
if (e.type.equals(Type.getFloatType())) {
this.ex.write(" movq %rax, %xmm0\n");
}
return null;
}
@Override
public Void visit(ConstructorCall e) {
// push arguments onto the stack
for (var arg: e.args) {
arg.welcome(this);
// move float values from xmm0 to rax first
if (arg.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n");
}
this.ex.write(" pushq %rax\n");
}
// allocate heap memory by calling malloc
var structDef = this.structs.get(e.structName);
this.ex.write(" movl $" + Helper.getFieldSizeBytes(structDef) + ", %edi\n");
this.ex.write(" call malloc@PLT\n"); // struct address now in rax
// push args into struct memory, last arg is ontop of the stack
for (int i = e.args.length - 1; i >= 0; i--) {
this.ex.write(" popq " + Helper.getFieldOffset(structDef, i) + "(%rax)\n");
}
return null;
}
@Override
public Void visit(NullExpression e) {
this.ex.write(" movq $0, %rax\n");
return null;
}
@Override
public Void visit(DestructorCall e) {
this.ex.write(" movq " +this.env.get(e.name) + "(%rbp), %rdi\n");
this.ex.write(" call free@PLT\n");
return null;
}
@Override
public Void visit(FieldAssignment e) {
var structDef = this.structs.get(e.structName);
int offset = this.env.get(e.varName);
String fieldNameToUpdate = e.path[e.path.length - 1];
e.expression.welcome(this);
// Move it from xmm0 rax if its a flaot
if (e.expression.type.equals(Type.getFloatType())) {
this.ex.write(" movq %xmm0, %rax\n");
}
// Push the expression onto the stack
this.ex.write(" pushq %rax\n");
// move struct address into rax
this.ex.write(" movq " + offset + "(%rbp), %rax\n");
// If there are at least two elements in the path,
// move the address of the next referenced struct into rax
for (int i = 1; i < e.path.length - 1; i++) {
structDef = this.structs.get(structDef.fields[Helper.getFieldIndex(structDef, e.path[i - 1])].type.getName());
this.ex.write(" movq " + Helper.getFieldOffset(structDef, e.path[i]) + "(%rax), %rax\n");
}
// pop the expression that is ontop of the stack into the field of the struct that has to be updated
this.ex.write(" popq " + Helper.getFieldOffset(structDef, fieldNameToUpdate) + "(%rax)\n");
this.ex.write(" movq $0, %rax\n"); // clear rax sind an assignment has no result
return null;
}
}

View File

@@ -234,4 +234,38 @@ class GetVars implements Visitor<Void> {
return null;
}
@Override
public Void visit(StructDefinition e) {
return null;
}
@Override
public Void visit(StructField e) {
return null;
}
@Override
public Void visit(StructFieldAccessExpression e) {
return null;
}
@Override
public Void visit(ConstructorCall e) {
return null;
}
@Override
public Void visit(NullExpression e) {
return null;
}
@Override
public Void visit(DestructorCall e) {
return null;
}
@Override
public Void visit(FieldAssignment e) {
return null;
}
}

View File

@@ -61,6 +61,13 @@ public class PrettyPrintVisitor implements Visitor<Void> {
ex.nl();
ex.nl();
}
for (var structDef: e.structs.values()) {
structDef.welcome(this);
ex.nl();
ex.nl();
}
e.expression.welcome(this);
ex.write(";");
return null;
@@ -370,4 +377,76 @@ public class PrettyPrintVisitor implements Visitor<Void> {
return null;
}
@Override
public Void visit(StructDefinition e) {
ex.write("struct " + e.name + " {");
ex.addIndent();
for(var field: e.fields) {
ex.nl();
field.welcome(this);
}
ex.subIndent();
ex.nl();
ex.write("}");
return null;
}
@Override
public Void visit(StructField e) {
ex.write(e.name +": " + e.type.getName() + ";");
return null;
}
@Override
public Void visit(StructFieldAccessExpression e) {
ex.write(e.varName);
for (int i = 0; i < e.path.length; i++) {
ex.write(".");
ex.write(e.path[i]);
}
return null;
}
@Override
public Void visit(ConstructorCall e) {
ex.write("create " + e.structName + "(");
boolean first = true;
for (Expression arg : e.args) {
if (!first) {
ex.write(", ");
} else {
first = false;
}
arg.welcome(this);
}
ex.write(")");
return null;
}
@Override
public Void visit(NullExpression e) {
ex.write("null");
return null;
}
@Override
public Void visit(DestructorCall e) {
ex.write("destroy " + e.name + ";");
return null;
}
@Override
public Void visit(FieldAssignment e) {
ex.write(e.varName);
for (int i = 0; i < e.path.length; i++) {
ex.write(".");
ex.write(e.path[i]);
}
ex.write(" = ");
e.expression.welcome(this);
ex.write(";");
return null;
}
}

View File

@@ -4,6 +4,8 @@ import de.hsrm.compiler.Klang.nodes.Block;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
import de.hsrm.compiler.Klang.nodes.Parameter;
import de.hsrm.compiler.Klang.nodes.Program;
import de.hsrm.compiler.Klang.nodes.StructDefinition;
import de.hsrm.compiler.Klang.nodes.StructField;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.*;
import de.hsrm.compiler.Klang.nodes.statements.*;
@@ -11,7 +13,7 @@ import de.hsrm.compiler.Klang.nodes.statements.*;
public interface Visitor<R> {
R visit(OrExpression e);
R visit(AndExpression e);
R visit (NotExpression e);
R visit(NotExpression e);
R visit(IntegerExpression e);
R visit(FloatExpression e);
R visit(BooleanExpression e);
@@ -40,4 +42,11 @@ public interface Visitor<R> {
R visit(FunctionCall e);
R visit(Program e);
R visit(Parameter e);
R visit(StructDefinition e);
R visit(StructField e);
R visit(StructFieldAccessExpression e);
R visit(ConstructorCall e);
R visit(NullExpression e);
R visit(DestructorCall e);
R visit(FieldAssignment e);
}