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 | 
