diff options
Diffstat (limited to 'src/Compiler')
| -rw-r--r-- | src/Compiler/Environment.java | 6 | ||||
| -rw-r--r-- | src/Compiler/ExecuteC.java | 3 | ||||
| -rw-r--r-- | src/Compiler/Expression.java | 31 | ||||
| -rw-r--r-- | src/Compiler/Language.java | 60 | ||||
| -rw-r--r-- | src/Compiler/Parser.java | 241 | ||||
| -rw-r--r-- | src/Compiler/Statement.java | 48 | ||||
| -rw-r--r-- | src/Compiler/Token.java | 18 | ||||
| -rw-r--r-- | src/Compiler/TokenScanner.java | 80 | ||||
| -rw-r--r-- | src/Compiler/TokenType.java | 2 | ||||
| -rw-r--r-- | src/Compiler/Translator.java | 197 | 
10 files changed, 605 insertions, 81 deletions
| diff --git a/src/Compiler/Environment.java b/src/Compiler/Environment.java index 1bb0e88..8ae8724 100644 --- a/src/Compiler/Environment.java +++ b/src/Compiler/Environment.java @@ -2,11 +2,13 @@ package Compiler;  import java.util.HashMap;  import java.util.Map; +/** + * Class to record defined variables during the translation stage + */  public class Environment {      private final Map<String,Object> variableMap = new HashMap<>();      //Define a variable inside the current environment -    //Maybe check if variable is already defined?      public void defineVariable(String name,Object value){          variableMap.put(name, value);      } @@ -20,7 +22,7 @@ public class Environment {          throw new Error();      } -    //Get a variable if it is defined, or report an error +    //Check if a variable is defined, or report an error      public Boolean checkVariable(Token token){          if(variableMap.containsKey(token.text)){              return true; diff --git a/src/Compiler/ExecuteC.java b/src/Compiler/ExecuteC.java index fbc7ade..128f541 100644 --- a/src/Compiler/ExecuteC.java +++ b/src/Compiler/ExecuteC.java @@ -69,7 +69,6 @@ public class ExecuteC {      public Boolean compileC(){          try{ -            String s= null;              Process p;              if (System.getProperty("os.name").equals("Linux")) {                  p = Runtime.getRuntime().exec(String.format( @@ -86,7 +85,7 @@ public class ExecuteC {              BufferedReader stdError = new BufferedReader(new                   InputStreamReader(p.getErrorStream()));              boolean error=false; -             while ((s = stdError.readLine()) != null) { +             while ((stdError.readLine()) != null) {                   error=true;               }               return error; diff --git a/src/Compiler/Expression.java b/src/Compiler/Expression.java index 2789462..056019a 100644 --- a/src/Compiler/Expression.java +++ b/src/Compiler/Expression.java @@ -2,7 +2,12 @@ package Compiler;  import java.util.List; +//Class for possible expressions in the language  abstract class Expression { + +    /** +     * Class for representing a binary expression, with two values between an operator +     */      static class Binary extends Expression{          final Expression left; @@ -22,6 +27,9 @@ abstract class Expression {      } +    /** +     * Class for representing a singlular expression, with one value and one operator +     */      static class Singular extends Expression{          final Expression right; @@ -39,6 +47,9 @@ abstract class Expression {      } +    /** +     * Class for representing a literal value +     */      static class Literal extends Expression{          final Token value;          final String type; @@ -56,6 +67,9 @@ abstract class Expression {      } +    /** +     * Class for representing an expression between brackets +     */      static class BracketedExpression extends Expression{          final Expression expr; @@ -66,11 +80,12 @@ abstract class Expression {          @Override          public String getExpressionType() {              return "bracket"; -        } -         -         +        }           } +    /** +     * Class for represening an assignment expression +     */      static class AssignmentExpression extends Expression{          final Expression variable;          final Expression value; @@ -87,6 +102,9 @@ abstract class Expression {          }         } +    /** +     * Class for representing a single variable +     */      static class Variable extends Expression{          Variable(Token name){ @@ -101,6 +119,9 @@ abstract class Expression {      } +    /** +     * Class for representing an array variable +     */      static class ArrayVariable extends Expression{          ArrayVariable(Token name,List<Expression> positions){              this.name=name; @@ -115,6 +136,9 @@ abstract class Expression {          final List<Expression> positions;      } +    /** +     * Class for representing a function call +     */      static class FunctionCall extends Expression{          FunctionCall(Token name, List<Token> arguments){              this.arguments=arguments; @@ -130,5 +154,6 @@ abstract class Expression {      } +    //Abstract method for getting an expression type      public abstract String getExpressionType();  } diff --git a/src/Compiler/Language.java b/src/Compiler/Language.java index b433b36..5013cfd 100644 --- a/src/Compiler/Language.java +++ b/src/Compiler/Language.java @@ -8,7 +8,10 @@ import java.util.List;  import java.util.Scanner;  import java.util.ArrayList; -//Base class for the interpreter +/**  + *Base class for running the Compiler  + *Can run either from a file, or in an interactive mode + */  public class Language {      static boolean leaveCFile = false; @@ -17,8 +20,10 @@ public class Language {      static boolean executeAfter = false;      static Path sourcefile; +    //Main function for the compiler      public static void main(String[] args) { +        //Extract required command line arguments          try {              sourcefile = Paths.get(args[0]);          } catch (java.lang.ArrayIndexOutOfBoundsException e) { @@ -38,6 +43,8 @@ public class Language {          Path initOutPath = Paths.get(args[0]);          String outname = initOutPath.getName(initOutPath.getNameCount() - 1).toString().split("\\.(?=[^\\.]+$)")[0]; + +        //Extract optional command line arguments          ArrayList<String> arrayArgs = new ArrayList<>();          for (int i = 0; i < args.length; i++) {              String arg = args[i]; @@ -66,21 +73,24 @@ public class Language {              return;          } +        //Run the compiler on the source code          try { -            runInterpreter(Files.readString(sourcefile), outname); +            runCompiler(Files.readString(sourcefile), outname);          } catch (IOException e) {              e.printStackTrace();          }      } -    //Function to take source code and output the result -    private static void runInterpreter(String sourceCode, String outName){ +    /**  +     * Function to take source code, run the compiler and write to C +     * +     *  @param sourcecode the full source code as a string +     *  @param outName the name to write the compiled code to +     */ +    private static void runCompiler(String sourceCode, String outName){          //Extract tokens from the source code          TokenScanner scanner = new TokenScanner();          List<Token> tokens = scanner.extractTokens(sourceCode); -        //for (Token token : tokens) { -        //    System.out.println(token); -        //}          if (hadError) return;          //Parse into AST @@ -98,25 +108,37 @@ public class Language {          cExecutor.compileAndExecuteC(code, outName, executeAfter, leaveCFile);      } +    /** +     * Method for running the compiler in an interactive mode +     */      private static void interactiveMode() {          Scanner input = new Scanner(System.in); -            String sourceCode = "1"; -            while (sourceCode!=""){ -                System.out.print("Code: "); -                sourceCode = input.nextLine(); -                runInterpreter(sourceCode, "out"); -                hadError=false; -            } -            input.close(); +        String sourceCode = "1"; +        //Run compiler line by line +        while (sourceCode!=""){ +            System.out.print("Code: "); +            sourceCode = input.nextLine(); +            runCompiler(sourceCode, "out"); +            hadError=false; +        } +        input.close();      } -    //Basic error reporting +    /** +     * Method for displaying an error to the user +     * @param line the line the error occured on +     * @param message an error message to display to the user +     */      static void displayError(int line,String message){          hadError=true;          System.out.println("An error was encountered on line: "+line);          System.out.println(message);      } -    //Basic error reporting +    /** +     * Method for displaying error based on a specific token +     * @param token the token the parser detected the error on +     * @param message an error message to display to the user +     */      static void displayError(Token token,String message){          hadError=true;          System.out.println("An error was encountered on line: "+token.line); @@ -124,6 +146,10 @@ public class Language {          System.out.println(message);      } +    /** +     * Method for getting the helpfile text +     * @return the text for the helpfile +     */      private static String getHelpText(){          String helpText = "";          try { diff --git a/src/Compiler/Parser.java b/src/Compiler/Parser.java index ae2ed23..433035c 100644 --- a/src/Compiler/Parser.java +++ b/src/Compiler/Parser.java @@ -5,17 +5,27 @@ import java.util.HashMap;  import java.util.List;  import java.util.Map; - +/** + * Class for parsing the source code, utilising the recursive descent parsing method + */  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<>(); +    /** +     * Constructor of a parser object +     * @param tokens the list of tokens representing the source code +     */      Parser(List<Token> tokens){          this.tokens=tokens;      } +    /** +     * Method for parsing the list of tokens +     * @return A list of statements for the source code +     */      List<Statement> parse(){          try{              while (!checkEOF()){ @@ -28,32 +38,53 @@ public class Parser {      } +    /** +     * Method to extracting the main function, or other functions +     * @return A statement for a function or main function +     */      private Statement functionCalls(){ + +        //Check for main function definition          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"); + +            //Check program names match              if(!(getPreviousToken().text.equals(programname.text))){                  throw error(getPreviousToken(), "Program names do not match");              }              return new Statement.MainFunction(block); -        } else if(matchAndAdvance(TokenType.FUNCTION)){ + +        //Check for function or subroutine definition +        } else if(matchAndAdvance(TokenType.FUNCTION)||matchAndAdvance(TokenType.SUBROUTINE)){ +              String returntype=null; -            if(matchAndAdvance(TokenType.INT)){ -                returntype="int"; -            } else if (matchAndAdvance(TokenType.REAL)){ -                returntype="real"; -            }else{ -                throw error(getCurrentToken(), "Expected function return type"); +            boolean isfunction=false; +            //Get function return data type +            if(getPreviousToken().type==TokenType.FUNCTION){ +                isfunction=true; +                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(); + +            //Add function name to the list of defined values              definedVars.put(name.text, "function");              matchOrError(TokenType.LEFT_PAREN, "Expected '(");              List<Expression> arguments=new ArrayList<>();              List<String> types = new ArrayList<>(); + +            //Extract the arguments for the function              if(!matchAndAdvance(TokenType.RIGHT_PAREN)){                  do{                      if(matchAndAdvance(TokenType.INT)){ @@ -67,52 +98,85 @@ public class Parser {                  } while(matchAndAdvance(TokenType.COMMA));                  matchOrError(TokenType.RIGHT_PAREN, "Expected ')'");              } -            Statement block = blockStatement(true); + +            Statement block = blockStatement(isfunction); + +            //Add the function declaration to the start of the list of statements              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"); +        throw error(getCurrentToken(), "Program must start with program or function");      } +    /** +     * Method for extracting a single statement, excluding function definitions +     * @return any type of statement +     */      private Statement statement(){ +        //Check for variable definition          if(checkToken(TokenType.INT)||checkToken(TokenType.REAL)||checkToken(TokenType.STRING)){              return declaration();          } + +        //Check for print statement          else if (matchAndAdvance(TokenType.PRINT)){              return printStatement(); + +        //Check for if statement          }else if (matchAndAdvance(TokenType.IF)){              return ifStatement(); + +        //Check for do statement          }else if (matchAndAdvance(TokenType.DO)){              return doStatement(); + +        //Check for return statement          }else if (matchAndAdvance(TokenType.RETURN)){              return returnStatement();          } + +        //If no statement matches, assume an expression statement          return expressionStatement();      } -    //Clean up and reduce code mess +    /** +     * Method for parsing a variable definition +     * @return A variable definition statement +     */      private Statement declaration(){ +        //Check if the variable is an integer          if (matchAndAdvance(TokenType.INT)){ + +            //Check for array declaration              if(matchAndAdvance(TokenType.DIMENSION)){                  return arrayDeclaration("int");              } +              matchOrError(TokenType.DEFINE, ":: Required for variable definition");              matchOrError(TokenType.IDENTIFIER,"Expected variable name.");              Token varName = getPreviousToken(); + +            //Ensure that a variable with the same name has not been defined              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");              } +         +        //Check if the variable is a real type          } else if (matchAndAdvance(TokenType.REAL)){ + +            //Check for array declaration              if(matchAndAdvance(TokenType.DIMENSION)){                  return arrayDeclaration("real");              }              matchOrError(TokenType.DEFINE, ":: Required for variable definition");              matchOrError(TokenType.IDENTIFIER,"Expected variable name.");              Token varName = getPreviousToken(); + +            //Ensure that a variable with the same name has not been defined              if(definedVars.containsKey(varName.text)){                  throw error(varName, "Cannot define multiple variables with the same name");              }else{ @@ -120,12 +184,15 @@ public class Parser {                  return new Statement.VariableDeclaration(varName,"real");              } -        //Could be improved significatly when verifiying length is a positive integer +        //Check if the variable is a string          } else if (matchAndAdvance(TokenType.STRING)){ +            //Extract the length of the string              matchOrError(TokenType.LEFT_PAREN, "Length of string must be defined");              matchOrError(TokenType.LEN, "Length of string must be defined");              matchOrError(TokenType.EQUALS, "Length of string must be defined");              Expression length = expression(); + +            //Ensure the length of the string is a positive integer              if(!(length instanceof Expression.Literal)){                  throw error(getCurrentToken(),"String length must be a number");              } @@ -135,11 +202,13 @@ public class Parser {              if((int)((Expression.Literal)length).value.value<1){                  throw error(getCurrentToken(),"String length must be greater then 0");               } -            matchOrError(TokenType.RIGHT_PAREN, "Length of string must be defined"); +            matchOrError(TokenType.RIGHT_PAREN, "Length of string must be defined");              matchOrError(TokenType.DEFINE, ":: Required for variable definition");              matchOrError(TokenType.IDENTIFIER,"Expected variable name.");              Token varName = getPreviousToken(); + +            //Ensure that a variable with the same name has not been defined              if(definedVars.containsKey(varName.text)){                  throw error(varName, "Cannot define multiple variables with the same name");              }else{ @@ -150,19 +219,31 @@ public class Parser {          return null;      } +    /** +     * Method to parse an array declaration +     * @param type The data type of the array +     * @return A statement representing the array declaration +     */      private Statement arrayDeclaration(String type){          matchOrError(TokenType.LEFT_PAREN,"Expected ')'"); + +        //Extract the size of the array          List<Expression> dimensions = new ArrayList<>();          Expression dimension = expression();          dimensions.add(dimension); + +        //Extract the size of each dimension of the array          while(matchAndAdvance(TokenType.COMMA)){              dimension = expression();              dimensions.add(dimension);          } +          matchOrError(TokenType.RIGHT_PAREN, "Expected ')'");          matchOrError(TokenType.DEFINE, ":: Required for variable definition");          matchOrError(TokenType.IDENTIFIER,"Expected variable name.");          Token varName = getPreviousToken(); + +        //Ensure that a variable with the same name has not been defined          if(definedVars.containsKey(varName.text)){              throw error(varName, "Cannot define multiple variables with the same name");          }else{ @@ -171,10 +252,18 @@ public class Parser {          }      } +    /** +     * Method for parsing a block of statements +     * @param isFunction whether the block is for a function and so must contain a return statement +     * @return a statement representing a block +     */      private Statement blockStatement(boolean isFunction){          boolean hasReturn=false;          List<Statement> statements = new ArrayList<>(); + +        //Match statement until an end or else token has been reached          while(!matchAndAdvance(TokenType.END)&&!checkToken(TokenType.ELSE)){ +            //Ensure the end of the file has not been reached              if(checkEOF()){                  throw error(getCurrentToken(),"end missing from block");              } @@ -184,15 +273,22 @@ public class Parser {              }              statements.add(statement);          } +        //Ensure that a function block contains a return statement          if(isFunction&&!hasReturn){              throw error(getPreviousToken(), "Function must contain a return statement");          }          return new Statement.BlockStatement(statements);      } +    /** +     * Method for parsing a print statement +     * @return A print statement object +     */      private Statement printStatement(){          matchOrError(TokenType.STAR, "Syntax, print *, item1, item2...");          List<Expression> exprList = new ArrayList<>(); + +        //Get the list of printed values          while(matchAndAdvance(TokenType.COMMA)){              if(checkEOF()){                  throw error(getCurrentToken(),"reached end of file"); @@ -203,13 +299,18 @@ public class Parser {          return new Statement.PrintStatement(exprList);      } -    //Could be cleaned up to handle else statements better +    /** +     * Method for parsing an if statement +     * @return A statement representing an if statement +     */      private Statement ifStatement(){          Expression condition = expression();          if(matchOrError(TokenType.THEN, "then expected after if statement")){ +            //Extract if block              Statement ifBlock = blockStatement(false);              Statement elseBlock=null; +            //Extract else block if required              if(matchAndAdvance(TokenType.ELSE)){                  elseBlock=blockStatement(false);              } @@ -220,12 +321,19 @@ public class Parser {          return null;      } +    /** +     * Method for parsing a do statement +     * @return A statement representing a do statement +     */      private Statement doStatement(){ +        //Check for do while statement          if(matchAndAdvance(TokenType.WHILE)){              return whileStatement();          }          Expression variable =expression();          matchOrError(TokenType.EQUALS, "'=' missing"); + +        //Extract start, stop, and step variables          Expression start = expression();          matchOrError(TokenType.COMMA, " use ',' between values");          Expression stop = expression(); @@ -239,7 +347,12 @@ public class Parser {      } +    /** +     * Method to parse a do-while statement +     * @return a do-while statement object +     */      private Statement whileStatement(){ +        //Extract condition          matchOrError(TokenType.LEFT_PAREN, " missing '(' for do statement condition");          Expression condition = expression();          matchOrError(TokenType.RIGHT_PAREN, " missing ')' for do  condition"); @@ -248,18 +361,31 @@ public class Parser {          return new Statement.DoWhileStatement(condition,codeBlock);      } +    /** +     * Method to parse a return statement +     * @return a return statement object +     */      private Statement returnStatement(){          Expression returnValue = expression();          return new Statement.ReturnStatement(returnValue);      } +    /** +     * Method to parse an expression statement +     * @return an expression statement object +     */      private Statement expressionStatement(){          Expression expression = assignment();          return new Statement.ExpressionStatement(expression);      } +    /** +     * Method to parse an assignment expression +     * @return an assignment expression object +     */      private Expression assignment(){          Expression variable = expression(); +        //Check for an equals token          if (matchAndAdvance(TokenType.EQUALS)){              Expression assignedvalue = expression();              return new Expression.AssignmentExpression(variable,assignedvalue); @@ -267,11 +393,19 @@ public class Parser {          return variable;      } +    /** +     * Method to parse a non-assignment expression +     * @return an expression object +     */      private Expression expression(){          Expression createdExpression = equality();          return createdExpression;      } +    /** +     * Method to parse an equality comparison expression +     * @return an expression of rank equality or lower +     */      private Expression equality(){          Expression createdExpression = logical();          while (matchAndAdvance(TokenType.EQUALITY)){ @@ -282,6 +416,10 @@ public class Parser {          return createdExpression;      } +    /** +     * Method to parse a logical expression +     * @return an expression of rank logical or lower  +     */      private Expression logical(){          if(matchAndAdvance(TokenType.NOT)){              Token op = getPreviousToken(); @@ -298,8 +436,13 @@ public class Parser {          return createdExpression;      } +    /** +     * Method to parse a comparison expression +     * @return an expression of rank comparison or lower  +     */      private Expression comparison(){          Expression createdExpression = term(); +        //Check for different types of comparison          while (matchAndAdvance(TokenType.GREATER)||matchAndAdvance(TokenType.LESS)||matchAndAdvance(TokenType.GREATER_EQUAL)||matchAndAdvance(TokenType.LESS_EQUAL)){              Token op = getPreviousToken();              Expression right = term(); @@ -308,8 +451,13 @@ public class Parser {          return createdExpression;      } +    /** +     * Method to parse a term expression +     * @return an expression of rank term or lower  +     */      private Expression term(){          Expression createdExpression = factor(); +        //Check for addition or subtraction expression          while (matchAndAdvance(TokenType.PLUS)||matchAndAdvance(TokenType.MINUS)){              Token op = getPreviousToken();              Expression right = factor(); @@ -318,8 +466,13 @@ public class Parser {          return createdExpression;      } +    /** +     * Method to parse a factor expression +     * @return an expression of rank factor or lower  +     */      private Expression factor(){          Expression createdExpression = primary(); +        //Check for multiplication or division expressions          while (matchAndAdvance(TokenType.STAR)||matchAndAdvance(TokenType.SLASH)){              Token op = getPreviousToken();              Expression right = primary(); @@ -328,26 +481,36 @@ public class Parser {          return createdExpression;      } +    /** +     * Method to parse a primary expression +     * @return a primary expression  +     */      private Expression primary(){ +        //Check for singular numbers          if (matchAndAdvance(TokenType.NUMBER)){ +            //Check number type              if(getPreviousToken().value instanceof Integer){                  return new Expression.Literal(getPreviousToken(),"int");              } else if(getPreviousToken().value instanceof Double){                  return new Expression.Literal(getPreviousToken(),"double");              }          } +        //Check for strings          if (matchAndAdvance(TokenType.STRING)){              return new Expression.Literal(getPreviousToken(), "string");          } - +        //Check for variable names          if (matchAndAdvance(TokenType.IDENTIFIER)) {              Token name= getPreviousToken(); +            //Check for function calls or array assignments              if(matchAndAdvance(TokenType.LEFT_PAREN)){ +                //Parse array if array with same name has been previously defined                  if(definedVars.containsKey(name.text)){                      if(definedVars.get(name.text).equals("array")){                          List<Expression> positions = new ArrayList<>();                          Expression position = expression();                          positions.add(position); +                        //Parse array positions for each dimension                          while(matchAndAdvance(TokenType.COMMA)){                              position = expression();                              positions.add(position); @@ -356,7 +519,9 @@ public class Parser {                          return new Expression.ArrayVariable(name, positions);                      }                  } +                //If not previously declared, assume function call                  List<Token> arguments = new ArrayList<>(); +                //Parse function call arguments                  do{                      matchOrError(TokenType.IDENTIFIER, "Expected argument");                      Token argumentValue = getPreviousToken(); @@ -369,24 +534,35 @@ public class Parser {                  matchOrError(TokenType.RIGHT_PAREN, "Expected ')");                  return new Expression.FunctionCall(name, arguments);              } +            //Parse single variable name              return new Expression.Variable(getPreviousToken());            } +        //Parse bracketed expressions          if (matchAndAdvance(TokenType.LEFT_PAREN)){              Expression expr = expression();              matchOrError(TokenType.RIGHT_PAREN,"Expected ')'");              return new Expression.BracketedExpression(expr);          } +        //Throw an error if the expression was not recognised          throw error(getCurrentToken(),"Unknown syntax error");      } - +    /** +     * Method to move ahead one token +     */      private void advanceToken(){          if (!checkEOF()) {              currentToken++;          };      } - +     +    /** +     * Method to compare the current token with a given token, and move ahead if a match is found +     * @param type the token type to compare against +     * @return if the match was successful +     */      private boolean matchAndAdvance(TokenType type){ +        //If the tokens are a match, advance ahead          if (checkToken(type)) {              advanceToken();              return true; @@ -394,6 +570,12 @@ public class Parser {          return false;      } +    /** +     * Method to check if the current token matches a given token, and throw an error if they do not match +     * @param type the token type to compare against +     * @param errorMessage the message to report if a match is not found +     * @return a boolean to say if a match was successful +     */      private boolean matchOrError(TokenType type,String errorMessage){          if (matchAndAdvance(type)){              return true; @@ -401,23 +583,46 @@ public class Parser {          throw error(getCurrentToken(),errorMessage);      } +    /** +     * Method to comapre the current token to a given token +     * @param type the token type to compare against +     * @return if the match was successful +     */      private boolean checkToken(TokenType type){          if (checkEOF()) return false;          return getCurrentToken().type == type;       } +    /** +     * Method to check for the end of the file +     * @return if the end of the file has been reached +     */      private boolean checkEOF(){          return tokens.get(currentToken).type==TokenType.EOF;      } - +     +    /** +     * Method to get the current token +     * @return the token at the current position +     */      private Token getCurrentToken(){          return tokens.get(currentToken);      } +    /** +     * Method to get the previous token +     * @return the previous token +     */      private Token getPreviousToken(){          return tokens.get(currentToken - 1);      } +    /** +     * Method to report an error to the user during parsing +     * @param token the token the error was detected on +     * @param message the message to display to the user +     * @return An error object to stop parsing +     */      private Error error(Token token, String message){          Language.displayError(token ,message);          return new Error(); diff --git a/src/Compiler/Statement.java b/src/Compiler/Statement.java index dd3ff04..5e94d35 100644 --- a/src/Compiler/Statement.java +++ b/src/Compiler/Statement.java @@ -1,9 +1,14 @@  package Compiler;  import java.util.List; - +/** + * Classes for possible statements in the language + */  abstract class Statement { +    /** +     * Class for the main function of the program +     */      static class MainFunction extends Statement{          MainFunction(Statement block){              this.block = block; @@ -18,6 +23,9 @@ abstract class Statement {      } +    /** +     * Class for representing a function +     */      static class Function extends Statement{          Function(Token name,Statement block,List<Expression> arguments,List<String> argumentTypes,String returnType){              this.block=block; @@ -39,6 +47,10 @@ abstract class Statement {          }       } +    /** +     * Class for representing a function declaration  +     * needed for compiling to C +     */      static class FunctionDeclaration extends Statement{          FunctionDeclaration(Token name,List<String> argumentTypes,String returnType){              this.returnType=returnType; @@ -56,6 +68,9 @@ abstract class Statement {          }       } +    /** +     * Class for representing a return statement +     */      static class ReturnStatement extends Statement{          ReturnStatement(Expression returnValue){ @@ -71,6 +86,9 @@ abstract class Statement {      } +    /** +     * Class for representing a statement for an expression +     */      static class ExpressionStatement extends Statement{          ExpressionStatement(Expression expr){              this.expr = expr; @@ -85,6 +103,9 @@ abstract class Statement {          }      } +    /** +     * Class for representing a block of statements +     */      static class BlockStatement extends Statement{          BlockStatement(List<Statement> statements){              this.statements=statements; @@ -99,6 +120,9 @@ abstract class Statement {      } +    /** +     * Class for representing an if statement +     */      static class IfStatement extends Statement{          IfStatement(Expression condition, Statement ifBlock,Statement elseBlock){              this.condition=condition; @@ -116,6 +140,9 @@ abstract class Statement {          }      } +    /** +     * Class for representing a do loop +     */      static class DoStatement extends Statement{          DoStatement(Expression variable, Expression start,Expression stop,Expression step,Statement codeBlock){              this.variable=variable; @@ -138,6 +165,9 @@ abstract class Statement {          }      } +    /** +     * Class for representing a do while loop +     */      static class DoWhileStatement extends Statement{          DoWhileStatement(Expression condition,Statement codeBlock){              this.condition=condition; @@ -154,7 +184,10 @@ abstract class Statement {          }      } - +    /** +     * Class for representing a integer or real declaration +     *  +     */      static class VariableDeclaration extends Statement{          VariableDeclaration(Token name,String type){              this.name=name; @@ -172,6 +205,9 @@ abstract class Statement {      } +    /** +     * Class for representing a string variable declaration +     */      static class StringDeclaration extends Statement{          StringDeclaration(Token name,Expression length){              this.name=name; @@ -189,6 +225,9 @@ abstract class Statement {      } +    /** +     * Class for representing an array declaration +     */      static class ArrayDeclaration extends Statement{          ArrayDeclaration(Token name,String type,List<Expression> dimensions){              this.name=name; @@ -207,6 +246,9 @@ abstract class Statement {      } +    /** +     * Class for representing a print statement +     */      static class PrintStatement extends Statement{          PrintStatement(List<Expression> exprList){              this.exprList=exprList; @@ -219,6 +261,6 @@ abstract class Statement {          }      } - +    //Abstract function for getting the statement type      public abstract String getStatmentType();  } diff --git a/src/Compiler/Token.java b/src/Compiler/Token.java index 50d3804..b6f11a8 100644 --- a/src/Compiler/Token.java +++ b/src/Compiler/Token.java @@ -1,15 +1,23 @@  package Compiler; +/** + * Class for storing information about a single token + */  public class Token { - -    //Stores the token type, the actual text and the runtime object      public final TokenType type;      final String text;      final Object value;      final int line; +    /** +     * Contructor for a token object +     * @param type the type of token +     * @param text the actual text for the token +     * @param value the value the token represents +     * @param line the line of code the token is on +     */      Token(TokenType type, String text, Object value,int line){          this.type=type;          this.text=text; @@ -17,10 +25,4 @@ public class Token {          this.line=line;      } - -    @Override -    public String toString() { -        return type + " " + text + " " + value; -    } -  } diff --git a/src/Compiler/TokenScanner.java b/src/Compiler/TokenScanner.java index 02bbbc0..ecb5ad3 100644 --- a/src/Compiler/TokenScanner.java +++ b/src/Compiler/TokenScanner.java @@ -5,6 +5,9 @@ import java.util.HashMap;  import java.util.List;  import java.util.Map; +/** + * The lexical scanner, which takes the source code and extracts a list of token objects + */  public class TokenScanner {      private String sourceCode;      List<Token> tokens = new ArrayList<>(); @@ -12,23 +15,33 @@ public class TokenScanner {      private int currentLoc=0;      private int line=0; -    //Extract tokens from the source code by reading character by character +    /** +     * Method for extracting tokens, checking each character at a time +     * @param sourceCode the original source code as a string +     * @return a list of tokens represeting the source code +     */      List<Token> extractTokens(String sourceCode){          this.sourceCode=sourceCode; +        //Read until the end of the source code as been reached          while (!checkEOF()){              tokenStart=currentLoc;              readToken();          } +        //Add a EOF token on the end          tokens.add(new Token(TokenType.EOF, "", null,line));          return tokens;      } -    //Extract a single token +    /** +     * Extract a single token +     */      private void readToken(){ +        //Get the current character and find the matching token          char checkChar = sourceCode.charAt(currentLoc);          switch(checkChar){              case ' ':break; +            //Advance line when line break found              case '\n':                  line++;                  break; @@ -45,7 +58,7 @@ public class TokenScanner {              case ';': createTokenNull(TokenType.SEMI_COLON); break;              case ',': createTokenNull(TokenType.COMMA); break; -            //Some tokens are multiple characters long (==, <=) etc +            //Some tokens are multiple characters long              //so need to check next char as well              case '=':                   if (checkNextChar('=')){ @@ -55,6 +68,7 @@ public class TokenScanner {                      createTokenNull(TokenType.EQUALS);                      break;                  } +              case ':':                   if (checkNextChar(':')){                      createTokenNull(TokenType.DEFINE); @@ -63,6 +77,7 @@ public class TokenScanner {                      createTokenNull(TokenType.COLON);                      break;                  } +              case '<':                   if (checkNextChar('=')){                      createTokenNull(TokenType.LESS_EQUAL); @@ -71,6 +86,7 @@ public class TokenScanner {                      createTokenNull(TokenType.LESS);                      break;                  } +              case '>':                   if (checkNextChar('=')){                      createTokenNull(TokenType.GREATER_EQUAL); @@ -79,6 +95,7 @@ public class TokenScanner {                      createTokenNull(TokenType.GREATER);                      break;                  } +              case '"':                  while(lookAhead()!='"' && !checkEOF()){                      currentLoc++; @@ -90,6 +107,7 @@ public class TokenScanner {                  currentLoc++;                  createToken(TokenType.STRING, sourceCode.substring(tokenStart, currentLoc+1));                  break; +              case '.':                  if(checkIsAlpha(lookAhead()))                      while (checkIsAlpha(lookAhead())){ @@ -112,9 +130,11 @@ public class TokenScanner {                      } else {                          Language.displayError(line, "Expected '.' after logical expression");                      } +             +            //Find tokens starting with alphanumeric characters              default: -                //Check for numer +                //Check for numeric characters                  if (checkIsDigit(checkChar)){                      String type = "int";                      while (checkIsDigit(lookAhead())){ @@ -134,19 +154,24 @@ public class TokenScanner {                          createToken(TokenType.NUMBER, Integer.parseInt(sourceCode.substring(tokenStart, currentLoc+1)));                      }                  } + +                //Check alphabetical character                  else if (checkIsAlpha(checkChar)){                      while (checkIsAlpha(lookAhead())){                          currentLoc++;                      }                       String text = sourceCode.substring(tokenStart, currentLoc+1); + +                    //Compare against a list of keywords in the language                      TokenType type = keywords.get(text);                      if(type == null){                          createToken(TokenType.IDENTIFIER, text);                      } else{                          createToken(type, text);                      } - +                                  } else { +                    //Report an unknown character                      Language.displayError(line,"Unexpected Character");                  }          } @@ -154,23 +179,37 @@ public class TokenScanner {      } -    //Test for end of file +    /** +     * Method to check the end of the source code +     * @return if the end of the source code has been reached +     */      private boolean checkEOF(){          return currentLoc>=sourceCode.length();      } -    //Create a token without a value +    /** +     * Create a token with a value of null +     * @param type the token type +     */      private void createTokenNull(TokenType type){          createToken(type, null);      } -    //Create token +    /** +     * Create a token and add to the list +     * @param type the token type +     * @param value the value of the token +     */      private void createToken(TokenType type, Object value){          String tokenText = sourceCode.substring(tokenStart, currentLoc+1);          tokens.add(new Token(type, tokenText, value, line));      } -    //Check if the next char matches a given char +    /** +     * Compare the next character in the source code to a given character +     * @param matchChar the character to compare against +     * @return if the character matches +     */      private boolean checkNextChar(char matchChar){          if (checkEOF()){              return false; @@ -182,7 +221,10 @@ public class TokenScanner {          return false;      } -    //Look at the next char in the source code +    /** +     * gets the next character in the source code +     * @return the next character +     */      private char lookAhead(){          if (currentLoc+1>=sourceCode.length()){              return ' '; @@ -192,7 +234,10 @@ public class TokenScanner {          }      } -    //Look 2 chars ahead in the source code +    /** +     * look at the character two ahead in the source code +     * @return the character two ahead +     */      private char lookTwoAhead(){          if (currentLoc+2>=sourceCode.length()){              return ' '; @@ -202,16 +247,26 @@ public class TokenScanner {          }      } -    //Check if a given char is a digit +    /** +     * checks if a given character is numerical +     * @param checkChar the character to check +     * @return if the character is numerical +     */      private boolean checkIsDigit(char checkChar){          return checkChar>='0' && checkChar<='9';      } +    /** +     * check if a character is alphabetical +     * @param checkChar the character to check +     * @return  if the character is alphabetical +     */      private boolean checkIsAlpha(char checkChar){          return ('a'<=checkChar && checkChar<='z')||                 ('A'<=checkChar && checkChar<='Z');      } +    //A hashmap of the keywords used in the language      private static final Map<String, TokenType> keywords;      static { @@ -232,5 +287,6 @@ public class TokenScanner {          keywords.put("program",    TokenType.PROGRAM);          keywords.put("return",    TokenType.RETURN);          keywords.put("function",    TokenType.FUNCTION); +        keywords.put("subroutine",    TokenType.SUBROUTINE);        }  } diff --git a/src/Compiler/TokenType.java b/src/Compiler/TokenType.java index 6a8f5c0..d3df02e 100644 --- a/src/Compiler/TokenType.java +++ b/src/Compiler/TokenType.java @@ -13,7 +13,7 @@ public enum TokenType {      NUMBER,IDENTIFIER,STRING,      INT,REAL,PRINT,ENDPRINT,IF,THEN,END,ELSE,LEN,DO,WHILE, -    AND,OR,NOT,DIMENSION,PROGRAM,FUNCTION,RETURN, +    AND,OR,NOT,DIMENSION,PROGRAM,FUNCTION,RETURN,SUBROUTINE,      EOF  } diff --git a/src/Compiler/Translator.java b/src/Compiler/Translator.java index e0bff23..d520b67 100644 --- a/src/Compiler/Translator.java +++ b/src/Compiler/Translator.java @@ -6,16 +6,25 @@ import java.util.List;  import Compiler.Expression.*;  import Compiler.Statement.*; - +/** + * Class to take a convert a list of statements into equivalent C source code + */  public class Translator{      List<String> CCode = new ArrayList<>();      private Environment environment = new Environment(); - +    /** +     * Method to take a list of statements and convert to C code +     * @param statements a list of statement objects +     * @param printC a variable to say if the produced C code should be outputted +     * @return a list of strings for each line of the produced C code +     */      public List<String> compileToC(List<Statement> statements, boolean printC){ +        //Write basic include header files          CCode.add("#include <stdio.h>");          CCode.add("#include <string.h>"); +        //Try and write each statement, with a space between each          try{              for (Statement statement: statements){                  evaluateStatement(statement); @@ -25,6 +34,7 @@ public class Translator{          } +        //Output the C code if desired          if (printC) {              for(String t:CCode){                  System.out.println(t); @@ -34,7 +44,12 @@ public class Translator{          return CCode;       } +    /** +     * Method to write a single statement to C +     * @param statement the statement to write to C +     */      private void evaluateStatement(Statement statement){ +        //Call the correct function for each statement type          switch(statement.getStatmentType()){              case "main":                  evalMainFunction((MainFunction)statement); @@ -78,40 +93,76 @@ public class Translator{          }      }  +    /** +     * Method to write the main function +     * @param stmt statement to write +     */      private void evalMainFunction(MainFunction stmt){          CCode.add("int main(){");          evaluateStatement(stmt.block);          CCode.add("}");      } +    /** +     * Method to write a function +     * @param stmt statement to write +     */      private void evalFunction(Function stmt){ - -        String functionString = stmt.returnType+" "+stmt.name.text+"("; +        String functionString; +        if(!(stmt.returnType==null)){ +            functionString = stmt.returnType+" "+stmt.name.text+"("; +        }else{ +            functionString = "void "+stmt.name.text+"("; +        }          boolean first=true; +        //Write each function argument into C          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)); +            String type = stmt.argumentTypes.get(i); +            Expression arg = stmt.arguments.get(i); + +            //Define each argument variable in the environment +            if(type.equals("char*")){ +                environment.defineVariable(evaluateExpression(arg), "string"); +            }else{ +                environment.defineVariable(evaluateExpression(arg), type); +            } + +            functionString+=type+" "+evaluateExpression(arg);              first=false;          }          functionString+="){"; + +        //Write function block          CCode.add(functionString);          evaluateStatement(stmt.block);          CCode.add("}");      } +    /** +     * Method to write a return statement +     * @param stmt statement to write +     */      private void evalReturnStatement(ReturnStatement stmt){          CCode.add("return "+evaluateExpression(stmt.returnValue)+";"); - -              } +    /** +     * Method to write a function declaration +     * @param stmt statement to write +     */      private void evalFunctionDeclaration(FunctionDeclaration stmt){ - -        String functionString = stmt.returnType+" "+stmt.name.text+"("; +        String functionString; +        if(!(stmt.returnType==null)){ +            functionString = stmt.returnType+" "+stmt.name.text+"("; +        }else{ +            functionString = "void "+stmt.name.text+"("; +        }          boolean first=true; + +        //Write each argument data type          for(int i=0;i<stmt.argumentTypes.size();i++){              if(!first){                  functionString+=","; @@ -120,22 +171,42 @@ public class Translator{              first=false;          }          functionString+=");"; +          CCode.add(functionString);      } +    /** +     * Method to write an expression statement +     * @param stmt statement to write +     */      private void evalExpressionStatement(ExpressionStatement stmt){ -        evaluateExpression(stmt.expr); +        String value = evaluateExpression(stmt.expr); +        CCode.add(value+";");      } +    /** +     * Method to write a string declaration statement +     * @param stringdec statement to write +     */      private void evalStringDeclaration(StringDeclaration stringdec){ +        //Add variable to the environment          environment.defineVariable(stringdec.name.text, "string"); + +        //Get the size of the string          int size = (int)((Expression.Literal)stringdec.length).value.value; +        //Increase the size of the string by 1 to match C code correctly          size++;          CCode.add("char "+stringdec.name.text+"["+size+"];");      } +    /** +     * Method to write a variable declaration +     * @param vardec statement to write +     */      private void evalVariableDeclaration(VariableDeclaration vardec){ +        //Add variable to the environment          environment.defineVariable(vardec.name.text, vardec.type); +        //Write correct data type          if(vardec.type.equals("int")){              CCode.add("int "+vardec.name.text+";");          } else if(vardec.type.equals("real")){ @@ -143,8 +214,15 @@ public class Translator{          }       } +    /** +     * Method to write an array declaration +     * @param arraydec statement to write +     */      private void evalArrayDeclaration(ArrayDeclaration arraydec){ +        //Add variable to the environment          environment.defineVariable(arraydec.name.text, arraydec.type); + +        //Get the array data type          String arrayString = "";          if(arraydec.type.equals("int")){              arrayString+="int "; @@ -152,6 +230,8 @@ public class Translator{              arrayString+="real ";          }           arrayString+=arraydec.name.text; + +        //Write each dimension of the array          for(Expression expr:arraydec.dimensions){              arrayString+="[";              arrayString+=evaluateExpression(expr); @@ -163,23 +243,36 @@ public class Translator{      } +    /** +     * Method to write a block statement +     * @param block statement to write +     */      private void evalBlockStatement(BlockStatement block){ +        //Write each statement in the block          for(Statement stmt:block.statements){              evaluateStatement(stmt);          }      } +    /** +     * Method to write a print statement +     * @param print statement to write +     */      private void evalPrintStatement(PrintStatement print){          String types="";          String values="";          boolean first=true; + +        //Write each expression in the print statement          for(Expression expr:print.exprList){ +            //Don't add a comma for the first value in the print statement              if(!first){                  values+=",";              }else{                  first=false;              }              String exprType=""; +            //Get the data type for each expression              if(expr instanceof Expression.Literal){                  exprType=((Expression.Literal)expr).type;              } @@ -189,6 +282,8 @@ public class Translator{              else if (expr instanceof Expression.ArrayVariable){                  exprType=(String)environment.getVariable((((Expression.ArrayVariable)expr).name));              } + +            //Set the correct data type for the print statement              if (exprType.equals("int")){                  types+="%d";              } else if (exprType.equals("double")){ @@ -196,16 +291,24 @@ public class Translator{              } else if (exprType.equals("string")){                  types+="%s";              } +            //Add the actual expression value              values+=evaluateExpression(expr);          } +        //Add a line break at the end of the print statement text          types+="\\n";          CCode.add("printf(\""+types+"\","+values+");");        } +    /** +     * Method to write an if statement +     * @param ifstatement statement to write +     */      private void evalIfStatement(IfStatement ifstatement){ -         +        //Write if statement block          CCode.add("if("+evaluateExpression(ifstatement.condition)+"){");          evaluateStatement(ifstatement.ifBlock); +         +        //Write else block if needed          if(!(ifstatement.elseBlock==null)){              CCode.add("}");              CCode.add("else {"); @@ -214,24 +317,43 @@ public class Translator{          CCode.add("}");      } +    /** +     * Method to write a do statement +     * @param dostatement statement to write +     */      private void evalDoStatement(DoStatement dostatement){ +        //Form the sections of the for loop, assuming the step variable has not been defined          String start = evaluateExpression(dostatement.variable)+"="+evaluateExpression(dostatement.start);          String stop = evaluateExpression(dostatement.variable)+"<="+evaluateExpression(dostatement.stop);          String step = evaluateExpression(dostatement.variable)+"++"; + +        //Update step variable if needed          if(!(dostatement.step==null)){              step = evaluateExpression(dostatement.variable)+"+="+evaluateExpression(dostatement.step);          } +              CCode.add("for("+start+";"+stop+";"+step+"){");          evaluateStatement(dostatement.codeBlock);          CCode.add("}");      } +    /** +     * Method to write a do while loop +     * @param dowhilestatement statement to write +     */      private void evalDoWhileStatement(DoWhileStatement dowhilestatement){          CCode.add("while("+evaluateExpression(dowhilestatement.condition)+"){");          evaluateStatement(dowhilestatement.codeBlock);          CCode.add("}");      } +    /** +     * Method to write the correct expression object to C +     * Since an expression is not a full line, the string is returned +     * Except for assignment expressions +     * @param expression the expression to write +     * @return the string representation of an expression +     */      private String evaluateExpression(Expression expression){          switch(expression.getExpressionType()){              case "binary": @@ -243,6 +365,7 @@ public class Translator{              case "bracket":                  return evaluateBracketedExpression((BracketedExpression)expression);              case "assign": +                //Assignments are full lines, so no need to return the string                  evaluateAssignmentExpression((AssignmentExpression)expression);                  return "";              case "arrayvar": @@ -256,6 +379,11 @@ public class Translator{          }      } +    /** +     * Method to write a binary expression +     * @param expr the expression to write +     * @return the string representation of the expression +     */      private String evaluateBinaryExpression(Binary expr){          switch (expr.op.type){              case PLUS: @@ -288,6 +416,11 @@ public class Translator{          return null;      } +    /** +     * Method to write a singular expression +     * @param expr the expression to write +     * @return the string representation of the expression +     */      private String evaluateSingularExpression(Singular expr){          switch (expr.op.type){              case NOT: @@ -298,24 +431,42 @@ public class Translator{          return null;      } +    /** +     * Method to write a literal expression +     * @param expr the expression to write +     * @return the string representation of the expression +     */      private String evaluateLiteralExpression(Literal expr){          return (expr.value.value).toString();      } +    /** +     * Method to write a bracketed expression +     * @param expr the expression to write +     * @return the string representation of the expression +     */      private String evaluateBracketedExpression(BracketedExpression expr){          return "("+evaluateExpression(expr.expr)+")";      } +    /** +     * Method to write an assignment expression +     * @param expr the expression to write +     */      private void evaluateAssignmentExpression(AssignmentExpression expr){          Token name=null; +        //Get the name of the variable being assigned to          if(expr.variable instanceof Expression.Variable){              name = ((Expression.Variable)expr.variable).name;          }          else if(expr.variable instanceof Expression.ArrayVariable){              name = ((Expression.ArrayVariable)expr.variable).name;          } +        //Check if the variable has been previously declared          if(environment.checkVariable(name)){ +            //Check if the value being assigned is a literal or some other expression              if(expr.value instanceof Expression.Literal){ +                //Strings are handled differently in C, so the string has to be copied into the variable                  if(((Expression.Literal)expr.value).type.equals("string")){                      CCode.add("strcpy("+evaluateExpression(expr.variable)+","+evaluateExpression(expr.value)+");");                  }else{ @@ -328,10 +479,17 @@ public class Translator{          }      } +    /** +     * Method to write an array variable +     * @param expr the expression to write +     * @return the string representation of the expression +     */      private String evaluateArrayVariable(ArrayVariable expr){ +        //Check the array has been defined          if(environment.checkVariable(expr.name)){              String arrayString="";              arrayString+=expr.name.text; +            //Write each dimension value              for(Expression position:expr.positions){                  arrayString+="[";                  arrayString+=evaluateExpression(position); @@ -342,14 +500,26 @@ public class Translator{          return null;      } +    /** +     * Method to write a variable expression +     * @param expr the expression to write +     * @return the string representation of the expression +     */      private String evaluateVariableExpression(Variable expr){          return expr.name.text;      } +    /** +     * Method to write a function call +     * @param expr the expression to write +     * @return the string representation of the expression +     */      private String evaluateFunctionCall(FunctionCall expr){          String functioncall="";          functioncall+=expr.name.text+"(";          boolean first=true; + +        //Write each argument of the function call          for(Token arg:expr.arguments){              if(!first){                  functioncall+=","; @@ -360,9 +530,6 @@ public class Translator{          functioncall+=")";          return functioncall;      } - - -  } | 
