diff --git a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 index 136d971..0354533 100644 --- a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 +++ b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 @@ -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]+ ; diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 80facdb..090fac0 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -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 { - Set vars = new HashSet<>(); + Map vars = new HashMap<>(); + Map funcs; + Type currentDeclaredReturnType; + + public ContextAnalysis(Map funcs) { + this.funcs = funcs; + } @Override public Node visitProgram(KlangParser.ProgramContext ctx) { @@ -21,7 +28,9 @@ public class ContextAnalysis extends KlangBaseVisitor { 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 { @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 { @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 { @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; } } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java b/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java new file mode 100644 index 0000000..9250ad9 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java @@ -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 { + + private Map funcs; + + public GetFunctions(Map funcs) { + this.funcs = funcs; + } + + @Override + public Void visitProgram(KlangParser.ProgramContext ctx) { + for (int i = 0; i < ctx.functionDef().size(); i++) { + this.visit(ctx.functionDef(i)); + } + return null; + } + + @Override + public Void visitFunctionDef(KlangParser.FunctionDefContext ctx) { + String name = ctx.funcName.getText(); + + if (this.funcs.containsKey(name)) { + throw new Error("Function " + name + " defined multiple times"); + } + + Type returnType = Type.getByName(ctx.returnType.type().getText()); + + TreeMap parameters = new TreeMap(); + + // Process the paremter list by visiting every paremter in it + int paramCount = ctx.params.parameter().size(); + Type[] signature = new Type[paramCount]; + for (int i = 0; i < paramCount; i++) { + Type paramType = Type.getByName(ctx.params.parameter(i).type_annotation().type().getText()); + String paramName = ctx.params.parameter(i).IDENT().getText(); + parameters.put(paramName, paramType); + signature[i] = paramType; + } + + FunctionInformation information = new FunctionInformation(name, returnType, parameters, signature); + this.funcs.put(name, information); + return null; + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/Klang.java b/src/main/java/de/hsrm/compiler/Klang/Klang.java index 5696616..44a12fc 100644 --- a/src/main/java/de/hsrm/compiler/Klang/Klang.java +++ b/src/main/java/de/hsrm/compiler/Klang/Klang.java @@ -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(); + 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) { diff --git a/src/main/java/de/hsrm/compiler/Klang/helper/FunctionInformation.java b/src/main/java/de/hsrm/compiler/Klang/helper/FunctionInformation.java new file mode 100644 index 0000000..97508f0 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/helper/FunctionInformation.java @@ -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 parameters; + public Type[] signature; + + public FunctionInformation(String name, Type returnType, Map parameters, Type[] signature) { + this.name = name; + this.returnType = returnType; + this.parameters = parameters; + this.signature = signature; + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/FunctionDefinition.java b/src/main/java/de/hsrm/compiler/Klang/nodes/FunctionDefinition.java index afc2af6..45c1ca8 100644 --- a/src/main/java/de/hsrm/compiler/Klang/nodes/FunctionDefinition.java +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/FunctionDefinition.java @@ -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 welcome(Visitor v) { return v.visit(this); diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/Parameter.java b/src/main/java/de/hsrm/compiler/Klang/nodes/Parameter.java new file mode 100644 index 0000000..678a0ba --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/Parameter.java @@ -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 welcome(Visitor v) { + return v.visit(this); + } +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/loops/DoWhileLoop.java b/src/main/java/de/hsrm/compiler/Klang/nodes/loops/DoWhileLoop.java index 8e49150..6398afa 100644 --- a/src/main/java/de/hsrm/compiler/Klang/nodes/loops/DoWhileLoop.java +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/loops/DoWhileLoop.java @@ -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; diff --git a/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java b/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java index d5d1517..78bcb6b 100644 --- a/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java +++ b/src/main/java/de/hsrm/compiler/Klang/types/BooleanType.java @@ -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()); + } + } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java b/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java index 3803b9e..c3fad32 100644 --- a/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java +++ b/src/main/java/de/hsrm/compiler/Klang/types/IntegerType.java @@ -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()); + } + } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/types/Type.java b/src/main/java/de/hsrm/compiler/Klang/types/Type.java index bc429d7..a4771b5 100644 --- a/src/main/java/de/hsrm/compiler/Klang/types/Type.java +++ b/src/main/java/de/hsrm/compiler/Klang/types/Type.java @@ -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(); } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java index 7151a76..a999389 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java @@ -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 { e.step.welcome(this); cond = e.condition.welcome(this); } - + return result; } @@ -278,7 +279,7 @@ public class EvalVisitor implements Visitor { // Baue ein neues environment Map 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 { return e.expression.welcome(this); } + @Override + public Value visit(Parameter e) { + return null; + } + } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java index 8dcc6e0..d9ecf17 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java @@ -507,7 +507,7 @@ public class GenASM implements Visitor { 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 { 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 { return null; } + @Override + public Void visit(Parameter e) { + // The work for a paremeter node is implement + // in the function definition visitor + return null; + } + } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java index 0d55d41..b471245 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java @@ -225,4 +225,9 @@ class GetVars implements Visitor { return null; } + @Override + public Void visit(Parameter e) { + return null; + } + } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java index 0b35907..4fe6ed2 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java @@ -328,13 +328,15 @@ public class PrettyPrintVisitor implements Visitor { 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 { return null; } + @Override + public Void visit(Parameter e) { + // The work is already done in the function definition visitor + return null; + } + } \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java index b0abb24..77ffc43 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/Visitor.java @@ -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 visit(FunctionDefinition e); R visit(FunctionCall e); R visit(Program e); + R visit(Parameter e); } \ No newline at end of file diff --git a/src/test/comparison/comparison.h b/src/test/comparison/comparison.h index f5a9566..2030b9f 100644 --- a/src/test/comparison/comparison.h +++ b/src/test/comparison/comparison.h @@ -1,11 +1,11 @@ #include -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); diff --git a/src/test/test.k b/src/test/test.k index 5478bca..a7d1603 100644 --- a/src/test/test.k +++ b/src/test/test.k @@ -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; }