diff --git a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 index bc821d6..a1a176a 100644 --- a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 +++ b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 @@ -5,7 +5,15 @@ parse ; program - : functionDef* expression SCOL + : (functionDef | structDef)* expression SCOL + ; + +structDef + : STRUCT structName=IDENT OBRK structField+ CBRK + ; + +structField + : IDENT type_annotation SCOL ; functionDef @@ -31,10 +39,12 @@ statement : if_statement | variable_declaration SCOL | variable_assignment SCOL + | field_assignment SCOL | return_statement | whileLoop | doWhileLoop | forLoop + | destroy_statement ; if_statement @@ -54,12 +64,21 @@ variable_assignment : IDENT EQUAL expression ; +field_assignment + : IDENT (PERIOD IDENT)+ EQUAL expression + ; + return_statement : RETURN expression SCOL ; +destroy_statement + : DESTROY IDENT SCOL + ; + expression : atom #atomExpression + | IDENT (PERIOD IDENT)+ #structFieldAccessExpression | OPAR expression CPAR #parenthesisExpression | lhs=expression MUL rhs=expression #multiplicationExpression | lhs=expression DIV rhs=expression #divisionExpression @@ -77,12 +96,14 @@ expression | SUB expression #negateExpression | NOT expression #NotExpression | functionCall #functionCallExpression + | CREATE IDENT OPAR arguments CPAR # constructorCallExpression ; atom : INTEGER_LITERAL #intAtom | BOOLEAN_LITERAL #boolAtom | FLOAT_LITERAL #floatAtom + | NULL # nullAtom | IDENT #variable ; @@ -94,6 +115,7 @@ type : INTEGER | BOOLEAN | FLOAT + | IDENT ; functionCall @@ -121,11 +143,15 @@ forLoop IF: 'if'; ELSE: 'else'; FUNC: 'function'; +STRUCT: 'struct'; RETURN: 'return'; LET: 'let'; WHILE: 'while'; DO: 'do'; FOR: 'for'; +CREATE: 'create'; +DESTROY: 'destroy'; +NULL: 'naught'; PERIOD: '.'; COL: ':'; diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 6bc892f..61275a6 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -16,6 +16,7 @@ import de.hsrm.compiler.Klang.types.Type; public class ContextAnalysis extends KlangBaseVisitor { Map vars = new HashMap<>(); Map funcs; + Map structs; Type currentDeclaredReturnType; private void checkNumeric(Node lhs, Node rhs, int line, int col) { @@ -25,18 +26,21 @@ public class ContextAnalysis extends KlangBaseVisitor { } } - public ContextAnalysis(Map funcs) { + public ContextAnalysis(Map funcs, Map 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 { 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 { 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 { 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 { 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 { @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 { 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; + } } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/GetStructNames.java b/src/main/java/de/hsrm/compiler/Klang/GetStructNames.java new file mode 100644 index 0000000..0e5fcb3 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/GetStructNames.java @@ -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 { + private Set structNames; + + public GetStructNames(Set 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; + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/GetStructs.java b/src/main/java/de/hsrm/compiler/Klang/GetStructs.java new file mode 100644 index 0000000..ca1dc2f --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/GetStructs.java @@ -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 { + + private Set structNames; + private Map structs; + + public GetStructs(Set structNames, Map 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; + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/Klang.java b/src/main/java/de/hsrm/compiler/Klang/Klang.java index 555302d..1c951d0 100644 --- a/src/main/java/de/hsrm/compiler/Klang/Klang.java +++ b/src/main/java/de/hsrm/compiler/Klang/Klang.java @@ -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 structs; try { // Extract information about all functions var functionDefinitions = new HashMap(); new GetFunctions(functionDefinitions).visit(tree); + // Extract names of all structs + var structNames = new HashSet(); + new GetStructNames(structNames).visit(tree); + + // Extract information about all structs + structs = new HashMap(); + 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()); } diff --git a/src/main/java/de/hsrm/compiler/Klang/Value.java b/src/main/java/de/hsrm/compiler/Klang/Value.java index c0551c6..112d1de 100644 --- a/src/main/java/de/hsrm/compiler/Klang/Value.java +++ b/src/main/java/de/hsrm/compiler/Klang/Value.java @@ -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 asStruct() { + return (Map) this.value; + } } diff --git a/src/main/java/de/hsrm/compiler/Klang/helper/Helper.java b/src/main/java/de/hsrm/compiler/Klang/helper/Helper.java index 2788a28..648c4e8 100644 --- a/src/main/java/de/hsrm/compiler/Klang/helper/Helper.java +++ b/src/main/java/de/hsrm/compiler/Klang/helper/Helper.java @@ -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 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; + } } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java b/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java index e02d893..f56d749 100644 --- a/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java @@ -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 structs; public Expression expression; - public Program(FunctionDefinition[] funcs, Expression expression) { + public Program(FunctionDefinition[] funcs, Map structs, Expression expression) { this.funcs = funcs; + this.structs = structs; this.expression = expression; } @@ -17,4 +21,4 @@ public class Program extends Node { public R welcome(Visitor v) { return v.visit(this); } -} \ No newline at end of file +} diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/StructDefinition.java b/src/main/java/de/hsrm/compiler/Klang/nodes/StructDefinition.java new file mode 100644 index 0000000..ef0c142 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/StructDefinition.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/StructField.java b/src/main/java/de/hsrm/compiler/Klang/nodes/StructField.java new file mode 100644 index 0000000..2456081 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/StructField.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/ConstructorCall.java b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/ConstructorCall.java new file mode 100644 index 0000000..ef36b09 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/ConstructorCall.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/NullExpression.java b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/NullExpression.java new file mode 100644 index 0000000..cad431d --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/NullExpression.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } + +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java new file mode 100644 index 0000000..73b9ac8 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/expressions/StructFieldAccessExpression.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/statements/DestructorCall.java b/src/main/java/de/hsrm/compiler/Klang/nodes/statements/DestructorCall.java new file mode 100644 index 0000000..77dd461 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/statements/DestructorCall.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/statements/FieldAssignment.java b/src/main/java/de/hsrm/compiler/Klang/nodes/statements/FieldAssignment.java new file mode 100644 index 0000000..c655106 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/statements/FieldAssignment.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } + +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/types/NullType.java b/src/main/java/de/hsrm/compiler/Klang/types/NullType.java new file mode 100644 index 0000000..452efd8 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/types/NullType.java @@ -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; + } + +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/types/StructType.java b/src/main/java/de/hsrm/compiler/Klang/types/StructType.java new file mode 100644 index 0000000..23cac1c --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/types/StructType.java @@ -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; + } +} \ 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 75a8157..15b2810 100644 --- a/src/main/java/de/hsrm/compiler/Klang/types/Type.java +++ b/src/main/java/de/hsrm/compiler/Klang/types/Type.java @@ -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); } } 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 74473ca..dad982e 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java @@ -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 { Map funcs = new HashMap<>(); + Map structs; Map env = new HashMap<>(); + Map> heap = new HashMap<>(); + + public EvalVisitor(Map structs) { + this.structs = structs; + } @Override public Value visit(IntegerExpression e) { @@ -463,4 +471,70 @@ public class EvalVisitor implements Visitor { 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 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 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 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; + } + } \ No newline at end of file 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 661562f..1bc49dc 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java @@ -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 { private FloatWriter fw = new FloatWriter(); private String mainName; Map env = new HashMap<>(); + Map structs; Set 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 { return false; } } - public GenASM(ExWriter ex, String mainName) { + + public GenASM(ExWriter ex, String mainName, Map 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 structs) { + this(ex, "main", structs); } @Override @@ -790,4 +793,115 @@ public class GenASM implements Visitor { 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; + } + } \ No newline at end of file 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 6976677..50d807d 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java @@ -234,4 +234,38 @@ class GetVars implements Visitor { 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; + } } \ No newline at end of file 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 c82d4d7..563779b 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java @@ -61,6 +61,13 @@ public class PrettyPrintVisitor implements Visitor { 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 { 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; + } + } \ No newline at end of file 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 727bcd7..cfef604 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java @@ -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 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 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); } \ No newline at end of file diff --git a/src/test/print/print.c b/src/test/print/print.c index b995765..71df87a 100644 --- a/src/test/print/print.c +++ b/src/test/print/print.c @@ -38,6 +38,26 @@ void err_f(char* name, double expected, double result) { printf("\033[0;31mERROR:\t\t%s:\tGOT: %f\tExpected: %f\033[0;0m\n", name, result, expected); } +void succ_s(char* name, void* expected, void* result) { + incSuccess(); + printf("\033[0;32mSUCCESS:\t%s:\tGOT: %p\tExpected: %p\033[0;0m\n", name, result, expected); +} + +void err_s(char* name, void* expected, void* result) { + incFailure(); + printf("\033[0;31mERROR:\t\t%s:\tGOT: %p\tExpected: %p\033[0;0m\n", name, result, expected); +} + +void succ_b(char* name, bool expected, bool result) { + incSuccess(); + printf("\033[0;32mSUCCESS:\t%s:\tGOT: %s\tExpected: %s\033[0;0m\n", name, printBool(result), printBool(expected)); +} + +void err_b(char* name, bool expected, bool result) { + incFailure(); + printf("\033[0;31mERROR:\t\t%s:\tGOT: %s\tExpected: %s\033[0;0m\n", name, printBool(result), printBool(expected)); +} + void succPrefixOne(char* name, long x, long expected, long result) { incSuccess(); printf("\033[0;32mSUCCESS:\t%s(%ld)\tGOT: %ld\tExpected: %ld\033[0;0m\n", name, x, result, expected); diff --git a/src/test/print/print.h b/src/test/print/print.h index 2c08103..df17dc0 100644 --- a/src/test/print/print.h +++ b/src/test/print/print.h @@ -9,6 +9,12 @@ void err(char* name, long expected, long result); void succ_f(char* name, double expected, double result); void err_f(char* name, double expected, double result); +void succ_s(char* name, void* expected, void* result); +void err_s(char* name, void* expected, void* result); + +void succ_b(char* name, bool expected, bool result); +void err_b(char* name, bool expected, bool result); + void succPrefixOne(char* name, long x, long expected, long result); void errPrefixOne(char* name, long x, long expected, long result); diff --git a/src/test/struct/struct.c b/src/test/struct/struct.c new file mode 100644 index 0000000..ca512fe --- /dev/null +++ b/src/test/struct/struct.c @@ -0,0 +1,127 @@ +#include +#include +#include "struct.h" +#include "../print/print.h" + +// C equivalent implementations of the funcitons written in k +struct testStruct* cGetTestStruct(long a, bool b, long c) { + struct testStruct* result = (struct testStruct*) malloc(sizeof(struct testStruct)); + result->a = a; + result->b = b; + result->c = c; + return result; +} + +struct testStructRec* cGetTestStructRec(long a, struct testStructRec* b) { + struct testStructRec* result = (struct testStructRec*) malloc(sizeof(struct testStructRec)); + result->a = a; + result->b = b; + return result; +} + +int struct_testExpected_l(char *name, long expected, long result) +{ + if (expected == result) + { + succ(name, expected, result); + return 0; + } + else + { + err(name, expected, result); + return 1; + } +} + +int struct_testExpected_s(char *name, void* expected, void* result) +{ + if (expected == result) + { + succ_s(name, expected, result); + return 0; + } + else + { + err_s(name, expected, result); + return 1; + } +} + +int struct_testExpected_f(char *name, double expected, double result) +{ + if (expected == result) + { + succ_f(name, expected, result); + return 0; + } + else + { + err_f(name, expected, result); + return 1; + } +} + +int struct_testExpected_b(char *name, bool expected, bool result) +{ + if (expected == result) + { + succ_b(name, expected, result); + return 0; + } + else + { + err_b(name, expected, result); + return 1; + } +} + +int testStructCreation() { + printf("\nStruct creation tests\n"); + struct testStruct* result = getTestStruct(1, false, 23.3); + + struct_testExpected_l("init field a", 1, result->a); + struct_testExpected_b("init field b", false, result->b); + struct_testExpected_f("init field c", 23.3, result->c); + + free(result); + + printf("\nRecursive struct creation tests\n"); + struct testStructRec* innerStruct = getTestStructRec(20, NULL); + struct testStructRec* resultRec = getTestStructRec(10, innerStruct); + + struct_testExpected_l("init rec field a", 10, resultRec->a); + struct_testExpected_s("init rec field b", innerStruct, resultRec->b); + struct_testExpected_l("init inner field a", 20, resultRec->b->a); + struct_testExpected_s("init inner field b", NULL, resultRec->b->b); + + free(resultRec); + free(innerStruct); +} + +int testStructGet() { + printf("\nStruct getter tests\n"); + struct testStruct* result = getTestStruct(1, false, 23.3); + + struct_testExpected_l("get field a", 1, getStructFieldA(result)); + struct_testExpected_b("get field b", false, getStructFieldB(result)); + struct_testExpected_f("get field c", 23.3, getStructFieldC(result)); + + free(result); + + printf("\nStruct getter tests\n"); + struct testStructRec* innerStruct = getTestStructRec(1, NULL); + struct testStructRec* resultRec = getTestStructRec(20, innerStruct); + + struct_testExpected_l("get rec field a", 20, getStructFieldRecA(resultRec)); + struct_testExpected_s("get rec field b", innerStruct, getStructFieldRecB(resultRec)); + struct_testExpected_l("get inner field a", 1, getStructFieldRecA(getStructFieldRecB(resultRec))); + struct_testExpected_s("get inner field b", NULL, getStructFieldRecB(getStructFieldRecB(resultRec))); + + free(resultRec); + free(innerStruct); +} + +void runStructTests() { + testStructCreation(); + testStructGet(); +} \ No newline at end of file diff --git a/src/test/struct/struct.h b/src/test/struct/struct.h new file mode 100644 index 0000000..459e30c --- /dev/null +++ b/src/test/struct/struct.h @@ -0,0 +1,31 @@ +#include + +struct testStruct +{ + long a; + bool b; + double c; +}; + +struct testStructRec +{ + long a; + struct testStructRec *b; +}; + +struct testStruct* getTestStruct(long a, bool b, double c); +struct testStructRec* getTestStructRec(long a, struct testStructRec* b); + +long getStructFieldA(struct testStruct *); +bool getStructFieldB(struct testStruct *); +double getStructFieldC(struct testStruct *); + +struct testStruct *setStructFieldA(struct testStruct *t, long a); +struct testStruct *setStructFieldB(struct testStruct *t, bool b); +struct testStruct *setStructFieldC(struct testStruct *t, double c); + +long getStructFieldRecA(struct testStructRec *t); +struct testStructRec *getStructFieldRecB(struct testStructRec *t); + +struct testStructRec *setStructFieldRecA(struct testStructRec *t, long a); +struct testStructRec *setStructFieldRecB(struct testStructRec *t, struct testStructRec *b); \ No newline at end of file diff --git a/src/test/test.c b/src/test/test.c index b5f5589..8a11ea9 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -28,6 +28,9 @@ int main(){ // Tests for while loop runLoopTests(); + // Tests for structs + runStructTests(); + printf("\n%d tests in total\n", successes + failures); if (failures > 0) { diff --git a/src/test/test.h b/src/test/test.h index be87439..f25b0e1 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -6,4 +6,5 @@ void runFunctionCallTests(); void runRecursiveTests(); void runComparisonTests(); void runLoopTests(); -void runMathTests(); \ No newline at end of file +void runMathTests(); +void runStructTests(); \ No newline at end of file diff --git a/src/test/test.k b/src/test/test.k index 539f333..395afa2 100644 --- a/src/test/test.k +++ b/src/test/test.k @@ -415,4 +415,68 @@ function mixdiv(x: float, y: int): float { return x / y; } +struct testStruct { + a: int; + b: bool; + c: float; +} + +struct testStructRec { + a: int; + b: testStructRec; +} + +function getTestStruct(a: int, b: bool, c: float): testStruct { + return create testStruct(a, b, c); +} + +function getTestStructRec(a: int, b: testStructRec): testStructRec { + return create testStructRec(a, b); +} + +function getStructFieldA(t: testStruct): int { + return t.a; +} + +function getStructFieldB(t: testStruct): bool { + return t.b; +} + +function getStructFieldC(t: testStruct): float { + return t.c; +} + +function setStructFieldA(t: testStruct, a: int): testStruct { + t.a = a; + return t; +} + +function setStructFieldB(t: testStruct, b: bool): testStruct { + t.b = b; + return t; +} + +function setStructFieldC(t: testStruct, c: float): testStruct { + t.c = c; + return t; +} + +function getStructFieldRecA(t: testStructRec): int { + return t.a; +} + +function getStructFieldRecB(t: testStructRec): testStructRec { + return t.b; +} + +function setStructFieldRecA(t: testStructRec, a: int): testStructRec { + t.a = a; + return t; +} + +function setStructFieldRecB(t: testStructRec, b: testStructRec): testStructRec { + t.b = b; + return t; +} + add(1, 1);