From 7af815042bd62463ce33fa51ab066b97ea292589 Mon Sep 17 00:00:00 2001 From: nitrix Date: Wed, 15 Mar 2023 15:55:41 +0100 Subject: [PATCH] WIP: Add enum support --- .../antlr4/de/hsrm/compiler/Klang/Klang.g4 | 7 +- .../hsrm/compiler/Klang/ContextAnalysis.java | 40 ++-- .../hsrm/compiler/Klang/GetDefinitions.java | 218 ++++++++++++++++++ .../de/hsrm/compiler/Klang/GetFunctions.java | 54 ----- .../hsrm/compiler/Klang/GetStructNames.java | 37 --- .../de/hsrm/compiler/Klang/GetStructs.java | 70 ------ .../java/de/hsrm/compiler/Klang/Klang.java | 50 ++-- .../compiler/Klang/nodes/EnumDefinition.java | 19 ++ .../de/hsrm/compiler/Klang/nodes/Program.java | 9 +- .../hsrm/compiler/Klang/types/EnumType.java | 48 ++++ .../compiler/Klang/visitors/EvalVisitor.java | 12 +- .../hsrm/compiler/Klang/visitors/GenASM.java | 13 +- .../hsrm/compiler/Klang/visitors/GetVars.java | 5 + .../Klang/visitors/PrettyPrintVisitor.java | 22 ++ .../hsrm/compiler/Klang/visitors/Visitor.java | 8 +- src/test/java/AndTest.java | 3 +- src/test/java/ConstructorCallTest.java | 9 +- src/test/java/DestroyStatementTest.java | 3 +- src/test/java/FieldAssignmentTest.java | 6 +- src/test/java/FunctionCallTest.java | 9 +- src/test/java/FunctionDefinitionTest.java | 6 +- src/test/java/Helper.java | 16 +- src/test/java/ModuloTest.java | 3 +- src/test/java/OrTest.java | 3 +- src/test/java/StructFieldAccessTest.java | 6 +- src/test/java/VariableAssignmentTest.java | 3 +- src/test/java/VariableDeclarationTest.java | 6 +- src/test/java/VariableTest.java | 6 +- 28 files changed, 436 insertions(+), 255 deletions(-) create mode 100644 src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java delete mode 100644 src/main/java/de/hsrm/compiler/Klang/GetFunctions.java delete mode 100644 src/main/java/de/hsrm/compiler/Klang/GetStructNames.java delete mode 100644 src/main/java/de/hsrm/compiler/Klang/GetStructs.java create mode 100644 src/main/java/de/hsrm/compiler/Klang/nodes/EnumDefinition.java create mode 100644 src/main/java/de/hsrm/compiler/Klang/types/EnumType.java diff --git a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 index a1a176a..a6fb2e0 100644 --- a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 +++ b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 @@ -5,7 +5,11 @@ parse ; program - : (functionDef | structDef)* expression SCOL + : (functionDef | structDef | enumDef)* expression SCOL + ; + +enumDef + : ENUM enumName=IDENT OBRK (IDENT (COMMA IDENT)*)+ CBRK ; structDef @@ -143,6 +147,7 @@ forLoop IF: 'if'; ELSE: 'else'; FUNC: 'function'; +ENUM: 'enum'; STRUCT: 'struct'; RETURN: 'return'; LET: 'let'; diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index ee49476..53dd7fd 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -15,8 +15,9 @@ import de.hsrm.compiler.Klang.types.Type; public class ContextAnalysis extends KlangBaseVisitor { Map vars = new HashMap<>(); - Map funcs; - Map structs; + Map functionDefs; + Map structDefs; + Map enumDefs; Type currentDeclaredReturnType; String currentFunctionDefinitionName; @@ -27,9 +28,14 @@ public class ContextAnalysis extends KlangBaseVisitor { } } - public ContextAnalysis(Map funcs, Map structs) { - this.funcs = funcs; - this.structs = structs; + public ContextAnalysis( + Map functionDefs, + Map structDefs, + Map enumDefs + ) { + this.functionDefs = functionDefs; + this.structDefs = structDefs; + this.enumDefs = enumDefs; } @Override @@ -41,7 +47,7 @@ public class ContextAnalysis extends KlangBaseVisitor { } Expression expression = (Expression) this.visit(ctx.expression()); - Program result = new Program(funcs, this.structs, expression); + Program result = new Program(funcs, structDefs, enumDefs, expression); result.type = expression.type; result.line = ctx.start.getLine(); result.col = ctx.start.getCharPositionInLine(); @@ -67,7 +73,7 @@ public class ContextAnalysis extends KlangBaseVisitor { statements[i] = (Statement) currentStatement; actualStatementCount += 1; - // We use the existance of a type to indicate that this statement returns + // We use the existence of a type to indicate that this statement returns // something for which the VariableDeclaration is an exception if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) { // check whether the type matches @@ -89,9 +95,7 @@ public class ContextAnalysis extends KlangBaseVisitor { // create a shorter statements array and copy the statements to there if (actualStatementCount < declaredStatementCount) { Statement[] newStatements = new Statement[actualStatementCount]; - for (int i = 0; i < actualStatementCount; i++) { - newStatements[i] = statements[i]; - } + System.arraycopy(statements, 0, newStatements, 0, actualStatementCount); statements = newStatements; } @@ -177,7 +181,7 @@ 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) { + if (!declaredType.isPrimitiveType() && this.structDefs.get(declaredType.getName()) == null) { String error = "Type " + declaredType.getName() + " not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } @@ -291,7 +295,7 @@ public class ContextAnalysis extends KlangBaseVisitor { String structName = variableDef.type.getName(); Type fieldType; try { - fieldType = Helper.drillType(this.structs, structName, path, 0); + fieldType = Helper.drillType(this.structDefs, structName, path, 0); } catch (Exception e) { throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage()); } @@ -338,7 +342,7 @@ public class ContextAnalysis extends KlangBaseVisitor { String structName = variableDef.type.getName(); Type resultType; try { - resultType = Helper.drillType(this.structs, structName, path, 0); + resultType = Helper.drillType(this.structDefs, structName, path, 0); } catch (Exception e) { throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage()); } @@ -708,7 +712,7 @@ public class ContextAnalysis extends KlangBaseVisitor { @Override public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) { - Node n = new BooleanExpression(ctx.getText().equals("true") ? true : false); + Node n = new BooleanExpression(ctx.getText().equals("true")); n.type = Type.getBooleanType(); n.line = ctx.start.getLine(); n.col = ctx.start.getCharPositionInLine(); @@ -733,7 +737,7 @@ public class ContextAnalysis extends KlangBaseVisitor { this.currentDeclaredReturnType = returnType; this.currentFunctionDefinitionName = name; - if (!returnType.isPrimitiveType() && this.structs.get(returnType.getName()) == null) { + if (!returnType.isPrimitiveType() && this.structDefs.get(returnType.getName()) == null) { String error = "Type " + returnType.getName() + " not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } @@ -779,7 +783,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); Type type = Type.getByName(ctx.type_annotation().type().getText()); - if (!type.isPrimitiveType() && this.structs.get(type.getName()) == null) { + if (!type.isPrimitiveType() && this.structDefs.get(type.getName()) == null) { String error = "Type " + type.getName() + " not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } @@ -797,7 +801,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int line = ctx.start.getLine(); int col = ctx.start.getCharPositionInLine(); - FunctionInformation func = this.funcs.get(name); + FunctionInformation func = this.functionDefs.get(name); if (func == null) { String error = "Function with name \"" + name + "\" not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); @@ -835,7 +839,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); // Get the corresponding struct definition - var struct = this.structs.get(name); + var struct = this.structDefs.get(name); if (struct == null) { String error = "Struct with name \"" + name + "\" not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); diff --git a/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java b/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java new file mode 100644 index 0000000..405cf97 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/GetDefinitions.java @@ -0,0 +1,218 @@ +package de.hsrm.compiler.Klang; + +import de.hsrm.compiler.Klang.helper.FunctionInformation; +import de.hsrm.compiler.Klang.helper.Helper; +import de.hsrm.compiler.Klang.nodes.*; +import de.hsrm.compiler.Klang.types.EnumType; +import de.hsrm.compiler.Klang.types.StructType; +import de.hsrm.compiler.Klang.types.Type; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +public class GetDefinitions extends KlangBaseVisitor { + private final Map functionDefs; + private final Map structDefs; + private final Map enumDefs; + + private Set functionNames; + private Set structNames; + private Set enumNames; + + public GetDefinitions( + Map functionDefs, + Map structDefs, + Map enumDefs + ) { + this.functionDefs = functionDefs; + this.structDefs = structDefs; + this.enumDefs = enumDefs; + } + + private Set collectFunctionNames(KlangParser.ProgramContext ctx) { + var result = new HashSet(); + for (int i = 0; i < ctx.functionDef().size(); i++) { + var currentFunctionDef = ctx.functionDef(i); + var funcName = currentFunctionDef.funcName.getText(); + if (result.contains(funcName)) { + var line = currentFunctionDef.funcName.getLine(); + var col = currentFunctionDef.funcName.getCharPositionInLine(); + var error = "Function " + funcName + " defined multiple times."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + result.add(funcName); + } + return result; + } + + private Set collectStructNames(KlangParser.ProgramContext ctx) { + var result = new HashSet(); + for (int i = 0; i < ctx.structDef().size(); i++) { + var currentStructDef = ctx.structDef(i); + var structName = currentStructDef.structName.getText(); + if (result.contains(structName)) { + var line = currentStructDef.structName.getLine(); + var col = currentStructDef.structName.getCharPositionInLine(); + var error = "Struct " + structName + " defined multiple times."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + result.add(structName); + } + return result; + } + + private Set collectEnumNames(KlangParser.ProgramContext ctx) { + var result = new HashSet(); + for (int i = 0; i < ctx.enumDef().size(); i++) { + var currentEnumDef = ctx.enumDef(i); + var enumName = currentEnumDef.enumName.getText(); + if (result.contains(enumName)) { + var line = currentEnumDef.enumName.getLine(); + var col = currentEnumDef.enumName.getCharPositionInLine(); + var error = "Enum " + enumName + " defined multiple times."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + result.add(enumName); + } + return result; + } + + @Override + public Node visitProgram(KlangParser.ProgramContext ctx) { + functionNames = collectFunctionNames(ctx); + structNames = collectStructNames(ctx); + enumNames = collectEnumNames(ctx); + + for (int i = 0; i < ctx.functionDef().size(); i++) { + visit(ctx.functionDef(i)); + } + + for (int i = 0; i < ctx.structDef().size(); i++) { + visit(ctx.structDef(i)); + } + + for (int i = 0; i < ctx.enumDef().size(); i++) { + visit(ctx.enumDef(i)); + } + + return null; + } + + @Override + public Node visitEnumDef(KlangParser.EnumDefContext ctx) { + // Check that there isn't a function or struct with the same name + var enumName = ctx.enumName.getText(); + if (functionNames.contains(enumName) || structNames.contains(enumName)) { + var line = ctx.start.getLine(); + var col = ctx.start.getCharPositionInLine(); + var error = "Duplicate use of name " + enumName + "."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + + // IDENT() includes the enumName as the first entry, which we skip + var enumFields = new HashSet(); + for (int i = 1; i < ctx.IDENT().size(); i++) { + var currentEnumField = ctx.IDENT(i); + var currentEnumFieldName = currentEnumField.getText(); + if (enumFields.contains(currentEnumFieldName)) { + var line = currentEnumField.getSymbol().getLine(); + var col = currentEnumField.getSymbol().getCharPositionInLine(); + var error = " Duplicate enum value " + currentEnumFieldName + " in enum " + enumName + "."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + enumFields.add(currentEnumFieldName); + } + + var enumDef = new EnumDefinition(enumName, enumFields.toArray(new String[0])); + enumDef.line = ctx.start.getLine(); + enumDef.col = ctx.start.getCharPositionInLine(); + enumDef.type = new EnumType(enumName); + enumDefs.put(enumName, enumDef); + + return null; + } + + @Override + public Node visitStructDef(KlangParser.StructDefContext ctx) { + var structName = ctx.structName.getText(); + var structFieldCount = ctx.structField().size(); + var structFields = new StructField[structFieldCount]; + var structFieldNames = new HashSet(); + var line = ctx.start.getLine(); + var col = ctx.start.getCharPositionInLine(); + + // Check that there isn't a function or enum with the same name + if (functionNames.contains(structName) || enumNames.contains(structName)) { + var error = "Duplicate use of name " + structName + "."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + + for (int i = 0; i < structFieldCount; i++) { + var currentStructField = ctx.structField(i); + var structFieldName = currentStructField.IDENT().getText(); + var structFieldTypeName = currentStructField.type_annotation().type().getText(); + var structFieldLine = currentStructField.start.getLine(); + var structFieldCol = currentStructField.start.getCharPositionInLine(); + + if (structFieldNames.contains(structFieldName)) { + var error = "Duplicate struct field " + structFieldName + " in struct " + structName + "."; + throw new RuntimeException(Helper.getErrorPrefix(structFieldLine, structFieldCol) + error); + } + + var structField = new StructField(structFieldName); + structField.type = Type.getByName(structFieldTypeName); + structField.line = structFieldLine; + structField.col = structFieldCol; + + structFieldNames.add(structFieldName); + structFields[i] = structField; + } + + var structDef = new StructDefinition(structName, structFields); + structDef.line = line; + structDef.col = col; + structDef.type = new StructType(structName); + structDefs.put(structName, structDef); + + return null; + } + + @Override + public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) { + var funcName = ctx.funcName.getText(); + var returnType = Type.getByName(ctx.returnType.getText()); + var parameters = new TreeMap(); + var paramCount = ctx.params.parameter().size(); + var signature = new Type[paramCount]; + + // Check that there isn't a struct or enum with the same name + if (structNames.contains(funcName) || enumNames.contains(funcName)) { + var line = ctx.start.getLine(); + var col = ctx.start.getCharPositionInLine(); + var error = "Duplicate use of name " + funcName + "."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + + for (int i = 0; i < paramCount; i++) { + var currentParam = ctx.params.parameter(i); + var paramType = Type.getByName(currentParam.type_annotation().type().getText()); + var paramName = currentParam.IDENT().getText(); + + if (parameters.containsKey(paramName)) { + var paramLine = currentParam.start.getLine(); + var paramCol = currentParam.start.getCharPositionInLine(); + var error = "Duplicate parameter name " + paramName + " in function " + funcName + "."; + throw new RuntimeException(Helper.getErrorPrefix(paramLine, paramCol) + error); + } + + parameters.put(paramName, paramType); + signature[i] = paramType; + } + + var information = new FunctionInformation(funcName, returnType, parameters, signature); + functionDefs.put(funcName, information); + return null; + } +} diff --git a/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java b/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java deleted file mode 100644 index f9faa5c..0000000 --- a/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.hsrm.compiler.Klang; - -import java.util.Map; -import java.util.TreeMap; - -import de.hsrm.compiler.Klang.types.*; -import de.hsrm.compiler.Klang.helper.*; - -public class GetFunctions extends KlangBaseVisitor { - - private Map funcs; - - public GetFunctions(Map funcs) { - this.funcs = funcs; - } - - @Override - public Void visitProgram(KlangParser.ProgramContext ctx) { - for (int i = 0; i < ctx.functionDef().size(); i++) { - this.visit(ctx.functionDef(i)); - } - return null; - } - - @Override - public Void visitFunctionDef(KlangParser.FunctionDefContext ctx) { - String name = ctx.funcName.getText(); - int line = ctx.start.getLine(); - int col = ctx.start.getCharPositionInLine(); - - if (this.funcs.containsKey(name)) { - String error = "Function " + name + " defined multiple times."; - throw new Error(Helper.getErrorPrefix(line, col) + error); - } - - Type returnType = Type.getByName(ctx.returnType.type().getText()); - - TreeMap parameters = new TreeMap(); - - // Process the paremter list by visiting every paremter in it - int paramCount = ctx.params.parameter().size(); - Type[] signature = new Type[paramCount]; - for (int i = 0; i < paramCount; i++) { - Type paramType = Type.getByName(ctx.params.parameter(i).type_annotation().type().getText()); - String paramName = ctx.params.parameter(i).IDENT().getText(); - parameters.put(paramName, paramType); - signature[i] = paramType; - } - - FunctionInformation information = new FunctionInformation(name, returnType, parameters, signature); - this.funcs.put(name, information); - return null; - } -} \ 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 deleted file mode 100644 index 0e5fcb3..0000000 --- a/src/main/java/de/hsrm/compiler/Klang/GetStructNames.java +++ /dev/null @@ -1,37 +0,0 @@ -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 deleted file mode 100644 index ca1dc2f..0000000 --- a/src/main/java/de/hsrm/compiler/Klang/GetStructs.java +++ /dev/null @@ -1,70 +0,0 @@ -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 029fdd6..136630b 100644 --- a/src/main/java/de/hsrm/compiler/Klang/Klang.java +++ b/src/main/java/de/hsrm/compiler/Klang/Klang.java @@ -1,19 +1,22 @@ package de.hsrm.compiler.Klang; -// import ANTLR's runtime libraries -import org.antlr.v4.runtime.*; -import org.antlr.v4.runtime.tree.*; - -import java.io.*; -import java.util.Arrays; -import java.util.List; -import java.util.HashMap; -import java.util.HashSet; - +import de.hsrm.compiler.Klang.helper.FunctionInformation; +import de.hsrm.compiler.Klang.nodes.EnumDefinition; 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.*; +import de.hsrm.compiler.Klang.visitors.EvalVisitor; +import de.hsrm.compiler.Klang.visitors.GenASM; +import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.io.FileWriter; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; public class Klang { @@ -87,22 +90,15 @@ public class Klang { // Context Analysis and DAST generation Node root; - HashMap structs; + var functionDefs = new HashMap(); + var structDefs = new HashMap(); + var enumDefs = new HashMap(); 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); + // Extract information about all definitions + new GetDefinitions(functionDefs, structDefs, enumDefs).visit(tree); // Create the DAST - ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions, structs); + ContextAnalysis ctxAnal = new ContextAnalysis(functionDefs, structDefs, enumDefs); root = ctxAnal.visit(tree); } catch (Exception e) { System.err.println(e.getMessage()); @@ -122,14 +118,14 @@ public class Klang { if (evaluate) { // Evaluate the sourcecode and print the result System.out.println("\nEvaluating the source code:"); - EvalVisitor evalVisitor = new EvalVisitor(structs); + EvalVisitor evalVisitor = new EvalVisitor(structDefs); Value result = root.welcome(evalVisitor); generateOutput(out, "Result was: " + result.asObject().toString()); return; } // Generate assembler code - GenASM genasm = new GenASM(mainName, structs); + GenASM genasm = new GenASM(mainName, structDefs); root.welcome(genasm); generateOutput(out, genasm.toAsm()); } diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/EnumDefinition.java b/src/main/java/de/hsrm/compiler/Klang/nodes/EnumDefinition.java new file mode 100644 index 0000000..7631ef1 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/EnumDefinition.java @@ -0,0 +1,19 @@ +package de.hsrm.compiler.Klang.nodes; + +import de.hsrm.compiler.Klang.visitors.Visitor; + +public class EnumDefinition extends Node { + + public String name; + public String[] enums; + + public EnumDefinition(String name, String[] enums) { + this.name = name; + this.enums = enums; + } + + @Override + public R welcome(Visitor v) { + return v.visit(this); + } +} 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 f56d749..35e0c62 100644 --- a/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/Program.java @@ -9,11 +9,18 @@ public class Program extends Node { public FunctionDefinition[] funcs; public Map structs; + public Map enums; public Expression expression; - public Program(FunctionDefinition[] funcs, Map structs, Expression expression) { + public Program( + FunctionDefinition[] funcs, + Map structs, + Map enums, + Expression expression + ) { this.funcs = funcs; this.structs = structs; + this.enums = enums; this.expression = expression; } diff --git a/src/main/java/de/hsrm/compiler/Klang/types/EnumType.java b/src/main/java/de/hsrm/compiler/Klang/types/EnumType.java new file mode 100644 index 0000000..292a6fb --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/types/EnumType.java @@ -0,0 +1,48 @@ +package de.hsrm.compiler.Klang.types; + +public class EnumType extends Type { + + public String name; + + public EnumType(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public Type combine(Type that) { + if(this.equals(that)) { + return this; + } + + throw new RuntimeException("Type mismatch: cannot combine enum " + getName() + " and " + that.getName()); + } + + @Override + public boolean isPrimitiveType() { + return false; + } + + @Override + public boolean isNumericType() { + return false; + } + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + + if (that instanceof EnumType) { + var thatType = (EnumType) that; + return getName().equals(thatType.getName()); + } + + return false; + } +} 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 dad982e..b1738d7 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java @@ -4,12 +4,7 @@ import java.util.HashMap; import java.util.Map; import de.hsrm.compiler.Klang.Value; -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.*; import de.hsrm.compiler.Klang.nodes.expressions.*; import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop; import de.hsrm.compiler.Klang.nodes.loops.ForLoop; @@ -471,6 +466,11 @@ public class EvalVisitor implements Visitor { return null; } + @Override + public Value visit(EnumDefinition e) { + return null; + } + @Override public Value visit(StructDefinition e) { // We get these from a previous visitor 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 aad1f62..f9afa96 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java @@ -1,11 +1,5 @@ package de.hsrm.compiler.Klang.visitors; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - import de.hsrm.compiler.Klang.asm.ASM; import de.hsrm.compiler.Klang.helper.Helper; import de.hsrm.compiler.Klang.nodes.*; @@ -16,6 +10,8 @@ import de.hsrm.compiler.Klang.nodes.loops.WhileLoop; import de.hsrm.compiler.Klang.nodes.statements.*; import de.hsrm.compiler.Klang.types.Type; +import java.util.*; + public class GenASM implements Visitor { private class FloatWriter { private StringBuilder sb = new StringBuilder(); @@ -778,6 +774,11 @@ public class GenASM implements Visitor { return null; } + @Override + public Void visit(EnumDefinition e) { + return null; + } + @Override public Void visit(StructDefinition e) { // We get these from a previous visitor 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 50d807d..6e66d0e 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java @@ -234,6 +234,11 @@ class GetVars implements Visitor { return null; } + @Override + public Void visit(EnumDefinition e) { + return null; + } + @Override public Void visit(StructDefinition e) { return null; 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 563779b..5a35800 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java @@ -68,6 +68,12 @@ public class PrettyPrintVisitor implements Visitor { ex.nl(); } + for (var enumDef: e.enums.values()) { + enumDef.welcome(this); + ex.nl(); + ex.nl(); + } + e.expression.welcome(this); ex.write(";"); return null; @@ -377,6 +383,22 @@ public class PrettyPrintVisitor implements Visitor { return null; } + @Override + public Void visit(EnumDefinition e) { + ex.write("enum " + e.name + " { "); + var first = true; + for(var enumName: e.enums) { + if (!first) { + ex.write(", "); + } else { + first = false; + } + ex.write(enumName); + } + ex.write(" }"); + return null; + } + @Override public Void visit(StructDefinition e) { ex.write("struct " + e.name + " {"); 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 cfef604..f8d7e77 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java @@ -1,11 +1,6 @@ package de.hsrm.compiler.Klang.visitors; -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.*; import de.hsrm.compiler.Klang.nodes.expressions.*; import de.hsrm.compiler.Klang.nodes.loops.*; import de.hsrm.compiler.Klang.nodes.statements.*; @@ -42,6 +37,7 @@ public interface Visitor { R visit(FunctionCall e); R visit(Program e); R visit(Parameter e); + R visit(EnumDefinition e); R visit(StructDefinition e); R visit(StructField e); R visit(StructFieldAccessExpression e); diff --git a/src/test/java/AndTest.java b/src/test/java/AndTest.java index 03f4f33..a4fc6d5 100644 --- a/src/test/java/AndTest.java +++ b/src/test/java/AndTest.java @@ -12,7 +12,8 @@ public class AndTest { ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 && 2; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:30 && is only defined for bool.", e.getMessage()); diff --git a/src/test/java/ConstructorCallTest.java b/src/test/java/ConstructorCallTest.java index 99010bc..9d21d29 100644 --- a/src/test/java/ConstructorCallTest.java +++ b/src/test/java/ConstructorCallTest.java @@ -13,7 +13,8 @@ public class ConstructorCallTest { ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create schwurbel(1); } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:52 Struct with name \"schwurbel\" not defined.", e.getMessage()); @@ -24,7 +25,8 @@ public class ConstructorCallTest { ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:52 Struct \"bar\" defined 1 fields, but got 2 constructor parameters.", e.getMessage()); @@ -35,7 +37,8 @@ public class ConstructorCallTest { ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(false); } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); 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()); diff --git a/src/test/java/DestroyStatementTest.java b/src/test/java/DestroyStatementTest.java index f157d2b..523c4b5 100644 --- a/src/test/java/DestroyStatementTest.java +++ b/src/test/java/DestroyStatementTest.java @@ -13,7 +13,8 @@ public class DestroyStatementTest { ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): int { destroy x; return 1; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:45 Variable with name \"x\" not defined.", e.getMessage()); diff --git a/src/test/java/FieldAssignmentTest.java b/src/test/java/FieldAssignmentTest.java index 879e4c8..32eeacb 100644 --- a/src/test/java/FieldAssignmentTest.java +++ b/src/test/java/FieldAssignmentTest.java @@ -13,7 +13,8 @@ public class FieldAssignmentTest { ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { str.a = 1; return 1; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:46 Variable with name str not defined.", e.getMessage()); @@ -24,7 +25,8 @@ public class FieldAssignmentTest { ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { let x: int = 0; x.a = 0; return 1; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:62 Variable must reference a struct but references int.", e.getMessage()); diff --git a/src/test/java/FunctionCallTest.java b/src/test/java/FunctionCallTest.java index d0e5126..ce3876f 100644 --- a/src/test/java/FunctionCallTest.java +++ b/src/test/java/FunctionCallTest.java @@ -13,7 +13,8 @@ public class FunctionCallTest { ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } bar();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:34 Function with name \"bar\" not defined.", e.getMessage()); @@ -24,7 +25,8 @@ public class FunctionCallTest { ParseTree tree = Helper.prepareParser("function foo(): int { return 1; } foo(5);"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:34 Function \"foo\" expects 0 parameters, but got 1.", e.getMessage()); @@ -35,7 +37,8 @@ public class FunctionCallTest { ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:40 argument 0 Expected int but got: bool", e.getMessage()); diff --git a/src/test/java/FunctionDefinitionTest.java b/src/test/java/FunctionDefinitionTest.java index 442e792..665b6e9 100644 --- a/src/test/java/FunctionDefinitionTest.java +++ b/src/test/java/FunctionDefinitionTest.java @@ -13,7 +13,8 @@ public class FunctionDefinitionTest { ParseTree tree = Helper.prepareParser("function foo(): schwurbel { return 1; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:0 Type schwurbel not defined.", e.getMessage()); @@ -24,7 +25,8 @@ public class FunctionDefinitionTest { ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; x = 0; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:0 Function foo has to return something of type int.", e.getMessage()); diff --git a/src/test/java/Helper.java b/src/test/java/Helper.java index 68b0ff6..e364ac1 100644 --- a/src/test/java/Helper.java +++ b/src/test/java/Helper.java @@ -18,19 +18,19 @@ public class Helper { public static Map getFuncs(ParseTree tree) { var functionDefinitions = new HashMap(); - new GetFunctions(functionDefinitions).visit(tree); + new GetDefinitions(functionDefinitions, new HashMap<>(), new HashMap<>()).visit(tree); return functionDefinitions; } - public static Set getStructNames(ParseTree tree) { - var structNames = new HashSet(); - new GetStructNames(structNames).visit(tree); - return structNames; - } - public static Map getStructs(ParseTree tree) { var structs = new HashMap(); - new GetStructs(getStructNames(tree), structs).visit(tree); + new GetDefinitions(new HashMap<>(), structs, new HashMap<>()).visit(tree); return structs; } + + public static Map getEnums(ParseTree tree) { + var enums = new HashMap(); + new GetDefinitions(new HashMap<>(), new HashMap<>(), enums).visit(tree); + return enums; + } } \ No newline at end of file diff --git a/src/test/java/ModuloTest.java b/src/test/java/ModuloTest.java index c11a87f..57b2f8b 100644 --- a/src/test/java/ModuloTest.java +++ b/src/test/java/ModuloTest.java @@ -12,7 +12,8 @@ public class ModuloTest { ParseTree tree = Helper.prepareParser("function foo(): float { return 1.0 % 2.3; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:31 Only integers are allowed for modulo.", e.getMessage()); diff --git a/src/test/java/OrTest.java b/src/test/java/OrTest.java index 07dfc33..c5511d5 100644 --- a/src/test/java/OrTest.java +++ b/src/test/java/OrTest.java @@ -13,7 +13,8 @@ public class OrTest { ParseTree tree = Helper.prepareParser("function foo(): bool { return 1 || 2; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:30 || is only defined for bool.", e.getMessage()); diff --git a/src/test/java/StructFieldAccessTest.java b/src/test/java/StructFieldAccessTest.java index 79b9766..9bda8f3 100644 --- a/src/test/java/StructFieldAccessTest.java +++ b/src/test/java/StructFieldAccessTest.java @@ -12,7 +12,8 @@ public class StructFieldAccessTest { ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { return str.a; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:53 Variable with name str not defined.", e.getMessage()); @@ -23,7 +24,8 @@ public class StructFieldAccessTest { ParseTree tree = Helper.prepareParser("struct test { a: int; } function foo(): int { let x: int = 0; return x.a; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:69 Variable must reference a struct but references int.", e.getMessage()); diff --git a/src/test/java/VariableAssignmentTest.java b/src/test/java/VariableAssignmentTest.java index faa338d..292e5f4 100644 --- a/src/test/java/VariableAssignmentTest.java +++ b/src/test/java/VariableAssignmentTest.java @@ -13,7 +13,8 @@ public class VariableAssignmentTest { ParseTree tree = Helper.prepareParser("function foo(): int { x = 1; return 1; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:22 Variable with name \"x\" not defined.", e.getMessage()); diff --git a/src/test/java/VariableDeclarationTest.java b/src/test/java/VariableDeclarationTest.java index a52ea2e..acc1d1c 100644 --- a/src/test/java/VariableDeclarationTest.java +++ b/src/test/java/VariableDeclarationTest.java @@ -12,7 +12,8 @@ public class VariableDeclarationTest { ParseTree tree = Helper.prepareParser("function foo(): int { let X: unk; return 1; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:22 Type unk not defined.", e.getMessage()); @@ -24,7 +25,8 @@ public class VariableDeclarationTest { ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; let x: bool; return 1; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:34 Redeclaration of variable with name \"x\".", e.getMessage()); diff --git a/src/test/java/VariableTest.java b/src/test/java/VariableTest.java index 763226c..c9d2d39 100644 --- a/src/test/java/VariableTest.java +++ b/src/test/java/VariableTest.java @@ -13,7 +13,8 @@ public class VariableTest { ParseTree tree = Helper.prepareParser("function foo(): int { return x; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:29 Variable with name \"x\" not defined.", e.getMessage()); @@ -24,7 +25,8 @@ public class VariableTest { ParseTree tree = Helper.prepareParser("function foo(): int { let x: int; return x; } foo();"); var funcs = Helper.getFuncs(tree); var structs = Helper.getStructs(tree); - ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs); + var enums = Helper.getEnums(tree); + ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums); Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree)); assertEquals("Error in line 1:41 Variable with name \"x\" has not been initialized.", e.getMessage());