Compare commits

...

4 Commits

Author SHA1 Message Date
4219f93021 Merge pull request 'Add support for void types' (#2) from 31-void-type into master
Reviewed-on: #2
2023-03-23 14:28:24 +01:00
7965c89a60 VoidType: Add tests and fix some bugs 2023-03-23 13:10:20 +01:00
26eff47057 Fix typos. 2023-03-23 13:09:52 +01:00
Marvin Kaiser
53976615e1 31: Add void type 2023-03-23 12:47:22 +01:00
17 changed files with 238 additions and 38 deletions

View File

@@ -33,7 +33,7 @@ parameter
; ;
braced_block braced_block
: OBRK statement+ CBRK : OBRK (statement | functionCall SCOL)+ CBRK
; ;
@@ -73,7 +73,7 @@ field_assignment
; ;
return_statement return_statement
: RETURN expression SCOL : RETURN expression? SCOL
; ;
destroy_statement destroy_statement
@@ -120,6 +120,7 @@ type
| BOOLEAN | BOOLEAN
| FLOAT | FLOAT
| IDENT | IDENT
| VOID
; ;
functionCall functionCall
@@ -186,6 +187,7 @@ DIV: '/';
BOOLEAN: 'bool'; BOOLEAN: 'bool';
INTEGER: 'int'; INTEGER: 'int';
FLOAT: 'float'; FLOAT: 'float';
VOID: 'void';
INTEGER_LITERAL INTEGER_LITERAL
: [0-9]+ : [0-9]+

View File

@@ -68,13 +68,13 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) { public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
int actualStatementCount = 0; var actualStatementCount = 0;
int declaredStatementCount = ctx.statement().size(); var declaredStatementCount = ctx.statement().size();
boolean hasReturn = false; var hasReturn = false;
Statement[] statements = new Statement[declaredStatementCount]; var statements = new Statement[declaredStatementCount];
for (int i = 0; i < declaredStatementCount; i++) { for (int i = 0; i < declaredStatementCount; i++) {
Node currentStatement = this.visit(ctx.statement(i)); var currentStatement = visit(ctx.statement(i));
statements[i] = (Statement) currentStatement; statements[i] = (Statement) currentStatement;
actualStatementCount += 1; actualStatementCount += 1;
@@ -83,7 +83,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) { if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
// check whether the type matches // check whether the type matches
try { try {
this.currentDeclaredReturnType.combine(currentStatement.type); currentDeclaredReturnType.combine(currentStatement.type);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException( throw new RuntimeException(
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage()); Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
@@ -99,14 +99,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
// If there was unreachable code in this block, // If there was unreachable code in this block,
// create a shorter statements array and copy the statements to there // create a shorter statements array and copy the statements to there
if (actualStatementCount < declaredStatementCount) { if (actualStatementCount < declaredStatementCount) {
Statement[] newStatements = new Statement[actualStatementCount]; var newStatements = new Statement[actualStatementCount];
System.arraycopy(statements, 0, newStatements, 0, actualStatementCount); System.arraycopy(statements, 0, newStatements, 0, actualStatementCount);
statements = newStatements; statements = newStatements;
} }
// if this block contains at least one statement that guarantees a return value, // if this block contains at least one statement that guarantees a return value,
// we indicate that this block guarantees a return value by setting result.type // we indicate that this block guarantees a return value by setting result.type
Block result = new Block(statements); var result = new Block(statements);
if (hasReturn) { if (hasReturn) {
result.type = this.currentDeclaredReturnType; result.type = this.currentDeclaredReturnType;
} }
@@ -186,6 +187,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
var line = ctx.start.getLine(); var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
if (declaredType.equals(Type.getVoidType())) {
var error = "Type " + declaredType.getName() + " can not be used to declare variables.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) { if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
var error = "Type " + declaredType.getName() + " not defined."; var error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
@@ -266,19 +272,31 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
@Override @Override
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) { public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
Expression expression = (Expression) this.visit(ctx.expression()); if (currentDeclaredReturnType.equals(Type.getVoidType())) {
ReturnStatement result = new ReturnStatement(expression); var result = new ReturnStatement();
result.type = Type.getVoidType();
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
if (ctx.expression() != null) {
var error = "Cannot return an expression from a void function.";
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
}
return result;
}
var expression = (Expression) visit(ctx.expression());
// Check if this expression is a tail recursion // Check if this expression is a tail recursion
if (expression instanceof FunctionCall funCall) { if (expression instanceof FunctionCall funCall) {
if (funCall.name.equals(this.currentFunctionDefinitionName)) { if (funCall.name.equals(currentFunctionDefinitionName)) {
// Flag this function call // Flag this function call
funCall.isTailRecursive = true; funCall.isTailRecursive = true;
} }
} }
result.type = expression.type; var result = new ReturnStatement(expression);
result.line = ctx.start.getLine(); result.line = ctx.start.getLine();
result.type = expression.type;
result.col = ctx.start.getCharPositionInLine(); result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -749,6 +767,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
var line = ctx.start.getLine(); var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine(); var col = ctx.start.getCharPositionInLine();
if (structFieldType.equals(Type.getVoidType())) {
var error = "Type void can not be used as a struct field type.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (!structFieldType.isPrimitiveType() && !structDefs.containsKey(structFieldType.getName()) && !enumDefs.containsKey(structFieldType.getName())) { if (!structFieldType.isPrimitiveType() && !structDefs.containsKey(structFieldType.getName()) && !enumDefs.containsKey(structFieldType.getName())) {
var error = "Type " + structFieldType.getName() + " not defined."; var error = "Type " + structFieldType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
@@ -779,7 +802,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
currentDeclaredReturnType = returnType; currentDeclaredReturnType = returnType;
currentFunctionDefinitionName = name; currentFunctionDefinitionName = name;
if (!returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName())) { var typeNotDefined = !returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName());
if (!returnType.equals(Type.getVoidType()) && typeNotDefined) {
var line = ctx.returnType.start.getLine(); var line = ctx.returnType.start.getLine();
var col = ctx.returnType.start.getCharPositionInLine(); var col = ctx.returnType.start.getCharPositionInLine();
var error = "Type " + returnType.getName() + " not defined."; var error = "Type " + returnType.getName() + " not defined.";
@@ -807,7 +831,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
// Visit the block, make sure that a return value is guaranteed // Visit the block, make sure that a return value is guaranteed
var block = visit(ctx.braced_block()); var block = visit(ctx.braced_block());
if (block.type == null) { if (block.type == null && !returnType.equals(Type.getVoidType())) {
var line = ctx.braced_block().start.getLine(); var line = ctx.braced_block().start.getLine();
var col = ctx.braced_block().start.getCharPositionInLine(); var col = ctx.braced_block().start.getCharPositionInLine();
var error = "Function " + name + " has to return something of type " + returnType.getName() + "."; var error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
@@ -826,26 +850,29 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitParameter(KlangParser.ParameterContext ctx) { public Node visitParameter(KlangParser.ParameterContext ctx) {
var parameterName = ctx.IDENT().getText(); var parameterName = ctx.IDENT().getText();
var parameterType = Type.getByName(ctx.type_annotation().type().getText()); var parameterType = Type.getByName(ctx.type_annotation().type().getText());
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
if (parameterType.equals(Type.getVoidType())) {
var error = "Type " + parameterType.getName() + " cannot be used to declare a parameter.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (structDefs.containsKey(parameterName)) { if (structDefs.containsKey(parameterName)) {
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var error = "Parameter name " + parameterName + " duplicates a struct of the same name."; var error = "Parameter name " + parameterName + " duplicates a struct of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
if (enumDefs.containsKey(parameterName)) { if (enumDefs.containsKey(parameterName)) {
var line = ctx.start.getLine();
var col = ctx.start.getCharPositionInLine();
var error = "Parameter name " + parameterName + " duplicates an enum of the same name."; var error = "Parameter name " + parameterName + " duplicates an enum of the same name.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) { if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) {
var line = ctx.type_annotation().start.getLine(); var typeLine = ctx.type_annotation().start.getLine();
var col = ctx.type_annotation().start.getCharPositionInLine(); var typeCol = ctx.type_annotation().start.getCharPositionInLine();
var error = "Type " + parameterType.getName() + " not defined."; var error = "Type " + parameterType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); throw new RuntimeException(Helper.getErrorPrefix(typeLine, typeCol) + error);
} }
var parameter = new Parameter(parameterName); var parameter = new Parameter(parameterName);
@@ -983,4 +1010,4 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
} }
return path; return path;
} }
} }

View File

@@ -1,9 +1,11 @@
package de.hsrm.compiler.Klang; package de.hsrm.compiler.Klang;
import de.hsrm.compiler.Klang.helper.*;
import de.hsrm.compiler.Klang.nodes.EnumDefinition; import de.hsrm.compiler.Klang.nodes.EnumDefinition;
import de.hsrm.compiler.Klang.nodes.FunctionDefinition; import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
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.nodes.StructDefinition;
import de.hsrm.compiler.Klang.types.Type;
import de.hsrm.compiler.Klang.visitors.EvalVisitor; import de.hsrm.compiler.Klang.visitors.EvalVisitor;
import de.hsrm.compiler.Klang.visitors.GenASM; import de.hsrm.compiler.Klang.visitors.GenASM;
import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor; import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor;
@@ -120,7 +122,11 @@ public class Klang {
System.out.println("\nEvaluating the source code:"); System.out.println("\nEvaluating the source code:");
EvalVisitor evalVisitor = new EvalVisitor(structDefs); EvalVisitor evalVisitor = new EvalVisitor(structDefs);
Value result = root.welcome(evalVisitor); Value result = root.welcome(evalVisitor);
generateOutput(out, "Result was: " + result.asObject().toString()); if (result.type.equals(Type.getVoidType())) {
generateOutput(out, "Result was void");
} else {
generateOutput(out, "Result was: " + result.asObject().toString());
}
return; return;
} }

View File

@@ -11,6 +11,10 @@ public class ReturnStatement extends Statement {
this.expression = expression; this.expression = expression;
} }
public ReturnStatement() {
this.expression = null;
}
@Override @Override
public <R> R welcome(Visitor<R> v) { public <R> R welcome(Visitor<R> v) {
return v.visit(this); return v.visit(this);

View File

@@ -32,7 +32,7 @@ public class BooleanType extends PrimitiveType {
} }
// Every remaining type will throw a RuntimeException // Every remaining type will throw a RuntimeException
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
} }
@Override @Override

View File

@@ -36,7 +36,7 @@ public class FloatType extends NumericType {
} }
// Every remaining type will throw a RuntimeException // Every remaining type will throw a RuntimeException
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
} }
@Override @Override

