Void Type: Fix a problem where calls to void functions were ignored
The ContextAnalysis visitor for braced blocks was not updated during void support implementation which is why I do it now. Although we are re-using the functionCall production rule a different visitor is required for visiting function calls directly inside braced blocks. This is because the other place where functionCalls are used the functionCall is annotated with a label with wraps the functionCall context inside a label context. Fortunately this is in fact simple wrapping so we just implement a visitor for the wrapping class that unwraps the functionCall and passes it to the visitor that actually implements the functionCall.
This commit is contained in:
@@ -68,25 +68,32 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
|
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
|
||||||
var actualStatementCount = 0;
|
var statementsOrFunctionCalls = new ArrayList<Node>();
|
||||||
var declaredStatementCount = ctx.statement().size();
|
|
||||||
var hasReturn = false;
|
var hasReturn = false;
|
||||||
var statements = new Statement[declaredStatementCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < declaredStatementCount; i++) {
|
for (var child: ctx.children) {
|
||||||
var currentStatement = visit(ctx.statement(i));
|
var statementOrFunctionCall = visit(child);
|
||||||
statements[i] = (Statement) currentStatement;
|
|
||||||
actualStatementCount += 1;
|
|
||||||
|
|
||||||
// We use the existence of a type to indicate that this statement returns
|
// The children array contains more than just the statements or function calls
|
||||||
// something for which the VariableDeclaration is an exception
|
// but everything else evaluates to null, so we can skip it.
|
||||||
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
|
if (statementOrFunctionCall == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
statementsOrFunctionCalls.add(statementOrFunctionCall);
|
||||||
|
|
||||||
|
if (
|
||||||
|
statementOrFunctionCall.type != null
|
||||||
|
&& !(statementOrFunctionCall instanceof VariableDeclaration)
|
||||||
|
&& !statementOrFunctionCall.type.equals(Type.getVoidType())
|
||||||
|
) {
|
||||||
// check whether the type matches
|
// check whether the type matches
|
||||||
try {
|
try {
|
||||||
currentDeclaredReturnType.combine(currentStatement.type);
|
currentDeclaredReturnType.combine(statementOrFunctionCall.type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(
|
var line = statementOrFunctionCall.line;
|
||||||
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
|
var col = statementOrFunctionCall.col;
|
||||||
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// since we have a return guaranteed, every statement
|
// since we have a return guaranteed, every statement
|
||||||
@@ -96,20 +103,12 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there was unreachable code in this block,
|
|
||||||
// create a shorter statements array and copy the statements to there
|
|
||||||
if (actualStatementCount < declaredStatementCount) {
|
|
||||||
var 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,
|
// if this block contains at least one statement that guarantees a return value,
|
||||||
// we indicate that this block guarantees a return value by setting result.type
|
// we indicate that this block guarantees a return value by setting result.type
|
||||||
var result = new Block(statements);
|
var result = new Block(statementsOrFunctionCalls.toArray(new Node[0]));
|
||||||
|
|
||||||
if (hasReturn) {
|
if (hasReturn) {
|
||||||
result.type = this.currentDeclaredReturnType;
|
result.type = currentDeclaredReturnType;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.line = ctx.start.getLine();
|
result.line = ctx.start.getLine();
|
||||||
@@ -884,42 +883,47 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) {
|
public Node visitFunctionCall(KlangParser.FunctionCallContext ctx) {
|
||||||
String name = ctx.functionCall().IDENT().getText();
|
var name = ctx.IDENT().getText();
|
||||||
int line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
int col = ctx.start.getCharPositionInLine();
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
var functionDef = this.functionDefs.get(name);
|
var functionDef = functionDefs.get(name);
|
||||||
if (functionDef == null) {
|
if (functionDef == null) {
|
||||||
String error = "Function with name \"" + name + "\" not defined.";
|
var error = "Function with name \"" + name + "\" not defined.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the number of arguments matches the number of parameters
|
// Make sure the number of arguments matches the number of parameters
|
||||||
int argCount = ctx.functionCall().arguments().expression().size();
|
var argCount = ctx.arguments().expression().size();
|
||||||
int paramCount = functionDef.parameters.length;
|
var paramCount = functionDef.parameters.length;
|
||||||
if (argCount != paramCount) {
|
if (argCount != paramCount) {
|
||||||
String error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
|
var error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate every argument
|
// Evaluate every argument
|
||||||
Expression[] args = new Expression[argCount];
|
var args = new Expression[argCount];
|
||||||
for (int i = 0; i < argCount; i++) {
|
for (int i = 0; i < argCount; i++) {
|
||||||
Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
|
var expression = (Expression) visit(ctx.arguments().expression(i));
|
||||||
if (!expression.type.equals(functionDef.parameters[i].type)) {
|
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());
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " Expected " + functionDef.parameters[i].type.getName() + " but got: " + expression.type.getName());
|
||||||
}
|
}
|
||||||
args[i] = expression;
|
args[i] = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionCall result = new FunctionCall(name, args);
|
var result = new FunctionCall(name, args);
|
||||||
result.type = functionDef.type;
|
result.type = functionDef.type;
|
||||||
result.line = line;
|
result.line = line;
|
||||||
result.col = col;
|
result.col = col;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) {
|
||||||
|
return visit(ctx.functionCall());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitConstructorCallExpression(KlangParser.ConstructorCallExpressionContext ctx) {
|
public Node visitConstructorCallExpression(KlangParser.ConstructorCallExpressionContext ctx) {
|
||||||
String name = ctx.IDENT().getText();
|
String name = ctx.IDENT().getText();
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
package de.hsrm.compiler.Klang.nodes;
|
package de.hsrm.compiler.Klang.nodes;
|
||||||
|
|
||||||
import de.hsrm.compiler.Klang.nodes.statements.Statement;
|
|
||||||
import de.hsrm.compiler.Klang.visitors.Visitor;
|
import de.hsrm.compiler.Klang.visitors.Visitor;
|
||||||
|
|
||||||
public class Block extends Node {
|
public class Block extends Node {
|
||||||
|
|
||||||
public Statement[] statements;
|
public Node[] statementsOrFunctionCalls;
|
||||||
|
|
||||||
public Block(Statement[] statements) {
|
public Block(Node[] statements) {
|
||||||
this.statements = statements;
|
this.statementsOrFunctionCalls = statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(Block e) {
|
public Value visit(Block e) {
|
||||||
for (var stmt : e.statements) {
|
for (var stmt : e.statementsOrFunctionCalls) {
|
||||||
var result = stmt.welcome(this);
|
var result = stmt.welcome(this);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ public class GenASM implements Visitor<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(Block e) {
|
public Void visit(Block e) {
|
||||||
for (var statement : e.statements) {
|
for (var statement : e.statementsOrFunctionCalls) {
|
||||||
statement.welcome(this);
|
statement.welcome(this);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -1,288 +0,0 @@
|
|||||||
package de.hsrm.compiler.Klang.visitors;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
class GetVars implements Visitor<Void> {
|
|
||||||
|
|
||||||
public Set<String> vars;
|
|
||||||
public Map<String, Type> types;
|
|
||||||
|
|
||||||
public GetVars(Set<String> vars, Map<String, Type> types) {
|
|
||||||
this.vars = vars;
|
|
||||||
this.types = types;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(IntegerExpression e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(FloatExpression e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(BooleanExpression e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(Variable e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(EqualityExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(NotEqualityExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(GTExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(GTEExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(LTExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(LTEExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(AdditionExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(SubstractionExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(MultiplicationExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(DivisionExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(ModuloExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(NegateExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(OrExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(AndExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
e.rhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(NotExpression e) {
|
|
||||||
e.lhs.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(IfStatement e) {
|
|
||||||
e.cond.welcome(this);
|
|
||||||
e.then.welcome(this);
|
|
||||||
if (e.alt != null) {
|
|
||||||
e.alt.welcome(this);
|
|
||||||
} else if (e.elif != null) {
|
|
||||||
e.elif.welcome(this);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(WhileLoop e) {
|
|
||||||
e.cond.welcome(this);
|
|
||||||
e.block.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(DoWhileLoop e) {
|
|
||||||
e.cond.welcome(this);
|
|
||||||
e.block.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(ForLoop e) {
|
|
||||||
e.init.welcome(this);
|
|
||||||
e.condition.welcome(this);
|
|
||||||
e.step.welcome(this);
|
|
||||||
e.block.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(VariableDeclaration e) {
|
|
||||||
vars.add(e.name);
|
|
||||||
types.put(e.name, e.type);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(VariableAssignment e) {
|
|
||||||
e.expression.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(ReturnStatement e) {
|
|
||||||
if (e.expression != null) {
|
|
||||||
e.expression.welcome(this);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(Block e) {
|
|
||||||
for (var statement : e.statements) {
|
|
||||||
statement.welcome(this);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(FunctionDefinition e) {
|
|
||||||
e.block.welcome(this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(FunctionCall e) {
|
|
||||||
for (var expression : e.arguments) {
|
|
||||||
expression.welcome(this);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(Program e) {
|
|
||||||
e.expression.welcome(this);
|
|
||||||
for (var func : e.funcs) {
|
|
||||||
func.welcome(this);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(Parameter e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(EnumDefinition e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(EnumValue e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(StructDefinition e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(StructField e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(MemberAccessExpression e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(EnumAccessExpression e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(ConstructorCall e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(NullExpression e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(DestructorCall e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Void visit(FieldAssignment e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user