Merge branch '24-structs' into 'master'
Resolve "Structs" Closes #24 See merge request mkais001/klang!17
This commit is contained in:
@@ -5,7 +5,15 @@ parse
|
|||||||
;
|
;
|
||||||
|
|
||||||
program
|
program
|
||||||
: functionDef* expression SCOL
|
: (functionDef | structDef)* expression SCOL
|
||||||
|
;
|
||||||
|
|
||||||
|
structDef
|
||||||
|
: STRUCT structName=IDENT OBRK structField+ CBRK
|
||||||
|
;
|
||||||
|
|
||||||
|
structField
|
||||||
|
: IDENT type_annotation SCOL
|
||||||
;
|
;
|
||||||
|
|
||||||
functionDef
|
functionDef
|
||||||
@@ -31,10 +39,12 @@ statement
|
|||||||
: if_statement
|
: if_statement
|
||||||
| variable_declaration SCOL
|
| variable_declaration SCOL
|
||||||
| variable_assignment SCOL
|
| variable_assignment SCOL
|
||||||
|
| field_assignment SCOL
|
||||||
| return_statement
|
| return_statement
|
||||||
| whileLoop
|
| whileLoop
|
||||||
| doWhileLoop
|
| doWhileLoop
|
||||||
| forLoop
|
| forLoop
|
||||||
|
| destroy_statement
|
||||||
;
|
;
|
||||||
|
|
||||||
if_statement
|
if_statement
|
||||||
@@ -54,12 +64,21 @@ variable_assignment
|
|||||||
: IDENT EQUAL expression
|
: IDENT EQUAL expression
|
||||||
;
|
;
|
||||||
|
|
||||||
|
field_assignment
|
||||||
|
: IDENT (PERIOD IDENT)+ EQUAL expression
|
||||||
|
;
|
||||||
|
|
||||||
return_statement
|
return_statement
|
||||||
: RETURN expression SCOL
|
: RETURN expression SCOL
|
||||||
;
|
;
|
||||||
|
|
||||||
|
destroy_statement
|
||||||
|
: DESTROY IDENT SCOL
|
||||||
|
;
|
||||||
|
|
||||||
expression
|
expression
|
||||||
: atom #atomExpression
|
: atom #atomExpression
|
||||||
|
| IDENT (PERIOD IDENT)+ #structFieldAccessExpression
|
||||||
| OPAR expression CPAR #parenthesisExpression
|
| OPAR expression CPAR #parenthesisExpression
|
||||||
| lhs=expression MUL rhs=expression #multiplicationExpression
|
| lhs=expression MUL rhs=expression #multiplicationExpression
|
||||||
| lhs=expression DIV rhs=expression #divisionExpression
|
| lhs=expression DIV rhs=expression #divisionExpression
|
||||||
@@ -77,12 +96,14 @@ expression
|
|||||||
| SUB expression #negateExpression
|
| SUB expression #negateExpression
|
||||||
| NOT expression #NotExpression
|
| NOT expression #NotExpression
|
||||||
| functionCall #functionCallExpression
|
| functionCall #functionCallExpression
|
||||||
|
| CREATE IDENT OPAR arguments CPAR # constructorCallExpression
|
||||||
;
|
;
|
||||||
|
|
||||||
atom
|
atom
|
||||||
: INTEGER_LITERAL #intAtom
|
: INTEGER_LITERAL #intAtom
|
||||||
| BOOLEAN_LITERAL #boolAtom
|
| BOOLEAN_LITERAL #boolAtom
|
||||||
| FLOAT_LITERAL #floatAtom
|
| FLOAT_LITERAL #floatAtom
|
||||||
|
| NULL # nullAtom
|
||||||
| IDENT #variable
|
| IDENT #variable
|
||||||
;
|
;
|
||||||
|
|
||||||
@@ -94,6 +115,7 @@ type
|
|||||||
: INTEGER
|
: INTEGER
|
||||||
| BOOLEAN
|
| BOOLEAN
|
||||||
| FLOAT
|
| FLOAT
|
||||||
|
| IDENT
|
||||||
;
|
;
|
||||||
|
|
||||||
functionCall
|
functionCall
|
||||||
@@ -121,11 +143,15 @@ forLoop
|
|||||||
IF: 'if';
|
IF: 'if';
|
||||||
ELSE: 'else';
|
ELSE: 'else';
|
||||||
FUNC: 'function';
|
FUNC: 'function';
|
||||||
|
STRUCT: 'struct';
|
||||||
RETURN: 'return';
|
RETURN: 'return';
|
||||||
LET: 'let';
|
LET: 'let';
|
||||||
WHILE: 'while';
|
WHILE: 'while';
|
||||||
DO: 'do';
|
DO: 'do';
|
||||||
FOR: 'for';
|
FOR: 'for';
|
||||||
|
CREATE: 'create';
|
||||||
|
DESTROY: 'destroy';
|
||||||
|
NULL: 'naught';
|
||||||
|
|
||||||
PERIOD: '.';
|
PERIOD: '.';
|
||||||
COL: ':';
|
COL: ':';
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import de.hsrm.compiler.Klang.types.Type;
|
|||||||
public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||||
Map<String, VariableDeclaration> vars = new HashMap<>();
|
Map<String, VariableDeclaration> vars = new HashMap<>();
|
||||||
Map<String, FunctionInformation> funcs;
|
Map<String, FunctionInformation> funcs;
|
||||||
|
Map<String, StructDefinition> structs;
|
||||||
Type currentDeclaredReturnType;
|
Type currentDeclaredReturnType;
|
||||||
|
|
||||||
private void checkNumeric(Node lhs, Node rhs, int line, int col) {
|
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.funcs = funcs;
|
||||||
|
this.structs = structs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitProgram(KlangParser.ProgramContext ctx) {
|
public Node visitProgram(KlangParser.ProgramContext ctx) {
|
||||||
FunctionDefinition[] funcs = new FunctionDefinition[ctx.functionDef().size()];
|
FunctionDefinition[] funcs = new FunctionDefinition[ctx.functionDef().size()];
|
||||||
|
|
||||||
for (int i = 0; i < ctx.functionDef().size(); i++) {
|
for (int i = 0; i < ctx.functionDef().size(); i++) {
|
||||||
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
|
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression expression = (Expression) this.visit(ctx.expression());
|
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.type = expression.type;
|
||||||
result.line = ctx.start.getLine();
|
result.line = ctx.start.getLine();
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
result.col = ctx.start.getCharPositionInLine();
|
||||||
@@ -172,6 +176,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
int col = ctx.start.getCharPositionInLine();
|
int col = ctx.start.getCharPositionInLine();
|
||||||
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
|
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) {
|
if (this.vars.get(name) != null) {
|
||||||
String error = "Redeclaration of variable with name \"" + name + "\".";
|
String error = "Redeclaration of variable with name \"" + name + "\".";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
@@ -243,6 +252,93 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
return result;
|
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
|
@Override
|
||||||
public Node visitOrExpression(KlangParser.OrExpressionContext ctx) {
|
public Node visitOrExpression(KlangParser.OrExpressionContext ctx) {
|
||||||
Node lhs = this.visit(ctx.lhs);
|
Node lhs = this.visit(ctx.lhs);
|
||||||
@@ -608,6 +704,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
return n;
|
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
|
@Override
|
||||||
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
||||||
String name = ctx.funcName.getText();
|
String name = ctx.funcName.getText();
|
||||||
@@ -616,6 +721,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
Type returnType = Type.getByName(ctx.returnType.type().getText());
|
Type returnType = Type.getByName(ctx.returnType.type().getText());
|
||||||
this.currentDeclaredReturnType = returnType;
|
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
|
// Create a new set for the variables of the current function
|
||||||
// this will be filled in the variable declaration visitor aswell
|
// this will be filled in the variable declaration visitor aswell
|
||||||
this.vars = new HashMap<>();
|
this.vars = new HashMap<>();
|
||||||
@@ -653,11 +763,19 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
@Override
|
@Override
|
||||||
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
String name = ctx.IDENT().getText();
|
||||||
|
int line = ctx.start.getLine();
|
||||||
|
int col = ctx.start.getCharPositionInLine();
|
||||||
Type type = Type.getByName(ctx.type_annotation().type().getText());
|
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);
|
Parameter result = new Parameter(name);
|
||||||
result.type = type;
|
result.type = type;
|
||||||
result.line = ctx.start.getLine();
|
result.line = line;
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
result.col = col;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,4 +815,62 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
result.col = col;
|
result.col = col;
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
37
src/main/java/de/hsrm/compiler/Klang/GetStructNames.java
Normal file
37
src/main/java/de/hsrm/compiler/Klang/GetStructNames.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
70
src/main/java/de/hsrm/compiler/Klang/GetStructs.java
Normal file
70
src/main/java/de/hsrm/compiler/Klang/GetStructs.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,10 @@ import java.io.*;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.nodes.Node;
|
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.visitors.*;
|
||||||
import de.hsrm.compiler.Klang.helper.*;
|
import de.hsrm.compiler.Klang.helper.*;
|
||||||
|
|
||||||
@@ -85,13 +87,22 @@ public class Klang {
|
|||||||
|
|
||||||
// Context Analysis and DAST generation
|
// Context Analysis and DAST generation
|
||||||
Node root;
|
Node root;
|
||||||
|
HashMap<String, StructDefinition> structs;
|
||||||
try {
|
try {
|
||||||
// Extract information about all functions
|
// Extract information about all functions
|
||||||
var functionDefinitions = new HashMap<String, FunctionInformation>();
|
var functionDefinitions = new HashMap<String, FunctionInformation>();
|
||||||
new GetFunctions(functionDefinitions).visit(tree);
|
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
|
// Create the DAST
|
||||||
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions);
|
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions, structs);
|
||||||
root = ctxAnal.visit(tree);
|
root = ctxAnal.visit(tree);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
System.err.println(e.getMessage());
|
System.err.println(e.getMessage());
|
||||||
@@ -110,7 +121,7 @@ public class Klang {
|
|||||||
if (evaluate) {
|
if (evaluate) {
|
||||||
// Evaluate the sourcecode and print the result
|
// Evaluate the sourcecode and print the result
|
||||||
System.out.println("\nEvaluating the source code:");
|
System.out.println("\nEvaluating the source code:");
|
||||||
EvalVisitor evalVisitor = new EvalVisitor();
|
EvalVisitor evalVisitor = new EvalVisitor(structs);
|
||||||
Value result = root.welcome(evalVisitor);
|
Value result = root.welcome(evalVisitor);
|
||||||
generateOutput(out, "Result was: " + result.asObject().toString());
|
generateOutput(out, "Result was: " + result.asObject().toString());
|
||||||
return;
|
return;
|
||||||
@@ -120,7 +131,7 @@ public class Klang {
|
|||||||
// System.out.println("\nPrinting the assembler code");
|
// System.out.println("\nPrinting the assembler code");
|
||||||
StringWriter wAsm = new StringWriter();
|
StringWriter wAsm = new StringWriter();
|
||||||
GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm);
|
GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm);
|
||||||
GenASM genasm = new GenASM(exAsm, mainName);
|
GenASM genasm = new GenASM(exAsm, mainName, structs);
|
||||||
root.welcome(genasm);
|
root.welcome(genasm);
|
||||||
generateOutput(out, wAsm.toString());
|
generateOutput(out, wAsm.toString());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.hsrm.compiler.Klang;
|
package de.hsrm.compiler.Klang;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.types.Type;
|
import de.hsrm.compiler.Klang.types.Type;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Value {
|
public class Value {
|
||||||
public Type type;
|
public Type type;
|
||||||
@@ -30,4 +31,9 @@ public class Value {
|
|||||||
public boolean asBoolean() {
|
public boolean asBoolean() {
|
||||||
return (boolean) this.value;
|
return (boolean) this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Map<String, Value> asStruct() {
|
||||||
|
return (Map<String, Value>) this.value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,66 @@
|
|||||||
package de.hsrm.compiler.Klang.helper;
|
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 class Helper {
|
||||||
public static String getErrorPrefix(int line, int col) {
|
public static String getErrorPrefix(int line, int col) {
|
||||||
return "Error in line " + line + ":" + 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,19 @@
|
|||||||
package de.hsrm.compiler.Klang.nodes;
|
package de.hsrm.compiler.Klang.nodes;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.nodes.expressions.Expression;
|
import de.hsrm.compiler.Klang.nodes.expressions.Expression;
|
||||||
import de.hsrm.compiler.Klang.visitors.Visitor;
|
import de.hsrm.compiler.Klang.visitors.Visitor;
|
||||||
|
|
||||||
public class Program extends Node {
|
public class Program extends Node {
|
||||||
|
|
||||||
public FunctionDefinition[] funcs;
|
public FunctionDefinition[] funcs;
|
||||||
|
public Map<String, StructDefinition> structs;
|
||||||
public Expression expression;
|
public Expression expression;
|
||||||
|
|
||||||
public Program(FunctionDefinition[] funcs, Expression expression) {
|
public Program(FunctionDefinition[] funcs, Map<String, StructDefinition> structs, Expression expression) {
|
||||||
this.funcs = funcs;
|
this.funcs = funcs;
|
||||||
|
this.structs = structs;
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17,4 +21,4 @@ public class Program extends Node {
|
|||||||
public <R> R welcome(Visitor<R> v) {
|
public <R> R welcome(Visitor<R> v) {
|
||||||
return v.visit(this);
|
return v.visit(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/de/hsrm/compiler/Klang/nodes/StructField.java
Normal file
17
src/main/java/de/hsrm/compiler/Klang/nodes/StructField.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
src/main/java/de/hsrm/compiler/Klang/types/NullType.java
Normal file
41
src/main/java/de/hsrm/compiler/Klang/types/NullType.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
54
src/main/java/de/hsrm/compiler/Klang/types/StructType.java
Normal file
54
src/main/java/de/hsrm/compiler/Klang/types/StructType.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,12 +16,17 @@ public abstract class Type {
|
|||||||
return FloatType.getType();
|
return FloatType.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static NullType getNullType() {
|
||||||
|
return NullType.getType();
|
||||||
|
}
|
||||||
|
|
||||||
public static Type getByName(String name) {
|
public static Type getByName(String name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "bool": return getBooleanType();
|
case "bool": return getBooleanType();
|
||||||
case "int": return getIntegerType();
|
case "int": return getIntegerType();
|
||||||
case "float": return getFloatType();
|
case "float": return getFloatType();
|
||||||
default: throw new RuntimeException("Unknown type " + name);
|
case "null": return getNullType();
|
||||||
|
default: return new StructType(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import de.hsrm.compiler.Klang.nodes.Block;
|
|||||||
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
|
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
|
||||||
import de.hsrm.compiler.Klang.nodes.Parameter;
|
import de.hsrm.compiler.Klang.nodes.Parameter;
|
||||||
import de.hsrm.compiler.Klang.nodes.Program;
|
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.expressions.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
|
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> {
|
public class EvalVisitor implements Visitor<Value> {
|
||||||
|
|
||||||
Map<String, FunctionDefinition> funcs = new HashMap<>();
|
Map<String, FunctionDefinition> funcs = new HashMap<>();
|
||||||
|
Map<String, StructDefinition> structs;
|
||||||
Map<String, Value> env = new HashMap<>();
|
Map<String, Value> env = new HashMap<>();
|
||||||
|
Map<String, Map<String, Value>> heap = new HashMap<>();
|
||||||
|
|
||||||
|
public EvalVisitor(Map<String, StructDefinition> structs) {
|
||||||
|
this.structs = structs;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(IntegerExpression e) {
|
public Value visit(IntegerExpression e) {
|
||||||
@@ -463,4 +471,70 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
return null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.helper.Helper;
|
||||||
import de.hsrm.compiler.Klang.nodes.*;
|
import de.hsrm.compiler.Klang.nodes.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.expressions.*;
|
import de.hsrm.compiler.Klang.nodes.expressions.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
||||||
@@ -107,6 +108,7 @@ public class GenASM implements Visitor<Void> {
|
|||||||
private FloatWriter fw = new FloatWriter();
|
private FloatWriter fw = new FloatWriter();
|
||||||
private String mainName;
|
private String mainName;
|
||||||
Map<String, Integer> env = new HashMap<>();
|
Map<String, Integer> env = new HashMap<>();
|
||||||
|
Map<String, StructDefinition> structs;
|
||||||
Set<String> vars;
|
Set<String> vars;
|
||||||
String[] registers = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" };
|
String[] registers = { "%rdi", "%rsi", "%rdx", "%rcx", "%r8", "%r9" };
|
||||||
String[] floatRegisters = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" };
|
String[] floatRegisters = { "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" };
|
||||||
@@ -147,14 +149,15 @@ public class GenASM implements Visitor<Void> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public GenASM(ExWriter ex, String mainName) {
|
|
||||||
|
public GenASM(ExWriter ex, String mainName, Map<String, StructDefinition> structs) {
|
||||||
this.ex = ex;
|
this.ex = ex;
|
||||||
this.mainName = mainName;
|
this.mainName = mainName;
|
||||||
|
this.structs = structs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenASM(ExWriter ex) {
|
public GenASM(ExWriter ex, Map<String, StructDefinition> structs) {
|
||||||
this.ex = ex;
|
this(ex, "main", structs);
|
||||||
this.mainName = "main";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -790,4 +793,115 @@ public class GenASM implements Visitor<Void> {
|
|||||||
return null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -234,4 +234,38 @@ class GetVars implements Visitor<Void> {
|
|||||||
return null;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +61,13 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
ex.nl();
|
ex.nl();
|
||||||
ex.nl();
|
ex.nl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var structDef: e.structs.values()) {
|
||||||
|
structDef.welcome(this);
|
||||||
|
ex.nl();
|
||||||
|
ex.nl();
|
||||||
|
}
|
||||||
|
|
||||||
e.expression.welcome(this);
|
e.expression.welcome(this);
|
||||||
ex.write(";");
|
ex.write(";");
|
||||||
return null;
|
return null;
|
||||||
@@ -370,4 +377,76 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
return null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,8 @@ import de.hsrm.compiler.Klang.nodes.Block;
|
|||||||
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
|
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
|
||||||
import de.hsrm.compiler.Klang.nodes.Parameter;
|
import de.hsrm.compiler.Klang.nodes.Parameter;
|
||||||
import de.hsrm.compiler.Klang.nodes.Program;
|
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.expressions.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.loops.*;
|
import de.hsrm.compiler.Klang.nodes.loops.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.statements.*;
|
import de.hsrm.compiler.Klang.nodes.statements.*;
|
||||||
@@ -11,7 +13,7 @@ import de.hsrm.compiler.Klang.nodes.statements.*;
|
|||||||
public interface Visitor<R> {
|
public interface Visitor<R> {
|
||||||
R visit(OrExpression e);
|
R visit(OrExpression e);
|
||||||
R visit(AndExpression e);
|
R visit(AndExpression e);
|
||||||
R visit (NotExpression e);
|
R visit(NotExpression e);
|
||||||
R visit(IntegerExpression e);
|
R visit(IntegerExpression e);
|
||||||
R visit(FloatExpression e);
|
R visit(FloatExpression e);
|
||||||
R visit(BooleanExpression e);
|
R visit(BooleanExpression e);
|
||||||
@@ -40,4 +42,11 @@ public interface Visitor<R> {
|
|||||||
R visit(FunctionCall e);
|
R visit(FunctionCall e);
|
||||||
R visit(Program e);
|
R visit(Program e);
|
||||||
R visit(Parameter 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);
|
||||||
}
|
}
|
||||||
@@ -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);
|
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) {
|
void succPrefixOne(char* name, long x, long expected, long result) {
|
||||||
incSuccess();
|
incSuccess();
|
||||||
printf("\033[0;32mSUCCESS:\t%s(%ld)\tGOT: %ld\tExpected: %ld\033[0;0m\n", name, x, result, expected);
|
printf("\033[0;32mSUCCESS:\t%s(%ld)\tGOT: %ld\tExpected: %ld\033[0;0m\n", name, x, result, expected);
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ void err(char* name, long expected, long result);
|
|||||||
void succ_f(char* name, double expected, double result);
|
void succ_f(char* name, double expected, double result);
|
||||||
void err_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 succPrefixOne(char* name, long x, long expected, long result);
|
||||||
void errPrefixOne(char* name, long x, long expected, long result);
|
void errPrefixOne(char* name, long x, long expected, long result);
|
||||||
|
|
||||||
|
|||||||
127
src/test/struct/struct.c
Normal file
127
src/test/struct/struct.c
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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();
|
||||||
|
}
|
||||||
31
src/test/struct/struct.h
Normal file
31
src/test/struct/struct.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
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);
|
||||||
@@ -28,6 +28,9 @@ int main(){
|
|||||||
// Tests for while loop
|
// Tests for while loop
|
||||||
runLoopTests();
|
runLoopTests();
|
||||||
|
|
||||||
|
// Tests for structs
|
||||||
|
runStructTests();
|
||||||
|
|
||||||
printf("\n%d tests in total\n", successes + failures);
|
printf("\n%d tests in total\n", successes + failures);
|
||||||
|
|
||||||
if (failures > 0) {
|
if (failures > 0) {
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ void runFunctionCallTests();
|
|||||||
void runRecursiveTests();
|
void runRecursiveTests();
|
||||||
void runComparisonTests();
|
void runComparisonTests();
|
||||||
void runLoopTests();
|
void runLoopTests();
|
||||||
void runMathTests();
|
void runMathTests();
|
||||||
|
void runStructTests();
|
||||||
@@ -415,4 +415,68 @@ function mixdiv(x: float, y: int): float {
|
|||||||
return x / y;
|
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);
|
add(1, 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user