View File

@@ -36,7 +36,7 @@ public class IntegerType extends NumericType {
} }
// Every remaining type will throw a RuntimeException // Every remaining type will throw a RuntimeException
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
} }
@Override @Override

View File

@@ -23,7 +23,7 @@ public class NullType extends Type {
public Type combine(Type that) { public Type combine(Type that) {
// You can not combine null with a primitive type // You can not combine null with a primitive type
if (that.isPrimitiveType()) { if (that.isPrimitiveType()) {
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName()); throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
} }
// Everything else combines with null to the type it was before // Everything else combines with null to the type it was before

View File

@@ -27,12 +27,17 @@ public abstract class Type {
return NullType.getType(); return NullType.getType();
} }
public static VoidType getVoidType() {
return VoidType.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();
case "null": return getNullType(); case "null": return getNullType();
case "void": return getVoidType();
default: return new NamedType(name); default: return new NamedType(name);
} }
} }
@@ -42,4 +47,4 @@ public abstract class Type {
public abstract boolean valuesEqual(Value a, Value b); public abstract boolean valuesEqual(Value a, Value b);
public abstract boolean isPrimitiveType(); public abstract boolean isPrimitiveType();
public abstract boolean isNumericType(); public abstract boolean isNumericType();
} }

View File

@@ -0,0 +1,45 @@
package de.hsrm.compiler.Klang.types;
import de.hsrm.compiler.Klang.Value;
public class VoidType extends Type {
private static VoidType instance;
public static VoidType getType() {
if (instance != null) {
return instance;
}
instance = new VoidType();
return instance;
}
@Override
public String getName() {
return "void";
}
@Override
public Type combine(Type that) {
if (that.equals(this)) {
return this;
}
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
}
@Override
public boolean valuesEqual(Value a, Value b) {
throw new RuntimeException("Can not compare void types.");
}
@Override
public boolean isPrimitiveType() {
return false;
}
@Override
public boolean isNumericType() {
return false;
}
}

