Merge branch '1-grammatik-typinformationen-hinzufugen' into 'master'
Resolve "Grammatik: Typinformationen hinzufügen" Closes #1 See merge request mkais001/klang!7
This commit is contained in:
@@ -9,11 +9,15 @@ program
|
||||
;
|
||||
|
||||
functionDef
|
||||
: FUNC funcName=IDENT OPAR parameters CPAR braced_block
|
||||
: FUNC funcName=IDENT params=parameter_list returnType=type_annotation braced_block
|
||||
;
|
||||
|
||||
parameters
|
||||
: (IDENT (COMMA IDENT)*)?
|
||||
parameter_list
|
||||
: OPAR (parameter (COMMA parameter)*)? CPAR
|
||||
;
|
||||
|
||||
parameter
|
||||
: IDENT type_annotation
|
||||
;
|
||||
|
||||
braced_block
|
||||
@@ -48,7 +52,7 @@ variableDeclarationOrAssignment
|
||||
;
|
||||
|
||||
variable_declaration
|
||||
: LET IDENT (EQUAL expression)?
|
||||
: LET IDENT type_annotation (EQUAL expression)?
|
||||
;
|
||||
|
||||
variable_assignment
|
||||
@@ -85,6 +89,15 @@ atom
|
||||
| IDENT #variable
|
||||
;
|
||||
|
||||
type_annotation
|
||||
: COL type
|
||||
;
|
||||
|
||||
type
|
||||
: INTEGER
|
||||
| BOOLEAN
|
||||
;
|
||||
|
||||
functionCall
|
||||
: IDENT OPAR arguments CPAR
|
||||
;
|
||||
@@ -117,6 +130,7 @@ WHILE: 'while';
|
||||
DO: 'do';
|
||||
FOR: 'for';
|
||||
|
||||
COL: ':';
|
||||
SCOL: ';';
|
||||
OBRK: '{';
|
||||
CBRK: '}';
|
||||
@@ -140,6 +154,9 @@ SUB: '-';
|
||||
MOD: '%';
|
||||
DIV: '/';
|
||||
|
||||
BOOLEAN: 'bool';
|
||||
INTEGER: 'int';
|
||||
|
||||
INTEGER_LITERAL
|
||||
: [0-9]+
|
||||
;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package de.hsrm.compiler.Klang;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
|
||||
import de.hsrm.compiler.Klang.helper.FunctionInformation;
|
||||
import de.hsrm.compiler.Klang.nodes.*;
|
||||
import de.hsrm.compiler.Klang.nodes.expressions.*;
|
||||
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
||||
@@ -12,7 +13,13 @@ import de.hsrm.compiler.Klang.nodes.statements.*;
|
||||
import de.hsrm.compiler.Klang.types.Type;
|
||||
|
||||
public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
Set<String> vars = new HashSet<>();
|
||||
Map<String, VariableDeclaration> vars = new HashMap<>();
|
||||
Map<String, FunctionInformation> funcs;
|
||||
Type currentDeclaredReturnType;
|
||||
|
||||
public ContextAnalysis(Map<String, FunctionInformation> funcs) {
|
||||
this.funcs = funcs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitProgram(KlangParser.ProgramContext ctx) {
|
||||
@@ -21,7 +28,9 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i));
|
||||
}
|
||||
Expression expression = (Expression) this.visit(ctx.expression());
|
||||
return new Program(funcs, expression);
|
||||
Program result = new Program(funcs, expression);
|
||||
result.type = expression.type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -33,39 +42,80 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
|
||||
@Override
|
||||
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
|
||||
Statement[] statements = new Statement[ctx.statement().size()];
|
||||
|
||||
for (int i = 0; i < ctx.statement().size(); i++) {
|
||||
var stmtCtx = ctx.statement(i);
|
||||
Node currentStatement = this.visit(stmtCtx);
|
||||
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 existance 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
|
||||
this.currentDeclaredReturnType.combine(currentStatement.type);
|
||||
|
||||
// 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];
|
||||
for (int i = 0; i < actualStatementCount; i++) {
|
||||
newStatements[i] = statements[i];
|
||||
}
|
||||
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);
|
||||
result.type = null;
|
||||
if (hasReturn) {
|
||||
result.type = this.currentDeclaredReturnType;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitPrint(KlangParser.PrintContext ctx) {
|
||||
Node expression = this.visit(ctx.expression());
|
||||
return new PrintStatement((Expression) expression);
|
||||
PrintStatement result = new PrintStatement((Expression) expression);
|
||||
result.type = null;
|
||||
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);
|
||||
return new IfStatement((Expression) condition, (Block) thenBlock, (Block) elseBlock);
|
||||
result = new IfStatement((Expression) condition, (Block) thenBlock, (Block) elseBlock);
|
||||
type = elseBlock.type;
|
||||
} else if (ctx.elif != null) {
|
||||
Node elif = this.visit(ctx.elif);
|
||||
return new IfStatement((Expression) condition, (Block) thenBlock, (IfStatement) elif);
|
||||
result = new IfStatement((Expression) condition, (Block) thenBlock, (IfStatement) elif);
|
||||
type = elif.type;
|
||||
} else {
|
||||
return new IfStatement((Expression) condition, (Block) thenBlock);
|
||||
result = new IfStatement((Expression) condition, (Block) thenBlock);
|
||||
}
|
||||
|
||||
if (thenBlock.type != null && type != null) {
|
||||
result.type = thenBlock.type.combine(type);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,122 +144,231 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
@Override
|
||||
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
|
||||
String name = ctx.IDENT().getText();
|
||||
Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
|
||||
|
||||
if (this.vars.contains(name)) {
|
||||
throw new RuntimeException("Redeclaration of variable with name \"" + name +"\".");
|
||||
if (this.vars.get(name) != null) {
|
||||
throw new RuntimeException("Redeclaration of variable with name \"" + name + "\".");
|
||||
}
|
||||
|
||||
this.vars.add(name);
|
||||
|
||||
// Create the appropriate instance
|
||||
VariableDeclaration result;
|
||||
if (ctx.expression() != null) {
|
||||
return new VariableDeclaration(name, (Expression) this.visit(ctx.expression()));
|
||||
Node expression = this.visit(ctx.expression());
|
||||
declaredType = declaredType.combine(expression.type);
|
||||
result = new VariableDeclaration(name, (Expression) expression);
|
||||
result.type = declaredType; // add the type only if there is an expression
|
||||
} else {
|
||||
return new VariableDeclaration(name);
|
||||
result = new VariableDeclaration(name);
|
||||
}
|
||||
|
||||
// Add it to the global map of variable declarations
|
||||
this.vars.put(name, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) {
|
||||
String name = ctx.IDENT().getText();
|
||||
|
||||
if (!this.vars.contains(name)) {
|
||||
VariableDeclaration var = this.vars.get(name);
|
||||
if (var == null) {
|
||||
throw new RuntimeException("Variable with name \"" + name + "\" not defined.");
|
||||
}
|
||||
|
||||
// Evaluate the expression
|
||||
Expression expression = (Expression) this.visit(ctx.expression());
|
||||
|
||||
// Make sure expression can be assigned to the variable
|
||||
expression.type.combine(var.type);
|
||||
|
||||
// Create a new node and add the type of the expression to it
|
||||
return new VariableAssignment(name, expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
|
||||
Expression expression = (Expression) this.visit(ctx.expression());
|
||||
return new ReturnStatement(expression);
|
||||
ReturnStatement result = new ReturnStatement(expression);
|
||||
result.type = expression.type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitOrExpression(KlangParser.OrExpressionContext ctx) {
|
||||
return new OrExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
OrExpression result = new OrExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = lhs.type.combine(rhs.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitAndExpression(KlangParser.AndExpressionContext ctx) {
|
||||
return new AndExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
AndExpression result = new AndExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = lhs.type.combine(rhs.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitAdditionExpression(KlangParser.AdditionExpressionContext ctx) {
|
||||
return new AdditionExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
AdditionExpression result = new AdditionExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = lhs.type.combine(rhs.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) {
|
||||
return new EqualityExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
@Override
|
||||
public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
|
||||
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
|
||||
throw new RuntimeException("Both operants of this expression have to be a number");
|
||||
}
|
||||
|
||||
EqualityExpression result = new EqualityExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) {
|
||||
return new NotEqualityExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
@Override
|
||||
public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
|
||||
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
|
||||
throw new RuntimeException("Both operants of this expression have to be a number");
|
||||
}
|
||||
|
||||
NotEqualityExpression result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) {
|
||||
return new LTExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
@Override
|
||||
public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
|
||||
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
|
||||
throw new RuntimeException("Both operants of this expression have to be a number");
|
||||
}
|
||||
|
||||
LTExpression result = new LTExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) {
|
||||
return new GTExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
@Override
|
||||
public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
|
||||
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
|
||||
throw new RuntimeException("Both operants of this expression have to be a number");
|
||||
}
|
||||
|
||||
GTExpression result = new GTExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) {
|
||||
return new LTEExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
@Override
|
||||
public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
|
||||
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
|
||||
throw new RuntimeException("Both operants of this expression have to be a number");
|
||||
}
|
||||
|
||||
LTEExpression result = new LTEExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) {
|
||||
return new GTEExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
@Override
|
||||
public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) {
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
|
||||
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
|
||||
throw new RuntimeException("Both operants of this expression have to be a number");
|
||||
}
|
||||
|
||||
GTEExpression result = new GTEExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = Type.getBooleanType();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitSubstractionExpression(KlangParser.SubstractionExpressionContext ctx) {
|
||||
return new SubstractionExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
SubstractionExpression result = new SubstractionExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = lhs.type.combine(rhs.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitMultiplicationExpression(KlangParser.MultiplicationExpressionContext ctx) {
|
||||
return new MultiplicationExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
MultiplicationExpression result = new MultiplicationExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = lhs.type.combine(rhs.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitDivisionExpression(KlangParser.DivisionExpressionContext ctx) {
|
||||
return new DivisionExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
DivisionExpression result = new DivisionExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = lhs.type.combine(rhs.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitModuloExpression(KlangParser.ModuloExpressionContext ctx) {
|
||||
return new ModuloExpression((Expression) this.visit(ctx.lhs), (Expression) this.visit(ctx.rhs));
|
||||
Node lhs = this.visit(ctx.lhs);
|
||||
Node rhs = this.visit(ctx.rhs);
|
||||
ModuloExpression result = new ModuloExpression((Expression) lhs, (Expression) rhs);
|
||||
result.type = lhs.type.combine(rhs.type);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitNegateExpression(KlangParser.NegateExpressionContext ctx) {
|
||||
return new NegateExpression((Expression) this.visit(ctx.expression()));
|
||||
Node expression = this.visit(ctx.expression());
|
||||
NegateExpression result = new NegateExpression((Expression) expression);
|
||||
result.type = expression.type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitNotExpression(KlangParser.NotExpressionContext ctx) {
|
||||
return new NotExpression((Expression) this.visit(ctx.expression()));
|
||||
Node expression = this.visit(ctx.expression());
|
||||
NotExpression result = new NotExpression((Expression) expression);
|
||||
result.type = expression.type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitVariable(KlangParser.VariableContext ctx) {
|
||||
String name = ctx.IDENT().getText();
|
||||
|
||||
if (!this.vars.contains(name)) {
|
||||
VariableDeclaration var = this.vars.get(name);
|
||||
if (var == null) {
|
||||
throw new RuntimeException("Variable with name \"" + name + "\" not defined.");
|
||||
}
|
||||
|
||||
return new Variable(ctx.IDENT().getText());
|
||||
Variable result = new Variable(ctx.IDENT().getText());
|
||||
result.type = var.type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -234,28 +393,72 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
||||
@Override
|
||||
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
|
||||
String name = ctx.funcName.getText();
|
||||
Type returnType = Type.getByName(ctx.returnType.type().getText());
|
||||
this.currentDeclaredReturnType = returnType;
|
||||
|
||||
// Create a new set for the variables of the current function
|
||||
// this will be filled in the variable declaration visitor aswell
|
||||
this.vars = new HashSet<>();
|
||||
this.vars = new HashMap<>();
|
||||
|
||||
String[] params = new String[ctx.parameters().IDENT().size()];
|
||||
for (int i = 0; i < ctx.parameters().IDENT().size(); i++) {
|
||||
String paramName = ctx.parameters().IDENT(i).getText();
|
||||
params[i] = paramName;
|
||||
this.vars.add(paramName); // add the param as a variable
|
||||
// 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.type = param.type;
|
||||
this.vars.put(param.name, var);
|
||||
}
|
||||
|
||||
// Visit the block, make sure the types are matching
|
||||
Node block = this.visit(ctx.braced_block());
|
||||
return new FunctionDefinition(name, params, (Block) block);
|
||||
block.type.combine(returnType);
|
||||
|
||||
FunctionDefinition result = new FunctionDefinition(name, params, (Block) block);
|
||||
result.type = returnType;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
||||
String name = ctx.IDENT().getText();
|
||||
Type type = Type.getByName(ctx.type_annotation().type().getText());
|
||||
Parameter result = new Parameter(name);
|
||||
result.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) {
|
||||
String name = ctx.functionCall().IDENT().getText();
|
||||
Expression[] args = new Expression[ctx.functionCall().arguments().expression().size()];
|
||||
for (int i = 0; i < ctx.functionCall().arguments().expression().size(); i++) {
|
||||
args[i] = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
|
||||
|
||||
FunctionInformation func = this.funcs.get(name);
|
||||
if (func == null) {
|
||||
throw new RuntimeException("Function with name \"" + name + "\" not defined.");
|
||||
}
|
||||
return new FunctionCall(name, args);
|
||||
|
||||
// Make sure the number of arguments matches the number of parameters
|
||||
int argCount = ctx.functionCall().arguments().expression().size();
|
||||
int paramCount = func.parameters.size();
|
||||
if (argCount != paramCount) {
|
||||
throw new RuntimeException("Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".");
|
||||
}
|
||||
|
||||
// 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));
|
||||
expression.type.combine(func.signature[i]); // Make sure the types are matching
|
||||
args[i] = expression;
|
||||
}
|
||||
|
||||
FunctionCall result = new FunctionCall(name, args);
|
||||
result.type = func.returnType;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
51
src/main/java/de/hsrm/compiler/Klang/GetFunctions.java
Normal file
51
src/main/java/de/hsrm/compiler/Klang/GetFunctions.java
Normal file
@@ -0,0 +1,51 @@
|
||||
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<Void> {
|
||||
|
||||
private Map<String, FunctionInformation> funcs;
|
||||
|
||||
public GetFunctions(Map<String, FunctionInformation> 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();
|
||||
|
||||
if (this.funcs.containsKey(name)) {
|
||||
throw new Error("Function " + name + " defined multiple times");
|
||||
}
|
||||
|
||||
Type returnType = Type.getByName(ctx.returnType.type().getText());
|
||||
|
||||
TreeMap<String, Type> parameters = new TreeMap<String, Type>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,11 @@ import org.antlr.v4.runtime.tree.*;
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
|
||||
import de.hsrm.compiler.Klang.nodes.Node;
|
||||
import de.hsrm.compiler.Klang.visitors.*;
|
||||
import de.hsrm.compiler.Klang.helper.*;
|
||||
|
||||
public class Klang {
|
||||
|
||||
@@ -58,8 +60,15 @@ public class Klang {
|
||||
// create a parser that feeds off the tokens buffer
|
||||
KlangParser parser = new KlangParser(tokens);
|
||||
|
||||
// Parse tokens to AST
|
||||
ParseTree tree = parser.parse(); // begin parsing at init rule
|
||||
ContextAnalysis ctxAnal = new ContextAnalysis();
|
||||
|
||||
// Extract information about all functions
|
||||
var functionDefinitions = new HashMap<String, FunctionInformation>();
|
||||
new GetFunctions(functionDefinitions).visit(tree);
|
||||
|
||||
// Context Analysis and DAST generation
|
||||
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions);
|
||||
Node node = ctxAnal.visit(tree); // this gets us the DAST
|
||||
|
||||
if (prettyPrint) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package de.hsrm.compiler.Klang.helper;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import de.hsrm.compiler.Klang.types.Type;
|
||||
|
||||
public class FunctionInformation {
|
||||
public String name;
|
||||
public Type returnType;
|
||||
public Map<String, Type> parameters;
|
||||
public Type[] signature;
|
||||
|
||||
public FunctionInformation(String name, Type returnType, Map<String,Type> parameters, Type[] signature) {
|
||||
this.name = name;
|
||||
this.returnType = returnType;
|
||||
this.parameters = parameters;
|
||||
this.signature = signature;
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,15 @@ import de.hsrm.compiler.Klang.visitors.Visitor;
|
||||
public class FunctionDefinition extends Node {
|
||||
|
||||
public String name;
|
||||
public String[] parameters;
|
||||
public Parameter[] parameters;
|
||||
public Block block;
|
||||
|
||||
public FunctionDefinition(String name, String[] parameters, Block block) {
|
||||
public FunctionDefinition(String name, Parameter[] parameters, Block block) {
|
||||
this.name = name;
|
||||
this.parameters = parameters;
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <R> R welcome(Visitor<R> v) {
|
||||
return v.visit(this);
|
||||
|
||||
17
src/main/java/de/hsrm/compiler/Klang/nodes/Parameter.java
Normal file
17
src/main/java/de/hsrm/compiler/Klang/nodes/Parameter.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package de.hsrm.compiler.Klang.nodes;
|
||||
|
||||
import de.hsrm.compiler.Klang.visitors.Visitor;
|
||||
|
||||
public class Parameter extends Node {
|
||||
|
||||
public String name;
|
||||
|
||||
public Parameter(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R> R welcome(Visitor<R> v) {
|
||||
return v.visit(this);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ public class DoWhileLoop extends Statement {
|
||||
|
||||
public Expression cond;
|
||||
public Block block;
|
||||
public Block alt;
|
||||
|
||||
public DoWhileLoop(Expression cond, Block block) {
|
||||
this.cond = cond;
|
||||
|
||||
@@ -17,4 +17,20 @@ public class BooleanType extends PrimitiveType {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "bool";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type combine(Type that) {
|
||||
// Combining two equal types always works
|
||||
if (that.equals(this)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Every remaining type will throw a RuntimeException
|
||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,4 +17,23 @@ public class IntegerType extends PrimitiveType {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "int";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type combine(Type that) {
|
||||
// Combining two equal types always works
|
||||
if (that.equals(this)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Check other possible combinations
|
||||
// if (that.equals(Type.getFloatType())) return Type.getFloatType();
|
||||
|
||||
// Every remaining type will throw a RuntimeException
|
||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,5 +12,15 @@ public abstract class Type {
|
||||
return BooleanType.getType();
|
||||
}
|
||||
|
||||
public static Type getByName(String name) {
|
||||
switch (name) {
|
||||
case "bool": return getBooleanType();
|
||||
case "int": return getIntegerType();
|
||||
default: throw new RuntimeException("Unknown type " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract String getName();
|
||||
public abstract Type combine(Type that);
|
||||
public abstract boolean isPrimitiveType();
|
||||
}
|
||||
@@ -6,6 +6,7 @@ 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.expressions.*;
|
||||
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
|
||||
@@ -211,7 +212,7 @@ public class EvalVisitor implements Visitor<Value> {
|
||||
e.step.welcome(this);
|
||||
cond = e.condition.welcome(this);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -278,7 +279,7 @@ public class EvalVisitor implements Visitor<Value> {
|
||||
// Baue ein neues environment
|
||||
Map<String, Value> newEnv = new HashMap<>();
|
||||
for (int i = 0; i < func.parameters.length; i++) {
|
||||
newEnv.put(func.parameters[i], e.arguments[i].welcome(this));
|
||||
newEnv.put(func.parameters[i].name, e.arguments[i].welcome(this));
|
||||
}
|
||||
var oldEnv = this.env;
|
||||
this.env = newEnv;
|
||||
@@ -303,4 +304,9 @@ public class EvalVisitor implements Visitor<Value> {
|
||||
return e.expression.welcome(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value visit(Parameter e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -507,7 +507,7 @@ public class GenASM implements Visitor<Void> {
|
||||
int offset = 16; // Per Stack übergebene Parameter liegen über unserm BSP
|
||||
// Per stack übergebene variablen in env registrieren
|
||||
for (int i = this.rs.length; i < e.parameters.length; i++) {
|
||||
env.put(e.parameters[i], offset);
|
||||
env.put(e.parameters[i].name, offset);
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
@@ -516,7 +516,7 @@ public class GenASM implements Visitor<Void> {
|
||||
for (int i = 0; i < Math.min(this.rs.length, e.parameters.length); i++) {
|
||||
this.ex.write(" pushq " + this.rs[i] + "\n");
|
||||
offset -= 8;
|
||||
this.env.put(e.parameters[i], offset); // negative, liegt unter aktuellem BP
|
||||
this.env.put(e.parameters[i].name, offset); // negative, liegt unter aktuellem BP
|
||||
}
|
||||
|
||||
// Reserviere Platz auf dem Stack für jede lokale variable
|
||||
@@ -568,4 +568,11 @@ public class GenASM implements Visitor<Void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Parameter e) {
|
||||
// The work for a paremeter node is implement
|
||||
// in the function definition visitor
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -225,4 +225,9 @@ class GetVars implements Visitor<Void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Parameter e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -328,13 +328,15 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
||||
ex.write(e.name);
|
||||
ex.write("(");
|
||||
boolean first = true;
|
||||
for (String param : e.parameters) {
|
||||
for (Parameter param : e.parameters) {
|
||||
if (!first) {
|
||||
ex.write(", ");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
ex.write(param);
|
||||
ex.write(param.name);
|
||||
ex.write(":");
|
||||
ex.write(param.type.getName());
|
||||
}
|
||||
ex.write(") ");
|
||||
e.block.welcome(this);
|
||||
@@ -364,4 +366,10 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visit(Parameter e) {
|
||||
// The work is already done in the function definition visitor
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ 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.expressions.*;
|
||||
import de.hsrm.compiler.Klang.nodes.loops.*;
|
||||
@@ -38,4 +39,5 @@ public interface Visitor<R> {
|
||||
R visit(FunctionDefinition e);
|
||||
R visit(FunctionCall e);
|
||||
R visit(Program e);
|
||||
R visit(Parameter e);
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
int eq(int x, int y);
|
||||
int neq(int x, int y);
|
||||
int lt(int x, int y);
|
||||
int lte(int x, int y);
|
||||
int gt(int x, int y);
|
||||
int gte(int x, int y);
|
||||
bool eq(int x, int y);
|
||||
bool neq(int x, int y);
|
||||
bool lt(int x, int y);
|
||||
bool lte(int x, int y);
|
||||
bool gt(int x, int y);
|
||||
bool gte(int x, int y);
|
||||
|
||||
bool and(bool a, bool b);
|
||||
bool or(bool a, bool b);
|
||||
|
||||
@@ -1,176 +1,176 @@
|
||||
function add(x, y) {
|
||||
function add(x: int, y: int): int {
|
||||
return (x + y);
|
||||
}
|
||||
|
||||
function sub (x, y) {
|
||||
function sub (x: int, y: int): int {
|
||||
return (x - y);
|
||||
}
|
||||
|
||||
function mul(x, y) {
|
||||
function mul(x: int, y: int): int {
|
||||
return (x * y);
|
||||
}
|
||||
|
||||
function modulo(x, y) {
|
||||
function modulo(x: int, y: int): int {
|
||||
return (x % y);
|
||||
}
|
||||
|
||||
function neg(x) {
|
||||
function neg(x: int): int {
|
||||
return -x;
|
||||
}
|
||||
|
||||
function id(x) {
|
||||
function id(x: int): int {
|
||||
return x;
|
||||
}
|
||||
|
||||
function arg1(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg1(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return a;
|
||||
}
|
||||
|
||||
function get1() {
|
||||
function get1(): int {
|
||||
return arg1(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg2(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg2(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return b;
|
||||
}
|
||||
|
||||
function get2() {
|
||||
function get2(): int {
|
||||
return arg2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg3(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg3(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return c;
|
||||
}
|
||||
|
||||
function get3() {
|
||||
function get3(): int {
|
||||
return arg3(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg4(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg4(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return d;
|
||||
}
|
||||
|
||||
function get4() {
|
||||
function get4(): int {
|
||||
return arg4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg5(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg5(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return e;
|
||||
}
|
||||
|
||||
function get5() {
|
||||
function get5(): int {
|
||||
return arg5(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg6(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg6(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return f;
|
||||
}
|
||||
|
||||
function get6() {
|
||||
function get6(): int {
|
||||
return arg6(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg7(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg7(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return g;
|
||||
}
|
||||
|
||||
function get7() {
|
||||
function get7(): int {
|
||||
return arg7(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg8(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg8(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return h;
|
||||
}
|
||||
|
||||
function get8() {
|
||||
function get8(): int {
|
||||
return arg8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg9(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg9(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return i;
|
||||
}
|
||||
|
||||
function get9() {
|
||||
function get9(): int {
|
||||
return arg9(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function arg10(a, b, c, d, e, f, g, h, i ,j) {
|
||||
function arg10(a: int, b: int, c: int, d: int, e: int, f: int, g: int, h: int, i: int ,j: int): int {
|
||||
return j;
|
||||
}
|
||||
|
||||
function get10() {
|
||||
function get10(): int {
|
||||
return arg10(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
}
|
||||
|
||||
function fac(x) {
|
||||
function fac(x: int): int {
|
||||
if (x) {
|
||||
return (x * fac((x - 1)));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
function eq(x, y) {
|
||||
function eq(x: int, y: int): bool {
|
||||
return (x == y);
|
||||
}
|
||||
|
||||
function neq(x, y) {
|
||||
function neq(x: int, y: int): bool {
|
||||
return (x != y);
|
||||
}
|
||||
|
||||
function lt(x, y) {
|
||||
function lt(x: int, y: int): bool {
|
||||
return (x < y);
|
||||
}
|
||||
|
||||
function lte(x, y) {
|
||||
function lte(x: int, y: int): bool {
|
||||
return (x <= y);
|
||||
}
|
||||
|
||||
function gt(x, y) {
|
||||
function gt(x: int, y: int): bool {
|
||||
return (x > y);
|
||||
}
|
||||
|
||||
function gte(x, y) {
|
||||
function gte(x: int, y: int): bool {
|
||||
return (x >= y);
|
||||
}
|
||||
|
||||
function selfMinus(x) {
|
||||
function selfMinus(x: int): int {
|
||||
x = (x - 1);
|
||||
return x;
|
||||
}
|
||||
|
||||
function myWhile(end) {
|
||||
let cond = 0;
|
||||
function myWhile(end: int): int {
|
||||
let cond: int = 0;
|
||||
while ((cond < end)) {
|
||||
cond = (cond + 1);
|
||||
}
|
||||
return cond;
|
||||
}
|
||||
|
||||
function myDoWhile(end) {
|
||||
let cond = 0;
|
||||
function myDoWhile(end: int): int {
|
||||
let cond: int = 0;
|
||||
do {
|
||||
cond = (cond + 1);
|
||||
} while((cond < end));
|
||||
return cond;
|
||||
}
|
||||
|
||||
function myFor(end) {
|
||||
let x = 0;
|
||||
for (let i = 0; (i < end); i = (i + 1)) {
|
||||
function myFor(end: int): int {
|
||||
let x: int = 0;
|
||||
for (let i: int = 0; (i < end); i = (i + 1)) {
|
||||
x = (x + 1);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function and(a, b) {
|
||||
function and(a: bool, b: bool): bool {
|
||||
return (a && b);
|
||||
}
|
||||
|
||||
function or(a, b) {
|
||||
function or(a: bool, b: bool): bool {
|
||||
return (a || b);
|
||||
}
|
||||
|
||||
function not(a) {
|
||||
function not(a: bool): bool {
|
||||
return !a;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user