diff options
author | AidenRushbrooke <72034940+AidenRushbrooke@users.noreply.github.com> | 2021-12-04 05:24:43 +0000 |
---|---|---|
committer | AidenRushbrooke <72034940+AidenRushbrooke@users.noreply.github.com> | 2021-12-04 05:24:43 +0000 |
commit | 43909b350b9084ed33f121a15c5770224cbdc79f (patch) | |
tree | aff7f471784fbb1d1a3597acfeb43624d4ed94ad | |
parent | cc1f6e712520793d5a8c638a6e995c018917eadb (diff) | |
download | esotericFORTRAN-43909b350b9084ed33f121a15c5770224cbdc79f.tar.gz esotericFORTRAN-43909b350b9084ed33f121a15c5770224cbdc79f.zip |
Added basic function support
-rw-r--r-- | src/.vscode/launch.json | 2 | ||||
-rw-r--r-- | src/Compiler/Expression.java | 16 | ||||
-rw-r--r-- | src/Compiler/Parser.java | 140 | ||||
-rw-r--r-- | src/Compiler/Statement.java | 67 | ||||
-rw-r--r-- | src/Compiler/Token.java | 1 | ||||
-rw-r--r-- | src/Compiler/TokenScanner.java | 3 | ||||
-rw-r--r-- | src/Compiler/TokenType.java | 3 | ||||
-rw-r--r-- | src/Compiler/Translator.java | 78 | ||||
-rw-r--r-- | src/Compiler/Utils.java | 3 | ||||
-rw-r--r-- | src/examples/example.ft | 15 |
10 files changed, 301 insertions, 27 deletions
diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index f36d26d..bc86f64 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -23,7 +23,7 @@ "request": "launch", "mainClass": "Compiler.Language", "projectName": "src_1da2a030", - "args": "example.txt" + "args": "examples/example.ft -c -e" } ] }
\ No newline at end of file diff --git a/src/Compiler/Expression.java b/src/Compiler/Expression.java index 8bdf378..2789462 100644 --- a/src/Compiler/Expression.java +++ b/src/Compiler/Expression.java @@ -102,7 +102,6 @@ abstract class Expression { } static class ArrayVariable extends Expression{ - ArrayVariable(Token name,List<Expression> positions){ this.name=name; this.positions=positions; @@ -114,7 +113,22 @@ abstract class Expression { } final Token name; final List<Expression> positions; + } + + static class FunctionCall extends Expression{ + FunctionCall(Token name, List<Token> arguments){ + this.arguments=arguments; + this.name=name; + } + final List<Token> arguments; + final Token name; + + @Override + public String getExpressionType() { + return "functCall"; + } } + public abstract String getExpressionType(); } diff --git a/src/Compiler/Parser.java b/src/Compiler/Parser.java index eba3513..ae2ed23 100644 --- a/src/Compiler/Parser.java +++ b/src/Compiler/Parser.java @@ -1,22 +1,25 @@ package Compiler; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class Parser { private final List<Token> tokens; private int currentToken = 0; + private final Map<String,String> definedVars = new HashMap<>(); + List<Statement> statements = new ArrayList<>(); Parser(List<Token> tokens){ this.tokens=tokens; } List<Statement> parse(){ - List<Statement> statements = new ArrayList<>(); try{ while (!checkEOF()){ - statements.add(statement()); + statements.add(functionCalls()); } }catch (Error e){ return null; @@ -25,6 +28,52 @@ public class Parser { } + private Statement functionCalls(){ + if(matchAndAdvance(TokenType.PROGRAM)){ + matchOrError(TokenType.IDENTIFIER, "Expected program name"); + Token programname = getPreviousToken(); + Statement block = blockStatement(false); + matchOrError(TokenType.PROGRAM,"Programs must end in 'program'"); + matchOrError(TokenType.IDENTIFIER,"Expected program name"); + if(!(getPreviousToken().text.equals(programname.text))){ + throw error(getPreviousToken(), "Program names do not match"); + } + return new Statement.MainFunction(block); + } else if(matchAndAdvance(TokenType.FUNCTION)){ + String returntype=null; + if(matchAndAdvance(TokenType.INT)){ + returntype="int"; + } else if (matchAndAdvance(TokenType.REAL)){ + returntype="real"; + }else{ + throw error(getCurrentToken(), "Expected function return type"); + } + matchOrError(TokenType.IDENTIFIER, "Expected function name"); + Token name = getPreviousToken(); + definedVars.put(name.text, "function"); + matchOrError(TokenType.LEFT_PAREN, "Expected '("); + List<Expression> arguments=new ArrayList<>(); + List<String> types = new ArrayList<>(); + if(!matchAndAdvance(TokenType.RIGHT_PAREN)){ + do{ + if(matchAndAdvance(TokenType.INT)){ + types.add("int"); + } else if (matchAndAdvance(TokenType.REAL)){ + types.add("float"); + } else if (matchAndAdvance(TokenType.STRING)){ + types.add("char*"); + } + arguments.add(expression()); + } while(matchAndAdvance(TokenType.COMMA)); + matchOrError(TokenType.RIGHT_PAREN, "Expected ')'"); + } + Statement block = blockStatement(true); + statements.add(0,new Statement.FunctionDeclaration(name,types,returntype)); + return new Statement.Function(name,block,arguments,types,returntype); + } + throw error(getCurrentToken(), "Expected program or function"); + } + private Statement statement(){ if(checkToken(TokenType.INT)||checkToken(TokenType.REAL)||checkToken(TokenType.STRING)){ return declaration(); @@ -35,6 +84,8 @@ public class Parser { return ifStatement(); }else if (matchAndAdvance(TokenType.DO)){ return doStatement(); + }else if (matchAndAdvance(TokenType.RETURN)){ + return returnStatement(); } return expressionStatement(); } @@ -49,7 +100,12 @@ public class Parser { matchOrError(TokenType.DEFINE, ":: Required for variable definition"); matchOrError(TokenType.IDENTIFIER,"Expected variable name."); Token varName = getPreviousToken(); - return new Statement.VariableDeclaration(varName,"int"); + if(definedVars.containsKey(varName.text)){ + throw error(varName, "Cannot define multiple variables with the same name"); + }else{ + definedVars.put(varName.text,"int"); + return new Statement.VariableDeclaration(varName,"int"); + } } else if (matchAndAdvance(TokenType.REAL)){ if(matchAndAdvance(TokenType.DIMENSION)){ return arrayDeclaration("real"); @@ -57,7 +113,12 @@ public class Parser { matchOrError(TokenType.DEFINE, ":: Required for variable definition"); matchOrError(TokenType.IDENTIFIER,"Expected variable name."); Token varName = getPreviousToken(); - return new Statement.VariableDeclaration(varName,"real"); + if(definedVars.containsKey(varName.text)){ + throw error(varName, "Cannot define multiple variables with the same name"); + }else{ + definedVars.put(varName.text,"real"); + return new Statement.VariableDeclaration(varName,"real"); + } //Could be improved significatly when verifiying length is a positive integer } else if (matchAndAdvance(TokenType.STRING)){ @@ -79,7 +140,12 @@ public class Parser { matchOrError(TokenType.DEFINE, ":: Required for variable definition"); matchOrError(TokenType.IDENTIFIER,"Expected variable name."); Token varName = getPreviousToken(); - return new Statement.StringDeclaration(varName,length); + if(definedVars.containsKey(varName.text)){ + throw error(varName, "Cannot define multiple variables with the same name"); + }else{ + definedVars.put(varName.text,"string"); + return new Statement.StringDeclaration(varName,length); + } } return null; } @@ -97,16 +163,29 @@ public class Parser { matchOrError(TokenType.DEFINE, ":: Required for variable definition"); matchOrError(TokenType.IDENTIFIER,"Expected variable name."); Token varName = getPreviousToken(); - return new Statement.ArrayDeclaration(varName, type, dimensions); + if(definedVars.containsKey(varName.text)){ + throw error(varName, "Cannot define multiple variables with the same name"); + }else{ + definedVars.put(varName.text,"array"); + return new Statement.ArrayDeclaration(varName, type, dimensions); + } } - private Statement blockStatement(){ + private Statement blockStatement(boolean isFunction){ + boolean hasReturn=false; List<Statement> statements = new ArrayList<>(); while(!matchAndAdvance(TokenType.END)&&!checkToken(TokenType.ELSE)){ if(checkEOF()){ throw error(getCurrentToken(),"end missing from block"); } - statements.add(statement()); + Statement statement = statement(); + if(statement.getStatmentType()=="return"){ + hasReturn=true; + } + statements.add(statement); + } + if(isFunction&&!hasReturn){ + throw error(getPreviousToken(), "Function must contain a return statement"); } return new Statement.BlockStatement(statements); } @@ -128,11 +207,11 @@ public class Parser { private Statement ifStatement(){ Expression condition = expression(); if(matchOrError(TokenType.THEN, "then expected after if statement")){ - Statement ifBlock = blockStatement(); + Statement ifBlock = blockStatement(false); Statement elseBlock=null; if(matchAndAdvance(TokenType.ELSE)){ - elseBlock=blockStatement(); + elseBlock=blockStatement(false); } matchOrError(TokenType.IF, "If statements end with if"); Statement ifstatement = new Statement.IfStatement(condition, ifBlock,elseBlock); @@ -154,7 +233,7 @@ public class Parser { if(matchAndAdvance(TokenType.COMMA)){ step = expression(); } - Statement codeBlock = blockStatement(); + Statement codeBlock = blockStatement(false); matchOrError(TokenType.DO, "Do statements end with do"); return new Statement.DoStatement(variable, start, stop, step,codeBlock); @@ -164,11 +243,16 @@ public class Parser { matchOrError(TokenType.LEFT_PAREN, " missing '(' for do statement condition"); Expression condition = expression(); matchOrError(TokenType.RIGHT_PAREN, " missing ')' for do condition"); - Statement codeBlock = blockStatement(); + Statement codeBlock = blockStatement(false); matchOrError(TokenType.DO, "Do while statements end with do"); return new Statement.DoWhileStatement(condition,codeBlock); } + private Statement returnStatement(){ + Expression returnValue = expression(); + return new Statement.ReturnStatement(returnValue); + } + private Statement expressionStatement(){ Expression expression = assignment(); return new Statement.ExpressionStatement(expression); @@ -259,15 +343,31 @@ public class Parser { if (matchAndAdvance(TokenType.IDENTIFIER)) { Token name= getPreviousToken(); if(matchAndAdvance(TokenType.LEFT_PAREN)){ - List<Expression> positions = new ArrayList<>(); - Expression position = expression(); - positions.add(position); - while(matchAndAdvance(TokenType.COMMA)){ - position = expression(); - positions.add(position); + if(definedVars.containsKey(name.text)){ + if(definedVars.get(name.text).equals("array")){ + List<Expression> positions = new ArrayList<>(); + Expression position = expression(); + positions.add(position); + while(matchAndAdvance(TokenType.COMMA)){ + position = expression(); + positions.add(position); + } + matchOrError(TokenType.RIGHT_PAREN,"Expected ')'"); + return new Expression.ArrayVariable(name, positions); + } } - matchOrError(TokenType.RIGHT_PAREN,"Expected ')'"); - return new Expression.ArrayVariable(name, positions); + List<Token> arguments = new ArrayList<>(); + do{ + matchOrError(TokenType.IDENTIFIER, "Expected argument"); + Token argumentValue = getPreviousToken(); + if(definedVars.containsKey(argumentValue.text)){ + arguments.add(argumentValue); + }else{ + throw error(argumentValue, "Argument undefined"); + } + }while(matchAndAdvance(TokenType.COMMA)); + matchOrError(TokenType.RIGHT_PAREN, "Expected ')"); + return new Expression.FunctionCall(name, arguments); } return new Expression.Variable(getPreviousToken()); } diff --git a/src/Compiler/Statement.java b/src/Compiler/Statement.java index a4fbff7..dd3ff04 100644 --- a/src/Compiler/Statement.java +++ b/src/Compiler/Statement.java @@ -4,6 +4,73 @@ import java.util.List; abstract class Statement { + static class MainFunction extends Statement{ + MainFunction(Statement block){ + this.block = block; + } + + final Statement block; + + @Override + public String getStatmentType() { + return "main"; + } + + } + + static class Function extends Statement{ + Function(Token name,Statement block,List<Expression> arguments,List<String> argumentTypes,String returnType){ + this.block=block; + this.arguments=arguments; + this.returnType=returnType; + this.argumentTypes=argumentTypes; + this.name=name; + } + + final Statement block; + final List<Expression> arguments; + final List<String> argumentTypes; + final String returnType; + final Token name; + + @Override + public String getStatmentType() { + return "function"; + } + } + + static class FunctionDeclaration extends Statement{ + FunctionDeclaration(Token name,List<String> argumentTypes,String returnType){ + this.returnType=returnType; + this.argumentTypes=argumentTypes; + this.name=name; + } + + final List<String> argumentTypes; + final String returnType; + final Token name; + + @Override + public String getStatmentType() { + return "functionDec"; + } + } + + static class ReturnStatement extends Statement{ + + ReturnStatement(Expression returnValue){ + this.returnValue=returnValue; + + } + final Expression returnValue; + + @Override + public String getStatmentType() { + return "return"; + } + + } + static class ExpressionStatement extends Statement{ ExpressionStatement(Expression expr){ this.expr = expr; diff --git a/src/Compiler/Token.java b/src/Compiler/Token.java index 0af2c34..50d3804 100644 --- a/src/Compiler/Token.java +++ b/src/Compiler/Token.java @@ -22,4 +22,5 @@ public class Token { public String toString() { return type + " " + text + " " + value; } + } diff --git a/src/Compiler/TokenScanner.java b/src/Compiler/TokenScanner.java index aed5f38..02bbbc0 100644 --- a/src/Compiler/TokenScanner.java +++ b/src/Compiler/TokenScanner.java @@ -229,5 +229,8 @@ public class TokenScanner { keywords.put("do", TokenType.DO); keywords.put("while", TokenType.WHILE); keywords.put("dimension", TokenType.DIMENSION); + keywords.put("program", TokenType.PROGRAM); + keywords.put("return", TokenType.RETURN); + keywords.put("function", TokenType.FUNCTION); } } diff --git a/src/Compiler/TokenType.java b/src/Compiler/TokenType.java index bc82cde..6a8f5c0 100644 --- a/src/Compiler/TokenType.java +++ b/src/Compiler/TokenType.java @@ -12,7 +12,8 @@ public enum TokenType { NUMBER,IDENTIFIER,STRING, - INT,REAL,PRINT,ENDPRINT,IF,THEN,END,ELSE,LEN,DO,WHILE,AND,OR,NOT,DIMENSION, + INT,REAL,PRINT,ENDPRINT,IF,THEN,END,ELSE,LEN,DO,WHILE, + AND,OR,NOT,DIMENSION,PROGRAM,FUNCTION,RETURN, EOF } diff --git a/src/Compiler/Translator.java b/src/Compiler/Translator.java index a79242d..e0bff23 100644 --- a/src/Compiler/Translator.java +++ b/src/Compiler/Translator.java @@ -16,15 +16,14 @@ public class Translator{ public List<String> compileToC(List<Statement> statements, boolean printC){ CCode.add("#include <stdio.h>"); CCode.add("#include <string.h>"); - CCode.add("int main(){"); try{ for (Statement statement: statements){ evaluateStatement(statement); + CCode.add(""); } } catch (Error e){ } - CCode.add("}"); if (printC) { for(String t:CCode){ @@ -37,6 +36,15 @@ public class Translator{ private void evaluateStatement(Statement statement){ switch(statement.getStatmentType()){ + case "main": + evalMainFunction((MainFunction)statement); + break; + case "function": + evalFunction((Function)statement); + break; + case "functionDec": + evalFunctionDeclaration((FunctionDeclaration)statement); + break; case "exprStmt": evalExpressionStatement((ExpressionStatement)statement); break; @@ -64,8 +72,57 @@ public class Translator{ case "dowhileStmt": evalDoWhileStatement((DoWhileStatement)statement); break; + case "return": + evalReturnStatement((ReturnStatement)statement); + break; } } + + private void evalMainFunction(MainFunction stmt){ + CCode.add("int main(){"); + evaluateStatement(stmt.block); + CCode.add("}"); + } + + private void evalFunction(Function stmt){ + + String functionString = stmt.returnType+" "+stmt.name.text+"("; + boolean first=true; + for(int i=0;i<stmt.arguments.size();i++){ + if(!first){ + functionString+=","; + } + environment.defineVariable(evaluateExpression(stmt.arguments.get(i)), stmt.argumentTypes.get(i)); + functionString+=stmt.argumentTypes.get(i)+" "+evaluateExpression(stmt.arguments.get(i)); + first=false; + } + functionString+="){"; + CCode.add(functionString); + evaluateStatement(stmt.block); + CCode.add("}"); + } + + private void evalReturnStatement(ReturnStatement stmt){ + CCode.add("return "+evaluateExpression(stmt.returnValue)+";"); + + + } + + private void evalFunctionDeclaration(FunctionDeclaration stmt){ + + String functionString = stmt.returnType+" "+stmt.name.text+"("; + boolean first=true; + for(int i=0;i<stmt.argumentTypes.size();i++){ + if(!first){ + functionString+=","; + } + functionString+=stmt.argumentTypes.get(i); + first=false; + } + functionString+=");"; + CCode.add(functionString); + } + private void evalExpressionStatement(ExpressionStatement stmt){ evaluateExpression(stmt.expr); } @@ -192,6 +249,8 @@ public class Translator{ return evaluateArrayVariable((ArrayVariable)expression); case "var": return evaluateVariableExpression((Variable)expression); + case "functCall": + return evaluateFunctionCall((FunctionCall)expression); default: return null; } @@ -287,6 +346,21 @@ public class Translator{ return expr.name.text; } + private String evaluateFunctionCall(FunctionCall expr){ + String functioncall=""; + functioncall+=expr.name.text+"("; + boolean first=true; + for(Token arg:expr.arguments){ + if(!first){ + functioncall+=","; + } + functioncall+=arg.text; + first=false; + } + functioncall+=")"; + return functioncall; + } + } diff --git a/src/Compiler/Utils.java b/src/Compiler/Utils.java index f9bd41c..d0206c1 100644 --- a/src/Compiler/Utils.java +++ b/src/Compiler/Utils.java @@ -4,7 +4,6 @@ package Compiler; import java.io.*; -import java.nio.file.Files; public class Utils { // Adapted from here for now @@ -22,7 +21,7 @@ public class Utils { while ((line = br.readLine()) != null) //System.out.println(line); readFile = readFile.append(line + "\n"); - + br.close(); return readFile.toString(); } diff --git a/src/examples/example.ft b/src/examples/example.ft new file mode 100644 index 0000000..4a4981a --- /dev/null +++ b/src/examples/example.ft @@ -0,0 +1,15 @@ +program example +int ::test +int::testtwo +test=5 +testtwo=7 +int::testthree +testthree=add(test,testtwo) +print*,testthree +end program example + +function int add(int a, int b) +int::value +value=a+b +return value +end
\ No newline at end of file |