Files
klang/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java

891 lines
31 KiB
Java

package de.hsrm.compiler.Klang;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
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 java.util.HashMap;
import java.util.Map;
public class ContextAnalysis extends KlangBaseVisitor<Node> {
Map<String, VariableDeclaration> vars = new HashMap<>();
Map<String, FunctionDefinition> functionDefs;
Map<String, StructDefinition> structDefs;
Map<String, EnumDefinition> enumDefs;
Type currentDeclaredReturnType;
String currentFunctionDefinitionName;
private void checkNumeric(Node lhs, Node rhs, int line, int col) {
if (!lhs.type.isNumericType() || !rhs.type.isNumericType()) {
String error = "Only numeric types are allowed for this expression.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
}
public ContextAnalysis(
Map<String, FunctionDefinition> functionDefs,
Map<String, StructDefinition> structDefs,
Map<String, EnumDefinition> enumDefs
) {
this.functionDefs = functionDefs;
this.structDefs = structDefs;
this.enumDefs = enumDefs;
}
@Override
public Node visitProgram(KlangParser.ProgramContext ctx) {
FunctionDefinition[] funcs = new FunctionDefinition[ctx.functionDef().size()];
for (int i = 0; i < ctx.functionDef().size(); i++) {
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
}
Expression expression = (Expression) this.visit(ctx.expression());
Program result = new Program(funcs, structDefs, enumDefs, expression);
result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitStatement(KlangParser.StatementContext ctx) {
// The first child is the proper context we need to visit
// The second child is either null or just a SCOL!
return this.visit(ctx.getChild(0));
}
@Override
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
int actualStatementCount = 0;
int declaredStatementCount = ctx.statement().size();
boolean hasReturn = false;
Statement[] statements = new Statement[declaredStatementCount];
for (int i = 0; i < declaredStatementCount; i++) {
Node currentStatement = this.visit(ctx.statement(i));
statements[i] = (Statement) currentStatement;
actualStatementCount += 1;
// 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
try {
this.currentDeclaredReturnType.combine(currentStatement.type);
} catch (Exception e) {
throw new RuntimeException(
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
}
// since we have a return guaranteed, every statement
// after this one is unreachable code
hasReturn = true;
break;
}
}
// If there was unreachable code in this block,
// create a shorter statements array and copy the statements to there
if (actualStatementCount < declaredStatementCount) {
Statement[] newStatements = new Statement[actualStatementCount];
System.arraycopy(statements, 0, newStatements, 0, actualStatementCount);
statements = newStatements;
}
// 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
Block result = new Block(statements);
if (hasReturn) {
result.type = this.currentDeclaredReturnType;
}
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitIf_statement(KlangParser.If_statementContext ctx) {
Node condition = this.visit(ctx.cond);
Node thenBlock = this.visit(ctx.then);
Type type = null;
IfStatement result;
if (ctx.alt != null) {
Node elseBlock = this.visit(ctx.alt);
result = new IfStatement((Expression) condition, (Block) thenBlock, (Block) elseBlock);
type = elseBlock.type;
} else if (ctx.elif != null) {
Node elif = this.visit(ctx.elif);
result = new IfStatement((Expression) condition, (Block) thenBlock, (IfStatement) elif);
type = elif.type;
} else {
result = new IfStatement((Expression) condition, (Block) thenBlock);
}
if (thenBlock.type != null && type != null) {
// Since a block verifies that it can combine with the return type of the
// function it is defined in, we do not have check whether the then and
// alt block return types match
result.type = thenBlock.type;
}
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitWhileLoop(KlangParser.WhileLoopContext ctx) {
Node condition = this.visit(ctx.cond);
Node block = this.visit(ctx.braced_block());
Node result = new WhileLoop((Expression) condition, (Block) block);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitDoWhileLoop(KlangParser.DoWhileLoopContext ctx) {
Node block = this.visit(ctx.braced_block());
Node condition = this.visit(ctx.cond);
Node result = new DoWhileLoop((Expression) condition, (Block) block);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitForLoop(KlangParser.ForLoopContext ctx) {
Node init = this.visit(ctx.init);
Node condition = this.visit(ctx.cond);
Node step = this.visit(ctx.step);
Node block = this.visit(ctx.braced_block());
Node result = new ForLoop((Statement) init, (Expression) condition, (VariableAssignment) step, (Block) block);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
if (!declaredType.isPrimitiveType() && this.structDefs.get(declaredType.getName()) == null) {
String error = "Type " + declaredType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
if (this.vars.get(name) != null) {
String error = "Redeclaration of variable with name \"" + name + "\".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Create the appropriate instance
VariableDeclaration result;
if (ctx.expression() != null) {
Node expression = this.visit(ctx.expression());
try {
declaredType.combine(expression.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result = new VariableDeclaration(name, (Expression) expression);
result.initialized = true;
} else {
result = new VariableDeclaration(name);
}
// Add it to the global map of variable declarations
this.vars.put(name, result);
result.line = line;
result.col = col;
result.type = declaredType;
return result;
}
@Override
public Node visitVariable_assignment(KlangParser.Variable_assignmentContext 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);
}
// Evaluate the expression
Expression expression = (Expression) this.visit(ctx.expression());
// Make sure expression can be assigned to the variable
try {
expression.type.combine(var.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;
// Create a new node and add the type of the expression to it
Node result = new VariableAssignment(name, expression);
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
Expression expression = (Expression) this.visit(ctx.expression());
ReturnStatement result = new ReturnStatement(expression);
// Check if this expression is a tail recursion
if (expression instanceof FunctionCall) {
var funCall = (FunctionCall) expression;
if (funCall.name.equals(this.currentFunctionDefinitionName)) {
// Flag this function call
funCall.isTailRecursive = true;
}
}
result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
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.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());
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.structDefs, structName, path, 0);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
Node result = new StructFieldAccessExpression(varName, structName, path);
result.type = resultType;
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitOrExpression(KlangParser.OrExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
OrExpression result = new OrExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
if (!lhs.type.equals(Type.getBooleanType()) || !rhs.type.equals(Type.getBooleanType())) {
String error = "|| is only defined for bool.";
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
}
return result;
}
@Override
public Node visitAndExpression(KlangParser.AndExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
AndExpression result = new AndExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
if (!lhs.type.equals(Type.getBooleanType()) || !rhs.type.equals(Type.getBooleanType())) {
String error = "&& is only defined for bool.";
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
}
return result;
}
@Override
public Node visitAdditionExpression(KlangParser.AdditionExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
AdditionExpression result = new AdditionExpression((Expression) lhs, (Expression) rhs);
try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitParenthesisExpression(KlangParser.ParenthesisExpressionContext ctx) {
return this.visit(ctx.expression());
}
@Override
public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
EqualityExpression result = new EqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
NotEqualityExpression result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
LTExpression result = new LTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
if (!lhs.type.isNumericType() || !rhs.type.isNumericType()) {
String error = "Only numeric types are allowed for this expression.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
GTExpression result = new GTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
LTEExpression result = new LTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
try {
lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
GTEExpression result = new GTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitSubstractionExpression(KlangParser.SubstractionExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
SubstractionExpression result = new SubstractionExpression((Expression) lhs, (Expression) rhs);
try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitMultiplicationExpression(KlangParser.MultiplicationExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
MultiplicationExpression result = new MultiplicationExpression((Expression) lhs, (Expression) rhs);
try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitDivisionExpression(KlangParser.DivisionExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
DivisionExpression result = new DivisionExpression((Expression) lhs, (Expression) rhs);
try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitModuloExpression(KlangParser.ModuloExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
ModuloExpression result = new ModuloExpression((Expression) lhs, (Expression) rhs);
try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
checkNumeric(lhs, rhs, line, col);
if (lhs.type.equals(Type.getFloatType()) || rhs.type.equals(Type.getFloatType())) {
String error = "Only integers are allowed for modulo.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitNegateExpression(KlangParser.NegateExpressionContext ctx) {
Node expression = this.visit(ctx.expression());
NegateExpression result = new NegateExpression((Expression) expression);
result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
if (!result.type.isNumericType()) {
String error = "Only numeric types are allowed for this expression.";
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
}
return result;
}
@Override
public Node visitNotExpression(KlangParser.NotExpressionContext ctx) {
Node expression = this.visit(ctx.expression());
NotExpression result = new NotExpression((Expression) expression);
result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
if (!result.type.equals(Type.getBooleanType())) {
String error = "Only boolean type is allowed for this expression.";
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
}
return result;
}
@Override
public Node visitVariable(KlangParser.VariableContext 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);
}
// 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.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
Variable result = new Variable(ctx.IDENT().getText());
result.type = var.type;
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitAtomExpression(KlangParser.AtomExpressionContext ctx) {
return this.visit(ctx.atom());
}
@Override
public Node visitIntAtom(KlangParser.IntAtomContext ctx) {
Node n = new IntegerExpression(Integer.parseInt(ctx.getText()));
n.type = Type.getIntegerType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n;
}
@Override
public Node visitFloatAtom(KlangParser.FloatAtomContext ctx) {
Node n = new FloatExpression(Double.parseDouble(ctx.getText()));
n.type = Type.getFloatType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n;
}
@Override
public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) {
Node n = new BooleanExpression(ctx.getText().equals("true"));
n.type = Type.getBooleanType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n;
}
@Override
public Node visitNullAtom(KlangParser.NullAtomContext ctx) {
Node n = new NullExpression();
n.type = Type.getNullType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n;
}
@Override
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
String name = ctx.funcName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type returnType = Type.getByName(ctx.returnType.type().getText());
this.currentDeclaredReturnType = returnType;
this.currentFunctionDefinitionName = name;
if (!returnType.isPrimitiveType() && this.structDefs.get(returnType.getName()) == null) {
String error = "Type " + returnType.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Create a new set for the variables of the current function
// this will be filled in the variable declaration visitor aswell
this.vars = new HashMap<>();
// Process the paremter list by visiting every paremter in it
int paramCount = ctx.params.parameter().size();
Parameter[] params = new Parameter[paramCount];
for (int i = 0; i < paramCount; i++) {
// Add the parameter to the list of parameters
Parameter param = (Parameter) this.visit(ctx.params.parameter(i));
params[i] = param;
// add the param as a variable
VariableDeclaration var = new VariableDeclaration(param.name);
var.initialized = true; // parameters can always be considered initialized
var.type = param.type;
this.vars.put(param.name, var);
}
// Visit the block, make sure that a return value is guaranteed
Node block = this.visit(ctx.braced_block());
if (block.type == null) {
String error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
FunctionDefinition result = new FunctionDefinition(name, params, (Block) block);
result.type = returnType;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
}
@Override
public Node visitParameter(KlangParser.ParameterContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type type = Type.getByName(ctx.type_annotation().type().getText());
if (!type.isPrimitiveType() && this.structDefs.get(type.getName()) == null) {
String error = "Type " + type.getName() + " not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
Parameter result = new Parameter(name);
result.type = type;
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) {
String name = ctx.functionCall().IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
var functionDef = this.functionDefs.get(name);
if (functionDef == null) {
String error = "Function with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Make sure the number of arguments matches the number of parameters
int argCount = ctx.functionCall().arguments().expression().size();
int paramCount = functionDef.parameters.length;
if (argCount != paramCount) {
String error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
// Evaluate every argument
Expression[] args = new Expression[argCount];
for (int i = 0; i < argCount; i++) {
Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
if (!expression.type.equals(functionDef.parameters[i].type)) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + functionDef.parameters[i].type.getName() + " but got: " + expression.type.getName());
}
args[i] = expression;
}
FunctionCall result = new FunctionCall(name, args);
result.type = functionDef.type;
result.line = line;
result.col = col;
return result;
}
@Override
public Node visitConstructorCallExpression(KlangParser.ConstructorCallExpressionContext ctx) {
String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
// Get the corresponding struct definition
var struct = this.structDefs.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;
}
}