|
|
|
|
@@ -8,10 +8,12 @@ import de.hsrm.compiler.Klang.nodes.loops.ForLoop;
|
|
|
|
|
import de.hsrm.compiler.Klang.nodes.loops.WhileLoop;
|
|
|
|
|
import de.hsrm.compiler.Klang.nodes.statements.*;
|
|
|
|
|
import de.hsrm.compiler.Klang.types.Type;
|
|
|
|
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
|
|
|
|
|
|
|
|
|
import java.util.*;
|
|
|
|
|
|
|
|
|
|
public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
Map<String, VariableDeclaration> params = new HashMap<>();
|
|
|
|
|
Map<String, VariableDeclaration> vars = new HashMap<>();
|
|
|
|
|
Map<String, FunctionDefinition> functionDefs;
|
|
|
|
|
Map<String, StructDefinition> structDefs;
|
|
|
|
|
@@ -199,8 +201,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (vars.get(variableName) != null) {
|
|
|
|
|
var error = "Redeclaration of variable with name \"" + variableName + "\".";
|
|
|
|
|
if (vars.get(variableName) != null || params.get(variableName) != null) {
|
|
|
|
|
var error = "Redeclaration of variable or parameter with name \"" + variableName + "\".";
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -236,31 +238,27 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) {
|
|
|
|
|
String name = ctx.IDENT().getText();
|
|
|
|
|
int line = ctx.start.getLine();
|
|
|
|
|
int col = ctx.start.getCharPositionInLine();
|
|
|
|
|
var name = ctx.IDENT().getText();
|
|
|
|
|
var line = ctx.start.getLine();
|
|
|
|
|
var 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);
|
|
|
|
|
}
|
|
|
|
|
var variableOrParameter = getVariableOrParameter(name, line, col);
|
|
|
|
|
|
|
|
|
|
// Evaluate the expression
|
|
|
|
|
Expression expression = (Expression) this.visit(ctx.expression());
|
|
|
|
|
var expression = (Expression) visit(ctx.expression());
|
|
|
|
|
|
|
|
|
|
// Make sure expression can be assigned to the variable
|
|
|
|
|
try {
|
|
|
|
|
expression.type.combine(var.type);
|
|
|
|
|
expression.type.combine(variableOrParameter.type);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Since we assigned a value to this variable, we can consider it initialized
|
|
|
|
|
var.initialized = true;
|
|
|
|
|
variableOrParameter.initialized = true;
|
|
|
|
|
|
|
|
|
|
// Create a new node and add the type of the expression to it
|
|
|
|
|
Node result = new VariableAssignment(name, expression);
|
|
|
|
|
var result = new VariableAssignment(name, expression);
|
|
|
|
|
result.line = line;
|
|
|
|
|
result.col = col;
|
|
|
|
|
return result;
|
|
|
|
|
@@ -272,8 +270,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
ReturnStatement result = new ReturnStatement(expression);
|
|
|
|
|
|
|
|
|
|
// Check if this expression is a tail recursion
|
|
|
|
|
if (expression instanceof FunctionCall) {
|
|
|
|
|
var funCall = (FunctionCall) expression;
|
|
|
|
|
if (expression instanceof FunctionCall funCall) {
|
|
|
|
|
if (funCall.name.equals(this.currentFunctionDefinitionName)) {
|
|
|
|
|
// Flag this function call
|
|
|
|
|
funCall.isTailRecursive = true;
|
|
|
|
|
@@ -288,46 +285,24 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
|
|
|
|
|
@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];
|
|
|
|
|
var varName = ctx.IDENT(0).getText();
|
|
|
|
|
var line = ctx.start.getLine();
|
|
|
|
|
var col = ctx.start.getCharPositionInLine();
|
|
|
|
|
var path = createStructPath(ctx.IDENT());
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < ctx.IDENT().size(); i++) {
|
|
|
|
|
path[i - 1] = ctx.IDENT(i).getText();
|
|
|
|
|
}
|
|
|
|
|
var variableOrParameter = getVariableOrParameter(varName, line, col);
|
|
|
|
|
ensureReferencesStruct(variableOrParameter, line, col);
|
|
|
|
|
var fieldType = drillType(variableOrParameter, path, line, col);
|
|
|
|
|
|
|
|
|
|
// 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.structDefs, 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());
|
|
|
|
|
// Visit the expression and make sure the type combines properly
|
|
|
|
|
var expression = (Expression) 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);
|
|
|
|
|
var result = new FieldAssignment(varName, variableOrParameter.type.getName(), path, expression);
|
|
|
|
|
result.col = col;
|
|
|
|
|
result.line = line;
|
|
|
|
|
return result;
|
|
|
|
|
@@ -338,23 +313,17 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
var baseName = ctx.IDENT(0).getText();
|
|
|
|
|
var line = ctx.start.getLine();
|
|
|
|
|
var col = ctx.start.getCharPositionInLine();
|
|
|
|
|
|
|
|
|
|
// Create a list of member names. This excludes
|
|
|
|
|
// the first entry as it is the base name.
|
|
|
|
|
var path = new ArrayList<String>();
|
|
|
|
|
for (int i = 1; i < ctx.IDENT().size(); i++) {
|
|
|
|
|
path.add(ctx.IDENT(i).getText());
|
|
|
|
|
}
|
|
|
|
|
var path = createStructPath(ctx.IDENT());
|
|
|
|
|
|
|
|
|
|
// Determine if the base name points to an enum or a variable
|
|
|
|
|
var enumDef = enumDefs.get(baseName);
|
|
|
|
|
if (enumDef != null) {
|
|
|
|
|
if (path.size() != 1) {
|
|
|
|
|
if (path.length != 1) {
|
|
|
|
|
var error = "Illegal access to enum " + enumDef.name + ".";
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var enumValueName = path.get(0);
|
|
|
|
|
var enumValueName = path[0];
|
|
|
|
|
var enumValue = Arrays.stream(enumDef.enums)
|
|
|
|
|
.filter(e -> e.value.equals(enumValueName))
|
|
|
|
|
.findFirst()
|
|
|
|
|
@@ -371,29 +340,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
return enumAccessExpression;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the referenced variable, make sure it is defined
|
|
|
|
|
var variableDef = vars.get(baseName);
|
|
|
|
|
if (variableDef == null) {
|
|
|
|
|
var error = "Variable with name " + baseName + " not defined.";
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
var variableOrParameter = getVariableOrParameter(baseName, line, col);
|
|
|
|
|
ensureReferencesStruct(variableOrParameter, line, col);
|
|
|
|
|
var resultType = drillType(variableOrParameter, path, line, col);
|
|
|
|
|
|
|
|
|
|
// Make sure it references a struct
|
|
|
|
|
if (variableDef.type.isPrimitiveType()) {
|
|
|
|
|
var 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
|
|
|
|
|
var structName = variableDef.type.getName();
|
|
|
|
|
Type resultType;
|
|
|
|
|
try {
|
|
|
|
|
resultType = Helper.drillType(structDefs, structName, path.toArray(new String[0]), 0);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var memberAccessExpression = new MemberAccessExpression(baseName, structName, path.toArray(new String[0]));
|
|
|
|
|
var memberAccessExpression = new MemberAccessExpression(baseName, variableOrParameter.type.getName(), path);
|
|
|
|
|
memberAccessExpression.type = resultType;
|
|
|
|
|
memberAccessExpression.line = line;
|
|
|
|
|
memberAccessExpression.col = col;
|
|
|
|
|
@@ -711,24 +662,20 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Node visitVariable(KlangParser.VariableContext ctx) {
|
|
|
|
|
String name = ctx.IDENT().getText();
|
|
|
|
|
int line = ctx.start.getLine();
|
|
|
|
|
int col = ctx.start.getCharPositionInLine();
|
|
|
|
|
var variableName = ctx.IDENT().getText();
|
|
|
|
|
var line = ctx.start.getLine();
|
|
|
|
|
var 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);
|
|
|
|
|
}
|
|
|
|
|
var variableOrParameter = getVariableOrParameter(variableName, line, col);
|
|
|
|
|
|
|
|
|
|
// Make sure the variable has been initialized before it can be used
|
|
|
|
|
if (!var.initialized) {
|
|
|
|
|
String error = "Variable with name \"" + name + "\" has not been initialized.";
|
|
|
|
|
if (!variableOrParameter.initialized) {
|
|
|
|
|
var error = "Variable with name \"" + variableName + "\" has not been initialized.";
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Variable result = new Variable(ctx.IDENT().getText());
|
|
|
|
|
result.type = var.type;
|
|
|
|
|
var result = new Variable(ctx.IDENT().getText());
|
|
|
|
|
result.type = variableOrParameter.type;
|
|
|
|
|
result.line = line;
|
|
|
|
|
result.col = col;
|
|
|
|
|
return result;
|
|
|
|
|
@@ -839,23 +786,23 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
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 and parameters of the current function
|
|
|
|
|
// this will be filled in the variable declaration visitor as well
|
|
|
|
|
vars = new HashMap<>();
|
|
|
|
|
params = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Process the parameter list by visiting every parameter in it
|
|
|
|
|
var paramCount = ctx.params.parameter().size();
|
|
|
|
|
var params = new Parameter[paramCount];
|
|
|
|
|
var functionParameters = new Parameter[paramCount]; // the list of parameters that get passed to FunctionDefinition
|
|
|
|
|
for (int i = 0; i < paramCount; i++) {
|
|
|
|
|
// Add the parameter to the list of parameters
|
|
|
|
|
var param = (Parameter) visit(ctx.params.parameter(i));
|
|
|
|
|
params[i] = param;
|
|
|
|
|
functionParameters[i] = (Parameter) visit(ctx.params.parameter(i));
|
|
|
|
|
|
|
|
|
|
// add the param as a variable
|
|
|
|
|
var var = new VariableDeclaration(param.name);
|
|
|
|
|
var.initialized = true; // parameters can always be considered initialized
|
|
|
|
|
var.type = param.type;
|
|
|
|
|
vars.put(param.name, var);
|
|
|
|
|
// add the param as a variable to the global parameter map so that
|
|
|
|
|
// child nodes can access them.
|
|
|
|
|
var param = new VariableDeclaration(functionParameters[i].name);
|
|
|
|
|
param.initialized = true; // parameters can always be considered initialized
|
|
|
|
|
param.type = functionParameters[i].type;
|
|
|
|
|
params.put(param.name, param);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Visit the block, make sure that a return value is guaranteed
|
|
|
|
|
@@ -867,7 +814,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var functionDef = new FunctionDefinition(name, params, (Block) block);
|
|
|
|
|
var functionDef = new FunctionDefinition(name, functionParameters, vars.values().toArray(new VariableDeclaration[0]), (Block) block);
|
|
|
|
|
functionDef.type = returnType;
|
|
|
|
|
functionDef.line = ctx.start.getLine();
|
|
|
|
|
functionDef.col = ctx.start.getCharPositionInLine();
|
|
|
|
|
@@ -988,19 +935,52 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Node visitDestroy_statement(KlangParser.Destroy_statementContext ctx) {
|
|
|
|
|
String name = ctx.IDENT().getText();
|
|
|
|
|
int line = ctx.start.getLine();
|
|
|
|
|
int col = ctx.start.getCharPositionInLine();
|
|
|
|
|
var varName = ctx.IDENT().getText();
|
|
|
|
|
var line = ctx.start.getLine();
|
|
|
|
|
var 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);
|
|
|
|
|
}
|
|
|
|
|
var variableOrParameter = getVariableOrParameter(varName, line, col);
|
|
|
|
|
ensureReferencesStruct(variableOrParameter, line, col);
|
|
|
|
|
|
|
|
|
|
Node result = new DestructorCall(name);
|
|
|
|
|
var result = new DestructorCall(varName);
|
|
|
|
|
result.line = line;
|
|
|
|
|
result.col = col;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private VariableDeclaration getVariableOrParameter(String name, int line, int col) {
|
|
|
|
|
var variable = vars.get(name);
|
|
|
|
|
var parameter = params.get(name);
|
|
|
|
|
if (variable == null && parameter == null) {
|
|
|
|
|
var error = "Variable or parameter with name \"" + name + "\" not defined.";
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return variable != null ? variable : parameter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ensureReferencesStruct(VariableDeclaration variableOrParameter, int line, int col) {
|
|
|
|
|
if (variableOrParameter.type.isPrimitiveType() || enumDefs.containsKey(variableOrParameter.type.getName())) {
|
|
|
|
|
var error = "Variable or parameter must reference a struct but references " + variableOrParameter.type.getName() + ".";
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Type drillType(VariableDeclaration variableOrParameter, String[] path, int line, int col) {
|
|
|
|
|
try {
|
|
|
|
|
var structName = variableOrParameter.type.getName();
|
|
|
|
|
return Helper.drillType(structDefs, structName, path, 0);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String[] createStructPath(List<TerminalNode> identifiers) {
|
|
|
|
|
// Create a list of member names. This excludes the first entry as it is the base name.
|
|
|
|
|
var path = new String[identifiers.size() - 1];
|
|
|
|
|
for (int i = 1; i < identifiers.size(); i++) {
|
|
|
|
|
path[i - 1] = identifiers.get(i).getText();
|
|
|
|
|
}
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
}
|