diff --git a/src/main/java/com/InterpreterMain.java b/src/main/java/com/InterpreterMain.java index 4b63dc6..08912f3 100644 --- a/src/main/java/com/InterpreterMain.java +++ b/src/main/java/com/InterpreterMain.java @@ -23,4 +23,4 @@ public static void main(String[] args) throws Exception { outStream.flush(); } -} +} \ No newline at end of file diff --git a/src/main/java/com/StmtParserMain.java b/src/main/java/com/StmtParserMain.java index 5dc6118..b0ac771 100644 --- a/src/main/java/com/StmtParserMain.java +++ b/src/main/java/com/StmtParserMain.java @@ -12,8 +12,13 @@ public static void main(String[] args) throws Exception { DECLARE b; a = 1 + 2; b = 5; + FUNCTION foo(c,d){ + RETURN 0; + }; + c = 1; + PRINT c; PRINT a ? b + 1 : -1; - PRINT 1 + 2; + PRINT CALL foo(a,b); PRINT 3 + 4; } """; diff --git a/src/main/java/com/compiler/ExpressionParser.java b/src/main/java/com/compiler/ExpressionParser.java index 3231250..a286432 100644 --- a/src/main/java/com/compiler/ExpressionParser.java +++ b/src/main/java/com/compiler/ExpressionParser.java @@ -227,17 +227,18 @@ ASTExprNode getAndOrExpr() throws Exception { } ASTExprNode getCallExpr() throws Exception { + // callExpr: CALL IDENTIFIER LPAREN argList RPAREN if(m_lexer.m_currentToken.m_type != TokenIntf.Type.CALL) { return getParantheseExpr(); }else { m_lexer.advance(); String funcName = m_lexer.m_currentToken.m_value; + m_lexer.expect(TokenIntf.Type.IDENT); FunctionInfo functionInfo = m_functionTable.getFunction(funcName); if(functionInfo == null) { m_lexer.throwCompilerException(String.format("%s not declared" , funcName), ""); } - m_lexer.expect(TokenIntf.Type.IDENT); m_lexer.expect(TokenIntf.Type.LPAREN); List argumentList = getArgumentList(); if(argumentList.size() != functionInfo.varNames.size()){ @@ -251,8 +252,9 @@ ASTExprNode getCallExpr() throws Exception { } private List getArgumentList() throws Exception { + // argList: expr argListPost | eps + // argListPost: eps | COMMA expr argListPost List argumentList = new ArrayList<>(); - if(m_lexer.m_currentToken.m_type == Type.RPAREN){ return argumentList; }else { diff --git a/src/main/java/com/compiler/StmtParser.java b/src/main/java/com/compiler/StmtParser.java index 1c58a5c..5af42fd 100644 --- a/src/main/java/com/compiler/StmtParser.java +++ b/src/main/java/com/compiler/StmtParser.java @@ -92,25 +92,35 @@ public ASTStmtNode parseStmt() throws Exception { } public ASTStmtNode parseFunctionStmt() throws Exception { + // functionDecl: FUNCTION IDENTIFIER RPAREN paramList RPAREN LBRACE functionBody RBRACE SEMICOLON m_lexer.expect(Type.FUNCTION); String functionName = m_lexer.m_currentToken.m_value; + m_lexer.expect(TokenIntf.Type.IDENT); m_lexer.expect(Type.LPAREN); + List parameterList = parseParameterList(); - parameterList.forEach(e -> m_symbolTable.createSymbol(e)); - m_lexer.expect(TokenIntf.Type.RPAREN); + parameterList.forEach(m_symbolTable::createSymbol); + m_functionTable.createFunction(functionName, parameterList); + m_lexer.expect(TokenIntf.Type.RPAREN); m_lexer.expect(TokenIntf.Type.LBRACE); + List functionBody = parseFunctionBody(); + m_lexer.expect(TokenIntf.Type.RBRACE); m_lexer.expect(Type.SEMICOLON); - m_functionTable.createFunction(functionName, parameterList); + return new ASTFunctionStmtNode(functionName, parameterList, functionBody); } private List parseFunctionBody() throws Exception { + // functionBody: returnStmt | stmt functionBody List stmtList = new ArrayList<>(); while(m_lexer.m_currentToken.m_type != Type.RETURN){ + if(m_lexer.m_currentToken.m_type == Type.RBRACE){ + m_lexer.throwCompilerException("Invalid end of function body", "RETURN"); + } stmtList.add(parseStmt()); } stmtList.add(parseReturnStmt()); @@ -118,6 +128,7 @@ private List parseFunctionBody() throws Exception { } private ASTStmtNode parseReturnStmt() throws Exception { + // returnStmt: RETURN expr m_lexer.expect(Type.RETURN); ASTStmtNode returnStmtNode = new ASTReturnStmtNode(m_exprParser.getQuestionMarkExpr()); m_lexer.expect(Type.SEMICOLON); @@ -125,6 +136,8 @@ private ASTStmtNode parseReturnStmt() throws Exception { } private List parseParameterList() throws Exception { + // paramList: IDENTIFIER paramListPos | eps + // paramListPost: eps | COMMA IDENFIER paramListPost List parameterList = new ArrayList<>(); if(m_lexer.m_currentToken.m_type != Type.IDENT){ return parameterList; @@ -143,7 +156,6 @@ private List parseParameterList() throws Exception { public ASTStmtNode parsePrintStmt() throws Exception { m_lexer.expect(Type.PRINT); - ASTPrintStmtNode astPrintStmtNode = new ASTPrintStmtNode(m_exprParser.getQuestionMarkExpr()); m_lexer.expect(Type.SEMICOLON); return astPrintStmtNode; diff --git a/src/main/java/com/compiler/ast/ASTCallExprNode.java b/src/main/java/com/compiler/ast/ASTCallExprNode.java index 864a8c8..2076e55 100644 --- a/src/main/java/com/compiler/ast/ASTCallExprNode.java +++ b/src/main/java/com/compiler/ast/ASTCallExprNode.java @@ -1,5 +1,12 @@ package com.compiler.ast; +import com.compiler.InstrBlock; +import com.compiler.InstrIntf; +import com.compiler.instr.InstrJump; +import com.compiler.instr.InstrPopValueStack; +import com.compiler.instr.InstrPushCallStack; +import com.compiler.instr.InstrPushValueStack; + import java.io.OutputStreamWriter; import java.util.List; @@ -14,11 +21,33 @@ public ASTCallExprNode(String funcName, List argumentList) { @Override public int eval() { - return 0; + return 0; // not implemented, as Call Expression only returns a value with prior function stmt } @Override public void print(OutputStreamWriter outStream, String indent) throws Exception { + outStream.write(indent); + outStream.write("ASTCallExpressionNode "); + outStream.write("\n"); + for (ASTExprNode astExprNode : m_argumentList) { + astExprNode.print(outStream, indent + " "); + } + } + + @Override + public InstrIntf codegen(com.compiler.CompileEnvIntf env) { + m_argumentList.stream() + .map(arg -> arg.codegen(env)) + .forEach(argInstr -> env.addInstr(new InstrPushValueStack(argInstr))); + env.addInstr(new InstrPushCallStack()); + + InstrBlock functionBodyBlock = env.getFunctionTable().getFunction(m_funcName).m_body; + env.addInstr(new InstrJump(functionBodyBlock)); + + InstrIntf popInstr = new InstrPopValueStack(); + env.addInstr(popInstr); + return popInstr; } + } diff --git a/src/main/java/com/compiler/ast/ASTFunctionStmtNode.java b/src/main/java/com/compiler/ast/ASTFunctionStmtNode.java index 35c7d23..80e3140 100644 --- a/src/main/java/com/compiler/ast/ASTFunctionStmtNode.java +++ b/src/main/java/com/compiler/ast/ASTFunctionStmtNode.java @@ -1,12 +1,13 @@ package com.compiler.ast; import java.io.OutputStreamWriter; +import java.util.Collections; import java.util.List; import com.compiler.CompileEnvIntf; -import com.compiler.FunctionInfo; -import com.compiler.FunctionTable; import com.compiler.InstrBlock; +import com.compiler.instr.InstrAssign; +import com.compiler.instr.InstrPopValueStack; public class ASTFunctionStmtNode extends ASTStmtNode { String m_functionName; @@ -22,14 +23,40 @@ public ASTFunctionStmtNode(String functionName, List parameterList, List @Override public void execute(OutputStreamWriter out) { - + // nothing to do at runtime } @Override public void codegen(CompileEnvIntf env) { - super.codegen(env); + InstrBlock codegenEntryBlock = env.getCurrentBlock(); + + InstrBlock functionBodyBlock = env.createBlock("function_"+m_functionName); + env.setCurrentBlock(functionBodyBlock); + + m_parameterList.stream() + .sorted(Collections.reverseOrder()) + .map(env.getSymbolTable()::getSymbol) + .forEach(parameter -> { + InstrPopValueStack popStackInstr = new InstrPopValueStack(); + env.addInstr(popStackInstr); + InstrAssign assignInstr = new InstrAssign(parameter, popStackInstr); + env.addInstr(assignInstr); + }); + + m_functionBody.forEach(stmt -> stmt.codegen(env)); + + env.getFunctionTable().getFunction(m_functionName).m_body = functionBodyBlock; + + env.setCurrentBlock(codegenEntryBlock); } @Override - public void print(OutputStreamWriter outStream, String indent) throws Exception {} + public void print(OutputStreamWriter outStream, String indent) throws Exception { + outStream.write(indent); + outStream.write("ASTFunctionStmtNode "); + outStream.write("\n"); + for (ASTStmtNode astStmtNode : m_functionBody) { + astStmtNode.print(outStream, indent + " "); + } + } } diff --git a/src/main/java/com/compiler/ast/ASTReturnStmtNode.java b/src/main/java/com/compiler/ast/ASTReturnStmtNode.java index a258aef..39e6205 100644 --- a/src/main/java/com/compiler/ast/ASTReturnStmtNode.java +++ b/src/main/java/com/compiler/ast/ASTReturnStmtNode.java @@ -1,5 +1,9 @@ package com.compiler.ast; +import com.compiler.CompileEnvIntf; +import com.compiler.instr.InstrPushValueStack; +import com.compiler.instr.InstrReturn; + import java.io.OutputStreamWriter; public class ASTReturnStmtNode extends ASTStmtNode { @@ -11,11 +15,19 @@ public ASTReturnStmtNode(ASTExprNode questionMarkExpr) { @Override public void execute(OutputStreamWriter out) { - + // nothing to do } @Override public void print(OutputStreamWriter outStream, String indent) throws Exception { - + outStream.write(indent); + outStream.write("ASTReturnStmtNode "); + outStream.write("\n"); + m_expr.print(outStream, indent + " "); + } + @Override + public void codegen(CompileEnvIntf env) { + env.addInstr(new InstrPushValueStack(m_expr.codegen(env))); + env.addInstr(new InstrReturn()); } } diff --git a/src/main/java/com/compiler/instr/InstrPopValueStack.java b/src/main/java/com/compiler/instr/InstrPopValueStack.java new file mode 100644 index 0000000..6ca190b --- /dev/null +++ b/src/main/java/com/compiler/instr/InstrPopValueStack.java @@ -0,0 +1,23 @@ +package com.compiler.instr; + +import com.compiler.ExecutionEnvIntf; +import com.compiler.InstrIntf; + +import java.io.OutputStreamWriter; + +public class InstrPopValueStack extends InstrIntf { + + public InstrPopValueStack() {} + + @Override + public void execute(ExecutionEnvIntf env) throws Exception { + m_value = env.pop(); + } + + @Override + public void trace(OutputStreamWriter os) throws Exception { + os.write(String.format("%%%d = POP\n", + m_id + )); + } +} diff --git a/src/main/java/com/compiler/instr/InstrPushCallStack.java b/src/main/java/com/compiler/instr/InstrPushCallStack.java new file mode 100644 index 0000000..ecc3689 --- /dev/null +++ b/src/main/java/com/compiler/instr/InstrPushCallStack.java @@ -0,0 +1,19 @@ +package com.compiler.instr; + +import com.compiler.ExecutionEnvIntf; +import com.compiler.InstrIntf; + +import java.io.OutputStreamWriter; + +public class InstrPushCallStack extends InstrIntf { + @Override + public void execute(ExecutionEnvIntf env) throws Exception { + env.pushReturnAddr(env.getInstrIter()); + } + + @Override + public void trace(OutputStreamWriter os) throws Exception { + os.write("PUSH_RETURN_ADDR\n"); + + } +} diff --git a/src/main/java/com/compiler/instr/InstrPushValueStack.java b/src/main/java/com/compiler/instr/InstrPushValueStack.java new file mode 100644 index 0000000..c117416 --- /dev/null +++ b/src/main/java/com/compiler/instr/InstrPushValueStack.java @@ -0,0 +1,25 @@ +package com.compiler.instr; + +import com.compiler.ExecutionEnvIntf; +import com.compiler.InstrIntf; + +import java.io.OutputStreamWriter; + +public class InstrPushValueStack extends InstrIntf { + InstrIntf m_instruction; + public InstrPushValueStack(InstrIntf instruction) { + m_instruction = instruction; + } + + @Override + public void execute(ExecutionEnvIntf env) throws Exception { + env.push(m_instruction.getValue()); + } + + @Override + public void trace(OutputStreamWriter os) throws Exception { + os.write(String.format("PUSH %%%d\n", + m_instruction.getId() + )); + } +} diff --git a/src/main/java/com/compiler/instr/InstrReturn.java b/src/main/java/com/compiler/instr/InstrReturn.java new file mode 100644 index 0000000..31034c5 --- /dev/null +++ b/src/main/java/com/compiler/instr/InstrReturn.java @@ -0,0 +1,18 @@ +package com.compiler.instr; + +import com.compiler.ExecutionEnvIntf; +import com.compiler.InstrIntf; + +import java.io.OutputStreamWriter; + +public class InstrReturn extends InstrIntf { + @Override + public void execute(ExecutionEnvIntf env) throws Exception { + env.setInstrIter(env.popReturnAddr()); + } + + @Override + public void trace(OutputStreamWriter os) throws Exception { + os.write("JUMP POP_RETURN_ADDR"); + } +} diff --git a/src/test/java/com/compiler/InterpreterTestFunctionStmts.java b/src/test/java/com/compiler/InterpreterTestFunctionStmts.java new file mode 100644 index 0000000..8bd96b1 --- /dev/null +++ b/src/test/java/com/compiler/InterpreterTestFunctionStmts.java @@ -0,0 +1,99 @@ +package com.compiler; + +import org.junit.Test; + +public class InterpreterTestFunctionStmts extends InterpreterTestBase { + + @Test + public void testProgrammSimple() throws Exception { + String programm = """ + { + FUNCTION minüß3(x) { + EXECUTE 3 TIMES { + x = x - 1; + }; + RETURN x; + }; + PRINT CALL minüß3(45); + } + """; + String expectedOutput = """ + 42 + """; + testInterpreter(programm, expectedOutput); + } + + @Test + public void testFunction1NoParams() throws Exception { + String programm = """ + { + FUNCTION myFct() { + RETURN 1; + }; + PRINT CALL myFct(); + } + """; + String expectedOutput = """ + 1 + """; + testInterpreter(programm, expectedOutput); + } + + @Test + public void testFunction2WithParams() throws Exception { + String programm = """ + { + FUNCTION myFct(a, b) { + RETURN a + b; + }; + PRINT CALL myFct(1, 2); + } + """; + String expectedOutput = """ + 3 + """; + testInterpreter(programm, expectedOutput); + } + + @Test + public void testFunction3MultipleCalls() throws Exception { + String programm = """ + { + FUNCTION myFct1(a, b) { + DECLARE c; + c = a + 2 * b; + RETURN c; + }; + FUNCTION myFct2(d) { + RETURN d + 1; + }; + DECLARE e; + e = CALL myFct1(1, 2) + CALL myFct2(3); + PRINT e; + } + """; + String expectedOutput = """ + 9 + """; + testInterpreter(programm, expectedOutput); + } + + @Test + public void testFunction4CallInReturn() throws Exception { + String programm = """ + { + FUNCTION myFct1(a, b) { + RETURN a * b; + }; + FUNCTION myFct2(c) { + RETURN CALL myFct1(c, c + 1); + }; + PRINT CALL myFct2(5); + } + """; + String expectedOutput = """ + 30 + """; + testInterpreter(programm, expectedOutput); + } +}