View File

@@ -396,6 +396,9 @@ public class EvalVisitor implements Visitor<Value> {
@Override @Override
public Value visit(ReturnStatement e) { public Value visit(ReturnStatement e) {
if (e.expression == null) {
return new Value(null, Type.getVoidType());
}
return e.expression.welcome(this); return e.expression.welcome(this);
} }

View File

@@ -525,7 +525,9 @@ public class GenASM implements Visitor<Void> {
@Override @Override
public Void visit(ReturnStatement e) { public Void visit(ReturnStatement e) {
e.expression.welcome(this); if (e.expression != null) {
e.expression.welcome(this);
}
// The ReturnStatement visitor is kindly removing the // The ReturnStatement visitor is kindly removing the
// stack space that was allocated by the FunctionDefinition // stack space that was allocated by the FunctionDefinition
@@ -1020,4 +1022,4 @@ public class GenASM implements Visitor<Void> {
return false; return false;
} }
} }
} }

View File

@@ -194,7 +194,9 @@ class GetVars implements Visitor<Void> {
@Override @Override
public Void visit(ReturnStatement e) { public Void visit(ReturnStatement e) {
e.expression.welcome(this); if (e.expression != null) {
e.expression.welcome(this);
}
return null; return null;
} }

View File

@@ -310,8 +310,11 @@ public class PrettyPrintVisitor implements Visitor<Void> {
@Override @Override
public Void visit(ReturnStatement e) { public Void visit(ReturnStatement e) {
ex.write("return "); ex.write("return");
e.expression.welcome(this); if (e.expression != null) {
ex.write(" ");
e.expression.welcome(this);
}
ex.write(";"); ex.write(";");
return null; return null;
} }

View File

@@ -21,7 +21,7 @@ public class ConstructorCallTest {
} }
@Test @Test
void numConstructorParameterMissmatch() { void numConstructorParametermismatch() {
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();"); ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);
@@ -41,6 +41,6 @@ public class ConstructorCallTest {
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:63 argument 0 Type missmatch: cannot combine bool and int", e.getMessage()); assertEquals("Error in line 1:63 argument 0 Type mismatch: cannot combine bool and int", e.getMessage());
} }
} }

View File

@@ -33,7 +33,7 @@ public class FunctionCallTest {
} }
@Test @Test
void parameterTypeMissmatch() { void parameterTypeMismatch() {
ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);"); ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);");
var funcs = Helper.getFuncs(tree); var funcs = Helper.getFuncs(tree);
var structs = Helper.getStructs(tree); var structs = Helper.getStructs(tree);

101
src/test/java/VoidTest.java Normal file
View File

@@ -0,0 +1,101 @@
import de.hsrm.compiler.Klang.ContextAnalysis;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class VoidTest {
@Test
void shouldNotThrowIfVoidIsUsedAsReturnType() {
// given
var tree = Helper.prepareParser("function foo(): void { let a: int = 0; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldNotThrowIfVoidFunctionIsCalled() {
// given
var tree = Helper.prepareParser("""
function foo(): void {
let a: int = 0;
}
function bar(): int {
foo();
return 1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
assertDoesNotThrow(() -> ctxAnal.visit(tree));
}
@Test
void shouldThrowIfVoidFunctionIsAssignedToVariable() {
// given
var tree = Helper.prepareParser("""
function foo(): void {
let a: int = 0;
}
function bar(): int {
let a: int = foo();
return 1;
}
bar();
""");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 5:4 Type mismatch: cannot combine int and void", e.getMessage());
}
@Test
void shouldThrowIfValueIsReturnedFromVoidFunction() {
// given
var tree = Helper.prepareParser("function foo(): void { return 1; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:23 Cannot return an expression from a void function.", e.getMessage());
}
@Test
void shouldThrowIfVoidIsUsedAsParameterType() {
// given
var tree = Helper.prepareParser("function foo(a: void): int { return 1; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:13 Type void cannot be used to declare a parameter.", e.getMessage());
}
@Test
void shouldThrowIfVoidIsUsedAsVariableType() {
// given
var tree = Helper.prepareParser("function foo(): int { let a: void; return 1; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:22 Type void can not be used to declare variables.", e.getMessage());
}
@Test
void shouldThrowIfVoidIsUsedAsStructFieldType() {
// given
var tree = Helper.prepareParser("struct bar { a: void; } function foo(): int { return 1; } foo();");
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
// when / then
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
assertEquals("Error in line 1:13 Type void can not be used as a struct field type.", e.getMessage());
}
}